From f0d85a71251fe92506da0b39b7bc1728e7b1cbb8 Mon Sep 17 00:00:00 2001 From: Joubert RedRat Date: Mon, 11 Sep 2023 23:02:31 +0100 Subject: [PATCH 01/95] small refactor on return in function --- Strings/CheckAnagram.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Strings/CheckAnagram.php b/Strings/CheckAnagram.php index d36a17dc..ce4b558c 100644 --- a/Strings/CheckAnagram.php +++ b/Strings/CheckAnagram.php @@ -18,12 +18,5 @@ function isAnagram(string $originalString, string $testString, bool $caseInsensi // count_chars(string, mode = 1) returns key-value pairs with character as key, frequency as value // We can directly compare the arrays in this case - if (count_chars($originalString, 1) == count_chars($testString, 1)) - { - return true; - } - else - { - return false; - } + return count_chars($originalString, 1) === count_chars($testString, 1); } From e06139805a750d9581db7b1e825d3865d346377a Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Sat, 16 Sep 2023 16:31:10 +0330 Subject: [PATCH 02/95] add heap sort to sort algorithms --- Sorting/HeapSort.php | 62 ++++++++++++++++++++++++++++++++++ tests/Sorting/SortingTests.php | 26 ++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 Sorting/HeapSort.php diff --git a/Sorting/HeapSort.php b/Sorting/HeapSort.php new file mode 100644 index 00000000..308b2201 --- /dev/null +++ b/Sorting/HeapSort.php @@ -0,0 +1,62 @@ += 0; $i--) { + heapify($arr, $n, $i); + } + + // Extract elements from heap one by one + for ($i = $n - 1; $i >= 0; $i--) { + // Swap + [$arr[0], $arr[$i]] = [$arr[$i], $arr[0]]; + + // Heapify the reduced heap + heapify($arr, $i, 0); + } + + return $arr; +} + +/** + * Ensures that the array satisfies the heap property + * + * @param array $arr + * @param int $n + * @param int $i + */ +function heapify(array &$arr, int $n, int $i): void +{ + $largest = $i; + $left = 2 * $i + 1; + $right = 2 * $i + 2; + + if ($left < $n && $arr[$left] > $arr[$largest]) { + $largest = $left; + } + + if ($right < $n && $arr[$right] > $arr[$largest]) { + $largest = $right; + } + + if ($largest !== $i) { + [$arr[$i], $arr[$largest]] = [$arr[$largest], $arr[$i]]; + heapify($arr, $n, $largest); + } +} \ No newline at end of file diff --git a/tests/Sorting/SortingTests.php b/tests/Sorting/SortingTests.php index 16d0e09e..86ca32fb 100644 --- a/tests/Sorting/SortingTests.php +++ b/tests/Sorting/SortingTests.php @@ -6,6 +6,7 @@ require_once __DIR__ . '/../../Sorting/BubbleSort.php'; require_once __DIR__ . '/../../Sorting/BubbleSort2.php'; require_once __DIR__ . '/../../Sorting/CountSort.php'; +require_once __DIR__ . '/../../Sorting/HeapSort.php'; require_once __DIR__ . '/../../Sorting/InsertionSort.php'; require_once __DIR__ . '/../../Sorting/MergeSort.php'; require_once __DIR__ . '/../../Sorting/QuickSort.php'; @@ -172,4 +173,29 @@ public function testQuickSortCipher() $this->assertEquals($result1, $test1); $this->assertEquals($result2, $test2); } + + public function testHeapSortPerformance() + { + $array = range(1, 1000000); + shuffle($array); // Randomize the order + $start = microtime(true); + heapSort($array); + $end = microtime(true); + $this->assertLessThan(1, $end - $start); + } + + public function testHeapSortCipher() + { + $firstArray = [20, 16, -5, -8, 6, 12, 2, 4, -3, 9]; + $expectedResultOne = [-8, -5, -3, 2, 4, 6, 9, 12, 16, 20]; + + $secondArray = [-6, 12, 14, 17, 5, 4, -9, 15, 0, -8]; + $expectedResultTwo = [-9, -8, -6, 0, 4, 5, 12, 14, 15, 17]; + + $resultOne = heapSort($firstArray); + $resultTwo = heapSort($secondArray); + + $this->assertEquals($expectedResultOne, $resultOne); + $this->assertEquals($expectedResultTwo, $resultTwo); + } } From 50f436eb1793aca6380e19af88038e5cdf01d56d Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Sat, 16 Sep 2023 16:45:18 +0330 Subject: [PATCH 03/95] fix mode method to return multiple modes if needed --- Maths/Mode.php | 15 ++++++++------- tests/Maths/MathsTest.php | 8 +++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Maths/Mode.php b/Maths/Mode.php index d9c90c4d..38b6f044 100644 --- a/Maths/Mode.php +++ b/Maths/Mode.php @@ -1,20 +1,21 @@ assertEquals([3], mode(1, 2, 3, 3, 4, 5)); + $this->assertEquals([5, 6], mode(5, 5, 6, 6, 7)); + $this->assertEquals([1, 2, 3, 4, 5], mode(1, 2, 3, 4, 5)); + $this->assertEquals([2, 3, 4], mode(2, 2, 3, 3, 4, 4)); } + } From 62dc243c2ebd543bc038fd44f767148c2862b7f5 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 18 Sep 2023 04:48:52 +0000 Subject: [PATCH 04/95] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index d839f532..2ee4e28e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -65,6 +65,7 @@ * [Bubblesort2](./Sorting/BubbleSort2.php) * [Countsort](./Sorting/CountSort.php) * [Gnomesort](./Sorting/GnomeSort.php) + * [Heapsort](./Sorting/HeapSort.php) * [Insertionsort](./Sorting/InsertionSort.php) * [Mergesort](./Sorting/MergeSort.php) * [Quicksort](./Sorting/QuickSort.php) From 9dab8744ddf00518f9866f2d5e51ed6534b78c4b Mon Sep 17 00:00:00 2001 From: Neto Costa Date: Mon, 2 Oct 2023 21:12:05 -0300 Subject: [PATCH 05/95] fix: fix empty input validation on quicksort --- Sorting/QuickSort.php | 2 +- tests/Sorting/SortingTests.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sorting/QuickSort.php b/Sorting/QuickSort.php index 5c00e146..da35617b 100644 --- a/Sorting/QuickSort.php +++ b/Sorting/QuickSort.php @@ -9,7 +9,7 @@ function quickSort(array $input) { // Return nothing if input is empty - if(!isset($input)) + if(!empty($input)) { return []; } diff --git a/tests/Sorting/SortingTests.php b/tests/Sorting/SortingTests.php index 86ca32fb..f417e28a 100644 --- a/tests/Sorting/SortingTests.php +++ b/tests/Sorting/SortingTests.php @@ -59,6 +59,13 @@ public function testQuickSort() $this->assertEquals([272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711], $sorted); } + public function testQuickSortWithEmptyInput() + { + $array = []; + $sorted = quickSort($array); + $this->assertEmpty($sorted); + } + public function testRadixSort() { $array = [1, 5, 4, 2, 3]; From ca17a0db74d485c0420849d97acb21cf554bc33e Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:10:13 +0330 Subject: [PATCH 06/95] add cs checking to ci flow --- .github/workflows/ci.yml | 40 ++++++++++++++++---------------- .github/workflows/code-style.yml | 25 ++++++++++++++++++++ .gitignore | 3 +++ composer.json | 3 ++- phpcs.xml.dist | 9 +++++++ 5 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/code-style.yml create mode 100644 phpcs.xml.dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 014f7539..076bab24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,23 +12,23 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- - - - name: Install dependencies - if: steps.composer-cache.outputs.cache-hit != 'true' - run: composer install --prefer-dist --no-progress --no-suggest - - - name: Run PHPUnit - run: composer run-script test + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run PHPUnit + run: composer run-script test \ No newline at end of file diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 00000000..16ab9a56 --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,25 @@ +name: Code style + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + phpcs: + name: PHPCS + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + + - name: Install dependencies + run: composer update --prefer-dist --no-progress --no-suggest + + - name: Run script + run: vendor/bin/phpcs \ No newline at end of file diff --git a/.gitignore b/.gitignore index 96ccab44..8e409f4d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ .phan composer.lock +/.phpcs-cache +/phpcs.xml + .phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index d164a223..e3ca6e68 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "phan/phan": "^2.7" }, "require-dev": { - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.7" }, "scripts": { "test": "vendor/bin/phpunit tests" diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..15405496 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,9 @@ + + + + + + + . + From d7d34e357e36b538d0a26786f2cc38bfc52f7028 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:12:26 +0330 Subject: [PATCH 07/95] excluding namespace errors from cs check --- phpcs.xml.dist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 15405496..31069565 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -6,4 +6,8 @@ . + + + 0 + From 9c2726203cad27fd7834287bb9789126d3d7cd55 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 21:36:11 +0330 Subject: [PATCH 08/95] fix cs errors to follow the psr12 standard and also some minor fixes --- Ciphers/CaesarCipher.php | 8 +- Ciphers/MonoAlphabeticCipher.php | 66 ++++++++------- Ciphers/MorseCode.php | 8 +- Ciphers/XORCipher.php | 6 +- Conversions/BinaryToDecimal.php | 3 +- Conversions/DecimalToBinary.php | 3 +- Conversions/OctalToDecimal.php | 6 +- Conversions/SpeedConversion.php | 17 ++-- DataStructures/SinglyLinkedList.php | 6 +- Graphs/BreadthFirstSearch.php | 13 +-- Graphs/DepthFirstSearch.php | 17 ++-- Maths/AbsoluteMax.php | 4 +- Maths/AbsoluteMin.php | 4 +- Maths/ArmstrongNumber.php | 23 +++-- Maths/CheckPalindrome.php | 19 ++--- Maths/CheckPrime.php | 1 + Maths/Factorial.php | 16 ++-- Maths/FastExponentiation.php | 21 ++--- Maths/Fibonacci.php | 12 ++- Maths/Fibonacci2.php | 13 ++- Maths/Mean.php | 13 +-- Maths/Median.php | 13 +-- Maths/Mode.php | 4 +- Maths/NeonNumber.php | 16 ++-- Maths/PerfectSquare.php | 1 + Maths/ProjectEuler/Problem1.php | 4 +- Maths/ProjectEuler/Problem10.php | 12 +-- Maths/ProjectEuler/Problem11.php | 2 - Maths/ProjectEuler/Problem4.php | 9 +- Maths/ProjectEuler/Problem6.php | 4 +- Maths/ProjectEuler/Problem8.php | 6 +- Searches/BinarySearch.php | 9 +- Searches/ExponentialSearch.php | 78 +++++++++-------- Searches/FibonacciSearch.php | 9 +- Searches/InterpolationSearch.php | 73 ++++++++-------- Searches/JumpSearch.php | 54 ++++++------ Searches/LinearSearch.php | 11 +-- Searches/LowerBound.php | 21 ++--- Searches/TernarySearch.php | 97 +++++++++++----------- Searches/UpperBound.php | 19 +++-- Sorting/ArrayKeysSort.php | 71 +++++++--------- Sorting/BubbleSort.php | 9 +- Sorting/BubbleSort2.php | 17 ++-- Sorting/CountSort.php | 11 +-- Sorting/GnomeSort.php | 19 ++--- Sorting/HeapSort.php | 9 +- Sorting/InsertionSort.php | 10 +-- Sorting/MergeSort.php | 11 +-- Sorting/QuickSort.php | 12 +-- Sorting/RadixSort.php | 40 +++++---- Sorting/SelectionSort.php | 12 +-- Strings/CheckAnagram.php | 3 +- Strings/CheckPalindrome.php | 12 +-- Strings/CheckPalindrome2.php | 17 ++-- Strings/CountConsonants.php | 15 ++-- Strings/CountVowels.php | 31 +++---- Strings/Distance.php | 5 +- Strings/MaxCharacter.php | 15 ++-- Strings/ReverseWords.php | 5 +- Utils/ArrayHelpers.php | 17 ++-- composer.json | 4 +- phpcs.xml.dist | 2 + tests/Ciphers/CiphersTest.php | 5 +- tests/Ciphers/MonoAlphabeticCipherTest.php | 44 +++++----- tests/Ciphers/MorseCodeTest.php | 1 + tests/Conversions/ConversionsTest.php | 1 + tests/Graphs/BreadthFirstSearchTest.php | 1 - tests/Maths/MathsTest.php | 10 +-- tests/Maths/ProjectEulerTest.php | 2 +- tests/Searches/SearchesTest.php | 3 +- tests/Sorting/ArrayKeysSortTest.php | 11 +-- tests/Strings/StringsTest.php | 1 + 72 files changed, 573 insertions(+), 574 deletions(-) diff --git a/Ciphers/CaesarCipher.php b/Ciphers/CaesarCipher.php index 3af47ffe..18182f3d 100755 --- a/Ciphers/CaesarCipher.php +++ b/Ciphers/CaesarCipher.php @@ -3,8 +3,8 @@ /** * Encrypt given text using caesar cipher. * - * @param string text text to be encrypted - * @param int shift number of shifts to be applied + * @param string $text text text to be encrypted + * @param int $shift shift number of shifts to be applied * @return string new encrypted text */ function encrypt(string $text, int $shift): string @@ -27,8 +27,8 @@ function encrypt(string $text, int $shift): string /** * Decrypt given text using caesar cipher. - * @param string text text to be decrypted - * @param int shift number of shifts to be applied + * @param string $text text text to be decrypted + * @param int $shift shift number of shifts to be applied * @return string new decrypted text */ function decrypt(string $text, int $shift): string diff --git a/Ciphers/MonoAlphabeticCipher.php b/Ciphers/MonoAlphabeticCipher.php index 0810a4a5..6b362dbe 100644 --- a/Ciphers/MonoAlphabeticCipher.php +++ b/Ciphers/MonoAlphabeticCipher.php @@ -1,32 +1,34 @@ - \ No newline at end of file + 1 knot which is equal to 1 nautical mile (1852 km/h) * The conversion is made using kilometers as base * - * @param float $speed - * @param string $unitFrom - * @param string $unitTo - * @return int + * @param float $speed + * @param string $unitFrom + * @param string $unitTo + * @return float + * @throws \Exception */ function convertSpeed(float $speed, string $unitFrom, string $unitTo) { $speedUnitsFrom = [ 'mph' => 1.609344, 'km/h' => 1, - 'm/s'=> 3.6, - 'ft/s'=> 1.097, + 'm/s' => 3.6, + 'ft/s' => 1.097, 'kn' => 1.852, ]; $speedUnitsTo = [ 'mph' => 0.6213712, 'km/h' => 1, - 'm/s'=> 0.277778, - 'ft/s'=> 0.911344, + 'm/s' => 0.277778, + 'ft/s' => 0.911344, 'kn' => 0.539957, ]; $availableUnits = array_keys($speedUnitsFrom); diff --git a/DataStructures/SinglyLinkedList.php b/DataStructures/SinglyLinkedList.php index 44621d60..7256ed7d 100644 --- a/DataStructures/SinglyLinkedList.php +++ b/DataStructures/SinglyLinkedList.php @@ -1,13 +1,12 @@ data = $data; @@ -16,7 +15,6 @@ public function __construct($data) public function append($data): void { $current = $this; - while ($current instanceof SinglyLinkedList && isset($current->next)) { $current = $current->next; } @@ -27,7 +25,6 @@ public function append($data): void public function delete($data): SinglyLinkedList { $current = $this; - if ($current->data == $data) { return $current->next; } @@ -35,7 +32,6 @@ public function delete($data): SinglyLinkedList while ($current instanceof SinglyLinkedList && isset($current->next)) { if ($current->next->data === $data) { $current->next = $current->next->next; - return $this; } diff --git a/Graphs/BreadthFirstSearch.php b/Graphs/BreadthFirstSearch.php index 44110114..68579e10 100644 --- a/Graphs/BreadthFirstSearch.php +++ b/Graphs/BreadthFirstSearch.php @@ -3,21 +3,22 @@ /** * Breadth-first search (BFS) is an algorithm for searching a tree data structure for a node that satisfies a given property. * (https://en.wikipedia.org/wiki/Breadth-first_search). - * - * This is a non recursive implementation. - * + * + * This is a non-recursive implementation. + * * References: * https://cp-algorithms.com/graph/breadth-first-search.html - * + * * https://the-algorithms.com/algorithm/depth-first-search?lang=python - * + * * @author Aryansh Bhargavan https://github.com/aryanshb * @param array $adjList An array representing the grapth as an Adjacent List * @param int|string $start The starting vertex * @return bool if path between start and end vertex exists */ -function bfs($adjList, $start, $end, $yes = false){ +function bfs($adjList, $start, $end, $yes = false) +{ $visited = []; $queue = [$start]; while (!empty($queue)) { diff --git a/Graphs/DepthFirstSearch.php b/Graphs/DepthFirstSearch.php index 936484ce..3a8dea51 100644 --- a/Graphs/DepthFirstSearch.php +++ b/Graphs/DepthFirstSearch.php @@ -1,20 +1,20 @@ diff --git a/Maths/CheckPalindrome.php b/Maths/CheckPalindrome.php index 574e3407..d76b6da8 100644 --- a/Maths/CheckPalindrome.php +++ b/Maths/CheckPalindrome.php @@ -1,4 +1,5 @@ diff --git a/Maths/CheckPrime.php b/Maths/CheckPrime.php index 64630435..f9d14522 100644 --- a/Maths/CheckPrime.php +++ b/Maths/CheckPrime.php @@ -1,4 +1,5 @@ 0 && $set->valid()) - { + while ($i-- > 0 && $set->valid()) { yield $set->current(); $set->next(); } @@ -25,8 +24,7 @@ function fib() yield $i = 0; yield $j = 1; - while(true) - { + while (true) { yield $k = $i + $j; $i = $j; $j = $k; @@ -36,7 +34,6 @@ function fib() /* * Generate 100 Fibonacci numbers */ -foreach(loop(100, fib()) as $item) -{ - print($item.','); -} \ No newline at end of file +foreach (loop(100, fib()) as $item) { + print($item . ','); +} diff --git a/Maths/Mean.php b/Maths/Mean.php index 52f01202..b24b4e01 100644 --- a/Maths/Mean.php +++ b/Maths/Mean.php @@ -1,11 +1,13 @@ diff --git a/Maths/PerfectSquare.php b/Maths/PerfectSquare.php index 7bca0445..2b6fb747 100644 --- a/Maths/PerfectSquare.php +++ b/Maths/PerfectSquare.php @@ -1,4 +1,5 @@ $largest) { + if (strrev((string)$product) == (string)$product && $product > $largest) { $largest = $product; } } diff --git a/Maths/ProjectEuler/Problem6.php b/Maths/ProjectEuler/Problem6.php index 15a91ecb..35fd8e5c 100644 --- a/Maths/ProjectEuler/Problem6.php +++ b/Maths/ProjectEuler/Problem6.php @@ -5,10 +5,10 @@ * * Problem description: * The sum of the squares of the first ten natural numbers is, - * 1 ** 2 + 2 ** 2 + ... + 10 ** 2 = 385 + * 1 ** 2 + 2 ** 2 + ... + 10 ** 2 = 385 * * The square of the sum of the first ten natural numbers is, - * (1 + 2 + ... + 10) ** 2 = 3025 + * (1 + 2 + ... + 10) ** 2 = 3025 * * Hence the difference between the sum of the squares of the * first ten natural numbers and the square of the sum is . diff --git a/Maths/ProjectEuler/Problem8.php b/Maths/ProjectEuler/Problem8.php index 737254f1..aa0a215f 100644 --- a/Maths/ProjectEuler/Problem8.php +++ b/Maths/ProjectEuler/Problem8.php @@ -59,11 +59,11 @@ function problem8(): int $substringSize = 13; for ($i = 0; $i < strlen($theNumber) - $substringSize; $i++) { - $currentSubstring = substr($theNumber,$i,$substringSize); + $currentSubstring = substr($theNumber, $i, $substringSize); $currentProduct = 0; - for ($j = 0; $j < strlen ($currentSubstring); $j++) { - $currentProduct = ($currentProduct == 0 ? (int)$currentSubstring[$j] : $currentProduct * (int)$currentSubstring[$j]); + for ($j = 0; $j < strlen($currentSubstring); $j++) { + $currentProduct = ($currentProduct == 0 ? (int)$currentSubstring[$j] : $currentProduct * (int)$currentSubstring[$j]); } $greatestProduct = ($greatestProduct < $currentProduct ? $currentProduct : $greatestProduct); diff --git a/Searches/BinarySearch.php b/Searches/BinarySearch.php index e3b1e310..10789307 100644 --- a/Searches/BinarySearch.php +++ b/Searches/BinarySearch.php @@ -21,7 +21,7 @@ function binarySearchIterative($list, $target) { $first = 0; - $last = count($list)-1; + $last = count($list) - 1; while ($first <= $last) { @@ -70,8 +70,9 @@ function binarySearchByRecursion($list, $target, $start, $end) return $list[0] == $target ? 0 : null; } - if ($start > $end) + if ($start > $end) { return null; + } $mid = ($start + $end) >> 1; @@ -80,9 +81,9 @@ function binarySearchByRecursion($list, $target, $start, $end) if ($list[$mid] == $target) { return $mid; } elseif ($list[$mid] > $target) { - return binarySearchByRecursion($list, $target, $start, $mid-1); + return binarySearchByRecursion($list, $target, $start, $mid - 1); } elseif ($list[$mid] < $target) { - return binarySearchByRecursion($list, $target, $mid+1, $end); + return binarySearchByRecursion($list, $target, $mid + 1, $end); } return null; diff --git a/Searches/ExponentialSearch.php b/Searches/ExponentialSearch.php index 7073053d..f034c1f8 100644 --- a/Searches/ExponentialSearch.php +++ b/Searches/ExponentialSearch.php @@ -1,9 +1,10 @@ $ceiling) return -1; + if ($floor > $ceiling) { + return -1; + } // search the left part of the $array - // If the $middle element is great than the $value - if ($arr[$mid] > $value) { - return binarySearch($arr, $value, $floor, $mid - 1); - } + // If the $middle element is greater than the $value + if ($arr[$mid] > $value) { + return binarySearch($arr, $value, $floor, $mid - 1); + } // search the right part of the $array // If the $middle element is lower than the $value - else { - return binarySearch($arr, $value, $mid + 1, $ceiling); - } + else { + return binarySearch($arr, $value, $mid + 1, $ceiling); + } } - /** - * @param Array $arr - * @param int $length - * @param int $value - * @return int - **/ -function exponentialSearch ($arr, $value) { + +/** + * @param Array $arr + * @param int $value + * @return int + */ +function exponentialSearch($arr, $value) +{ + // If $value is the first element of the $array return this position - if ($arr[0] === $value) { - return 0; - } + if ($arr[0] === $value) { + return 0; + } // Find range for binary search - $i = 1; - $length = count($arr); - while ($i < $length && $arr[$i] <= $value) { - $i = $i * 2; - } - $floor = $i/2; - $ceiling = min($i, $length); - - // Call binary search for the range found above - return binarySearch($arr, $value, $floor, $ceiling); -} \ No newline at end of file + $i = 1; + $length = count($arr); + while ($i < $length && $arr[$i] <= $value) { + $i = $i * 2; + } + $floor = $i / 2; + $ceiling = min($i, $length); +// Call binary search for the range found above + return binarySearch($arr, $value, $floor, $ceiling); +} diff --git a/Searches/FibonacciSearch.php b/Searches/FibonacciSearch.php index 7e04a4b9..b103785a 100644 --- a/Searches/FibonacciSearch.php +++ b/Searches/FibonacciSearch.php @@ -11,13 +11,16 @@ */ function fibonacciPosition(int $n, array &$m = []) { - if(isset($m[$n])) return $m[$n]; - if($n < 2) return $n; + if (isset($m[$n])) { + return $m[$n]; + } + if ($n < 2) { + return $n; + } $m[$n] = fibonacciPosition($n - 1, $m) + fibonacciPosition($n - 2, $m); return $m[$n]; - } print fibonacciPosition(59); diff --git a/Searches/InterpolationSearch.php b/Searches/InterpolationSearch.php index 4e27c7dc..bb73d92d 100644 --- a/Searches/InterpolationSearch.php +++ b/Searches/InterpolationSearch.php @@ -1,12 +1,13 @@ key decrease the high index * repeat the loop */ -function interpolationSearch($arr, $key) { - $length = count($arr) - 1; - $low = 0; - $high = $length; - $position = -1; - //loop, between low & high - while ($low <= $high && $key >= $arr[$low] && $key <= $arr[$high]) { - //GET INDEX - $delta = ($key - $arr[$low]) / ($arr[$high] - $arr[$low]); - $index = $low + floor(($high - $low) * $delta); - //GET VALUE OF INDEX IN ARRAY... - $indexValue = $arr[$index]; - if ($indexValue === $key) { - //index value equals key - //FOUND TARGET - //return index value - $position = $index; - return (int) $position; +function interpolationSearch($arr, $key) +{ + + $length = count($arr) - 1; + $low = 0; + $high = $length; + $position = -1; +//loop, between low & high + while ($low <= $high && $key >= $arr[$low] && $key <= $arr[$high]) { +//GET INDEX + $delta = ($key - $arr[$low]) / ($arr[$high] - $arr[$low]); + $index = $low + floor(($high - $low) * $delta); +//GET VALUE OF INDEX IN ARRAY... + $indexValue = $arr[$index]; + if ($indexValue === $key) { + //index value equals key + //FOUND TARGET + //return index value + $position = $index; + return (int) $position; + } + if ($indexValue < $key) { + //index value lower than key + //increase low index + $low = $index + 1; + } + if ($indexValue > $key) { + //index value higher than key + //decrease high index + $high = $index - 1; + } } - if ($indexValue < $key) { - //index value lower than key - //increase low index - $low = $index + 1; - } - if ($indexValue > $key) { - //index value higher than key - //decrease high index - $high = $index - 1; - } - } //when key not found in array or array not sorted - return null; -} \ No newline at end of file + return null; +} diff --git a/Searches/JumpSearch.php b/Searches/JumpSearch.php index adda458b..7be70de6 100644 --- a/Searches/JumpSearch.php +++ b/Searches/JumpSearch.php @@ -1,40 +1,38 @@ = $num) - return -1; - } - /*Performing linear search for $key in block*/ - while ($list[$prev] < $key) - { - $prev++; - if ($prev == min($step, $num)) - return -1; - } - - return $list[$prev] === $key ? $prev : -1; -} +function jumpSearch($list, $key) +{ + /*number of elements in the sorted array*/ + $num = count($list); +/*block size to be jumped*/ + $step = (int)sqrt($num); + $prev = 0; + while ($list[min($step, $num) - 1] < $key) { + $prev = $step; + $step += (int)sqrt($num); + if ($prev >= $num) { + return -1; + } + } + /*Performing linear search for $key in block*/ + while ($list[$prev] < $key) { + $prev++; + if ($prev == min($step, $num)) { + return -1; + } + } + return $list[$prev] === $key ? $prev : -1; +} diff --git a/Searches/LinearSearch.php b/Searches/LinearSearch.php index 4de38be3..2d8ee41e 100644 --- a/Searches/LinearSearch.php +++ b/Searches/LinearSearch.php @@ -5,10 +5,6 @@ * * Reference: https://www.geeksforgeeks.org/linear-search/ * - * @param Array $list a array of integers to search - * @param integer $target an integer number to search for in the list - * @return integer the index where the target is found (or -1 if not found) - * * Examples: * data = 5, 7, 8, 11, 12, 15, 17, 18, 20 * x = 15 @@ -17,16 +13,15 @@ * x = 1 * Element not found * - * @param Array $list a array of integers to search + * @param Array $list an array of integers to search * @param integer $target an integer number to search for in the list * @return integer the index where the target is found (or -1 if not found) */ function linearSearch($list, $target) { $n = sizeof($list); - for($i = 0; $i < $n; $i++) - { - if($list[$i] == $target) { + for ($i = 0; $i < $n; $i++) { + if ($list[$i] == $target) { return $i + 1; } } diff --git a/Searches/LowerBound.php b/Searches/LowerBound.php index cf465555..ba4ddbb5 100644 --- a/Searches/LowerBound.php +++ b/Searches/LowerBound.php @@ -10,24 +10,25 @@ * [C++ Lower Bound](http://www.cplusplus.com/reference/algorithm/lower_bound/) * * It is assumed that an integer array is provided - * and the second parameter is also a integer + * and the second parameter is also an integer * - * @param array of sorted integers - * @param integer whose lower bound is to be found + * @param array $arr of sorted integers + * @param integer $elem whose lower bound is to be found * - * @return the index of lower bound of the given element + * @return int the index of lower bound of the given element */ -function lowerBound(array $arr, int $elem){ +function lowerBound(array $arr, int $elem) +{ isSortedAscendingInts($arr); $hi = count($arr); $lo = 0; - while($lo < $hi){ - $mid = $lo + floor(($hi - $lo)/2); + while ($lo < $hi) { + $mid = $lo + floor(($hi - $lo) / 2); - if($arr[$mid] < $elem){ - $lo = $mid+1; - }else{ + if ($arr[$mid] < $elem) { + $lo = $mid + 1; + } else { $hi = $mid; } } diff --git a/Searches/TernarySearch.php b/Searches/TernarySearch.php index a0e22353..21e4aad0 100644 --- a/Searches/TernarySearch.php +++ b/Searches/TernarySearch.php @@ -1,4 +1,5 @@ $arr[$mid2]) { - // the $key lies in between $mid2 and $high - return ternarySearchByRecursion($arr, $key, $mid2 + 1, $high); +// the $key lies in between $low and $mid1 + return ternarySearchByRecursion($arr, $key, $low, $mid1 - 1); + } elseif ($key > $arr[$mid2]) { + // the $key lies in between $mid2 and $high + return ternarySearchByRecursion($arr, $key, $mid2 + 1, $high); } else { - // the $key lies in between $mid1 and $mid2 - return ternarySearchByRecursion($arr, $key, $mid1 + 1, $mid2 - 1); + // the $key lies in between $mid1 and $mid2 + return ternarySearchByRecursion($arr, $key, $mid1 + 1, $mid2 - 1); } } -function ternarySearchIterative ($arr, $key) { - $low = 0; $high = count($arr) - 1; - while ($high >= $low) { +function ternarySearchIterative($arr, $key) +{ + $low = 0; + $high = count($arr) - 1; + while ($high >= $low) { // find the $mid1 and $mid2 - $mid1 = floor($low + ($high - $low) / 3); - $mid2 = floor($high - ($high - $low) / 3); - + $mid1 = floor($low + ($high - $low) / 3); + $mid2 = floor($high - ($high - $low) / 3); // check if $key is found at any $mid - if ($arr[$mid1] === $key) { - // return index of $key if found - return $mid1; - } - if ($arr[$mid2] === $key) { - // return index of $key if found - return $mid2; - } + if ($arr[$mid1] === $key) { +// return index of $key if found + return $mid1; + } + if ($arr[$mid2] === $key) { + // return index of $key if found + return $mid2; + } - // since the $key is not found at $mid, - // check in which region it is present - // and repeat the Search operation - // in that region - if ($key < $arr[$mid1]) { - // the $key lies in between $low and $mid1 - $high = $mid1 - 1; - } else if ($key > $arr[$mid2]) { - // the $key lies in between $mid2 and $high - $low = $mid2 + 1; - } else { - // the $key lies in between $mid1 and $mid2 - $low = $mid1 + 1; - $high = $mid2 - 1; + // since the $key is not found at $mid, + // check in which region it is present + // and repeat the Search operation + // in that region + if ($key < $arr[$mid1]) { + // the $key lies in between $low and $mid1 + $high = $mid1 - 1; + } elseif ($key > $arr[$mid2]) { + // the $key lies in between $mid2 and $high + $low = $mid2 + 1; + } else { + // the $key lies in between $mid1 and $mid2 + $low = $mid1 + 1; + $high = $mid2 - 1; + } } - } // the $key was not found - return null; + return null; } diff --git a/Searches/UpperBound.php b/Searches/UpperBound.php index 6cfbffe3..47d62518 100644 --- a/Searches/UpperBound.php +++ b/Searches/UpperBound.php @@ -10,24 +10,25 @@ * [C++ Lower Bound](http://www.cplusplus.com/reference/algorithm/upper_bound/) * * It is assumed that an integer array is provided - * and the second parameter is also a integer + * and the second parameter is also an integer * - * @param array of sorted integers - * @param integer whose upper bound is to be found + * @param array $arr of sorted integers + * @param integer $elem whose upper bound is to be found * - * @return the index of upper bound of the given element + * @return int the index of upper bound of the given element */ -function upperBound(array $arr, int $elem){ +function upperBound(array $arr, int $elem) +{ isSortedAscendingInts($arr); $hi = count($arr); $lo = 0; - while($lo < $hi){ - $mid = $lo + floor(($hi - $lo)/2); + while ($lo < $hi) { + $mid = $lo + floor(($hi - $lo) / 2); - if($arr[$mid] <= $elem){ + if ($arr[$mid] <= $elem) { $lo = $mid + 1; - }else{ + } else { $hi = $mid; } } diff --git a/Sorting/ArrayKeysSort.php b/Sorting/ArrayKeysSort.php index 034258ec..4c97830d 100644 --- a/Sorting/ArrayKeysSort.php +++ b/Sorting/ArrayKeysSort.php @@ -1,4 +1,5 @@ $key) || !isset($b->$key)) { - $errorMsg = 'The key "' . $key - . '" does not exist in the collection'; - throw new Exception($errorMsg); - } - $item1 = !$isCaseSensitive - ? strtolower($a->$key) : $a->$key; - $item2 = !$isCaseSensitive - ? strtolower($b->$key) : $b->$key; + throw new Exception($errorMsg); } - } while ($item1 === $item2 && !empty($keys[++$pos])); - - if ($item1 === $item2) { - return 0; - } elseif ($order === self::ORDER_ASC) { - return ($item1 < $item2) ? -1 : 1; + $item1 = !$isCaseSensitive + ? strtolower($a[$key]) : $a[$key]; + $item2 = !$isCaseSensitive + ? strtolower($b[$key]) : $b[$key]; } else { - return ($item1 > $item2) ? -1 : 1; + if (!isset($a->$key) || !isset($b->$key)) { + $errorMsg = 'The key "' . $key + . '" does not exist in the collection'; + throw new Exception($errorMsg); + } + $item1 = !$isCaseSensitive + ? strtolower($a->$key) : $a->$key; + $item2 = !$isCaseSensitive + ? strtolower($b->$key) : $b->$key; } + } while ($item1 === $item2 && !empty($keys[++$pos])); + if ($item1 === $item2) { + return 0; + } elseif ($order === self::ORDER_ASC) { + return ($item1 < $item2) ? -1 : 1; + } else { + return ($item1 > $item2) ? -1 : 1; } - ); + }); } catch (Exception $e) { echo $e->getMessage(); die(); diff --git a/Sorting/BubbleSort.php b/Sorting/BubbleSort.php index b46e7a0b..9bc66795 100644 --- a/Sorting/BubbleSort.php +++ b/Sorting/BubbleSort.php @@ -6,13 +6,14 @@ * @param array $array * @return array */ -function bubbleSort($array) { +function bubbleSort($array) +{ $length = count($array); for ($i = $length; $i > 0; $i--) { $swapped = true; - for ($j=0;$j<$i-1;$j++) { + for ($j = 0; $j < $i - 1; $j++) { if ($array[$j] > $array[$j + 1]) { $temp = $array[$j]; $array[$j] = $array[$j + 1]; @@ -21,7 +22,9 @@ function bubbleSort($array) { } } - if ($swapped) break; + if ($swapped) { + break; + } } return $array; diff --git a/Sorting/BubbleSort2.php b/Sorting/BubbleSort2.php index f29b3394..97d0757f 100644 --- a/Sorting/BubbleSort2.php +++ b/Sorting/BubbleSort2.php @@ -1,4 +1,5 @@ 0 ) { + for ($i = $min; $i <= $max; $i++) { + while ($count[$i]-- > 0) { $array[$z++] = $i; } } diff --git a/Sorting/GnomeSort.php b/Sorting/GnomeSort.php index 003e0970..a9a88ce8 100644 --- a/Sorting/GnomeSort.php +++ b/Sorting/GnomeSort.php @@ -4,26 +4,25 @@ * Gnome Sort * References: * https://www.geeksforgeeks.org/gnome-sort-a-stupid-one/ - * + * * The Gnome algorithm works by locating the first instance in which two adjoining elements are arranged incorrectly and swaps with each other. - * + * * @param array $array refers to the array to be sorted * @return array */ - -function gnomeSort($array){ +function gnomeSort($array) +{ $a = 1; $b = 2; - while($a < count($array)){ - - if ($array[$a-1] <= $array[$a]){ + while ($a < count($array)) { + if ($array[$a - 1] <= $array[$a]) { $a = $b; $b++; - }else{ - list($array[$a],$array[$a-1]) = array($array[$a-1],$array[$a]); + } else { + list($array[$a],$array[$a - 1]) = array($array[$a - 1],$array[$a]); $a--; - if($a == 0){ + if ($a == 0) { $a = $b; $b++; } diff --git a/Sorting/HeapSort.php b/Sorting/HeapSort.php index 308b2201..d999967b 100644 --- a/Sorting/HeapSort.php +++ b/Sorting/HeapSort.php @@ -1,4 +1,5 @@ = 0; $i--) { - // Swap +// Swap [$arr[0], $arr[$i]] = [$arr[$i], $arr[0]]; - - // Heapify the reduced heap +// Heapify the reduced heap heapify($arr, $i, 0); } @@ -46,7 +46,6 @@ function heapify(array &$arr, int $n, int $i): void $largest = $i; $left = 2 * $i + 1; $right = 2 * $i + 2; - if ($left < $n && $arr[$left] > $arr[$largest]) { $largest = $left; } @@ -59,4 +58,4 @@ function heapify(array &$arr, int $n, int $i): void [$arr[$i], $arr[$largest]] = [$arr[$largest], $arr[$i]]; heapify($arr, $n, $largest); } -} \ No newline at end of file +} diff --git a/Sorting/InsertionSort.php b/Sorting/InsertionSort.php index b184e545..fcffaa00 100644 --- a/Sorting/InsertionSort.php +++ b/Sorting/InsertionSort.php @@ -8,13 +8,11 @@ */ function insertionSort(array $array) { - for ($i = 1; $i < count($array); $i++) - { + for ($i = 1; $i < count($array); $i++) { $currentVal = $array[$i]; - for ($j = $i - 1; $j >= 0 && $array[$j] > $currentVal; $j--) - { - $array[$j + 1] = $array[$j]; + for ($j = $i - 1; $j >= 0 && $array[$j] > $currentVal; $j--) { + $array[$j + 1] = $array[$j]; } $array[$j + 1] = $currentVal; @@ -22,5 +20,3 @@ function insertionSort(array $array) return $array; } - - diff --git a/Sorting/MergeSort.php b/Sorting/MergeSort.php index 7923917a..23c64178 100644 --- a/Sorting/MergeSort.php +++ b/Sorting/MergeSort.php @@ -9,12 +9,12 @@ function mergeSort(array $arr) { if (count($arr) <= 1) { - return $arr; + return $arr; } - $mid = floor( count($arr) / 2 ); - $leftArray = mergeSort( array_slice($arr, 0, $mid) ); - $rightArray = mergeSort( array_slice($arr, $mid) ); + $mid = floor(count($arr) / 2); + $leftArray = mergeSort(array_slice($arr, 0, $mid)); + $rightArray = mergeSort(array_slice($arr, $mid)); return merge($leftArray, $rightArray); } @@ -52,6 +52,3 @@ function merge(array $leftArray, array $rightArray) return $result; } - - - diff --git a/Sorting/QuickSort.php b/Sorting/QuickSort.php index da35617b..0950effa 100644 --- a/Sorting/QuickSort.php +++ b/Sorting/QuickSort.php @@ -1,4 +1,5 @@ = 'a' && $string[$i] <= 'z') - { + for ($i = 0; $i < strlen($string); $i++) { + if ( + !in_array($string[$i], $vowels) && + $string[$i] >= 'a' && $string[$i] <= 'z' + ) { $consonantCount++; } } diff --git a/Strings/CountVowels.php b/Strings/CountVowels.php index e3eef0a5..34a78c0f 100644 --- a/Strings/CountVowels.php +++ b/Strings/CountVowels.php @@ -1,28 +1,30 @@ = 0; $i--) - { + for ($i = (count($words) - 1); $i >= 0; $i--) { $reversedWords[] = $words[$i]; } diff --git a/Utils/ArrayHelpers.php b/Utils/ArrayHelpers.php index 9666aa61..37602e99 100644 --- a/Utils/ArrayHelpers.php +++ b/Utils/ArrayHelpers.php @@ -1,24 +1,25 @@ . + vendor + 0 diff --git a/tests/Ciphers/CiphersTest.php b/tests/Ciphers/CiphersTest.php index d6204811..241e729b 100755 --- a/tests/Ciphers/CiphersTest.php +++ b/tests/Ciphers/CiphersTest.php @@ -1,6 +1,7 @@ assertEquals( $input_str, xorCipher( xorCipher( $input_str , $key) , $key)); - $this->assertNotEquals( $input_str, xorCipher( xorCipher( $input_str , $key) , $invalid_key)); + $this->assertEquals($input_str, xorCipher(xorCipher($input_str, $key), $key)); + $this->assertNotEquals($input_str, xorCipher(xorCipher($input_str, $key), $invalid_key)); } } diff --git a/tests/Ciphers/MonoAlphabeticCipherTest.php b/tests/Ciphers/MonoAlphabeticCipherTest.php index 4509ab4c..de900967 100644 --- a/tests/Ciphers/MonoAlphabeticCipherTest.php +++ b/tests/Ciphers/MonoAlphabeticCipherTest.php @@ -1,23 +1,21 @@ - +assertTrue($ans); } } - diff --git a/tests/Maths/MathsTest.php b/tests/Maths/MathsTest.php index 8684a738..d3bc9b47 100644 --- a/tests/Maths/MathsTest.php +++ b/tests/Maths/MathsTest.php @@ -3,6 +3,7 @@ use function PHPUnit\Framework\assertEquals; use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertTrue; + use PHPUnit\Framework\TestCase; require_once __DIR__ . '/../../vendor/autoload.php'; @@ -107,7 +108,7 @@ public function testNeonNumber() assertFalse(isNumberNeon(123)); assertTrue(isNumberNeon(9)); } - + public function testFibonacciGenerator() { assertEquals([0, 1, 1, 2, 3], iterator_to_array(loop(5, fib()))); @@ -120,15 +121,15 @@ public function testFibonacciGenerator() public function testMean() { assertEquals( - (2 + 4 + 6 + 8 + 20 + 50 + 70) / 7, + (2 + 4 + 6 + 8 + 20 + 50 + 70) / 7, mean(2, 4, 6, 8, 20, 50, 70) ); assertEquals( - (-5 - 7 + 10) / 3, + (-5 - 7 + 10) / 3, mean(-5, -7, 10) ); - + assertEquals(-1, mean(-1)); } @@ -146,5 +147,4 @@ public function testMode() $this->assertEquals([1, 2, 3, 4, 5], mode(1, 2, 3, 4, 5)); $this->assertEquals([2, 3, 4], mode(2, 2, 3, 3, 4, 4)); } - } diff --git a/tests/Maths/ProjectEulerTest.php b/tests/Maths/ProjectEulerTest.php index 3b74f3ed..43b1da47 100644 --- a/tests/Maths/ProjectEulerTest.php +++ b/tests/Maths/ProjectEulerTest.php @@ -62,7 +62,7 @@ public function testProblem9(): void { $this->assertSame(31875000, problem9()); } - + public function testProblem10(): void { $this->assertSame(142913828922, problem10()); diff --git a/tests/Searches/SearchesTest.php b/tests/Searches/SearchesTest.php index 08d2f716..3de89db9 100644 --- a/tests/Searches/SearchesTest.php +++ b/tests/Searches/SearchesTest.php @@ -3,6 +3,7 @@ use function PHPUnit\Framework\assertEquals; use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertTrue; + use PHPUnit\Framework\TestCase; require_once __DIR__ . '/../../vendor/autoload.php'; @@ -138,7 +139,7 @@ public function testUpperBound() $result = upperBound($list, $target); assertEquals(5, $result); } - + public function testJumpSearch() { $list = array( 3,5,6,7,9,10,12,20,22,24); diff --git a/tests/Sorting/ArrayKeysSortTest.php b/tests/Sorting/ArrayKeysSortTest.php index 2b054987..31c322df 100644 --- a/tests/Sorting/ArrayKeysSortTest.php +++ b/tests/Sorting/ArrayKeysSortTest.php @@ -7,7 +7,8 @@ class ArrayKeysSortTest extends TestCase { - public function testArrayKeysSort() { + public function testArrayKeysSort() + { //test array of arrays $array1 = [ ['fruit' => 'banana', 'color' => 'yellow', 'cant' => 5], @@ -36,22 +37,22 @@ public function testArrayKeysSort() { $this->assertEquals($result2, $test2); //test array of objects - $object1 = new \stdClass; + $object1 = new \stdClass(); $object1->fruit = 'banana'; $object1->color = 'yellow'; $object1->cant = 5; - $object2 = new \stdClass; + $object2 = new \stdClass(); $object2->fruit = 'apple'; $object2->color = 'red'; $object2->cant = 2; - $object3 = new \stdClass; + $object3 = new \stdClass(); $object3->fruit = 'apple'; $object3->color = 'green'; $object3->cant = 7; - $object4 = new \stdClass; + $object4 = new \stdClass(); $object4->fruit = 'grapes'; $object4->color = 'purple'; $object4->cant = 4; diff --git a/tests/Strings/StringsTest.php b/tests/Strings/StringsTest.php index 3b17907e..41cd170e 100644 --- a/tests/Strings/StringsTest.php +++ b/tests/Strings/StringsTest.php @@ -4,6 +4,7 @@ use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertNotEquals; use function PHPUnit\Framework\assertTrue; + use PHPUnit\Framework\TestCase; require_once __DIR__ . '/../../vendor/autoload.php'; From 39ab69ecea305299975d7f793bf0b8cbbe7bf668 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:56:49 +0330 Subject: [PATCH 09/95] fix string test structure --- tests/Strings/StringsTest.php | 76 +++++++++++++++++------------------ 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/tests/Strings/StringsTest.php b/tests/Strings/StringsTest.php index 41cd170e..99a176b7 100644 --- a/tests/Strings/StringsTest.php +++ b/tests/Strings/StringsTest.php @@ -1,10 +1,5 @@ assertTrue(isPalindrome('MaDam')); // true + $this->assertFalse(isPalindrome('ThisIsTest')); // false + $this->assertFalse(isPalindrome('')); // false + $this->assertTrue(isPalindrome('Sator Arepo Tenet Opera Rotas')); // true + $this->assertFalse(isPalindrome('Sator Arepo Tenet Opera Rotas', false)); // false since we are doing + // case-sensitive check } public function testCountSentences() { - assertEquals(countSentences('Hi! Hello world.'), 2); - assertEquals(countSentences('i am testing. test....'), 2); - assertEquals(countSentences('How are you?'), 1); + $this->assertEquals(2, countSentences('Hi! Hello world.')); + $this->assertEquals(2, countSentences('i am testing. test....')); + $this->assertEquals(1, countSentences('How are you?')); } public function testReverseString() { - assertEquals('txet emos si sihT', reverseString('This is some text')); - assertEquals('mADaM', reverseString('MaDAm')); - assertNotEquals('MaDAm', reverseString('MaDAm')); + $this->assertEquals('txet emos si sihT', reverseString('This is some text')); + $this->assertEquals('mADaM', reverseString('MaDAm')); + $this->assertNotEquals('MaDAm', reverseString('MaDAm')); } public function testReverseWords() { - assertEquals('Fun is Coding PHP', reverseWords('PHP Coding is Fun')); - assertEquals('OneWord', reverseWords('OneWord')); - assertEquals('Text different some is This', reverseWords('This is some different Text')); + $this->assertEquals('Fun is Coding PHP', reverseWords('PHP Coding is Fun')); + $this->assertEquals('OneWord', reverseWords('OneWord')); + $this->assertEquals('Text different some is This', reverseWords('This is some different Text')); } public function testIsAnagram() { - assertTrue(isAnagram("php", "PHP")); // By default it's case-insensitive - assertFalse(isAnagram("php", "PHP", false)); // Make case-sensitive check - assertTrue(isAnagram("php is fun", "pin hpf us")); - assertFalse(isAnagram("Hello", " Hello")); //Extra space character - assertTrue(isAnagram("ScRamble", "SRlmcaeb", false)); // Check with a mixture of upper and lower case + $this->assertTrue(isAnagram("php", "PHP")); // By default it's case-insensitive + $this->assertFalse(isAnagram("php", "PHP", false)); // Make case-sensitive check + $this->assertTrue(isAnagram("php is fun", "pin hpf us")); + $this->assertFalse(isAnagram("Hello", " Hello")); //Extra space character + $this->assertTrue(isAnagram("ScRamble", "SRlmcaeb", false)); // Check with a mixture of upper and lower case } public function testMaxCharacter() { - assertEquals(maxCharacter("this is test for max character repetition"), 't'); - assertEquals(maxCharacter("This is Test for max characTer repetition"), 't'); - assertEquals(maxCharacter(" "), ' '); + $this->assertEquals('t', maxCharacter("this is test for max character repetition")); + $this->assertEquals('t', maxCharacter("This is Test for max characTer repetition")); + $this->assertEquals(' ', maxCharacter(" ")); } public function testCountVowels() { - assertEquals(countVowelsSimple("This is a string with 7 vowels"), 7); - assertEquals(countVowelsSimple("hello world"), 3); - assertEquals(countVowelsRegex("Just A list of somE aaaaaaaaaa"), 16); + $this->assertEquals(7, countVowelsSimple("This is a string with 7 vowels")); + $this->assertEquals(3, countVowelsSimple("hello world")); + $this->assertEquals(16, countVowelsRegex("Just A list of somE aaaaaaaaaa")); - assertEquals(countVowelsRegex("This is a string with 7 vowels"), 7); - assertEquals(countVowelsRegex("hello world"), 3); - assertEquals(countVowelsRegex("Just A list of somE aaaaaaaaaa"), 16); + $this->assertEquals(7, countVowelsRegex("This is a string with 7 vowels")); + $this->assertEquals(3, countVowelsRegex("hello world")); + $this->assertEquals(16, countVowelsRegex("Just A list of somE aaaaaaaaaa")); } public function testCountConsonants() { - assertEquals(countConsonants("This is a string with 19 consonants"), 19); - assertEquals(countConsonants("hello world"), 7); - assertEquals(countConsonants("Just A list of somE aaaaaaaaaa"), 9); + $this->assertEquals(19, countConsonants("This is a string with 19 consonants")); + $this->assertEquals(7, countConsonants("hello world")); + $this->assertEquals(9, countConsonants("Just A list of somE aaaaaaaaaa")); } public function testFindDistance() { - assertEquals(findDistance("hello", "hallo"), 1); - assertEquals(findDistance("hallo", "hello"), 1); - assertEquals(findDistance("sunday", "sunday"), 0); - assertEquals(findDistance("saturday", "sunday"), 3); + $this->assertEquals(1, findDistance("hello", "hallo")); + $this->assertEquals(1, findDistance("hallo", "hello")); + $this->assertEquals(0, findDistance("sunday", "sunday")); + $this->assertEquals(3, findDistance("saturday", "sunday")); } } From 963bafca7178808a78b1a527044a7d677b5d51bc Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:03:29 +0330 Subject: [PATCH 10/95] fix searches test structure --- tests/Searches/SearchesTest.php | 80 ++++++++++++++++----------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/tests/Searches/SearchesTest.php b/tests/Searches/SearchesTest.php index 3de89db9..edf1618b 100644 --- a/tests/Searches/SearchesTest.php +++ b/tests/Searches/SearchesTest.php @@ -1,9 +1,5 @@ assertEquals(0, $result); $target = 15; $result = binarySearchIterative($list, $target); - assertEquals(4, $result); + $this->assertEquals(4, $result); $target = 5; $result = binarySearchIterative($list, $target); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 6; $result = binarySearchIterative($list, $target); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testBinarySearchByRecursion() @@ -42,16 +38,16 @@ public function testBinarySearchByRecursion() $list = [0, 5, 7, 10, 15]; $target = 0; $result = binarySearchByRecursion($list, $target, 0, 4); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 15; $result = binarySearchByRecursion($list, $target, 0, 4); - assertEquals(4, $result); + $this->assertEquals(4, $result); $target = 5; $result = binarySearchByRecursion($list, $target, 0, 4); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 6; $result = binarySearchByRecursion($list, $target, 0, 4); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testBinarySearchByRecursionWithEmptyList() @@ -59,7 +55,7 @@ public function testBinarySearchByRecursionWithEmptyList() $list = []; $target = 0; $result = binarySearchByRecursion($list, $target, 0, 0); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testBinarySearchByRecursionWithOneElementList() @@ -67,10 +63,10 @@ public function testBinarySearchByRecursionWithOneElementList() $list = [0]; $target = 0; $result = binarySearchByRecursion($list, $target, 0, 0); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 1; $result = binarySearchByRecursion($list, $target, 0, 0); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testBinarySearchByRecursionWithTwoElementList() @@ -78,13 +74,13 @@ public function testBinarySearchByRecursionWithTwoElementList() $list = [0, 1]; $target = 0; $result = binarySearchByRecursion($list, $target, 0, 1); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 1; $result = binarySearchByRecursion($list, $target, 0, 1); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 2; $result = binarySearchByRecursion($list, $target, 0, 1); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testBinarySearchByRecursionWithThreeElementList() @@ -92,28 +88,28 @@ public function testBinarySearchByRecursionWithThreeElementList() $list = [0, 1, 2]; $target = 0; $result = binarySearchByRecursion($list, $target, 0, 2); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 1; $result = binarySearchByRecursion($list, $target, 0, 2); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 2; $result = binarySearchByRecursion($list, $target, 0, 2); - assertEquals(2, $result); + $this->assertEquals(2, $result); $target = 3; $result = binarySearchByRecursion($list, $target, 0, 2); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testFibonacciSearch() { $test1 = fibonacciPosition(6); - assertEquals(8, $test1); + $this->assertEquals(8, $test1); $test2 = fibonacciPosition(9); - assertEquals(34, $test2); + $this->assertEquals(34, $test2); $test3 = fibonacciPosition(60); - assertEquals(1548008755920, $test3); + $this->assertEquals(1548008755920, $test3); } public function testLinearSearch() @@ -121,7 +117,7 @@ public function testLinearSearch() $list = [5, 7, 8, 11, 12, 15, 17, 18, 20]; $target = 15; $result = linearSearch($list, $target); - assertEquals(6, $result); + $this->assertEquals(6, $result); } public function testLowerBound() @@ -129,7 +125,7 @@ public function testLowerBound() $list = [1, 2, 3, 3, 3, 4, 5, 9]; $target = 3; $result = lowerBound($list, $target); - assertEquals(2, $result); + $this->assertEquals(2, $result); } public function testUpperBound() @@ -137,7 +133,7 @@ public function testUpperBound() $list = [1, 2, 3, 3, 3, 4, 5, 9]; $target = 3; $result = upperBound($list, $target); - assertEquals(5, $result); + $this->assertEquals(5, $result); } public function testJumpSearch() @@ -145,7 +141,7 @@ public function testJumpSearch() $list = array( 3,5,6,7,9,10,12,20,22,24); $target = 12; $result = jumpSearch($list, $target); - assertEquals(6, $result); + $this->assertEquals(6, $result); } public function testExponentialSearch() @@ -153,7 +149,7 @@ public function testExponentialSearch() $list = array(2,3,4,7,28,35,63,98); $target = 35; $result = exponentialSearch($list, $target); - assertEquals(5, $result); + $this->assertEquals(5, $result); } public function testTernarySearchIterative() @@ -161,16 +157,16 @@ public function testTernarySearchIterative() $list = [0, 5, 7, 10, 15]; $target = 0; $result = ternarySearchIterative($list, $target); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 15; $result = ternarySearchIterative($list, $target); - assertEquals(4, $result); + $this->assertEquals(4, $result); $target = 5; $result = ternarySearchIterative($list, $target); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 6; $result = ternarySearchIterative($list, $target); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testTernarySearchByRecursion() @@ -178,16 +174,16 @@ public function testTernarySearchByRecursion() $list = [0, 5, 7, 10, 15]; $target = 0; $result = ternarySearchByRecursion($list, $target, 0, 4); - assertEquals(0, $result); + $this->assertEquals(0, $result); $target = 15; $result = ternarySearchByRecursion($list, $target, 0, 4); - assertEquals(4, $result); + $this->assertEquals(4, $result); $target = 5; $result = ternarySearchByRecursion($list, $target, 0, 4); - assertEquals(1, $result); + $this->assertEquals(1, $result); $target = 6; $result = ternarySearchByRecursion($list, $target, 0, 4); - assertEquals(null, $result); + $this->assertEquals(null, $result); } public function testInterpolationSearch() @@ -195,15 +191,15 @@ public function testInterpolationSearch() $list = [2, 6, 8, 10, 12, 14, 16, 18, 20, 22, 26, 34, 39]; $target = 20; $result = interpolationSearch($list, $target); - assertEquals(8, $result); + $this->assertEquals(8, $result); $target = 12; $result = interpolationSearch($list, $target); - assertEquals(4, $result); + $this->assertEquals(4, $result); $target = 1000; $result = interpolationSearch($list, $target); - assertEquals(null, $result); + $this->assertEquals(null, $result); $target = 39; $result = interpolationSearch($list, $target); - assertEquals(12, $result); + $this->assertEquals(12, $result); } } From a305f06efe1206e87e80fcb32e6533a59dc215e8 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:09:58 +0330 Subject: [PATCH 11/95] fix maths test structure --- tests/Maths/MathsTest.php | 100 ++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/tests/Maths/MathsTest.php b/tests/Maths/MathsTest.php index d3bc9b47..3d6f3008 100644 --- a/tests/Maths/MathsTest.php +++ b/tests/Maths/MathsTest.php @@ -1,9 +1,5 @@ assertEquals(1, factorial(1)); + $this->assertEquals(120, factorial(5)); + $this->assertEquals(1, factorial(0)); $this->expectException(\Exception::class); factorial(-25); } public function testIsNumberArmstrong() { - assertTrue(isNumberArmstrong(153)); - assertFalse(isNumberArmstrong(123)); - assertTrue(isNumberArmstrong(370)); - assertFalse(isNumberArmstrong(2468)); + $this->assertTrue(isNumberArmstrong(153)); + $this->assertFalse(isNumberArmstrong(123)); + $this->assertTrue(isNumberArmstrong(370)); + $this->assertFalse(isNumberArmstrong(2468)); } public function testIsNumberPalindromic() { - assertTrue(isNumberPalindromic(121)); - assertFalse(isNumberPalindromic(123)); - assertTrue(isNumberPalindromic(123321)); - assertFalse(isNumberPalindromic(2468)); + $this->assertTrue(isNumberPalindromic(121)); + $this->assertFalse(isNumberPalindromic(123)); + $this->assertTrue(isNumberPalindromic(123321)); + $this->assertFalse(isNumberPalindromic(2468)); } public function testIsPrime() { - assertTrue(isPrime(73)); - assertFalse(isPrime(21)); - assertFalse(isPrime(1)); - assertTrue(isPrime(997)); + $this->assertTrue(isPrime(73)); + $this->assertFalse(isPrime(21)); + $this->assertFalse(isPrime(1)); + $this->assertTrue(isPrime(997)); } public function testAbsoluteMax() { - assertEquals(50, absolute_max(12, 20, 35, 50, 50, 23)); - assertEquals(13, absolute_max(13)); - assertEquals(54, absolute_max(12, 13, 54, 22)); + $this->assertEquals(50, absolute_max(12, 20, 35, 50, 50, 23)); + $this->assertEquals(13, absolute_max(13)); + $this->assertEquals(54, absolute_max(12, 13, 54, 22)); $this->expectException(\Exception::class); absolute_max(); } public function testAbsoluteMin() { - assertEquals(12, absolute_min(12, 20, 35, 50, 50, 23)); - assertEquals(13, absolute_min(13)); - assertEquals(12, absolute_min(12, 13, 54, 22)); + $this->assertEquals(12, absolute_min(12, 20, 35, 50, 50, 23)); + $this->assertEquals(13, absolute_min(13)); + $this->assertEquals(12, absolute_min(12, 13, 54, 22)); $this->expectException(\Exception::class); absolute_min(); } public function testPerfectSquare() { - assertTrue(isPerfectSquare(25)); - assertFalse(isPerfectSquare(43)); - assertFalse(isPerfectSquare(2)); - assertTrue(isPerfectSquare(225)); + $this->assertTrue(isPerfectSquare(25)); + $this->assertFalse(isPerfectSquare(43)); + $this->assertFalse(isPerfectSquare(2)); + $this->assertTrue(isPerfectSquare(225)); } public function testFastExponentiation() { - assertEquals(fastExponentiation(10, 0), 1); - assertEquals(fastExponentiation(10, 1), 10); - assertEquals(fastExponentiation(10, 2), 100); - assertEquals(fastExponentiation(10, 3), 1000); - assertEquals(fastExponentiation(20, 5), 3200000); + $this->assertEquals(1, fastExponentiation(10, 0)); + $this->assertEquals(10, fastExponentiation(10, 1)); + $this->assertEquals(100, fastExponentiation(10, 2)); + $this->assertEquals(1000, fastExponentiation(10, 3)); + $this->assertEquals(3200000, fastExponentiation(20, 5)); } public function testFibonacciSeries() { - assertEquals([0, 1, 1, 2, 3], fibonacciRecursive(5)); - assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], fibonacciRecursive(10)); + $this->assertEquals([0, 1, 1, 2, 3], fibonacciRecursive(5)); + $this->assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], fibonacciRecursive(10)); - assertEquals([0, 1, 1, 2, 3], fibonacciWithBinetFormula(5)); - assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], fibonacciWithBinetFormula(10)); + $this->assertEquals([0, 1, 1, 2, 3], fibonacciWithBinetFormula(5)); + $this->assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], fibonacciWithBinetFormula(10)); } public function testNeonNumber() { - assertTrue(isNumberNeon(1)); - assertFalse(isNumberNeon(43)); - assertFalse(isNumberNeon(123)); - assertTrue(isNumberNeon(9)); + $this->assertTrue(isNumberNeon(1)); + $this->assertFalse(isNumberNeon(43)); + $this->assertFalse(isNumberNeon(123)); + $this->assertTrue(isNumberNeon(9)); } public function testFibonacciGenerator() { - assertEquals([0, 1, 1, 2, 3], iterator_to_array(loop(5, fib()))); - assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], iterator_to_array(loop(10, fib()))); + $this->assertEquals([0, 1, 1, 2, 3], iterator_to_array(loop(5, fib()))); + $this->assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], iterator_to_array(loop(10, fib()))); - assertEquals([0, 1, 1, 2, 3], iterator_to_array(loop(5, fib()))); - assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], iterator_to_array(loop(10, fib()))); + $this->assertEquals([0, 1, 1, 2, 3], iterator_to_array(loop(5, fib()))); + $this->assertEquals([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], iterator_to_array(loop(10, fib()))); } public function testMean() { - assertEquals( + $this->assertEquals( (2 + 4 + 6 + 8 + 20 + 50 + 70) / 7, mean(2, 4, 6, 8, 20, 50, 70) ); - assertEquals( + $this->assertEquals( (-5 - 7 + 10) / 3, mean(-5, -7, 10) ); - assertEquals(-1, mean(-1)); + $this->assertEquals(-1, mean(-1)); } public function testMedian() { - assertEquals(3, median(1, 2, 3, 4, 5)); - assertEquals(4.5, median(1, 2, 3, 4, 5, 6, 7, 8)); - assertEquals(3, median(5, 3, 1, 2, 4)); + $this->assertEquals(3, median(1, 2, 3, 4, 5)); + $this->assertEquals(4.5, median(1, 2, 3, 4, 5, 6, 7, 8)); + $this->assertEquals(3, median(5, 3, 1, 2, 4)); } public function testMode() From e5523e0c4c5116f342d6a576e36db93c0064634a Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:40:29 +0330 Subject: [PATCH 12/95] fix conversions test structure --- tests/Conversions/ConversionsTest.php | 46 +++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/tests/Conversions/ConversionsTest.php b/tests/Conversions/ConversionsTest.php index cb8fb511..8280a2fd 100644 --- a/tests/Conversions/ConversionsTest.php +++ b/tests/Conversions/ConversionsTest.php @@ -1,7 +1,5 @@ assertEquals(7, binaryToDecimal(111)); + $this->assertEquals(5, binaryToDecimal(101)); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Binary Number for Converting it to a Decimal Number.'); binaryToDecimal("this is a string"); @@ -25,8 +23,8 @@ public function testBinaryToDecimal() public function testDecimalToBinary() { - assertEquals(decimalToBinary(7), 111); - assertEquals(decimalToBinary(5), 101); + $this->assertEquals(111, decimalToBinary(7)); + $this->assertEquals(101, decimalToBinary(5)); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Decimal Number for Converting it to a Binary Number.'); decimalToBinary("this is a string"); @@ -34,9 +32,9 @@ public function testDecimalToBinary() public function testOctalToDecimal() { - assertEquals(octalToDecimal(10), 8); - assertEquals(octalToDecimal(11), 9); - assertEquals(octalToDecimal(1115), 589); + $this->assertEquals(8, octalToDecimal(10)); + $this->assertEquals(9, octalToDecimal(11)); + $this->assertEquals(589, octalToDecimal(1115)); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Octal Number for Converting it to a Decimal Number.'); octalToDecimal("this is a string"); @@ -44,9 +42,9 @@ public function testOctalToDecimal() public function testDecimalToOctal() { - assertEquals(decimalToOctal(8), 10); - assertEquals(decimalToOctal(9), 11); - assertEquals(decimalToOctal(589), 1115); + $this->assertEquals(10, decimalToOctal(8)); + $this->assertEquals(11, decimalToOctal(9)); + $this->assertEquals(1115, decimalToOctal(589)); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Decimal Number for Converting it to an Octal Number.'); decimalToOctal("this is a string"); @@ -54,9 +52,9 @@ public function testDecimalToOctal() public function testDecimalToHex() { - assertEquals(decimalToHex(10), 'A'); - assertEquals(decimalToHex(489201875), '1D28A0D3'); - assertEquals(decimalToHex(171), 'AB'); + $this->assertEquals('A', decimalToHex(10)); + $this->assertEquals('1D28A0D3', decimalToHex(489201875)); + $this->assertEquals('AB', decimalToHex(171)); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Decimal Number for Converting it to a Hexadecimal Number.'); decimalToHex("this is a string"); @@ -64,9 +62,9 @@ public function testDecimalToHex() public function testHexToDecimal() { - assertEquals(hexToDecimal('A'), 10); - assertEquals(hexToDecimal('1D28A0D3'), 489201875); - assertEquals(hexToDecimal('AB'), 171); + $this->assertEquals(10, hexToDecimal('A')); + $this->assertEquals(489201875, hexToDecimal('1D28A0D3')); + $this->assertEquals(171, hexToDecimal('AB')); $this->expectException(\Exception::class); $this->expectExceptionMessage('Please pass a valid Hexadecimal Number for Converting it to a Decimal Number.'); hexToDecimal("this is a string"); @@ -74,12 +72,12 @@ public function testHexToDecimal() public function testSpeedConversion() { - assertEquals(convertSpeed(5, 'm/s', 'mph'), 11.18); - assertEquals(convertSpeed(5, 'ft/s', 'km/h'), 5.49); - assertEquals(convertSpeed(3, 'km/h', 'km/h'), 3); - assertEquals(convertSpeed(7, 'kn', 'km/h'), 12.96); - assertEquals(convertSpeed(12, 'mph', 'km/h'), 19.31); - assertEquals(convertSpeed(0.514, 'm/s', 'kn'), 1); + $this->assertEquals(11.18, convertSpeed(5, 'm/s', 'mph')); + $this->assertEquals(5.49, convertSpeed(5, 'ft/s', 'km/h')); + $this->assertEquals(3, convertSpeed(3, 'km/h', 'km/h')); + $this->assertEquals(12.96, convertSpeed(7, 'kn', 'km/h')); + $this->assertEquals(19.31, convertSpeed(12, 'mph', 'km/h')); + $this->assertEquals(1, convertSpeed(0.514, 'm/s', 'kn')); $this->expectException(\Exception::class); convertSpeed('1', 'km/h', 'mph'); From 1a04f5ea8f89ab273b442236b473b19d2d335197 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:44:04 +0330 Subject: [PATCH 13/95] fix morse test structure --- tests/Ciphers/MorseCodeTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Ciphers/MorseCodeTest.php b/tests/Ciphers/MorseCodeTest.php index 10bce3b3..bdb2b031 100644 --- a/tests/Ciphers/MorseCodeTest.php +++ b/tests/Ciphers/MorseCodeTest.php @@ -1,7 +1,5 @@ assertEquals('TEST 123', decode(encode('TEST 123'))); } } From 5817388856c96ac56eabee5b94eb888c3ee14516 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:45:09 +0330 Subject: [PATCH 14/95] fix mono alphabetic cipher test structure --- tests/Ciphers/MonoAlphabeticCipherTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Ciphers/MonoAlphabeticCipherTest.php b/tests/Ciphers/MonoAlphabeticCipherTest.php index de900967..b8f99788 100644 --- a/tests/Ciphers/MonoAlphabeticCipherTest.php +++ b/tests/Ciphers/MonoAlphabeticCipherTest.php @@ -2,8 +2,6 @@ use PHPUnit\Framework\TestCase; -use function PHPUnit\Framework\assertEquals; - require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../Ciphers/MonoAlphabeticCipher.php'; @@ -15,7 +13,7 @@ public function testMonoAlphabeticCipher() $key = "yhkqgvxfoluapwmtzecjdbsnri"; $text = "I love1234 GitHub"; $encryptedText = "O ambg XojFdh"; - assertEquals(maEncrypt($key, $alphabet, $text), $encryptedText); - assertEquals(maDecrypt($key, $alphabet, $encryptedText), "I love GitHub"); + $this->assertEquals(maEncrypt($key, $alphabet, $text), $encryptedText); + $this->assertEquals(maDecrypt($key, $alphabet, $encryptedText), "I love GitHub"); } } From b814cf266929b3f1f618c41e428b6a06a0de6242 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:48:42 +0330 Subject: [PATCH 15/95] fix some ci warnings --- Graphs/BreadthFirstSearch.php | 3 +- Searches/ExponentialSearch.php | 35 ++++++------- tests/Ciphers/CiphersTest.php | 2 - tests/Sorting/SortingTests.php | 96 ++++++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 26 deletions(-) diff --git a/Graphs/BreadthFirstSearch.php b/Graphs/BreadthFirstSearch.php index 68579e10..205e7f14 100644 --- a/Graphs/BreadthFirstSearch.php +++ b/Graphs/BreadthFirstSearch.php @@ -1,7 +1,8 @@ $ceiling) { return -1; } - // search the left part of the $array - // If the $middle element is greater than the $value + + // search the left part of the $array + // If the $middle element is greater than the $value if ($arr[$mid] > $value) { return binarySearch($arr, $value, $floor, $mid - 1); - } - // search the right part of the $array - // If the $middle element is lower than the $value - else { + } else { // search the right part of the $array If the $middle element is lower than the $value return binarySearch($arr, $value, $mid + 1, $ceiling); } } @@ -47,13 +45,12 @@ function binarySearch($arr, $value, $floor, $ceiling) */ function exponentialSearch($arr, $value) { - - // If $value is the first element of the $array return this position + // If $value is the first element of the $array return this position if ($arr[0] === $value) { return 0; } - // Find range for binary search + // Find range for binary search $i = 1; $length = count($arr); while ($i < $length && $arr[$i] <= $value) { diff --git a/tests/Ciphers/CiphersTest.php b/tests/Ciphers/CiphersTest.php index 241e729b..83e52546 100755 --- a/tests/Ciphers/CiphersTest.php +++ b/tests/Ciphers/CiphersTest.php @@ -1,7 +1,5 @@ assertEquals([272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711], $sorted); + $this->assertEquals([ + 272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, + 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, + 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, + 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, + 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, + 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, + 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, + 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, + 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, + 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, + 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, + 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, + 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, + 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, + 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, + 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, + 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, + 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, + 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, + 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, + 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711 + ], $sorted); } public function testCountSort() @@ -54,9 +98,53 @@ public function testMergeSort() public function testQuickSort() { - $array = [51158,1856,8459,67957,59748,58213,90876,39621,66570,64204,79935,27892,47615,94706,34201,74474,63968,4337,43688,42685,31577,5239,25385,56301,94083,23232,67025,44044,74813,34671,90235,65764,49709,12440,21165,20620,38265,12973,25236,93144,13720,4204,77026,42348,19756,97222,78828,73437,48208,69858,19713,29411,49809,66174,5257,43340,40565,9592,52328,16677,38386,55416,99519,13732,84076,52905,47662,31805,46184,2811,35374,50800,53635,51886,49052,75197,3720,75045,28309,63771,71526,16122,36625,44654,86814,98845,44637,54955,24714,81960,78095,49048,99711,272,45755,31919,8181,1392,15352,82656,27760,18362,43780,50209,51433,2847,62310,87450,22874,40789,56841,52928,5523,76474,8935,63245,16387,21746,47596,84402,49168,58223,26993,55908,92837,64208,86309,30819,83638,9508,44628,10786,68125,14123,70474,50596,44518,74872,61968,36828,17860,4605,68756,86070,52068,51830,56992,45799,42422,68514,92559,40206,31263,71774,14202,94807,25774,15003,54211,18708,32074,43836,48964,40742,26281,67338,75786,34925,34649,45519,72472,80188,40858,83246,92215,66178,67452,86198,82300,45894,97156,73907,31159,7018,55549,35245,68975,88246,14098,59973,7762,40459,86358,63178,47489,55515,79488,46528,99272,10867,75190,56963,5520,56494,42310,40171,78105,29724,30110,28493,36141,22479,85799,70466,92106,16868,57402,4813,47432,24689,78533,97577,32178,30258,82785,56063,76277,96407,77849,1807,45344,30298,18158,49935,90728,22192,36852,33982,66206,30948,40372,33446,99156,28651,61591,79028,1689,94257,32158,11122,81097,57981,26277,7515,7873,8350,28229,24105,76818,86897,18456,29373,7853,24932,93070,4696,63015,9358,28302,3938,11754,33679,18492,91503,63395,12029,23954,27230,58336,16544,23606,61349,37348,78629,96145,57954,32392,76201,54616,59992,5676,97799,47910,98758,75043,72849,6466,68831,2246,69091,22296,6506,93729,86968,39583,46186,96782,19074,46574,46704,99211,55295,33963,77977,86805,72686]; + $array = [ + 51158, 1856, 8459, 67957, 59748, 58213, 90876, 39621, 66570, 64204, 79935, 27892, 47615, 94706, 34201, + 74474, 63968, 4337, 43688, 42685, 31577, 5239, 25385, 56301, 94083, 23232, 67025, 44044, 74813, 34671, + 90235, 65764, 49709, 12440, 21165, 20620, 38265, 12973, 25236, 93144, 13720, 4204, 77026, 42348, 19756, + 97222, 78828, 73437, 48208, 69858, 19713, 29411, 49809, 66174, 5257, 43340, 40565, 9592, 52328, 16677, + 38386, 55416, 99519, 13732, 84076, 52905, 47662, 31805, 46184, 2811, 35374, 50800, 53635, 51886, 49052, + 75197, 3720, 75045, 28309, 63771, 71526, 16122, 36625, 44654, 86814, 98845, 44637, 54955, 24714, 81960, + 78095, 49048, 99711, 272, 45755, 31919, 8181, 1392, 15352, 82656, 27760, 18362, 43780, 50209, 51433, 2847, + 62310, 87450, 22874, 40789, 56841, 52928, 5523, 76474, 8935, 63245, 16387, 21746, 47596, 84402, 49168, + 58223, 26993, 55908, 92837, 64208, 86309, 30819, 83638, 9508, 44628, 10786, 68125, 14123, 70474, 50596, + 44518, 74872, 61968, 36828, 17860, 4605, 68756, 86070, 52068, 51830, 56992, 45799, 42422, 68514, 92559, + 40206, 31263, 71774, 14202, 94807, 25774, 15003, 54211, 18708, 32074, 43836, 48964, 40742, 26281, 67338, + 75786, 34925, 34649, 45519, 72472, 80188, 40858, 83246, 92215, 66178, 67452, 86198, 82300, 45894, 97156, + 73907, 31159, 7018, 55549, 35245, 68975, 88246, 14098, 59973, 7762, 40459, 86358, 63178, 47489, 55515, + 79488, 46528, 99272, 10867, 75190, 56963, 5520, 56494, 42310, 40171, 78105, 29724, 30110, 28493, 36141, + 22479, 85799, 70466, 92106, 16868, 57402, 4813, 47432, 24689, 78533, 97577, 32178, 30258, 82785, 56063, + 76277, 96407, 77849, 1807, 45344, 30298, 18158, 49935, 90728, 22192, 36852, 33982, 66206, 30948, 40372, + 33446, 99156, 28651, 61591, 79028, 1689, 94257, 32158, 11122, 81097, 57981, 26277, 7515, 7873, 8350, 28229, + 24105, 76818, 86897, 18456, 29373, 7853, 24932, 93070, 4696, 63015, 9358, 28302, 3938, 11754, 33679, 18492, + 91503, 63395, 12029, 23954, 27230, 58336, 16544, 23606, 61349, 37348, 78629, 96145, 57954, 32392, 76201, + 54616, 59992, 5676, 97799, 47910, 98758, 75043, 72849, 6466, 68831, 2246, 69091, 22296, 6506, 93729, 86968, + 39583, 46186, 96782, 19074, 46574, 46704, 99211, 55295, 33963, 77977, 86805, 72686 + ]; $sorted = quickSort($array); - $this->assertEquals([272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711], $sorted); + $this->assertEquals([ + 272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, + 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, + 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, + 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, + 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, + 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, + 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, + 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, + 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, + 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, + 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, + 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, + 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, + 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, + 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, + 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, + 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, + 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, + 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, + 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, + 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711 + ], $sorted); } public function testQuickSortWithEmptyInput() From 29db48075ef56e551cd28ca943e229beb86a1502 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Thu, 5 Oct 2023 00:10:11 +0330 Subject: [PATCH 16/95] ignoring warnings in cs checker --- .github/workflows/code-style.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index 16ab9a56..71333e19 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -22,4 +22,4 @@ jobs: run: composer update --prefer-dist --no-progress --no-suggest - name: Run script - run: vendor/bin/phpcs \ No newline at end of file + run: vendor/bin/phpcs -n \ No newline at end of file From 658618ae9510e50f39d021460482280bec14bf32 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:59:11 +0330 Subject: [PATCH 17/95] fix comment indents and docs --- Ciphers/MonoAlphabeticCipher.php | 5 ++- DataStructures/SinglyLinkedList.php | 1 + Maths/Factorial.php | 10 +++--- Searches/ExponentialSearch.php | 7 +++-- Searches/InterpolationSearch.php | 34 ++++++++++----------- Searches/JumpSearch.php | 3 +- Searches/TernarySearch.php | 47 ++++++++++++++--------------- Sorting/HeapSort.php | 15 ++++++--- Strings/CountVowels.php | 23 ++++++++------ Strings/MaxCharacter.php | 28 ++++++++++------- 10 files changed, 98 insertions(+), 75 deletions(-) diff --git a/Ciphers/MonoAlphabeticCipher.php b/Ciphers/MonoAlphabeticCipher.php index 6b362dbe..dd8be159 100644 --- a/Ciphers/MonoAlphabeticCipher.php +++ b/Ciphers/MonoAlphabeticCipher.php @@ -7,9 +7,11 @@ function monoAlphabeticCipher($key, $alphabet, $text) { $cipherText = ''; // the cipher text (can be decrypted and encrypted) + // check if the text length matches if (strlen($key) != strlen($alphabet)) { return false; - } // check if the text length matches + } + $text = preg_replace('/[0-9]+/', '', $text); // remove all the numbers for ($i = 0; $i < strlen($text); $i++) { @@ -20,6 +22,7 @@ function monoAlphabeticCipher($key, $alphabet, $text) $cipherText .= ( ctype_upper($text[$i]) ? strtoupper($key[$index]) : $key[$index] ); } } + return $cipherText; } diff --git a/DataStructures/SinglyLinkedList.php b/DataStructures/SinglyLinkedList.php index 7256ed7d..b7a97bce 100644 --- a/DataStructures/SinglyLinkedList.php +++ b/DataStructures/SinglyLinkedList.php @@ -7,6 +7,7 @@ class SinglyLinkedList { public ?SinglyLinkedList $next = null; public $data; + public function __construct($data) { $this->data = $data; diff --git a/Maths/Factorial.php b/Maths/Factorial.php index cce08d68..e0041004 100644 --- a/Maths/Factorial.php +++ b/Maths/Factorial.php @@ -12,23 +12,25 @@ */ function factorial(int $number) { - static $cache = []; -//internal caching memory for speed + static $cache = []; //internal caching memory for speed if ($number < 0) { throw new \Exception("Negative numbers are not allowed for calculating Factorial"); } + + // Factorial of 0 is 1 if ($number === 0) { return 1; -// Factorial of 0 is 1 } if (isset($cache[$number])) { return $cache[$number]; } + // Recursion since x! = x * (x-1)! $fact = ($number * factorial($number - 1)); -// Recursion since x! = x * (x-1)! + $cache[$number] = $fact; + return $fact; } diff --git a/Searches/ExponentialSearch.php b/Searches/ExponentialSearch.php index 9c5c2a6a..1ea8a1c9 100644 --- a/Searches/ExponentialSearch.php +++ b/Searches/ExponentialSearch.php @@ -20,10 +20,12 @@ function binarySearch($arr, $value, $floor, $ceiling) { // Get $middle index $mid = floor(($floor + $ceiling) / 2); -// Return position if $value is at the $mid position + + // Return position if $value is at the $mid position if ($arr[$mid] === $value) { return (int) $mid; } + //Return -1 is range is wrong if ($floor > $ceiling) { return -1; @@ -58,6 +60,7 @@ function exponentialSearch($arr, $value) } $floor = $i / 2; $ceiling = min($i, $length); -// Call binary search for the range found above + + // Call binary search for the range found above return binarySearch($arr, $value, $floor, $ceiling); } diff --git a/Searches/InterpolationSearch.php b/Searches/InterpolationSearch.php index bb73d92d..fcd3dee8 100644 --- a/Searches/InterpolationSearch.php +++ b/Searches/InterpolationSearch.php @@ -18,36 +18,36 @@ */ function interpolationSearch($arr, $key) { - $length = count($arr) - 1; $low = 0; $high = $length; $position = -1; -//loop, between low & high + //loop, between low & high while ($low <= $high && $key >= $arr[$low] && $key <= $arr[$high]) { -//GET INDEX + //GET INDEX $delta = ($key - $arr[$low]) / ($arr[$high] - $arr[$low]); $index = $low + floor(($high - $low) * $delta); -//GET VALUE OF INDEX IN ARRAY... + + //GET VALUE OF INDEX IN ARRAY... $indexValue = $arr[$index]; + if ($indexValue === $key) { - //index value equals key - //FOUND TARGET - //return index value - $position = $index; + //index value equals key + //FOUND TARGET + //return index value + $position = $index; return (int) $position; - } - if ($indexValue < $key) { - //index value lower than key - //increase low index + } elseif ($indexValue < $key) { + //index value lower than key + //increase low index $low = $index + 1; - } - if ($indexValue > $key) { - //index value higher than key - //decrease high index + } elseif ($indexValue > $key) { + //index value higher than key + //decrease high index $high = $index - 1; } } - //when key not found in array or array not sorted + + //when key not found in array or array not sorted return null; } diff --git a/Searches/JumpSearch.php b/Searches/JumpSearch.php index 7be70de6..a84daa7e 100644 --- a/Searches/JumpSearch.php +++ b/Searches/JumpSearch.php @@ -14,7 +14,8 @@ function jumpSearch($list, $key) { /*number of elements in the sorted array*/ $num = count($list); -/*block size to be jumped*/ + + /*block size to be jumped*/ $step = (int)sqrt($num); $prev = 0; diff --git a/Searches/TernarySearch.php b/Searches/TernarySearch.php index 21e4aad0..61dbee1c 100644 --- a/Searches/TernarySearch.php +++ b/Searches/TernarySearch.php @@ -10,75 +10,72 @@ */ function ternarySearchByRecursion($arr, $key, $low, $high) { - - //Return -1 if high lesser than low, we can't find item in the whole array + // Return null if high is less than low (base case: key not found). if ($high < $low) { return null; } - //get $mid1 and $mid2 + // Calculate the indices of the first and second midpoints. $mid1 = floor($low + ($high - $low) / 3); $mid2 = floor($high - ($high - $low) / 3); -// check if $key is found at any $mid + + // Check if key is located at either midpoint. if ($arr[$mid1] === $key) { -// return index of $key if found return $mid1; } + if ($arr[$mid2] === $key) { -// return index of $key if found return $mid2; } - // since the $key is not found at $mid, - // check in which region it is present - // and repeat the Search operation - // in that region + // Determine which section to continue searching in. if ($key < $arr[$mid1]) { -// the $key lies in between $low and $mid1 + // Key is in the left section, between $low and $mid1. return ternarySearchByRecursion($arr, $key, $low, $mid1 - 1); } elseif ($key > $arr[$mid2]) { - // the $key lies in between $mid2 and $high + // Key is in the right section, between $mid2 and $high. return ternarySearchByRecursion($arr, $key, $mid2 + 1, $high); } else { - // the $key lies in between $mid1 and $mid2 + // Key is in the middle section, between $mid1 and $mid2. return ternarySearchByRecursion($arr, $key, $mid1 + 1, $mid2 - 1); } } function ternarySearchIterative($arr, $key) { + // Initialize low and high pointers. $low = 0; $high = count($arr) - 1; + + // Continue searching while the high pointer is greater than or equal to the low pointer. while ($high >= $low) { - // find the $mid1 and $mid2 + // Calculate the first and second midpoints. $mid1 = floor($low + ($high - $low) / 3); $mid2 = floor($high - ($high - $low) / 3); - // check if $key is found at any $mid + + // Check if the key is found at either midpoint. if ($arr[$mid1] === $key) { -// return index of $key if found return $mid1; } + if ($arr[$mid2] === $key) { - // return index of $key if found return $mid2; } - // since the $key is not found at $mid, - // check in which region it is present - // and repeat the Search operation - // in that region + // Determine the section to continue the search in. if ($key < $arr[$mid1]) { - // the $key lies in between $low and $mid1 + // Key is in the left section, update the high pointer. $high = $mid1 - 1; } elseif ($key > $arr[$mid2]) { - // the $key lies in between $mid2 and $high + // Key is in the right section, update the low pointer. $low = $mid2 + 1; } else { - // the $key lies in between $mid1 and $mid2 + // Key is in the middle section, update both pointers. $low = $mid1 + 1; $high = $mid2 - 1; } } - // the $key was not found + + // Key was not found. return null; } diff --git a/Sorting/HeapSort.php b/Sorting/HeapSort.php index d999967b..8cd0c523 100644 --- a/Sorting/HeapSort.php +++ b/Sorting/HeapSort.php @@ -13,24 +13,29 @@ */ function heapSort(array $arr): array { + // Get the number of elements in the array. $n = count($arr); + + // Throw an exception if the array has no elements. if ($n <= 0) { throw new \UnexpectedValueException('Input array must have at least one element.'); } - // Build heap - for ($i = $n / 2 - 1; $i >= 0; $i--) { + // Build a max heap from the array. + for ($i = floor($n / 2) - 1; $i >= 0; $i--) { heapify($arr, $n, $i); } - // Extract elements from heap one by one + // Extract elements from the max heap and build the sorted array. for ($i = $n - 1; $i >= 0; $i--) { -// Swap + // Swap the root(maximum value) of the heap with the last element of the heap. [$arr[0], $arr[$i]] = [$arr[$i], $arr[0]]; -// Heapify the reduced heap + + // Heapify the reduced heap. heapify($arr, $i, 0); } + // Return the sorted array. return $arr; } diff --git a/Strings/CountVowels.php b/Strings/CountVowels.php index 34a78c0f..83513786 100644 --- a/Strings/CountVowels.php +++ b/Strings/CountVowels.php @@ -9,26 +9,31 @@ * @return int $numberOfVowels * @throws \Exception */ -function countVowelsSimple(string $string) +function countVowelsSimple(string $string): int { + // Check for an empty string and throw an exception if so. if (empty($string)) { - throw new \Exception('Please pass a non-empty string value'); + throw new \Exception('Please pass a non-empty string value.'); } + // Initialize variables. $numberOfVowels = 0; - $vowels = ['a', 'e', 'i', 'o', 'u']; -// Vowels Set - $string = strtolower($string); -// For case-insensitive checking + $vowels = ['a', 'e', 'i', 'o', 'u']; // Set of vowels for comparison. + + // Convert the string to lowercase for case-insensitive comparison. + $string = strtolower($string); + + // Split the string into an array of characters. $characters = str_split($string); -// Splitting the string to a Character Array. + // Loop through each character to count the vowels. foreach ($characters as $character) { if (in_array($character, $vowels)) { $numberOfVowels++; } } + // Return the total number of vowels found. return $numberOfVowels; } @@ -45,8 +50,8 @@ function countVowelsRegex(string $string) if (empty($string)) { throw new \Exception('Please pass a non-empty string value'); } - $string = strtolower($string); -// For case-insensitive checking + + $string = strtolower($string); // For case-insensitive checking return preg_match_all('/[a,e,i,o,u]/', $string); } diff --git a/Strings/MaxCharacter.php b/Strings/MaxCharacter.php index 7c74ed87..d64c2e04 100644 --- a/Strings/MaxCharacter.php +++ b/Strings/MaxCharacter.php @@ -8,28 +8,34 @@ * @return string * @throws \Exception */ -function maxCharacter(string $string) +function maxCharacter(string $string): string { + // Throw an exception if the string is empty. if (empty($string)) { - throw new \Exception('Please pass a non-empty string value'); + throw new \Exception('Please pass a non-empty string value.'); } + // Initialize an associative array to hold character counts. $characterCountTable = []; -// A variable to maintain the character counts - $string = strtolower($string); -// For case-insensitive checking - $characters = str_split($string); -// Splitting the string to a Character Array. + // Convert the string to lowercase for case-insensitive analysis. + $string = strtolower($string); + + // Convert the string into an array of characters. + $characters = str_split($string); + + // Loop through the characters to populate the count table. foreach ($characters as $character) { - $currentCharacterCount = 1; - if (isset($characterCountTable[$character])) { - $currentCharacterCount = $characterCountTable[$character] + 1; - } + // Initialize or update the count of the current character. + $currentCharacterCount = isset($characterCountTable[$character]) ? $characterCountTable[$character] + 1 : 1; + // Update the count in the table. $characterCountTable[$character] = $currentCharacterCount; } + // Sort the count table in descending order. arsort($characterCountTable); + + // Return the character that appears most frequently. return array_keys($characterCountTable)[0]; } From f595b7a71ae99e341cf8d833a9ac58c2d9fdc4aa Mon Sep 17 00:00:00 2001 From: Aryansh Bhargavan Date: Thu, 5 Oct 2023 21:10:00 +0530 Subject: [PATCH 18/95] Feat: Added Vignere Cipher --- Ciphers/VignereCipher.php | 57 +++++++++++++++++++++++++++++ tests/Ciphers/VignereCipherTest.php | 19 ++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Ciphers/VignereCipher.php create mode 100644 tests/Ciphers/VignereCipherTest.php diff --git a/Ciphers/VignereCipher.php b/Ciphers/VignereCipher.php new file mode 100644 index 00000000..1ced1ebc --- /dev/null +++ b/Ciphers/VignereCipher.php @@ -0,0 +1,57 @@ + diff --git a/tests/Ciphers/VignereCipherTest.php b/tests/Ciphers/VignereCipherTest.php new file mode 100644 index 00000000..6c738881 --- /dev/null +++ b/tests/Ciphers/VignereCipherTest.php @@ -0,0 +1,19 @@ +assertEquals($plaintext, $decryptedText); + } +} \ No newline at end of file From 1e039d22e5dbfdfb46623bfa7b3beff011f33624 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:40:18 +0000 Subject: [PATCH 19/95] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2ee4e28e..a7b80c97 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -4,6 +4,7 @@ * [Caesarcipher](./Ciphers/CaesarCipher.php) * [Monoalphabeticcipher](./Ciphers/MonoAlphabeticCipher.php) * [Morsecode](./Ciphers/MorseCode.php) + * [Vignerecipher](./Ciphers/VignereCipher.php) * [Xorcipher](./Ciphers/XORCipher.php) ## Conversions @@ -89,6 +90,7 @@ * [Cipherstest](./tests/Ciphers/CiphersTest.php) * [Monoalphabeticciphertest](./tests/Ciphers/MonoAlphabeticCipherTest.php) * [Morsecodetest](./tests/Ciphers/MorseCodeTest.php) + * [Vignereciphertest](./tests/Ciphers/VignereCipherTest.php) * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures From 6eea66cddf4ad8fa2594fe94fb7881a318b3f672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Fri, 6 Oct 2023 14:51:11 +0200 Subject: [PATCH 20/95] fix empty input validation. --- Sorting/QuickSort.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sorting/QuickSort.php b/Sorting/QuickSort.php index 0950effa..4fc82aad 100644 --- a/Sorting/QuickSort.php +++ b/Sorting/QuickSort.php @@ -10,7 +10,7 @@ function quickSort(array $input) { // Return nothing if input is empty - if (!empty($input)) { + if (empty($input)) { return []; } From cf9764890d4512cc62400a802a3d5aee3c840d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Fri, 6 Oct 2023 15:26:57 +0200 Subject: [PATCH 21/95] rename `SortingTests.php` to `SortingTest.php`. - made this change so that PHPUnit can execute sorting tests - sorting tests were previously ignored because of the file name typo --- DIRECTORY.md | 2 +- tests/Sorting/{SortingTests.php => SortingTest.php} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/Sorting/{SortingTests.php => SortingTest.php} (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2ee4e28e..1ea28c85 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -104,7 +104,7 @@ * Sorting * [Arraykeyssorttest](./tests/Sorting/ArrayKeysSortTest.php) * [Gnomesorttest](./tests/Sorting/GnomeSortTest.php) - * [Sortingtests](./tests/Sorting/SortingTests.php) + * [Sortingtest](./tests/Sorting/SortingTest.php) * Strings * [Stringstest](./tests/Strings/StringsTest.php) diff --git a/tests/Sorting/SortingTests.php b/tests/Sorting/SortingTest.php similarity index 100% rename from tests/Sorting/SortingTests.php rename to tests/Sorting/SortingTest.php From 4c77cd2ff825c66e424d4f6d9a964151ab1813c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Fri, 6 Oct 2023 16:10:18 +0200 Subject: [PATCH 22/95] rename test case class. - to fix "Test case class not matching filename" deprecation warning --- tests/Sorting/SortingTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Sorting/SortingTest.php b/tests/Sorting/SortingTest.php index 81ac2e82..963c7232 100644 --- a/tests/Sorting/SortingTest.php +++ b/tests/Sorting/SortingTest.php @@ -13,7 +13,7 @@ require_once __DIR__ . '/../../Sorting/RadixSort.php'; require_once __DIR__ . '/../../Sorting/SelectionSort.php'; -class SortingTests extends TestCase +class SortingTest extends TestCase { public function testBubbleSort() { From 240260b21bbfb70f62e8a30a58763e98c2960932 Mon Sep 17 00:00:00 2001 From: Aryansh Bhargavan Date: Fri, 6 Oct 2023 21:48:11 +0530 Subject: [PATCH 23/95] Fix: Changes in formatting? --- Ciphers/VignereCipher.php | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/Ciphers/VignereCipher.php b/Ciphers/VignereCipher.php index 1ced1ebc..0dd1621f 100644 --- a/Ciphers/VignereCipher.php +++ b/Ciphers/VignereCipher.php @@ -1,22 +1,27 @@ +} \ No newline at end of file From 831d93b45430cb1082e09dcb8302b6f2cbdaa470 Mon Sep 17 00:00:00 2001 From: Aryansh Bhargavan Date: Fri, 6 Oct 2023 21:51:15 +0530 Subject: [PATCH 24/95] Fix: Changes in formatting? --- Ciphers/VignereCipher.php | 8 ++++---- tests/Ciphers/VignereCipherTest.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Ciphers/VignereCipher.php b/Ciphers/VignereCipher.php index 0dd1621f..d3bf1bf2 100644 --- a/Ciphers/VignereCipher.php +++ b/Ciphers/VignereCipher.php @@ -8,7 +8,7 @@ * @param string $key The encryption key. * @return string The encrypted text. */ -function vigenere_encrypt($plaintext, $key) +function vigenere_encrypt($plaintext, $key): string { // Convert the input to uppercase for consistency $plaintext = strtoupper($plaintext); @@ -39,13 +39,13 @@ function vigenere_encrypt($plaintext, $key) * @param string $key The decryption key. * @return string The decrypted text. */ -function vigenere_decrypt($ciphertext, $key) +function vigenere_decrypt($ciphertext, $key): string { $ciphertext = strtoupper($ciphertext); $key = strtoupper($key); $keyLength = strlen($key); $decryptedText = ""; - for ($i = 0; $i < strlen($ciphertext); $i++){ + for ($i = 0; $i < strlen($ciphertext); $i++) { $char = $ciphertext[$i]; if (ctype_alpha($char)) { // Calculate the shift based on the key @@ -60,4 +60,4 @@ function vigenere_decrypt($ciphertext, $key) } } return $decryptedText; -} \ No newline at end of file +} diff --git a/tests/Ciphers/VignereCipherTest.php b/tests/Ciphers/VignereCipherTest.php index 6c738881..37e572b8 100644 --- a/tests/Ciphers/VignereCipherTest.php +++ b/tests/Ciphers/VignereCipherTest.php @@ -16,4 +16,4 @@ public function testVignereCipher() $this->assertEquals($plaintext, $decryptedText); } -} \ No newline at end of file +} From 0bfc5b2b753460139efd04a3565aa58979337f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Fri, 6 Oct 2023 19:28:27 +0200 Subject: [PATCH 25/95] rename function `selectionSorting` to `selectionSort`. - follows the function naming practices in the folder - fixes "undefined function selectionSort" in SortingTest.php - selection sort tests are now passing --- Sorting/SelectionSort.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sorting/SelectionSort.php b/Sorting/SelectionSort.php index 20898edd..cc4ad3e0 100644 --- a/Sorting/SelectionSort.php +++ b/Sorting/SelectionSort.php @@ -6,7 +6,7 @@ * @param array $array * @return array */ -function selectionSorting(array $array) +function selectionSort(array $array) { $length = count($array); From b3774d70b05205d3e1baa38eb256bf52d1e37c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Fri, 6 Oct 2023 19:57:48 +0200 Subject: [PATCH 26/95] fix count sort. - no min and max as function parameters - count sort tests are now passing --- Sorting/CountSort.php | 6 +++--- tests/Sorting/SortingTest.php | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Sorting/CountSort.php b/Sorting/CountSort.php index acec08bc..1dec50e8 100644 --- a/Sorting/CountSort.php +++ b/Sorting/CountSort.php @@ -2,13 +2,13 @@ /** * @param $array - * @param $min - * @param $max * @return mixed */ -function countSort($array, $min, $max) +function countSort($array) { $count = array(); + $min = min($array); + $max = max($array); for ($i = $min; $i <= $max; $i++) { $count[$i] = 0; diff --git a/tests/Sorting/SortingTest.php b/tests/Sorting/SortingTest.php index 963c7232..7690e90e 100644 --- a/tests/Sorting/SortingTest.php +++ b/tests/Sorting/SortingTest.php @@ -76,9 +76,7 @@ public function testBubbleSort2() public function testCountSort() { $array = [-5, -10, 0, -3, 8, 5, -1, 10]; - $min = 0; - $max = 9; - $sorted = countSort($array, 0, 9); + $sorted = countSort($array); $this->assertEquals([-10, -5, -3, -1, 0, 5, 8, 10], $sorted); } @@ -247,8 +245,8 @@ public function testCountSortCipher() $secondArray = array(-6, 12, 14, 17, 5, 4, -9, 15, 0, -8); $expectedResultTwo = array(-9, -8, -6, 0, 4, 5, 12, 14, 15, 17); - $resultOne = countSort($firstArray, $minRange = -10, $maxRange = 20); - $resultTwo = countSort($secondArray, $minRange = -10, $maxRange = 20); + $resultOne = countSort($firstArray); + $resultTwo = countSort($secondArray); $this->assertEquals($expectedResultOne, $resultOne); $this->assertEquals($expectedResultTwo, $resultTwo); From 9b0cae985e821865e1726107850d09bafb3208cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Sat, 7 Oct 2023 08:30:28 +0200 Subject: [PATCH 27/95] adjust the given ranges in sorting tests. - shortening the given ranges - applied on the tests that hit PHP memory_limit - applied on the tests that exceed the expected duration time --- tests/Sorting/SortingTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Sorting/SortingTest.php b/tests/Sorting/SortingTest.php index 7690e90e..4537302d 100644 --- a/tests/Sorting/SortingTest.php +++ b/tests/Sorting/SortingTest.php @@ -213,7 +213,7 @@ public function testMergeSortPerformance() public function testQuickSortPerformance() { - $array = range(1, 1000000); + $array = range(1, 1000); $start = microtime(true); quickSort($array); $end = microtime(true); @@ -222,7 +222,7 @@ public function testQuickSortPerformance() public function testRadixSortPerformance() { - $array = range(1, 1000000); + $array = range(1, 100000); $start = microtime(true); radixSort($array); $end = microtime(true); @@ -231,7 +231,7 @@ public function testRadixSortPerformance() public function testSelectionSortPerformance() { - $array = range(1, 1000000); + $array = range(1, 10000); $start = microtime(true); selectionSort($array); $end = microtime(true); @@ -269,7 +269,7 @@ public function testQuickSortCipher() public function testHeapSortPerformance() { - $array = range(1, 1000000); + $array = range(1, 100000); shuffle($array); // Randomize the order $start = microtime(true); heapSort($array); From aca121fff0c54ed19b6564a5e2d3a36313fee12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Sat, 7 Oct 2023 08:33:44 +0200 Subject: [PATCH 28/95] rename test case class. - to fix "Test case not matching filename" deprecation warning --- tests/Ciphers/VignereCipherTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Ciphers/VignereCipherTest.php b/tests/Ciphers/VignereCipherTest.php index 37e572b8..dc39626f 100644 --- a/tests/Ciphers/VignereCipherTest.php +++ b/tests/Ciphers/VignereCipherTest.php @@ -5,7 +5,7 @@ require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../Ciphers/VignereCipher.php'; -class VignereCipher extends TestCase +class VignereCipherTest extends TestCase { public function testVignereCipher() { From 9910612fb9f124f40f0a271f30730f0d60d3e3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Sat, 7 Oct 2023 08:39:39 +0200 Subject: [PATCH 29/95] adjust the given ranges in sorting tests. --- tests/Sorting/SortingTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Sorting/SortingTest.php b/tests/Sorting/SortingTest.php index 4537302d..295304b2 100644 --- a/tests/Sorting/SortingTest.php +++ b/tests/Sorting/SortingTest.php @@ -204,7 +204,7 @@ public function testInsertionSortPerformance() public function testMergeSortPerformance() { - $array = range(1, 1000000); + $array = range(1, 100000); $start = microtime(true); mergeSort($array); $end = microtime(true); @@ -231,7 +231,7 @@ public function testRadixSortPerformance() public function testSelectionSortPerformance() { - $array = range(1, 10000); + $array = range(1, 1000); $start = microtime(true); selectionSort($array); $end = microtime(true); @@ -269,7 +269,7 @@ public function testQuickSortCipher() public function testHeapSortPerformance() { - $array = range(1, 100000); + $array = range(1, 10000); shuffle($array); // Randomize the order $start = microtime(true); heapSort($array); From 1d22da3b534a1a49183eca7e90312526ca21a758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Sat, 7 Oct 2023 09:20:06 +0200 Subject: [PATCH 30/95] modify xdebug.max_nesting_level. --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 076bab24..8f38f6ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,12 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + ini-values: xdebug.max_nesting_level=512 + - name: Validate composer.json and composer.lock run: composer validate From 53a432f59d0875878781dfc6675845b9c92ecc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anamarija=20Papi=C4=87?= Date: Sat, 7 Oct 2023 09:22:53 +0200 Subject: [PATCH 31/95] adjust the given ranges in sorting tests. --- tests/Sorting/SortingTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Sorting/SortingTest.php b/tests/Sorting/SortingTest.php index 295304b2..2f93b62a 100644 --- a/tests/Sorting/SortingTest.php +++ b/tests/Sorting/SortingTest.php @@ -204,7 +204,7 @@ public function testInsertionSortPerformance() public function testMergeSortPerformance() { - $array = range(1, 100000); + $array = range(1, 10000); $start = microtime(true); mergeSort($array); $end = microtime(true); @@ -222,7 +222,7 @@ public function testQuickSortPerformance() public function testRadixSortPerformance() { - $array = range(1, 100000); + $array = range(1, 10000); $start = microtime(true); radixSort($array); $end = microtime(true); From d4b163a564f73628224c960a220a9c4ed1db38ab Mon Sep 17 00:00:00 2001 From: "ravikishan.69" Date: Sun, 8 Oct 2023 14:41:32 +0530 Subject: [PATCH 32/95] Adding The Doubly Linked List In PHP --- DataStructures/DoublyLinkedList.php | 393 ++++++++++++++++++ tests/DataStructures/DoublyLinkedListTest.php | 164 ++++++++ 2 files changed, 557 insertions(+) create mode 100644 DataStructures/DoublyLinkedList.php create mode 100644 tests/DataStructures/DoublyLinkedListTest.php diff --git a/DataStructures/DoublyLinkedList.php b/DataStructures/DoublyLinkedList.php new file mode 100644 index 00000000..fef5f652 --- /dev/null +++ b/DataStructures/DoublyLinkedList.php @@ -0,0 +1,393 @@ +data = $data; + } +} + +/** + * Doubly Linked List + */ +class DoublyLinkedList { + public ?Node $head = null; + public ?Node $tail = null; + + // Constructor + public function __construct() + { + $this->head = null; + $this->tail = null; + } + + // Destructor + public function __destruct() + { + $this->head = null; + $this->tail = null; + } + + + // Append to the end of the list + public function append($data): void + { + $newNode = new Node($data); + + // If the list is empty, set the head and tail to the new node + if ($this->head === null) { + $this->head = $newNode; + $this->tail = $newNode; + return; + } + + // Otherwise, set the tail's next node to the new node + $this->tail->next = $newNode; + + // Set the new node's previous node to the tail + $newNode->prev = $this->tail; + + // Set the tail to the new node + $this->tail = $newNode; + } + + // Insert a node after a given position + public function insert($data, $position): void + { + $newNode = new Node($data); + + // If the list is empty, set the head and tail to the new node + if ($this->head === null) { + $this->head = $newNode; + $this->tail = $newNode; + return; + } + + // If the position is 0, set the new node's next node to the head + // Set the head's previous node to the new node + // Set the head to the new node + if ($position === 0) { + $newNode->next = $this->head; + $this->head->prev = $newNode; + $this->head = $newNode; + return; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the position + for ($i = 0; $i < $position; $i++) { + // If the current node is null, we've reached the end of the list + // Set the tail's next node to the new node + // Set the new node's previous node to the tail + // Set the tail to the new node + if ($current === null) { + $this->tail->next = $newNode; + $newNode->prev = $this->tail; + $this->tail = $newNode; + return; + } + + // Otherwise, set the current node to the next node + $current = $current->next; + } + + // Set the new node's next node to the current node + // Set the new node's previous node to the current node's previous node + // Set the current node's previous node's next node to the new node + // Set the current node's previous node to the new node + $newNode->next = $current; + $newNode->prev = $current->prev; + $current->prev->next = $newNode; + $current->prev = $newNode; + } + + // Delete a node from the list + public function delete($data): void + { + // If the list is empty, return + if ($this->head === null) { + return; + } + + // If the head's data is the data we're looking for + // Set the head to the head's next node + // Set the head's previous node to null + if ($this->head->data === $data) { + $this->head = $this->head->next; + $this->head->prev = null; + return; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // If the current node's data is the data we're looking for + // Set the current node's previous node's next node to the current node's next node + // Set the current node's next node's previous node to the current node's previous node + if ($current->data === $data) { + $current->prev->next = $current->next; + $current->next->prev = $current->prev; + return; + } + + // Otherwise, set the current node to the next node + $current = $current->next; + } + } + + // Delete a node from a given position + public function deleteAt($position): void + { + // If the list is empty, return + if ($this->head === null) { + return; + } + + // If the position is 0 + // Set the head to the head's next node + // Set the head's previous node to null + if ($position === 0) { + $this->head = $this->head->next; + $this->head->prev = null; + return; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the position + for ($i = 0; $i < $position; $i++) { + // If the current node is null, we've reached the end of the list + // Set the tail to the current node's previous node + // Set the tail's next node to null + if ($current === null) { + $this->tail = $current->prev; + $this->tail->next = null; + return; + } + + // Otherwise, set the current node to the next node + $current = $current->next; + } + + // Set the current node's previous node's next node to the current node's next node + // Set the current node's next node's previous node to the current node's previous node + $current->prev->next = $current->next; + $current->next->prev = $current->prev; + } + + // Print the list + public function printList(): void + { + // If the list is empty, return + if ($this->head === null) { + return; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // Print the current node's data + echo $current->data . "\n"; + + // Set the current node to the next node + $current = $current->next; + } + } + + // Print the list in reverse + public function printListReverse(): void + { + // If the list is empty, return + if ($this->head === null) { + return; + } + + // Otherwise, set the current node to the tail + $current = $this->tail; + + // Loop through the list until we reach the beginning of the list + while ($current !== null) { + // Print the current node's data + echo $current->data . "\n"; + + // Set the current node to the previous node + $current = $current->prev; + } + } + + // Reverse the list + public function reverse(): void + { + // If the list is empty, return + if ($this->head === null) { + return; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // Set the temp node to the current node's next node + $temp = $current->next; + + // Set the current node's next node to the current node's previous node + $current->next = $current->prev; + + // Set the current node's previous node to the temp node + $current->prev = $temp; + + // Set the current node to the temp node + $current = $temp; + } + + // Set the temp node to the head + $temp = $this->head; + + // Set the head to the tail + $this->head = $this->tail; + + // Set the tail to the temp node + $this->tail = $temp; + } + + // Get the length of the list + public function length(): int + { + // If the list is empty, return 0 + if ($this->head === null) { + return 0; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Set the length to 0 + $length = 0; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // Increment the length + $length++; + + // Set the current node to the next node + $current = $current->next; + } + + // Return the length + return $length; + } + + // Search for a node + public function search($data): ?Node + { + // If the list is empty, return null + if ($this->head === null) { + return null; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // If the current node's data is the data we're looking for, return the current node + if ($current->data === $data) { + return $current; + } + + // Set the current node to the next node + $current = $current->next; + } + + // Return null + return null; + } + + // Is the list empty? + public function isEmpty(): bool + { + // If the head is null, return true + if ($this->head === null) { + return true; + } + + // Otherwise, return false + return false; + } + + // To String + public function __toString(): string + { + // If the list is empty, return an empty string + if ($this->head === null) { + return ''; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Set the string to an empty string + $string = ''; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // Append the current node's data to the string + $string .= $current->data; + + // If the current node's next node is not null, append a comma and a space to the string + if ($current->next !== null) { + $string .= ', '; + } + + // Set the current node to the next node + $current = $current->next; + } + + // Return the string + return $string; + } + + // To Array + public function toArray(): array + { + // If the list is empty, return an empty array + if ($this->head === null) { + return []; + } + + // Otherwise, set the current node to the head + $current = $this->head; + + // Set the array to an empty array + $array = []; + + // Loop through the list until we reach the end of the list + while ($current !== null) { + // Append the current node's data to the array + $array[] = $current->data; + + // Set the current node to the next node + $current = $current->next; + } + + // Return the array + return $array; + } +} \ No newline at end of file diff --git a/tests/DataStructures/DoublyLinkedListTest.php b/tests/DataStructures/DoublyLinkedListTest.php new file mode 100644 index 00000000..724b1555 --- /dev/null +++ b/tests/DataStructures/DoublyLinkedListTest.php @@ -0,0 +1,164 @@ +assertEquals(0, $list->length()); + } + + /** + * Test for the append method + */ + public function testAppend() + { + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->assertEquals(3, $list->length()); + } + + /** + * Test for the insert method + */ + public function testInsert() + { + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $list->insert(1, 4); + $this->assertEquals(4, $list->length()); + } + + /** + * Test for the delete method + */ + public function testDelete() + { + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $list->delete(1); + $this->assertEquals(2, $list->length()); + } + + /** + * Test for the deleteAt method + */ + public function testDeleteAt() + { + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $list->deleteAt(1); + $this->assertEquals(2, $list->length()); + } + + /** + * Test for printList method + */ + public function testPrintList() + { + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->expectOutputString("1\n2\n3\n"); + $list->printList(); + } + + /** + * Test for the printListReverse method + */ + public function testPrintListReverse(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->expectOutputString("3\n2\n1\n"); + $list->printListReverse(); + } + + /** + * Test for the reverse method + */ + public function testReverse(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $list->reverse(); + $this->expectOutputString("3\n2\n1\n"); + $list->printList(); + } + + /** + * Test for the length method + */ + public function testLength(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->assertEquals(3, $list->length()); + } + /** + * Test for the Search method + */ + public function testSearch(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $searchItem = $list->search(2); + $this->assertEquals(2, $searchItem->data); + } + + /** + * Test for the isEmpty method + */ + public function testIsEmpty(){ + $list = new DoublyLinkedList(); + $this->assertEquals(true, $list->isEmpty()); + } + + /** + * Test for __toString method + */ + public function testToString(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->expectOutputString("1, 2, 3"); + echo $list; + } + + /** + * Test for the toArray method + */ + public function testToArray(){ + $list = new DoublyLinkedList(); + $list->append(1); + $list->append(2); + $list->append(3); + $this->assertEquals([1,2,3], $list->toArray()); + } +} From 0288d0f468f227be5fb022f8cdaf92bbce8b18e3 Mon Sep 17 00:00:00 2001 From: "ravikishan.69" Date: Sun, 8 Oct 2023 14:45:19 +0530 Subject: [PATCH 33/95] Update the Directory.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 5e975d81..888ac78a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -16,6 +16,7 @@ ## Datastructures * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) + * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) ## Graphs * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) @@ -95,6 +96,7 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) + * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * Graphs * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) From 599577bf9a38102dc760293f9bcd2b41837b7241 Mon Sep 17 00:00:00 2001 From: Aryansh B Date: Mon, 9 Oct 2023 06:11:13 +0530 Subject: [PATCH 34/95] Added Railfence Cipher (#134) * Feat: Added Railfence Cipher --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- Ciphers/RailfenceCipher.php | 81 +++++++++++++++++++++++++++ DIRECTORY.md | 2 + tests/Ciphers/RailfenceCipherTest.php | 26 +++++++++ 3 files changed, 109 insertions(+) create mode 100644 Ciphers/RailfenceCipher.php create mode 100644 tests/Ciphers/RailfenceCipherTest.php diff --git a/Ciphers/RailfenceCipher.php b/Ciphers/RailfenceCipher.php new file mode 100644 index 00000000..683b8a09 --- /dev/null +++ b/Ciphers/RailfenceCipher.php @@ -0,0 +1,81 @@ + $rowIndex) { + $lengths[$rowIndex]++; + } + if ($balance > ($rails + ($rails - $rowIndex) - 2)) { + $lengths[$rowIndex]++; + } + $strings[] = substr($cipherMessage, $totalLengths, $lengths[$rowIndex]); + $totalLengths += $lengths[$rowIndex]; + } + // Convert the rows of characters to plain message + $plainText = ''; + while (strlen($plainText) < $textLength) { + for ($charIndex = 0; $charIndex < $position; $charIndex++) { + if (isset($strings[$charIndex])) { + $index = $charIndex; + } else { + $index = $position - $charIndex; + } + $plainText .= substr($strings[$index], 0, 1); + $strings[$index] = substr($strings[$index], 1); + } + } + return $plainText; +} diff --git a/DIRECTORY.md b/DIRECTORY.md index 5e975d81..1ba781fa 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -4,6 +4,7 @@ * [Caesarcipher](./Ciphers/CaesarCipher.php) * [Monoalphabeticcipher](./Ciphers/MonoAlphabeticCipher.php) * [Morsecode](./Ciphers/MorseCode.php) + * [Railfencecipher](./Ciphers/RailfenceCipher.php) * [Vignerecipher](./Ciphers/VignereCipher.php) * [Xorcipher](./Ciphers/XORCipher.php) @@ -90,6 +91,7 @@ * [Cipherstest](./tests/Ciphers/CiphersTest.php) * [Monoalphabeticciphertest](./tests/Ciphers/MonoAlphabeticCipherTest.php) * [Morsecodetest](./tests/Ciphers/MorseCodeTest.php) + * [Railfenceciphertest](./tests/Ciphers/RailfenceCipherTest.php) * [Vignereciphertest](./tests/Ciphers/VignereCipherTest.php) * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) diff --git a/tests/Ciphers/RailfenceCipherTest.php b/tests/Ciphers/RailfenceCipherTest.php new file mode 100644 index 00000000..dbfcc0d2 --- /dev/null +++ b/tests/Ciphers/RailfenceCipherTest.php @@ -0,0 +1,26 @@ +assertEquals($plainMessage, $decodedMessage); + } + public function testRailFenceCipherCase2() + { + $plainMessage = "THIS IS RAILFENCE"; + $rails = 3; + $cipherMessage = Railencode($plainMessage, $rails); + $decodedMessage = Raildecode($cipherMessage, $rails); + $this->assertEquals($plainMessage, $decodedMessage); + } +} From 91da5832673f1802be5f66eaabc3222d4fafe4ef Mon Sep 17 00:00:00 2001 From: "ravikishan.69" Date: Tue, 10 Oct 2023 08:11:21 +0530 Subject: [PATCH 35/95] Fixes the lint errors --- DataStructures/DoublyLinkedList.php | 22 ++++--------------- DataStructures/Node.php | 17 ++++++++++++++ tests/DataStructures/DoublyLinkedListTest.php | 21 ++++++++++++------ 3 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 DataStructures/Node.php diff --git a/DataStructures/DoublyLinkedList.php b/DataStructures/DoublyLinkedList.php index fef5f652..42a423b3 100644 --- a/DataStructures/DoublyLinkedList.php +++ b/DataStructures/DoublyLinkedList.php @@ -1,25 +1,12 @@ data = $data; - } -} +require_once __DIR__ . '/Node.php'; /** * Doubly Linked List */ -class DoublyLinkedList { +class DoublyLinkedList +{ public ?Node $head = null; public ?Node $tail = null; @@ -36,7 +23,6 @@ public function __destruct() $this->head = null; $this->tail = null; } - // Append to the end of the list public function append($data): void @@ -390,4 +376,4 @@ public function toArray(): array // Return the array return $array; } -} \ No newline at end of file +} diff --git a/DataStructures/Node.php b/DataStructures/Node.php new file mode 100644 index 00000000..e7178d08 --- /dev/null +++ b/DataStructures/Node.php @@ -0,0 +1,17 @@ +data = $data; + } +} diff --git a/tests/DataStructures/DoublyLinkedListTest.php b/tests/DataStructures/DoublyLinkedListTest.php index 724b1555..a3a62072 100644 --- a/tests/DataStructures/DoublyLinkedListTest.php +++ b/tests/DataStructures/DoublyLinkedListTest.php @@ -87,7 +87,8 @@ public function testPrintList() /** * Test for the printListReverse method */ - public function testPrintListReverse(){ + public function testPrintListReverse() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); @@ -99,7 +100,8 @@ public function testPrintListReverse(){ /** * Test for the reverse method */ - public function testReverse(){ + public function testReverse() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); @@ -112,7 +114,8 @@ public function testReverse(){ /** * Test for the length method */ - public function testLength(){ + public function testLength() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); @@ -122,7 +125,8 @@ public function testLength(){ /** * Test for the Search method */ - public function testSearch(){ + public function testSearch() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); @@ -134,7 +138,8 @@ public function testSearch(){ /** * Test for the isEmpty method */ - public function testIsEmpty(){ + public function testIsEmpty() + { $list = new DoublyLinkedList(); $this->assertEquals(true, $list->isEmpty()); } @@ -142,7 +147,8 @@ public function testIsEmpty(){ /** * Test for __toString method */ - public function testToString(){ + public function testToString() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); @@ -154,7 +160,8 @@ public function testToString(){ /** * Test for the toArray method */ - public function testToArray(){ + public function testToArray() + { $list = new DoublyLinkedList(); $list->append(1); $list->append(2); From 53d6cd64ea9bd07c03cd23908fa3d5c561265436 Mon Sep 17 00:00:00 2001 From: Aryansh B Date: Tue, 10 Oct 2023 09:12:42 +0530 Subject: [PATCH 36/95] Added Atbash Cipher! (#136) --- Ciphers/AtbashCipher.php | 39 ++++++++++++++++++++++++++++++ DIRECTORY.md | 2 ++ tests/Ciphers/AtbashCipherTest.php | 25 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 Ciphers/AtbashCipher.php create mode 100644 tests/Ciphers/AtbashCipherTest.php diff --git a/Ciphers/AtbashCipher.php b/Ciphers/AtbashCipher.php new file mode 100644 index 00000000..655365c4 --- /dev/null +++ b/Ciphers/AtbashCipher.php @@ -0,0 +1,39 @@ +assertEquals($plainText, $decryptedText); + } + + public function testWithNonAlphabetCharacters() + { + $plainText = "HELLO, WORLD!"; + $encryptedText = atbash_encrypt($plainText); + $decryptedText = atbash_decrypt($encryptedText); + $this->assertEquals($plainText, $decryptedText); + } +} From e4457ea9faa8dc85a9226ca0b28ce49aba025abd Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 10 Oct 2023 03:46:03 +0000 Subject: [PATCH 37/95] updating DIRECTORY.md --- DIRECTORY.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 837a4c1b..d6df201e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -17,8 +17,9 @@ * [Speedconversion](./Conversions/SpeedConversion.php) ## Datastructures - * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) + * [Node](./DataStructures/Node.php) + * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) ## Graphs * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) @@ -99,8 +100,8 @@ * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures - * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) + * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * Graphs * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) From 4c3507b5629c29e5faca5a68dfb623894dcefa87 Mon Sep 17 00:00:00 2001 From: salehhashemi1992 <81674631+salehhashemi1992@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:40:11 +0330 Subject: [PATCH 38/95] update checkout to v4 --- .github/workflows/ci.yml | 2 +- .github/workflows/code-style.yml | 2 +- .github/workflows/directory_workflow.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 076bab24..5df17acd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Validate composer.json and composer.lock run: composer validate diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index 71333e19..e852dfc9 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/directory_workflow.yml b/.github/workflows/directory_workflow.yml index 0b9b06d8..7564a2df 100644 --- a/.github/workflows/directory_workflow.yml +++ b/.github/workflows/directory_workflow.yml @@ -8,7 +8,7 @@ jobs: name: DIRECTORY.md runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 # v2 is broken for git diff + - uses: actions/checkout@v4 - uses: actions/setup-python@v2 - name: Setup Git Specs run: | From 2553639335f4f5885e61e6e5a7c9c0cdbda82735 Mon Sep 17 00:00:00 2001 From: Carlos Afonso Date: Sun, 22 Oct 2023 21:35:08 +0100 Subject: [PATCH 39/95] Add Greatest Common Divisor --- Maths/GreatestCommonDivisor.php | 16 ++++++++++++++++ tests/Maths/MathsTest.php | 11 +++++++++++ 2 files changed, 27 insertions(+) create mode 100644 Maths/GreatestCommonDivisor.php diff --git a/Maths/GreatestCommonDivisor.php b/Maths/GreatestCommonDivisor.php new file mode 100644 index 00000000..54758359 --- /dev/null +++ b/Maths/GreatestCommonDivisor.php @@ -0,0 +1,16 @@ + 6 + * @param int $a + * @param int $b + * @return int + */ +function gcd(int $a, int $b): int +{ + if ($b == 0) { + return $a; + } + return gcd($b, $a % $b); +} diff --git a/tests/Maths/MathsTest.php b/tests/Maths/MathsTest.php index 3d6f3008..e4222889 100644 --- a/tests/Maths/MathsTest.php +++ b/tests/Maths/MathsTest.php @@ -12,6 +12,7 @@ require_once __DIR__ . '/../../Maths/FastExponentiation.php'; require_once __DIR__ . '/../../Maths/Fibonacci.php'; require_once __DIR__ . '/../../Maths/Fibonacci2.php'; +require_once __DIR__ . '/../../Maths/GreatestCommonDivisor.php'; require_once __DIR__ . '/../../Maths/NeonNumber.php'; require_once __DIR__ . '/../../Maths/PerfectSquare.php'; require_once __DIR__ . '/../../Maths/Mean.php'; @@ -143,4 +144,14 @@ public function testMode() $this->assertEquals([1, 2, 3, 4, 5], mode(1, 2, 3, 4, 5)); $this->assertEquals([2, 3, 4], mode(2, 2, 3, 3, 4, 4)); } + + public function testGreatestCommonDivisor() + { + $this->assertEquals(8, gcd(24, 16)); + $this->assertEquals(5, gcd(10, 5)); + $this->assertEquals(25, gcd(100, 75)); + $this->assertEquals(6, gcd(12, 18)); + $this->assertEquals(5, gcd(10, 15)); + $this->assertEquals(3, gcd(9, 12)); + } } From 7bb741407983e022c78277069747eb8a3a05373d Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 22 Oct 2023 20:35:49 +0000 Subject: [PATCH 40/95] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index d6df201e..e4f956d3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -35,6 +35,7 @@ * [Fastexponentiation](./Maths/FastExponentiation.php) * [Fibonacci](./Maths/Fibonacci.php) * [Fibonacci2](./Maths/Fibonacci2.php) + * [Greatestcommondivisor](./Maths/GreatestCommonDivisor.php) * [Mean](./Maths/Mean.php) * [Median](./Maths/Median.php) * [Mode](./Maths/Mode.php) From 9e8e95a5acff560014b60ab224f8df6728174145 Mon Sep 17 00:00:00 2001 From: Shivani Yadav <137782859+shivani51yadav@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:13:28 +0530 Subject: [PATCH 41/95] Adding the Stack Data Structure (#137) * Adding the Stack Data Structure * Fixing the Lint Issue --- DIRECTORY.md | 2 + DataStructures/Stack.php | 85 ++++++++++++++++++++++ tests/DataStructures/StackTest.php | 110 +++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 DataStructures/Stack.php create mode 100644 tests/DataStructures/StackTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index e4f956d3..8a44204a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -20,6 +20,7 @@ * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) + * [Stack](./DataStructures/Stack.php) ## Graphs * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) @@ -103,6 +104,7 @@ * Datastructures * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) + * [Stacktest](./tests/DataStructures/StackTest.php) * Graphs * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) diff --git a/DataStructures/Stack.php b/DataStructures/Stack.php new file mode 100644 index 00000000..eaeb7c58 --- /dev/null +++ b/DataStructures/Stack.php @@ -0,0 +1,85 @@ +stack = $array; + } + + public function __destruct() + { + unset($this->stack); + } + + + public function push($data): void + { + array_push($this->stack, $data); + } + + public function pop() + { + return array_pop($this->stack); + } + + public function peek() + { + return $this->stack[count($this->stack) - 1]; + } + + public function isEmpty(): bool + { + return empty($this->stack); + } + + public function print(): void + { + echo implode(', ', $this->stack); + } + + public function __toString(): string + { + return implode(', ', $this->stack); + } + + public function length(): int + { + return count($this->stack); + } + + public function clear(): void + { + $this->stack = []; + } + + public function search($data): int + { + return array_search($data, $this->stack); + } + + public function toArray(): array + { + return $this->stack; + } + + public function fromArray(array $array): void + { + $this->stack = $array; + } + + public function reverse(): void + { + $this->stack = array_reverse($this->stack); + } + + public function sort(): void + { + sort($this->stack); + } +} diff --git a/tests/DataStructures/StackTest.php b/tests/DataStructures/StackTest.php new file mode 100644 index 00000000..7f1c44be --- /dev/null +++ b/tests/DataStructures/StackTest.php @@ -0,0 +1,110 @@ +assertEquals([1, 2, 3], $stack->toArray()); + } + + public function testDestruct() + { + $stack = new Stack([1, 2, 3]); + unset($stack); + $this->expectNotToPerformAssertions(); + } + + public function testPush() + { + $stack = new Stack(); + $stack->push(1); + $stack->push(2); + $stack->push(3); + $this->assertEquals([1, 2, 3], $stack->toArray()); + } + + public function testPop() + { + $stack = new Stack([1, 2, 3]); + $this->assertEquals(3, $stack->pop()); + $this->assertEquals([1, 2], $stack->toArray()); + } + + public function testPeek() + { + $stack = new Stack([1, 2, 3]); + $this->assertEquals(3, $stack->peek()); + } + + public function testIsEmpty() + { + $stack = new Stack(); + $this->assertTrue($stack->isEmpty()); + } + + public function testPrint() + { + $stack = new Stack([1, 2, 3]); + $this->expectOutputString("1, 2, 3"); + $stack->print(); + } + + public function testToString() + { + $stack = new Stack([1, 2, 3]); + $this->expectOutputString("1, 2, 3"); + echo $stack; + } + + public function testLength() + { + $stack = new Stack([1, 2, 3]); + $this->assertEquals(3, $stack->length()); + } + + public function testClear() + { + $stack = new Stack([1, 2, 3]); + $stack->clear(); + $this->assertEquals([], $stack->toArray()); + } + + public function testSearch() + { + $stack = new Stack([1, 2, 3]); + $this->assertEquals(2, $stack->search(3)); + } + + public function testToArray() + { + $stack = new Stack([1, 2, 3]); + $this->assertEquals([1, 2, 3], $stack->toArray()); + } + + public function testFromArray() + { + $stack = new Stack(); + $stack->fromArray([1, 2, 3]); + $this->assertEquals([1, 2, 3], $stack->toArray()); + } + + public function testReverse() + { + $stack = new Stack([1, 2, 3]); + $stack->reverse(); + $this->assertEquals([3, 2, 1], $stack->toArray()); + } + + public function testSort() + { + $stack = new Stack([3, 2, 1]); + $stack->sort(); + $this->assertEquals([1, 2, 3], $stack->toArray()); + } +} From 33c30c297fbf125c79f2af1e2dfa499197bec2f7 Mon Sep 17 00:00:00 2001 From: Rohit Chaudhari <100275369+rohitkbc@users.noreply.github.com> Date: Fri, 27 Oct 2023 23:09:40 +0530 Subject: [PATCH 42/95] Add Gitpod setup (#129) * Create .gitpod.Dockerfile * Create .gitpod.yml * Add gitpod badge --- .gitpod.Dockerfile | 1 + .gitpod.yml | 6 ++++++ README.md | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 .gitpod.Dockerfile create mode 100644 .gitpod.yml diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 00000000..44d236a3 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1 @@ +FROM gitpod/workspace-full:2022-05-08-14-31-53 diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..94081db9 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +image: + file: .gitpod.Dockerfile + +tasks: + - init: | + echo "Welcome to TheAlgorithms/PHP" diff --git a/README.md b/README.md index 7afa9dc6..f7a4a144 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # The Algorithms - PHP +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/PHP) + The Algorithms - PHP, is a library or framework written in the PHP programming language that provides a set of algorithms and data structures for various computational tasks. It aims to simplify the implementation of common algorithms and data structures in PHP, making it easier for developers to leverage these powerful tools in their projects. ### All algorithms implemented in PHP - for education From e0d414bb4a4e2307a13fe57333642282109d9efd Mon Sep 17 00:00:00 2001 From: Aryansh B Date: Mon, 30 Oct 2023 23:52:15 +0530 Subject: [PATCH 43/95] Added Fast Inverse Square root (#142) --- DIRECTORY.md | 1 + Maths/FastInverseSquareRoot.php | 22 ++++++++++++++++++++++ tests/Maths/MathsTest.php | 7 +++++++ 3 files changed, 30 insertions(+) create mode 100644 Maths/FastInverseSquareRoot.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 8a44204a..fbe8733d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -34,6 +34,7 @@ * [Checkprime](./Maths/CheckPrime.php) * [Factorial](./Maths/Factorial.php) * [Fastexponentiation](./Maths/FastExponentiation.php) + * [Fastinversesquareroot](./Maths/FastInverseSquareRoot.php) * [Fibonacci](./Maths/Fibonacci.php) * [Fibonacci2](./Maths/Fibonacci2.php) * [Greatestcommondivisor](./Maths/GreatestCommonDivisor.php) diff --git a/Maths/FastInverseSquareRoot.php b/Maths/FastInverseSquareRoot.php new file mode 100644 index 00000000..3cc9dd70 --- /dev/null +++ b/Maths/FastInverseSquareRoot.php @@ -0,0 +1,22 @@ +> 1); + // Convert the integer back to a float + $y = unpack('f', pack('l', $i))[1]; + // Perform the final calculation + return $y * (1.5 - 0.5 * $x * $y * $y); +} diff --git a/tests/Maths/MathsTest.php b/tests/Maths/MathsTest.php index e4222889..276ad97d 100644 --- a/tests/Maths/MathsTest.php +++ b/tests/Maths/MathsTest.php @@ -18,6 +18,7 @@ require_once __DIR__ . '/../../Maths/Mean.php'; require_once __DIR__ . '/../../Maths/Median.php'; require_once __DIR__ . '/../../Maths/Mode.php'; +require_once __DIR__ . '/../../Maths/FastInverseSquareRoot.php'; class MathsTest extends TestCase { @@ -154,4 +155,10 @@ public function testGreatestCommonDivisor() $this->assertEquals(5, gcd(10, 15)); $this->assertEquals(3, gcd(9, 12)); } + + public function testFastInverseSquareRoot() + { + $this->assertEqualsWithDelta(0.31568579235273, fastInvSqrt(10), 0.00001); + $this->assertEqualsWithDelta(0.49915357479239, fastInvSqrt(4), 0.00001); + } } From 747e8d1197e78529aa1388d6b2960fcbcdfe3208 Mon Sep 17 00:00:00 2001 From: Sevada <64130935+Sevada797@users.noreply.github.com> Date: Wed, 13 Dec 2023 21:30:45 +0400 Subject: [PATCH 44/95] Create BaseX.php (#140) * Create BaseX.php This function let's you calculate base X of any number <11 * Update BaseX.php * Update DIRECTORY.md * Update BaseX.php * Update BaseX.php * Update DIRECTORY.md Co-authored-by: Brandon Johnson * Update MathsTest.php * Update tests/Maths/MathsTest.php Co-authored-by: Brandon Johnson * Update MathsTest.php * Update tests/Maths/MathsTest.php Co-authored-by: Brandon Johnson * Update tests/Maths/MathsTest.php Co-authored-by: Brandon Johnson --------- Co-authored-by: Brandon Johnson --- DIRECTORY.md | 1 + Maths/BaseX.php | 29 +++++++++++++++++++++++++++++ tests/Maths/MathsTest.php | 9 +++++++++ 3 files changed, 39 insertions(+) create mode 100644 Maths/BaseX.php diff --git a/DIRECTORY.md b/DIRECTORY.md index fbe8733d..1fea47f5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -30,6 +30,7 @@ * [Absolutemax](./Maths/AbsoluteMax.php) * [Absolutemin](./Maths/AbsoluteMin.php) * [Armstrongnumber](./Maths/ArmstrongNumber.php) + * [BaseX](./Maths/BaseX.php) * [Checkpalindrome](./Maths/CheckPalindrome.php) * [Checkprime](./Maths/CheckPrime.php) * [Factorial](./Maths/Factorial.php) diff --git a/Maths/BaseX.php b/Maths/BaseX.php new file mode 100644 index 00000000..58483980 --- /dev/null +++ b/Maths/BaseX.php @@ -0,0 +1,29 @@ +assertEquals(11, baseX(3, 2)); + $this->assertEquals(22, baseX(8, 3)); + $this->assertEquals(21, baseX(15, 7)); + $this->assertEquals(20, baseX(16, 8)); + } + public function testFactorial() { $this->assertEquals(1, factorial(1)); From 210c00d2c1f318bd1d534c708f069477fb2bbc2c Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:30:59 +0000 Subject: [PATCH 45/95] updating DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 1fea47f5..d5b06fe9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -30,7 +30,7 @@ * [Absolutemax](./Maths/AbsoluteMax.php) * [Absolutemin](./Maths/AbsoluteMin.php) * [Armstrongnumber](./Maths/ArmstrongNumber.php) - * [BaseX](./Maths/BaseX.php) + * [Basex](./Maths/BaseX.php) * [Checkpalindrome](./Maths/CheckPalindrome.php) * [Checkprime](./Maths/CheckPrime.php) * [Factorial](./Maths/Factorial.php) From 7ef67994670caf1153965e6639fd793fb0953075 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:19:37 +0100 Subject: [PATCH 46/95] fix: use `GITHUB_ACTOR` in `git config` (#147) --- .github/workflows/directory_workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/directory_workflow.yml b/.github/workflows/directory_workflow.yml index 7564a2df..05fca5d4 100644 --- a/.github/workflows/directory_workflow.yml +++ b/.github/workflows/directory_workflow.yml @@ -12,8 +12,8 @@ jobs: - uses: actions/setup-python@v2 - name: Setup Git Specs run: | - git config --global user.name github-actions - git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY - name: Update DIRECTORY.md shell: python From 09edaaa60b65f6b60524c9b78d53d76f03d6646f Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:46:27 +0100 Subject: [PATCH 47/95] chore: update `actions/cache` to `v3` (#149) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5752cc3..35560791 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: vendor key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} From 13972e0072a3eb715be63fa8ca3a8f87dc2c3af3 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:46:54 +0100 Subject: [PATCH 48/95] chore: update `actions/setup-python` to `v5` (#148) --- .github/workflows/directory_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/directory_workflow.yml b/.github/workflows/directory_workflow.yml index 05fca5d4..a75aeeeb 100644 --- a/.github/workflows/directory_workflow.yml +++ b/.github/workflows/directory_workflow.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 - name: Setup Git Specs run: | git config --global user.name "$GITHUB_ACTOR" From e502f58c807b778a946479b89ae939122cd8cf39 Mon Sep 17 00:00:00 2001 From: Michal Zarnecki Date: Sun, 5 May 2024 13:08:47 +0200 Subject: [PATCH 49/95] Bellman-Ford shortest path algorithm PHP implementation --- Graphs/BellmanFord.php | 51 ++++++++++++++++++++++++++++++++ tests/Graphs/BellmanFordTest.php | 48 ++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 Graphs/BellmanFord.php create mode 100644 tests/Graphs/BellmanFordTest.php diff --git a/Graphs/BellmanFord.php b/Graphs/BellmanFord.php new file mode 100644 index 00000000..f830d20d --- /dev/null +++ b/Graphs/BellmanFord.php @@ -0,0 +1,51 @@ + $minWeight) { + if ($verbose) { + echo "checking vertice $vertice\n"; + } + if ($start === $vertice) { + $vertices[$vertice] = 0; + } + + foreach ($edges[$vertice] as $edge) { + if ($vertices[$edge->end] > $vertices[$vertice] + $edge->weight ) { + if ($verbose) { + echo "replace $vertice " . $vertices[$edge->end] . " with " . $vertices[$vertice] + $edge->weight . "\n "; + } + $vertices[$edge->end] = $vertices[$vertice] + $edge->weight; + $change = true; + } + } + } + $round++; + } + return $vertices; +} \ No newline at end of file diff --git a/tests/Graphs/BellmanFordTest.php b/tests/Graphs/BellmanFordTest.php new file mode 100644 index 00000000..ff2543c5 --- /dev/null +++ b/tests/Graphs/BellmanFordTest.php @@ -0,0 +1,48 @@ +start = $edgeRaw[0]; + $edge->end = $edgeRaw[2]; + $edge->weight = $edgeRaw[1]; + if (! isset($edges[$edgeRaw[0]])) { + $edges[$edgeRaw[0]] = []; + } + $edges[$edgeRaw[0]][] = $edge; + } + + $result = bellmanFord($vertices, $edges, 'S'); + + $this->assertEquals($result, [ + 'S' => 0, + 'A' => 5, + 'B' => 5, + 'C' => 7, + 'D' => 9, + 'E'=> 8 + ]); + } +} From 4661257890981f62747cef10ea387b36ebbcb789 Mon Sep 17 00:00:00 2001 From: Michal Zarnecki Date: Sun, 5 May 2024 13:12:27 +0200 Subject: [PATCH 50/95] Bellman-Ford code formatting --- Graphs/BellmanFord.php | 7 ++++--- tests/Graphs/BellmanFordTest.php | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Graphs/BellmanFord.php b/Graphs/BellmanFord.php index f830d20d..271941e3 100644 --- a/Graphs/BellmanFord.php +++ b/Graphs/BellmanFord.php @@ -1,6 +1,7 @@ end] > $vertices[$vertice] + $edge->weight ) { + if ($vertices[$edge->end] > $vertices[$vertice] + $edge->weight) { if ($verbose) { echo "replace $vertice " . $vertices[$edge->end] . " with " . $vertices[$vertice] + $edge->weight . "\n "; } - $vertices[$edge->end] = $vertices[$vertice] + $edge->weight; + $vertices[$edge->end] = $vertices[$vertice] + $edge->weight; $change = true; } } diff --git a/tests/Graphs/BellmanFordTest.php b/tests/Graphs/BellmanFordTest.php index ff2543c5..c483cb34 100644 --- a/tests/Graphs/BellmanFordTest.php +++ b/tests/Graphs/BellmanFordTest.php @@ -19,16 +19,16 @@ public function testBellmanFord() ['C', -2, 'B'], ['B', 1, 'A'], ]; - $vertices = [ 'S', 'A', 'B', 'C', 'D', 'E',]; + $vertices = ['S', 'A', 'B', 'C', 'D', 'E',]; #prepare array of edges listed by edge start to simplify Bellman-Ford updating weights of other edges $edges = []; - foreach($edgesRaw as $edgeRaw) { + foreach ($edgesRaw as $edgeRaw) { $edge = new Edge(); $edge->start = $edgeRaw[0]; $edge->end = $edgeRaw[2]; $edge->weight = $edgeRaw[1]; - if (! isset($edges[$edgeRaw[0]])) { + if (!isset($edges[$edgeRaw[0]])) { $edges[$edgeRaw[0]] = []; } $edges[$edgeRaw[0]][] = $edge; @@ -42,7 +42,7 @@ public function testBellmanFord() 'B' => 5, 'C' => 7, 'D' => 9, - 'E'=> 8 + 'E' => 8 ]); } } From 46582b87029fd58ce475abbc1e2153cc008992e9 Mon Sep 17 00:00:00 2001 From: Michal Zarnecki Date: Fri, 17 May 2024 22:08:48 +0200 Subject: [PATCH 51/95] directory reference for Bellman-Ford --- DIRECTORY.md | 1 + Graphs/BellmanFord.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index d5b06fe9..b107efff 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -23,6 +23,7 @@ * [Stack](./DataStructures/Stack.php) ## Graphs + * [Bellmanford](./Graphs/BellmanFord.php) * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) * [Depthfirstsearch](./Graphs/DepthFirstSearch.php) diff --git a/Graphs/BellmanFord.php b/Graphs/BellmanFord.php index 271941e3..37dbcd7d 100644 --- a/Graphs/BellmanFord.php +++ b/Graphs/BellmanFord.php @@ -49,4 +49,4 @@ function bellmanFord(array $verticesNames, array $edges, string $start, bool $ve $round++; } return $vertices; -} \ No newline at end of file +} From aa72f5558a86c51a81387594814564f2799a1d25 Mon Sep 17 00:00:00 2001 From: darwinz Date: Wed, 12 Jun 2024 14:53:12 +0000 Subject: [PATCH 52/95] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index b107efff..e244ecc1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -109,6 +109,7 @@ * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * Graphs + * [Bellmanfordtest](./tests/Graphs/BellmanFordTest.php) * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) * Maths From afc6d11844b8d12c8fa3b8081c63880d8ad4e803 Mon Sep 17 00:00:00 2001 From: niharikamahajan02 <72392580+niharikamahajan02@users.noreply.github.com> Date: Fri, 14 Jun 2024 10:56:17 +0530 Subject: [PATCH 53/95] Added code for findng no of homogenous substrings (#146) * Create CountHomogenous.php Added count no of homogenous substrings * Added program for finding no. of homogenous substrings * Update Strings/CountHomogenous.php Co-authored-by: Brandon Johnson * Update Strings/CountHomogenous.php Co-authored-by: Brandon Johnson * Update tests/Strings/StringsTest.php Co-authored-by: Brandon Johnson * Update StringsTest.php * Update Strings/CountHomogenous.php Co-authored-by: Brandon Johnson * Update tests/Strings/StringsTest.php Co-authored-by: Brandon Johnson * Update CountHomogenous.php * Update tests/Strings/StringsTest.php * Update tests/Strings/StringsTest.php * Fix homogenous count unit test * Fix count homogenous substrings algorithm * PHPCBF: remove inline control structure --------- Co-authored-by: Brandon Johnson --- DIRECTORY.md | 1 + Strings/CountHomogenous.php | 32 ++++++++++++++++++++++++++++++++ tests/Strings/StringsTest.php | 6 ++++++ 3 files changed, 39 insertions(+) create mode 100644 Strings/CountHomogenous.php diff --git a/DIRECTORY.md b/DIRECTORY.md index e244ecc1..f2fb121b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -87,6 +87,7 @@ * [Checkpalindrome](./Strings/CheckPalindrome.php) * [Checkpalindrome2](./Strings/CheckPalindrome2.php) * [Countconsonants](./Strings/CountConsonants.php) + * [Counthomogenous](./Strings/CountHomogenous.php) * [Countsentences](./Strings/CountSentences.php) * [Countvowels](./Strings/CountVowels.php) * [Distance](./Strings/Distance.php) diff --git a/Strings/CountHomogenous.php b/Strings/CountHomogenous.php new file mode 100644 index 00000000..8478a9d6 --- /dev/null +++ b/Strings/CountHomogenous.php @@ -0,0 +1,32 @@ +assertEquals(7, countConsonants("hello world")); $this->assertEquals(9, countConsonants("Just A list of somE aaaaaaaaaa")); } + public function testCountHomogenous() + { + $this->assertEquals(4, countHomogenous("abbcccaa")); + $this->assertEquals(2, countHomogenous("xy")); + } public function testFindDistance() { From 500b1bc44d48054a4a3ff9e73b88dfe819be2a14 Mon Sep 17 00:00:00 2001 From: Arafin Mridul <110626481+arafinmridul@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:30:53 +0600 Subject: [PATCH 54/95] Two Pointers Algorithm added in PHP/Searches/TwoPointers.php (#152) * Create TwoPointers.php Two Pointers technique implementation written in PHP * Update TwoPointers.php * Update DIRECTORY.md * Update TwoPointers.php * Update TwoPointers.php * Update SearchesTest.php * Update TwoPointers.php * Update TwoPointers.php --- DIRECTORY.md | 1 + Searches/TwoPointers.php | 62 +++++++++++++++++++++++++++++++++ tests/Searches/SearchesTest.php | 15 ++++++++ 3 files changed, 78 insertions(+) create mode 100644 Searches/TwoPointers.php diff --git a/DIRECTORY.md b/DIRECTORY.md index f2fb121b..06925956 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -68,6 +68,7 @@ * [Lowerbound](./Searches/LowerBound.php) * [Ternarysearch](./Searches/TernarySearch.php) * [Upperbound](./Searches/UpperBound.php) + * [TwoPointers](./Searches/TwoPointers.php) ## Sorting * [Arraykeyssort](./Sorting/ArrayKeysSort.php) diff --git a/Searches/TwoPointers.php b/Searches/TwoPointers.php new file mode 100644 index 00000000..f05e5daa --- /dev/null +++ b/Searches/TwoPointers.php @@ -0,0 +1,62 @@ + $target) { + // the sum is greater than the target, so we need to decrease the sum + // to decrease the sum we will move our $right pointer to the left + $right--; + } else if ($list[$left] + $list[$right] == $target) { + // if it's true, we have found a pair + $ans++; + // now we will move one of our pointers, otherwise it'll run forever + $left++; # doesn't matter which one we move + } + // The loop will go until the pointers point to the same element + } + return $ans; # returning the number of pairs +} diff --git a/tests/Searches/SearchesTest.php b/tests/Searches/SearchesTest.php index edf1618b..94dc8876 100644 --- a/tests/Searches/SearchesTest.php +++ b/tests/Searches/SearchesTest.php @@ -12,6 +12,7 @@ require_once __DIR__ . '/../../Searches/ExponentialSearch.php'; require_once __DIR__ . '/../../Searches/TernarySearch.php'; require_once __DIR__ . '/../../Searches/InterpolationSearch.php'; +require_once __DIR__ . '/../../Searches/TwoPointers.php'; class SearchesTest extends TestCase @@ -202,4 +203,18 @@ public function testInterpolationSearch() $result = interpolationSearch($list, $target); $this->assertEquals(12, $result); } + + public function testTwoPointers() + { + $list = [1, 2, 4, 7, 8, 10, 11, 12, 15]; + $target = 3; + $result = twoPointers($list, $target); + $this->assertEquals(1, $result); + $target = 12; + $result = twoPointers($list, $target); + $this->assertEquals(3, $result); + $target = 18; + $result = twoPointers($list, $target); + $this->assertEquals(2, $result); + } } From 5d9350b58f28af6b20cd455d9c03fb7390b86037 Mon Sep 17 00:00:00 2001 From: darwinz Date: Fri, 14 Jun 2024 05:31:05 +0000 Subject: [PATCH 55/95] updating DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 06925956..fcdc15b9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -67,8 +67,8 @@ * [Linearsearch](./Searches/LinearSearch.php) * [Lowerbound](./Searches/LowerBound.php) * [Ternarysearch](./Searches/TernarySearch.php) + * [Twopointers](./Searches/TwoPointers.php) * [Upperbound](./Searches/UpperBound.php) - * [TwoPointers](./Searches/TwoPointers.php) ## Sorting * [Arraykeyssort](./Sorting/ArrayKeysSort.php) From 8389d29c761032501b8cf616fcf41764cbb0e29a Mon Sep 17 00:00:00 2001 From: Abhishek Pashte <90621734+Abhishek-Pashte@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:12:22 +0530 Subject: [PATCH 56/95] Create SentinelSearch.php (#124) * Create SentinelSearch.php Added Sentinel Search algorithm in Searches folder * Update SentinelSearch.php * Update SearchesTest.php Added tests for SentinelSearch.php * Update DIRECTORY.md Added link for sentinel search * Update SearchesTest.php Testcase corrected * Updated SentinelSearch.php * Updated SearchesTest.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php * Update Searches/SentinelSearch.php --------- Co-authored-by: Brandon Johnson --- DIRECTORY.md | 2 ++ Searches/SentinelSearch.php | 42 +++++++++++++++++++++++++++++++++ tests/Searches/SearchesTest.php | 18 ++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 Searches/SentinelSearch.php diff --git a/DIRECTORY.md b/DIRECTORY.md index fcdc15b9..6b397928 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -66,9 +66,11 @@ * [Jumpsearch](./Searches/JumpSearch.php) * [Linearsearch](./Searches/LinearSearch.php) * [Lowerbound](./Searches/LowerBound.php) + * [SentinelSearch](./Searches/SentinelSearch.php) * [Ternarysearch](./Searches/TernarySearch.php) * [Twopointers](./Searches/TwoPointers.php) * [Upperbound](./Searches/UpperBound.php) + ## Sorting * [Arraykeyssort](./Sorting/ArrayKeysSort.php) diff --git a/Searches/SentinelSearch.php b/Searches/SentinelSearch.php new file mode 100644 index 00000000..135f5471 --- /dev/null +++ b/Searches/SentinelSearch.php @@ -0,0 +1,42 @@ +assertEquals(12, $result); } + public function testSentinelSearch() + { + $list = [1,3,5,2,4,13,18,23,25,30]; + $target = 1; + $result = SentinelSearch($list, $target); + $this->assertEquals(0, $result); + $target = 2; + $result = SentinelSearch($list, $target); + $this->assertEquals(3, $result); + $target = 1000; + $result = SentinelSearch($list, $target); + $this->assertEquals(-1, $result); + $target = -2; + $result = SentinelSearch($list, $target); + $this->assertEquals(-1, $result); + } + public function testTwoPointers() { $list = [1, 2, 4, 7, 8, 10, 11, 12, 15]; From 0cb4a4b5f225f237fc8f339efa77c6c91872bd53 Mon Sep 17 00:00:00 2001 From: darwinz Date: Fri, 14 Jun 2024 05:42:33 +0000 Subject: [PATCH 57/95] updating DIRECTORY.md --- DIRECTORY.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 6b397928..64105adb 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -66,11 +66,10 @@ * [Jumpsearch](./Searches/JumpSearch.php) * [Linearsearch](./Searches/LinearSearch.php) * [Lowerbound](./Searches/LowerBound.php) - * [SentinelSearch](./Searches/SentinelSearch.php) + * [Sentinelsearch](./Searches/SentinelSearch.php) * [Ternarysearch](./Searches/TernarySearch.php) * [Twopointers](./Searches/TwoPointers.php) * [Upperbound](./Searches/UpperBound.php) - ## Sorting * [Arraykeyssort](./Sorting/ArrayKeysSort.php) From e6791356a1ced8fff98b9f8e9e8e8e258a0526f3 Mon Sep 17 00:00:00 2001 From: Gabriele Dongiovanni <48358130+dongio99@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:08:05 +0200 Subject: [PATCH 58/95] Add shell sort (#154) * add shell sort * updating DIRECTORY.md * rename shell sort test class * updating DIRECTORY.md --------- Co-authored-by: dongio99 --- DIRECTORY.md | 2 + Sorting/ShellSort.php | 53 ++++++++++++++++++++++ tests/Sorting/ShellSortTest.php | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 Sorting/ShellSort.php create mode 100644 tests/Sorting/ShellSortTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 64105adb..784ee99f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -83,6 +83,7 @@ * [Quicksort](./Sorting/QuickSort.php) * [Radixsort](./Sorting/RadixSort.php) * [Selectionsort](./Sorting/SelectionSort.php) + * [Shellsort](./Sorting/ShellSort.php) ## Strings * [Checkanagram](./Strings/CheckAnagram.php) @@ -123,6 +124,7 @@ * Sorting * [Arraykeyssorttest](./tests/Sorting/ArrayKeysSortTest.php) * [Gnomesorttest](./tests/Sorting/GnomeSortTest.php) + * [Shellsorttest](./tests/Sorting/ShellSortTest.php) * [Sortingtest](./tests/Sorting/SortingTest.php) * Strings * [Stringstest](./tests/Strings/StringsTest.php) diff --git a/Sorting/ShellSort.php b/Sorting/ShellSort.php new file mode 100644 index 00000000..07ec64c6 --- /dev/null +++ b/Sorting/ShellSort.php @@ -0,0 +1,53 @@ += $gap && $array[$j - $gap] > $temp) { + $array[$j] = $array[$j - $gap]; + $j -= $gap; + } + + $array[$j] = $temp; + } + } + + return $array; +} + +/** + * Calculate Knuth's series + * + * @param int $n Size of the array + * @return array + */ +function calculateKnuthSeries(int $n): array +{ + $h = 1; + $series = []; + + while ($h < $n) { + array_unshift($series, $h); + $h = 3 * $h + 1; + } + + return $series; +} diff --git a/tests/Sorting/ShellSortTest.php b/tests/Sorting/ShellSortTest.php new file mode 100644 index 00000000..f19aadb0 --- /dev/null +++ b/tests/Sorting/ShellSortTest.php @@ -0,0 +1,80 @@ +assertEquals([ + 272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, + 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, + 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, + 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, + 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, + 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, + 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, + 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, + 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, + 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, + 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, + 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, + 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, + 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, + 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, + 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, + 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, + 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, + 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, + 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, + 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711 + ], $sorted); + } + + public function testShellSortWithEmptyInput() + { + $array = []; + $sorted = shellSort($array); + $this->assertEmpty($sorted); + } + + public function testShellSortPerformance() + { + $array = range(1, 10000); + $n = count($array); + shuffle($array); // Randomize the order + $start = microtime(true); + shellSort($array); + $end = microtime(true); + $this->assertLessThan(pow($n, 3 / 2), $end - $start); + } +} From 8a1fe4ccbd5ce4276a120362913b302dbdd54cba Mon Sep 17 00:00:00 2001 From: Eric Neves Date: Thu, 25 Jul 2024 19:37:29 -0400 Subject: [PATCH 59/95] feat(queue): setup queue data structure --- DataStructures/Queue.php | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 DataStructures/Queue.php diff --git a/DataStructures/Queue.php b/DataStructures/Queue.php new file mode 100644 index 00000000..360e0acc --- /dev/null +++ b/DataStructures/Queue.php @@ -0,0 +1,80 @@ +elements = []; + $this->count = 0; + $this->lowestCount = 0; + } + + public function enqueue($element): void + { + $this->elements[$this->count] = $element; + $this->count++; + } + + public function dequeue() + { + if ($this->isEmpty()) { + return null; + } + + $element = $this->elements[$this->lowestCount]; + + unset($this->elements[$this->lowestCount]); + + $this->lowestCount++; + + return $element; + } + + public function isEmpty(): bool + { + return $this->count - $this->lowestCount === 0; + } + + public function size(): int + { + return $this->count - $this->lowestCount; + } + + public function peek() + { + if ($this->isEmpty()) { + return null; + } + + return $this->elements[$this->lowestCount]; + } + + public function clear(): void + { + $this->elements = []; + $this->count = 0; + $this->lowestCount = 0; + } + + public function toString(string $delimiter = ''): string + { + if ($this->isEmpty()) { + return ''; + } + + $result = "{$this->elements[$this->lowestCount]}"; + + for ($i = $this->lowestCount + 1; $i < $this->count; $i++) { + $result .= "{$delimiter}{$this->elements[$i]}"; + } + + return $result; + } +} From ec6014ba0f7c9dcccf10667e9b96dee698689006 Mon Sep 17 00:00:00 2001 From: Eric Neves Date: Thu, 25 Jul 2024 19:37:53 -0400 Subject: [PATCH 60/95] test(queue): write tests for queue operations --- tests/DataStructures/QueueTest.php | 137 +++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/DataStructures/QueueTest.php diff --git a/tests/DataStructures/QueueTest.php b/tests/DataStructures/QueueTest.php new file mode 100644 index 00000000..66379205 --- /dev/null +++ b/tests/DataStructures/QueueTest.php @@ -0,0 +1,137 @@ +enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertEquals(3, $queue->size()); + $this->assertEquals(1, $queue->peek()); + } + + /** + * @test + */ + public function shouldRemoveElementFromQueue(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertEquals(1, $queue->dequeue()); + $this->assertEquals(2, $queue->peek()); + $this->assertEquals(2, $queue->size()); + } + + /** + * @test + */ + public function shouldReturnNullWhenDequeueEmptyQueue(): void + { + $queue = new Queue(); + + $this->assertNull($queue->dequeue()); + } + + /** + * @test + */ + public function shouldReturnTrueIfQueueIsEmpty(): void + { + $queue = new Queue(); + + $this->assertTrue($queue->isEmpty()); + } + + /** + * @test + */ + public function shouldReturnFalseIfQueueIsNotEmpty(): void + { + $queue = new Queue(); + $queue->enqueue(1); + + $this->assertFalse($queue->isEmpty()); + } + + /** + * @test + */ + public function shouldReturnQueueSize(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertEquals(3, $queue->size()); + $this->assertEquals(1, $queue->peek()); + } + + /** + * @test + */ + public function shouldReturnFrontElement(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertEquals(1, $queue->peek()); + $this->assertEquals(3, $queue->size()); + } + + /** + * @test + */ + public function shouldReturnNullWhenPeekEmptyQueue(): void + { + $queue = new Queue(); + + $this->assertNull($queue->peek()); + } + + /** + * @test + */ + public function shouldClearQueue(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $queue->clear(); + + $this->assertTrue($queue->isEmpty()); + $this->assertEquals(0, $queue->size()); + $this->assertNull($queue->peek()); + } + + /** + * @test + */ + public function shouldReturnStringRepresentation(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertIsString($queue->toString()); + $this->assertEquals("1, 2, 3", $queue->toString(', ')); + } +} From 19183045f9123329b934993ea964c7cb3a269f52 Mon Sep 17 00:00:00 2001 From: Eric Neves Date: Thu, 25 Jul 2024 19:41:39 -0400 Subject: [PATCH 61/95] doc: update DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 64105adb..b109bb4f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -19,6 +19,7 @@ ## Datastructures * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) + * [Queue](./DataStructures/Queue.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) * [Stack](./DataStructures/Stack.php) @@ -109,6 +110,7 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) + * [QueueTest](./tests/DataStructures/QueueTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * Graphs From 16be0f2763d38135c77dc044aa69173e9b3c894a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Mon, 19 Aug 2024 06:32:25 +0200 Subject: [PATCH 62/95] Sieve of Eratosthenes algorithm (#159) Co-authored-by: Michal Zarnecki --- DIRECTORY.md | 2 ++ Maths/EratosthenesSieve.php | 30 +++++++++++++++++++++++++++ tests/Maths/EratosthenesSieveTest.php | 16 ++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 Maths/EratosthenesSieve.php create mode 100644 tests/Maths/EratosthenesSieveTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 784ee99f..95a62b4c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -57,6 +57,7 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) + * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## Searches * [Binarysearch](./Searches/BinarySearch.php) @@ -119,6 +120,7 @@ * Maths * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) + * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting diff --git a/Maths/EratosthenesSieve.php b/Maths/EratosthenesSieve.php new file mode 100644 index 00000000..d0380d09 --- /dev/null +++ b/Maths/EratosthenesSieve.php @@ -0,0 +1,30 @@ +assertEquals($result, [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); + } +} From 29cfd3f9c7147eb805a52a24694269c0c72d153a Mon Sep 17 00:00:00 2001 From: darwinz Date: Mon, 19 Aug 2024 04:32:36 +0000 Subject: [PATCH 63/95] updating DIRECTORY.md --- DIRECTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 95a62b4c..85ddfad7 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -34,6 +34,7 @@ * [Basex](./Maths/BaseX.php) * [Checkpalindrome](./Maths/CheckPalindrome.php) * [Checkprime](./Maths/CheckPrime.php) + * [Eratosthenessieve](./Maths/EratosthenesSieve.php) * [Factorial](./Maths/Factorial.php) * [Fastexponentiation](./Maths/FastExponentiation.php) * [Fastinversesquareroot](./Maths/FastInverseSquareRoot.php) @@ -57,7 +58,6 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) - * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## Searches * [Binarysearch](./Searches/BinarySearch.php) @@ -118,9 +118,9 @@ * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) * Maths + * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) - * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting From 05efb2aae0a02339651ef5bdeaa195b20ef35c95 Mon Sep 17 00:00:00 2001 From: Eric Neves Date: Mon, 19 Aug 2024 10:05:30 -0400 Subject: [PATCH 64/95] Add test to assert the value of a dequeued element --- tests/DataStructures/QueueTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/DataStructures/QueueTest.php b/tests/DataStructures/QueueTest.php index 66379205..07006d4c 100644 --- a/tests/DataStructures/QueueTest.php +++ b/tests/DataStructures/QueueTest.php @@ -35,6 +35,21 @@ public function shouldRemoveElementFromQueue(): void $this->assertEquals(2, $queue->size()); } + /** + * @test + */ + public function shouldReturnCorrectValueWhenDequeue(): void + { + $queue = new Queue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + + $this->assertEquals(1, $queue->dequeue()); + $this->assertEquals(2, $queue->dequeue()); + $this->assertEquals(3, $queue->dequeue()); + } + /** * @test */ From 6290bfaffb9c0e33ba265e6802271d566c832805 Mon Sep 17 00:00:00 2001 From: Brandon Johnson Date: Wed, 21 Aug 2024 14:15:27 -0600 Subject: [PATCH 65/95] psr-12 header block guidlines --- tests/DataStructures/QueueTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/DataStructures/QueueTest.php b/tests/DataStructures/QueueTest.php index 07006d4c..e77d568b 100644 --- a/tests/DataStructures/QueueTest.php +++ b/tests/DataStructures/QueueTest.php @@ -1,4 +1,5 @@ Date: Wed, 21 Aug 2024 20:46:23 +0000 Subject: [PATCH 66/95] updating DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 99b8f705..53f60033 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -112,7 +112,7 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) - * [QueueTest](./tests/DataStructures/QueueTest.php) + * [Queuetest](./tests/DataStructures/QueueTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * Graphs From 18ca8fe9c000244b6d1ce6a4c3d821b01631871b Mon Sep 17 00:00:00 2001 From: Brandon Johnson Date: Tue, 10 Sep 2024 23:18:00 -0600 Subject: [PATCH 67/95] test: fix railfence cipher test improper class and func naming --- tests/Ciphers/RailfenceCipherTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Ciphers/RailfenceCipherTest.php b/tests/Ciphers/RailfenceCipherTest.php index dbfcc0d2..b37fe81c 100644 --- a/tests/Ciphers/RailfenceCipherTest.php +++ b/tests/Ciphers/RailfenceCipherTest.php @@ -5,9 +5,9 @@ require_once __DIR__ . '/../../vendor/autoload.php'; // Adjust the path as needed require_once __DIR__ . '/../../Ciphers/RailfenceCipher.php'; // Adjust the path as needed -class RailFenceCipherTest extends TestCase +class RailfenceCipherTest extends TestCase { - public function testRailFenceCipherCase1() + public function testRailfenceCipherCase1() { $plainMessage = "ABCDEF"; $rails = 3; @@ -15,7 +15,7 @@ public function testRailFenceCipherCase1() $decodedMessage = Raildecode($cipherMessage, $rails); $this->assertEquals($plainMessage, $decodedMessage); } - public function testRailFenceCipherCase2() + public function testRailfenceCipherCase2() { $plainMessage = "THIS IS RAILFENCE"; $rails = 3; From 193d032f20e85521e2fb9c6ca0d71ea22c02fb1c Mon Sep 17 00:00:00 2001 From: Ramy <126559907+Ramy-Badr-Ahmed@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:06:44 +0200 Subject: [PATCH 68/95] Implemented Disjoint Set (Union-Find) Data Structure (#160) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 4 + DataStructures/DisjointSets/DisjointSet.php | 41 +++++++++ .../DisjointSets/DisjointSetNode.php | 21 +++++ composer.json | 3 +- tests/DataStructures/DisjointSetTest.php | 87 +++++++++++++++++++ 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 DataStructures/DisjointSets/DisjointSet.php create mode 100644 DataStructures/DisjointSets/DisjointSetNode.php create mode 100644 tests/DataStructures/DisjointSetTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 53f60033..9e567459 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -17,6 +17,9 @@ * [Speedconversion](./Conversions/SpeedConversion.php) ## Datastructures + * Disjointsets + * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) + * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) @@ -111,6 +114,7 @@ * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures + * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) diff --git a/DataStructures/DisjointSets/DisjointSet.php b/DataStructures/DisjointSets/DisjointSet.php new file mode 100644 index 00000000..4212ece1 --- /dev/null +++ b/DataStructures/DisjointSets/DisjointSet.php @@ -0,0 +1,41 @@ +parent) { + // Path compression: make the parent point directly to the root + $node->parent = $this->findSet($node->parent); + } + return $node->parent; + } + + /** + * Unites the sets that contain x and y. + */ + public function unionSet(DisjointSetNode $nodeX, DisjointSetNode $nodeY): void + { + $rootX = $this->findSet($nodeX); + $rootY = $this->findSet($nodeY); + + if ($rootX === $rootY) { + return; // They are already in the same set + } + + // Union by rank: attach the smaller tree under the larger tree + if ($rootX->rank > $rootY->rank) { + $rootY->parent = $rootX; + } else { + $rootX->parent = $rootY; + if ($rootX->rank === $rootY->rank) { + $rootY->rank += 1; + } + } + } +} diff --git a/DataStructures/DisjointSets/DisjointSetNode.php b/DataStructures/DisjointSets/DisjointSetNode.php new file mode 100644 index 00000000..ed9d7556 --- /dev/null +++ b/DataStructures/DisjointSets/DisjointSetNode.php @@ -0,0 +1,21 @@ +data = $data; + $this->rank = 0; + $this->parent = $this; // Initialize parent to itself + } +} diff --git a/composer.json b/composer.json index 8cc0cbd0..534b9823 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name" : "thealgorithms/php", + "name": "thealgorithms/php", "description": "All Algorithms implemented in PHP", "config": { "platform": { @@ -19,3 +19,4 @@ "test": "vendor/bin/phpunit tests" } } + diff --git a/tests/DataStructures/DisjointSetTest.php b/tests/DataStructures/DisjointSetTest.php new file mode 100644 index 00000000..0a52dea7 --- /dev/null +++ b/tests/DataStructures/DisjointSetTest.php @@ -0,0 +1,87 @@ +ds = new DisjointSet(); + $this->nodes = []; + + // Create 20 nodes + for ($i = 0; $i < 20; $i++) { + $this->nodes[$i] = new DisjointSetNode($i); + } + + // Perform union operations to form several disjoint sets + $this->ds->unionSet($this->nodes[0], $this->nodes[1]); + $this->ds->unionSet($this->nodes[1], $this->nodes[2]); + + $this->ds->unionSet($this->nodes[3], $this->nodes[4]); + $this->ds->unionSet($this->nodes[4], $this->nodes[5]); + + $this->ds->unionSet($this->nodes[6], $this->nodes[7]); + $this->ds->unionSet($this->nodes[7], $this->nodes[8]); + + $this->ds->unionSet($this->nodes[9], $this->nodes[10]); + $this->ds->unionSet($this->nodes[10], $this->nodes[11]); + + $this->ds->unionSet($this->nodes[12], $this->nodes[13]); + $this->ds->unionSet($this->nodes[13], $this->nodes[14]); + + $this->ds->unionSet($this->nodes[15], $this->nodes[16]); + $this->ds->unionSet($this->nodes[16], $this->nodes[17]); + + $this->ds->unionSet($this->nodes[18], $this->nodes[19]); + } + + public function testFindSet(): void + { + // Nodes in the same sets should have the same root + for ($i = 0; $i < 6; $i++) { + for ($j = 0; $j < 6; $j++) { + $setI = $this->ds->findSet($this->nodes[$i]); + $setJ = $this->ds->findSet($this->nodes[$j]); + + if ($this->inSameSet($i, $j)) { + $this->assertSame($setI, $setJ, "Nodes $i and $j should be in the same set"); + } else { + $this->assertNotSame($setI, $setJ, "Nodes $i and $j should be in different sets"); + } + } + } + } + + private function inSameSet(int $i, int $j): bool + { + // Define which nodes should be in the same set based on union operations + $sets = [ + [0, 1, 2], // Set A + [3, 4, 5], // Set B + [6, 7, 8], // Set C + [9, 10, 11], // Set D + [12, 13, 14], // Set E + [15, 16, 17], // Set F + [18, 19] // Set G + ]; + + foreach ($sets as $set) { + if (in_array($i, $set) && in_array($j, $set)) { + return true; + } + } + + return false; + } +} From 42badbfdac4d640e1a13dbfd80866606f6b2bbff Mon Sep 17 00:00:00 2001 From: Ramy <126559907+Ramy-Badr-Ahmed@users.noreply.github.com> Date: Wed, 18 Sep 2024 07:00:52 +0200 Subject: [PATCH 69/95] Implemented Trie Data Structure (#162) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 4 + DataStructures/Trie/Trie.php | 163 ++++++++++++++++++++++++++++++ DataStructures/Trie/TrieNode.php | 43 ++++++++ tests/DataStructures/TrieTest.php | 137 +++++++++++++++++++++++++ 4 files changed, 347 insertions(+) create mode 100644 DataStructures/Trie/Trie.php create mode 100644 DataStructures/Trie/TrieNode.php create mode 100644 tests/DataStructures/TrieTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 9e567459..724b3fa9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -20,6 +20,9 @@ * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) + * Trie + * [Trie](./DataStructures/Trie/Trie.php) + * [TrieNode](./DataStructures/Trie/TrieNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) @@ -115,6 +118,7 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) + * [Trie](./tests/DataStructures/TrieTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) diff --git a/DataStructures/Trie/Trie.php b/DataStructures/Trie/Trie.php new file mode 100644 index 00000000..062d7f33 --- /dev/null +++ b/DataStructures/Trie/Trie.php @@ -0,0 +1,163 @@ +root = new TrieNode(); + } + + /** + * Get the root node of the Trie. + */ + public function getRoot(): TrieNode + { + return $this->root; + } + + /** + * Insert a word into the Trie. + */ + public function insert(string $word): void + { + $node = $this->root; + for ($i = 0; $i < strlen($word); $i++) { + $char = $word[$i]; + $node = $node->addChild($char); + } + $node->isEndOfWord = true; + } + + /** + * Search for a word in the Trie. + */ + public function search(string $word): bool + { + $node = $this->root; + for ($i = 0; $i < strlen($word); $i++) { + $char = $word[$i]; + if (!$node->hasChild($char)) { + return false; + } + $node = $node->getChild($char); + } + return $node->isEndOfWord; + } + + /** + * Find all words that start with a given prefix. + */ + public function startsWith(string $prefix): array + { + $node = $this->root; + for ($i = 0; $i < strlen($prefix); $i++) { + $char = $prefix[$i]; + if (!$node->hasChild($char)) { + return []; + } + $node = $node->getChild($char); + } + return $this->findWordsFromNode($node, $prefix); + } + + /** + * Helper function to find all words from a given node. + */ + private function findWordsFromNode(TrieNode $node, string $prefix): array + { + $words = []; + if ($node->isEndOfWord) { + $words[] = $prefix; + } + + foreach ($node->children as $char => $childNode) { + $words = array_merge($words, $this->findWordsFromNode($childNode, $prefix . $char)); + } + + return $words; + } + + /** + * Delete a word from the Trie. + * Recursively traverses the Trie and removes nodes + * + * @param string $word The word to delete. + * @return bool Returns true if the word was successfully deleted, otherwise false. + */ + public function delete(string $word): bool + { + return $this->deleteHelper($this->root, $word, 0); + } + + /** + * Helper function for deleting a word. + * Recursively traverse the Trie and removes nodes. + * + * @param TrieNode $node The current node in the Trie. + * @param string $word The word being deleted. + * @param int $index The current index in the word. + * @return bool Returns true if the current node should be deleted, otherwise false. + */ + private function deleteHelper(TrieNode $node, string &$word, int $index): bool + { + if ($index === strlen($word)) { + if (!$node->isEndOfWord) { + return false; + } + $node->isEndOfWord = false; + return empty($node->children); + } + + $char = $word[$index]; + $childNode = $node->getChild($char); + if ($childNode === null) { + return false; + } + + // Recursively delete the child node + $shouldDeleteCurrentNode = $this->deleteHelper($childNode, $word, $index + 1); + + if ($shouldDeleteCurrentNode) { + unset($node->children[$char]); + return !$node->isEndOfWord; // true if current node is not the end of another word + } + + return false; + } + + /** + * Recursively traverses the Trie starting from the given node and collects all words. + * + * @param TrieNode $node The starting node for traversal. + * @param string $prefix The prefix of the current path in the Trie. + * @return array An array of words found in the Trie starting from the given node. + */ + public function traverseTrieNode(TrieNode $node, string $prefix = ''): array + { + $words = []; + + if ($node->isEndOfWord) { + $words[] = $prefix; + } + + foreach ($node->children as $char => $childNode) { + $words = array_merge($words, $this->traverseTrieNode($childNode, $prefix . $char)); + } + + return $words; + } + + /** + * Gets all words stored in the Trie. + * + * @return array An array of all words in the Trie. + */ + public function getWords(): array + { + return $this->traverseTrieNode($this->root); + } +} diff --git a/DataStructures/Trie/TrieNode.php b/DataStructures/Trie/TrieNode.php new file mode 100644 index 00000000..fed8dbcc --- /dev/null +++ b/DataStructures/Trie/TrieNode.php @@ -0,0 +1,43 @@ + */ + public array $children; + public bool $isEndOfWord; + + public function __construct() + { + $this->children = []; // Associative array where [ char => TrieNode ] + $this->isEndOfWord = false; + } + + /** + * Add a child node for a character. + */ + public function addChild(string $char): TrieNode + { + if (!isset($this->children[$char])) { + $this->children[$char] = new TrieNode(); + } + return $this->children[$char]; + } + + /** + * Check if a character has a child node. + */ + public function hasChild(string $char): bool + { + return isset($this->children[$char]); + } + + /** + * Get the child node corresponding to a character. + */ + public function getChild(string $char): ?TrieNode + { + return $this->children[$char] ?? null; + } +} diff --git a/tests/DataStructures/TrieTest.php b/tests/DataStructures/TrieTest.php new file mode 100644 index 00000000..d2fa39d5 --- /dev/null +++ b/tests/DataStructures/TrieTest.php @@ -0,0 +1,137 @@ +trie = new Trie(); + } + + public function testInsertAndSearch() + { + $this->trie->insert('the'); + $this->trie->insert('universe'); + $this->trie->insert('is'); + $this->trie->insert('vast'); + + $this->assertTrue($this->trie->search('the'), 'Expected "the" to be found in the Trie.'); + $this->assertTrue($this->trie->search('universe'), 'Expected "universe" to be found in the Trie.'); + $this->assertTrue($this->trie->search('is'), 'Expected "is" to be found in the Trie.'); + $this->assertTrue($this->trie->search('vast'), 'Expected "vast" to be found in the Trie.'); + $this->assertFalse( + $this->trie->search('the universe'), + 'Expected "the universe" not to be found in the Trie.' + ); + } + + public function testStartsWith() + { + $this->trie->insert('hello'); + $this->assertEquals(['hello'], $this->trie->startsWith('he'), 'Expected words starting with "he" to be found.'); + $this->assertEquals( + ['hello'], + $this->trie->startsWith('hello'), + 'Expected words starting with "hello" to be found.' + ); + $this->assertEquals( + [], + $this->trie->startsWith('world'), + 'Expected no words starting with "world" to be found.' + ); + } + + public function testDelete() + { + // Insert words into the Trie + $this->trie->insert('the'); + $this->trie->insert('universe'); + $this->trie->insert('is'); + $this->trie->insert('vast'); + $this->trie->insert('big'); + $this->trie->insert('rather'); + + // Test deleting an existing word + $this->trie->delete('the'); + $this->assertFalse($this->trie->search('the'), 'Expected "the" not to be found after deletion.'); + + // Test that other words are still present + $this->assertTrue($this->trie->search('universe'), 'Expected "universe" to be found.'); + $this->assertTrue($this->trie->search('is'), 'Expected "is" to be found.'); + $this->assertTrue($this->trie->search('vast'), 'Expected "vast" to be found.'); + $this->assertTrue($this->trie->search('big'), 'Expected "big" to be found.'); + $this->assertTrue($this->trie->search('rather'), 'Expected "rather" to be found.'); + } + + public function testDeleteNonExistentWord() + { + $this->trie->delete('nonexistent'); + $this->assertFalse($this->trie->search('nonexistent'), 'Expected "nonexistent" to not be found.'); + } + + public function testTraverseTrieNode() + { + $this->trie->insert('hello'); + $this->trie->insert('helium'); + $this->trie->insert('helicopter'); + + $words = $this->trie->getWords(); + $this->assertContains('hello', $words, 'Expected "hello" to be found in the Trie.'); + $this->assertContains('helium', $words, 'Expected "helium" to be found in the Trie.'); + $this->assertContains('helicopter', $words, 'Expected "helicopter" to be found in the Trie.'); + $this->assertCount(3, $words, 'Expected 3 words in the Trie.'); + } + + public function testEmptyTrie() + { + $this->assertEquals([], $this->trie->getWords(), 'Expected an empty Trie to return an empty array.'); + } + + public function testGetWords() + { + $this->trie->insert('apple'); + $this->trie->insert('app'); + $this->trie->insert('applet'); + + $words = $this->trie->getWords(); + $this->assertContains('apple', $words, 'Expected "apple" to be found in the Trie.'); + $this->assertContains('app', $words, 'Expected "app" to be found in the Trie.'); + $this->assertContains('applet', $words, 'Expected "applet" to be found in the Trie.'); + $this->assertCount(3, $words, 'Expected 3 words in the Trie.'); + } + + public function testInsertEmptyString() + { + $this->trie->insert(''); + $this->assertTrue($this->trie->search(''), 'Expected empty string to be found in the Trie.'); + } + + public function testDeleteEmptyString() + { + $this->trie->insert(''); + $this->trie->delete(''); + $this->assertFalse($this->trie->search(''), 'Expected empty string not to be found after deletion.'); + } + + public function testStartsWithWithCommonPrefix() + { + $this->trie->insert('trie'); + $this->trie->insert('tried'); + $this->trie->insert('trier'); + + $words = $this->trie->startsWith('tri'); + $this->assertContains('trie', $words, 'Expected "trie" to be found with prefix "tri".'); + $this->assertContains('tried', $words, 'Expected "tried" to be found with prefix "tri".'); + $this->assertContains('trier', $words, 'Expected "trier" to be found with prefix "tri".'); + $this->assertCount(3, $words, 'Expected 3 words with prefix "tri".'); + } +} From 8c808cd2e6080120f4c060d436659086e352a654 Mon Sep 17 00:00:00 2001 From: darwinz Date: Wed, 18 Sep 2024 05:01:03 +0000 Subject: [PATCH 70/95] updating DIRECTORY.md --- DIRECTORY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 724b3fa9..7be3da43 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -20,14 +20,14 @@ * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) - * Trie - * [Trie](./DataStructures/Trie/Trie.php) - * [TrieNode](./DataStructures/Trie/TrieNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) * [Stack](./DataStructures/Stack.php) + * Trie + * [Trie](./DataStructures/Trie/Trie.php) + * [Trienode](./DataStructures/Trie/TrieNode.php) ## Graphs * [Bellmanford](./Graphs/BellmanFord.php) @@ -118,11 +118,11 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) - * [Trie](./tests/DataStructures/TrieTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) + * [Trietest](./tests/DataStructures/TrieTest.php) * Graphs * [Bellmanfordtest](./tests/Graphs/BellmanFordTest.php) * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) From e43b4bfec82ad976db3d2507fe20f94259a30276 Mon Sep 17 00:00:00 2001 From: Ramy <126559907+Ramy-Badr-Ahmed@users.noreply.github.com> Date: Wed, 18 Sep 2024 07:07:12 +0200 Subject: [PATCH 71/95] Implemented AVL Tree Data Structure (#163) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented AVLTree DataStructure --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 4 + DataStructures/AVLTree/AVLTree.php | 306 +++++++++++++++++++++++ DataStructures/AVLTree/AVLTreeNode.php | 41 +++ DataStructures/AVLTree/TreeTraversal.php | 80 ++++++ tests/DataStructures/AVLTreeTest.php | 295 ++++++++++++++++++++++ 5 files changed, 726 insertions(+) create mode 100644 DataStructures/AVLTree/AVLTree.php create mode 100644 DataStructures/AVLTree/AVLTreeNode.php create mode 100644 DataStructures/AVLTree/TreeTraversal.php create mode 100644 tests/DataStructures/AVLTreeTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 7be3da43..a1641443 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -17,6 +17,9 @@ * [Speedconversion](./Conversions/SpeedConversion.php) ## Datastructures + * AVLTree + * [AVLTree](./DataStructures/AVLTree/AVLTree.php) + * [AVLTreeNode](./DataStructures/AVLTree/AVLTreeNode.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) @@ -117,6 +120,7 @@ * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures + * [AVLTreeTest](./tests/DataStructures/AVLTreeTest.php) * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) diff --git a/DataStructures/AVLTree/AVLTree.php b/DataStructures/AVLTree/AVLTree.php new file mode 100644 index 00000000..a93b5f32 --- /dev/null +++ b/DataStructures/AVLTree/AVLTree.php @@ -0,0 +1,306 @@ +root = null; + $this->counter = 0; + } + + /** + * Get the root node of the AVL Tree. + */ + public function getRoot(): ?AVLTreeNode + { + return $this->root; + } + + /** + * Retrieve a node by its key. + * + * @param mixed $key The key of the node to retrieve. + * @return ?AVLTreeNode The node with the specified key, or null if not found. + */ + public function getNode($key): ?AVLTreeNode + { + return $this->searchNode($this->root, $key); + } + + /** + * Get the number of nodes in the AVL Tree. + */ + public function size(): int + { + return $this->counter; + } + + /** + * Insert a key-value pair into the AVL Tree. + * + * @param mixed $key The key to insert. + * @param mixed $value The value associated with the key. + */ + public function insert($key, $value): void + { + $this->root = $this->insertNode($this->root, $key, $value); + $this->counter++; + } + + /** + * Delete a node by its key from the AVL Tree. + * + * @param mixed $key The key of the node to delete. + */ + public function delete($key): void + { + $this->root = $this->deleteNode($this->root, $key); + $this->counter--; + } + + /** + * Search for a value by its key. + * + * @param mixed $key The key to search for. + * @return mixed The value associated with the key, or null if not found. + */ + public function search($key) + { + $node = $this->searchNode($this->root, $key); + return $node ? $node->value : null; + } + + /** + * Perform an in-order traversal of the AVL Tree. + * Initiates the traversal on the root node directly and returns the array of key-value pairs. + */ + public function inOrderTraversal(): array + { + return TreeTraversal::inOrder($this->root); + } + + /** + * Perform a pre-order traversal of the AVL Tree. + * Initiates the traversal on the root node directly and returns the array of key-value pairs. + */ + public function preOrderTraversal(): array + { + return TreeTraversal::preOrder($this->root); + } + + /** + * Perform a post-order traversal of the AVL Tree. + * Initiates the traversal on the root node directly and returns the array of key-value pairs. + */ + public function postOrderTraversal(): array + { + return TreeTraversal::postOrder($this->root); + } + + /** + * Perform a breadth-first traversal of the AVL Tree. + */ + public function breadthFirstTraversal(): array + { + return TreeTraversal::breadthFirst($this->root); + } + + /** + * Check if the AVL Tree is balanced. + * This method check balance starting from the root node directly + */ + public function isBalanced(): bool + { + return $this->isBalancedHelper($this->root); + } + + /** + * Insert a node into the AVL Tree and balance the tree. + * + * @param ?AVLTreeNode $node The current node. + * @param mixed $key The key to insert. + * @param mixed $value The value to insert. + * @return AVLTreeNode The new root of the subtree. + */ + private function insertNode(?AVLTreeNode $node, $key, $value): AVLTreeNode + { + if ($node === null) { + return new AVLTreeNode($key, $value); + } + + if ($key < $node->key) { + $node->left = $this->insertNode($node->left, $key, $value); + } elseif ($key > $node->key) { + $node->right = $this->insertNode($node->right, $key, $value); + } else { + $node->value = $value; // Update existing value + } + + $node->updateHeight(); + return $this->balance($node); + } + + /** + * Delete a node by its key and balance the tree. + * + * @param ?AVLTreeNode $node The current node. + * @param mixed $key The key of the node to delete. + * @return ?AVLTreeNode The new root of the subtree. + */ + private function deleteNode(?AVLTreeNode $node, $key): ?AVLTreeNode + { + if ($node === null) { + return null; + } + + if ($key < $node->key) { + $node->left = $this->deleteNode($node->left, $key); + } elseif ($key > $node->key) { + $node->right = $this->deleteNode($node->right, $key); + } else { + if (!$node->left) { + return $node->right; + } + if (!$node->right) { + return $node->left; + } + + $minNode = $this->getMinNode($node->right); + $node->key = $minNode->key; + $node->value = $minNode->value; + $node->right = $this->deleteNode($node->right, $minNode->key); + } + + $node->updateHeight(); + return $this->balance($node); + } + + /** + * Search for a node by its key. + * + * @param ?AVLTreeNode $node The current node. + * @param mixed $key The key to search for. + * @return ?AVLTreeNode The node with the specified key, or null if not found. + */ + private function searchNode(?AVLTreeNode $node, $key): ?AVLTreeNode + { + if ($node === null) { + return null; + } + + if ($key < $node->key) { + return $this->searchNode($node->left, $key); + } elseif ($key > $node->key) { + return $this->searchNode($node->right, $key); + } else { + return $node; + } + } + + /** + * Helper method to check if a subtree is balanced. + * + * @param ?AVLTreeNode $node The current node. + * @return bool True if the subtree is balanced, false otherwise. + */ + private function isBalancedHelper(?AVLTreeNode $node): bool + { + if ($node === null) { + return true; + } + + $leftHeight = $node->left ? $node->left->height : 0; + $rightHeight = $node->right ? $node->right->height : 0; + + $balanceFactor = abs($leftHeight - $rightHeight); + if ($balanceFactor > 1) { + return false; + } + + return $this->isBalancedHelper($node->left) && $this->isBalancedHelper($node->right); + } + + /** + * Balance the subtree rooted at the given node. + * + * @param ?AVLTreeNode $node The current node. + * @return ?AVLTreeNode The new root of the subtree. + */ + private function balance(?AVLTreeNode $node): ?AVLTreeNode + { + if ($node->balanceFactor() > 1) { + if ($node->left && $node->left->balanceFactor() < 0) { + $node->left = $this->rotateLeft($node->left); + } + return $this->rotateRight($node); + } + + if ($node->balanceFactor() < -1) { + if ($node->right && $node->right->balanceFactor() > 0) { + $node->right = $this->rotateRight($node->right); + } + return $this->rotateLeft($node); + } + + return $node; + } + + /** + * Perform a left rotation on the given node. + * + * @param AVLTreeNode $node The node to rotate. + * @return AVLTreeNode The new root of the rotated subtree. + */ + private function rotateLeft(AVLTreeNode $node): AVLTreeNode + { + $newRoot = $node->right; + $node->right = $newRoot->left; + $newRoot->left = $node; + + $node->updateHeight(); + $newRoot->updateHeight(); + + return $newRoot; + } + + /** + * Perform a right rotation on the given node. + * + * @param AVLTreeNode $node The node to rotate. + * @return AVLTreeNode The new root of the rotated subtree. + */ + private function rotateRight(AVLTreeNode $node): AVLTreeNode + { + $newRoot = $node->left; + $node->left = $newRoot->right; + $newRoot->right = $node; + + $node->updateHeight(); + $newRoot->updateHeight(); + + return $newRoot; + } + + /** + * Get the node with the minimum key in the given subtree. + * + * @param AVLTreeNode $node The root of the subtree. + * @return AVLTreeNode The node with the minimum key. + */ + private function getMinNode(AVLTreeNode $node): AVLTreeNode + { + while ($node->left) { + $node = $node->left; + } + return $node; + } +} diff --git a/DataStructures/AVLTree/AVLTreeNode.php b/DataStructures/AVLTree/AVLTreeNode.php new file mode 100644 index 00000000..707bf97f --- /dev/null +++ b/DataStructures/AVLTree/AVLTreeNode.php @@ -0,0 +1,41 @@ +key = $key; + $this->value = $value; + $this->left = $left; + $this->right = $right; + $this->height = 1; // New node is initially at height 1 + } + + public function updateHeight(): void + { + $leftHeight = $this->left ? $this->left->height : 0; + $rightHeight = $this->right ? $this->right->height : 0; + $this->height = max($leftHeight, $rightHeight) + 1; + } + + public function balanceFactor(): int + { + $leftHeight = $this->left ? $this->left->height : 0; + $rightHeight = $this->right ? $this->right->height : 0; + return $leftHeight - $rightHeight; + } +} diff --git a/DataStructures/AVLTree/TreeTraversal.php b/DataStructures/AVLTree/TreeTraversal.php new file mode 100644 index 00000000..803a856a --- /dev/null +++ b/DataStructures/AVLTree/TreeTraversal.php @@ -0,0 +1,80 @@ +left)); + $result[] = [$node->key => $node->value]; + $result = array_merge($result, self::inOrder($node->right)); + } + return $result; + } + + /** + * Perform a pre-order traversal of the subtree. + * Recursively traverses the subtree rooted at the given node. + */ + public static function preOrder(?AVLTreeNode $node): array + { + $result = []; + if ($node !== null) { + $result[] = [$node->key => $node->value]; + $result = array_merge($result, self::preOrder($node->left)); + $result = array_merge($result, self::preOrder($node->right)); + } + return $result; + } + + /** + * Perform a post-order traversal of the subtree. + * Recursively traverses the subtree rooted at the given node. + */ + public static function postOrder(?AVLTreeNode $node): array + { + $result = []; + if ($node !== null) { + $result = array_merge($result, self::postOrder($node->left)); + $result = array_merge($result, self::postOrder($node->right)); + $result[] = [$node->key => $node->value]; + } + return $result; + } + + /** + * Perform a breadth-first traversal of the AVL Tree. + */ + public static function breadthFirst(?AVLTreeNode $root): array + { + $result = []; + if ($root === null) { + return $result; + } + + $queue = []; + $queue[] = $root; + + while (!empty($queue)) { + $currentNode = array_shift($queue); + $result[] = [$currentNode->key => $currentNode->value]; + + if ($currentNode->left !== null) { + $queue[] = $currentNode->left; + } + + if ($currentNode->right !== null) { + $queue[] = $currentNode->right; + } + } + + return $result; + } +} diff --git a/tests/DataStructures/AVLTreeTest.php b/tests/DataStructures/AVLTreeTest.php new file mode 100644 index 00000000..8f556de8 --- /dev/null +++ b/tests/DataStructures/AVLTreeTest.php @@ -0,0 +1,295 @@ +tree = new AVLTree(); + } + + private function populateTree(): void + { + $this->tree->insert(10, 'Value 10'); + $this->tree->insert(20, 'Value 20'); + $this->tree->insert(5, 'Value 5'); + $this->tree->insert(15, 'Value 15'); + } + + public function testInsertAndSearch(): void + { + $this->populateTree(); + + $this->assertEquals('Value 10', $this->tree->search(10), 'Value for key 10 should be "Value 10"'); + $this->assertEquals('Value 20', $this->tree->search(20), 'Value for key 20 should be "Value 20"'); + $this->assertEquals('Value 5', $this->tree->search(5), 'Value for key 5 should be "Value 5"'); + $this->assertNull($this->tree->search(25), 'Value for non-existent key 25 should be null'); + } + + public function testDelete(): void + { + $this->populateTree(); + + $this->tree->delete(20); + $this->tree->delete(5); + + $this->assertNull($this->tree->search(20), 'Value for deleted key 20 should be null'); + $this->assertNull($this->tree->search(5), 'Value for deleted key 5 should be null'); + + $this->tree->delete(50); + + $this->assertNotNull($this->tree->search(10), 'Value for key 10 should still exist'); + $this->assertNotNull($this->tree->search(15), 'Value for key 15 should still exist'); + $this->assertNull($this->tree->search(50), 'Value for non-existent key 50 should be null'); + + $expectedInOrderAfterDelete = [ + [10 => 'Value 10'], + [15 => 'Value 15'] + ]; + + $result = TreeTraversal::inOrder($this->tree->getRoot()); + $this->assertEquals( + $expectedInOrderAfterDelete, + $result, + 'In-order traversal after deletion should match expected result' + ); + } + + public function testInOrderTraversal(): void + { + $this->populateTree(); + + $expectedInOrder = [ + [5 => 'Value 5'], + [10 => 'Value 10'], + [15 => 'Value 15'], + [20 => 'Value 20'] + ]; + + $result = $this->tree->inOrderTraversal(); + $this->assertEquals($expectedInOrder, $result, 'In-order traversal should match expected result'); + } + + public function testPreOrderTraversal(): void + { + $this->populateTree(); + + $expectedPreOrder = [ + [10 => 'Value 10'], + [5 => 'Value 5'], + [20 => 'Value 20'], + [15 => 'Value 15'] + ]; + + $result = $this->tree->preOrderTraversal(); + $this->assertEquals($expectedPreOrder, $result, 'Pre-order traversal should match expected result'); + } + + public function testPostOrderTraversal(): void + { + $this->populateTree(); + + $expectedPostOrder = [ + [5 => 'Value 5'], + [15 => 'Value 15'], + [20 => 'Value 20'], + [10 => 'Value 10'] + ]; + + $result = TreeTraversal::postOrder($this->tree->getRoot()); + $this->assertEquals($expectedPostOrder, $result, 'Post-order traversal should match expected result'); + } + + public function testBreadthFirstTraversal(): void + { + $this->populateTree(); + + $expectedBFT = [ + [10 => 'Value 10'], + [5 => 'Value 5'], + [20 => 'Value 20'], + [15 => 'Value 15'] + ]; + + $result = TreeTraversal::breadthFirst($this->tree->getRoot()); + $this->assertEquals($expectedBFT, $result, 'Breadth-first traversal should match expected result'); + } + + public function testInsertAndDeleteSingleNode(): void + { + $this->tree = new AVLTree(); + + $this->tree->insert(1, 'One'); + $this->assertEquals('One', $this->tree->search(1), 'Value for key 1 should be "One"'); + $this->tree->delete(1); + $this->assertNull($this->tree->search(1), 'Value for key 1 should be null after deletion'); + } + + public function testDeleteFromEmptyTree(): void + { + $this->tree = new AVLTree(); + + $this->tree->delete(1); + $this->assertNull($this->tree->search(1), 'Value for key 1 should be null as it was never inserted'); + } + + public function testInsertDuplicateKeys(): void + { + $this->tree = new AVLTree(); + + $this->tree->insert(1, 'One'); + $this->tree->insert(1, 'One Updated'); + $this->assertEquals( + 'One Updated', + $this->tree->search(1), + 'Value for key 1 should be "One Updated" after updating' + ); + } + + public function testLargeTree(): void + { + // Inserting a large number of nodes + for ($i = 1; $i <= 1000; $i++) { + $this->tree->insert($i, "Value $i"); + } + + // Verify that all inserted nodes can be searched + for ($i = 1; $i <= 1000; $i++) { + $this->assertEquals("Value $i", $this->tree->search($i), "Value for key $i should be 'Value $i'"); + } + + // Verify that all inserted nodes can be deleted + for ($i = 1; $i <= 5; $i++) { + $this->tree->delete($i); + $this->assertNull($this->tree->search($i), "Value for key $i should be null after deletion"); + } + } + + public function testBalance(): void + { + $this->populateTree(); + + // Perform operations that may unbalance the tree + $this->tree->insert(30, 'Value 30'); + $this->tree->insert(25, 'Value 25'); + + // After insertions, check the balance + $this->assertTrue($this->tree->isBalanced(), 'Tree should be balanced after insertions'); + } + + public function testRightRotation(): void + { + $this->populateTreeForRightRotation(); + + // Insert a node that will trigger a right rotation + $this->tree->insert(40, 'Value 40'); + + // Verify the tree structure after rotation + $root = $this->tree->getRoot(); + $this->assertEquals(20, $root->key, 'Root should be 20 after right rotation'); + $this->assertEquals(10, $root->left->key, 'Left child of root should be 10'); + $this->assertEquals(30, $root->right->key, 'Right child of root should be 30'); + } + + private function populateTreeForRightRotation(): void + { + // Insert nodes in a way that requires a right rotation + $this->tree->insert(10, 'Value 10'); + $this->tree->insert(20, 'Value 20'); + $this->tree->insert(30, 'Value 30'); // This should trigger a right rotation around 10 + } + + public function testLeftRotation(): void + { + $this->populateTreeForLeftRotation(); + + // Insert a node that will trigger a left rotation + $this->tree->insert(5, 'Value 5'); + + // Verify the tree structure after rotation + $root = $this->tree->getRoot(); + $this->assertEquals(20, $root->key, 'Root should be 20 after left rotation'); + $this->assertEquals(10, $root->left->key, 'Left child of root should be 10'); + $this->assertEquals(30, $root->right->key, 'Right child of root should be 30'); + } + + private function populateTreeForLeftRotation(): void + { + $this->tree->insert(30, 'Value 30'); + $this->tree->insert(20, 'Value 20'); + $this->tree->insert(10, 'Value 10'); // This should trigger a left rotation around 30 + } + + /** + * @throws ReflectionException + */ + public function testGetMinNode(): void + { + $this->populateTree(); + + // Using Reflection to access the private getMinNode method + $reflection = new ReflectionClass($this->tree); + $method = $reflection->getMethod('getMinNode'); + $method->setAccessible(true); + + $minNode = $method->invoke($this->tree, $this->tree->getRoot()); + + // Verify the minimum node + $this->assertEquals(5, $minNode->key, 'Minimum key in the tree should be 5'); + $this->assertEquals('Value 5', $minNode->value, 'Value for minimum key 5 should be "Value 5"'); + } + + public function testSizeAfterInsertions(): void + { + $this->tree = new AVLTree(); + + $this->assertEquals(0, $this->tree->size(), 'Size should be 0 initially'); + + $this->tree->insert(10, 'Value 10'); + $this->tree->insert(20, 'Value 20'); + $this->tree->insert(5, 'Value 5'); + + $this->assertEquals(3, $this->tree->size(), 'Size should be 3 after 3 insertions'); + + $this->tree->delete(20); + + $this->assertEquals(2, $this->tree->size(), 'Size should be 2 after deleting 1 node'); + } + + public function testSizeAfterMultipleInsertionsAndDeletions(): void + { + $this->tree = new AVLTree(); + + // Insert nodes + for ($i = 1; $i <= 10; $i++) { + $this->tree->insert($i, "Value $i"); + } + + $this->assertEquals(10, $this->tree->size(), 'Size should be 10 after 10 insertions'); + + for ($i = 1; $i <= 5; $i++) { + $this->tree->delete($i); + } + + $this->assertEquals(5, $this->tree->size(), 'Size should be 5 after deleting 5 nodes'); + } + + public function testSizeOnEmptyTree(): void + { + $this->tree = new AVLTree(); + $this->assertEquals(0, $this->tree->size(), 'Size should be 0 for an empty tree'); + } +} From 95286b241c54e658933843ff07d4d8ec5eb2763c Mon Sep 17 00:00:00 2001 From: darwinz Date: Wed, 18 Sep 2024 05:07:22 +0000 Subject: [PATCH 72/95] updating DIRECTORY.md --- DIRECTORY.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index a1641443..c1ece342 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -17,9 +17,10 @@ * [Speedconversion](./Conversions/SpeedConversion.php) ## Datastructures - * AVLTree - * [AVLTree](./DataStructures/AVLTree/AVLTree.php) - * [AVLTreeNode](./DataStructures/AVLTree/AVLTreeNode.php) + * Avltree + * [Avltree](./DataStructures/AVLTree/AVLTree.php) + * [Avltreenode](./DataStructures/AVLTree/AVLTreeNode.php) + * [Treetraversal](./DataStructures/AVLTree/TreeTraversal.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) @@ -120,7 +121,7 @@ * Conversions * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures - * [AVLTreeTest](./tests/DataStructures/AVLTreeTest.php) + * [Avltreetest](./tests/DataStructures/AVLTreeTest.php) * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) From eba642dbbe02f9bea672590ef9c8061bc3e43ea6 Mon Sep 17 00:00:00 2001 From: Ramy Date: Tue, 1 Oct 2024 07:28:03 +0200 Subject: [PATCH 73/95] Implemented Segment Tree Data Structure (#166) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md * updating DIRECTORY.md * Implemented AVLTree DataStructure * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented SegmentTreeNode.php * Implementing SegmentTree * Implementing SegmentTree with updateTree * Implementing SegmentTree with rangeUpdateTree * Implementing SegmentTree with query and queryTree * Added serializing and deserializing of the SegmentTree * Adding unit tests SegmentTree implementation * Added unit tests for SegmentTree updates and range updates * considering PHPCS for Added unit tests for SegmentTree updates and range updates * Added unit tests for SegmentTree serialization/deserialization and array updates reflections * Added unit tests for SegmentTree Edge Cases * Added unit tests for SegmentTree Exceptions (OutOfBoundsException, InvalidArgumentException) * Added SegmentTree to DIRECTORY.md * Implemented Segment Tree Data Structure * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added comments time complexity for query(), update() and buildTree() --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 4 + DataStructures/AVLTree/AVLTree.php | 8 + DataStructures/AVLTree/AVLTreeNode.php | 8 + DataStructures/AVLTree/TreeTraversal.php | 8 + DataStructures/DisjointSets/DisjointSet.php | 8 + .../DisjointSets/DisjointSetNode.php | 8 + DataStructures/SegmentTree/SegmentTree.php | 332 +++++++++++++++++ .../SegmentTree/SegmentTreeNode.php | 38 ++ DataStructures/Trie/Trie.php | 8 + DataStructures/Trie/TrieNode.php | 8 + composer.json | 3 +- tests/DataStructures/AVLTreeTest.php | 8 + tests/DataStructures/DisjointSetTest.php | 8 + tests/DataStructures/SegmentTreeTest.php | 347 ++++++++++++++++++ tests/DataStructures/TrieTest.php | 8 + 15 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 DataStructures/SegmentTree/SegmentTree.php create mode 100644 DataStructures/SegmentTree/SegmentTreeNode.php create mode 100644 tests/DataStructures/SegmentTreeTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index c1ece342..34964991 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -27,6 +27,9 @@ * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) + * SegmentTree + * [SegmentTree](./DataStructures/SegmentTree/SegmentTree.php) + * [SegmentTreeNode](./DataStructures/SegmentTree/SegmentTreeNode.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) * [Stack](./DataStructures/Stack.php) * Trie @@ -125,6 +128,7 @@ * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) + * [SegmentTreeTest](./tests/DataStructures/SegmentTreeTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * [Trietest](./tests/DataStructures/TrieTest.php) diff --git a/DataStructures/AVLTree/AVLTree.php b/DataStructures/AVLTree/AVLTree.php index a93b5f32..ff05ff86 100644 --- a/DataStructures/AVLTree/AVLTree.php +++ b/DataStructures/AVLTree/AVLTree.php @@ -1,5 +1,13 @@ max($a, $b)); + * + * @param array $arr The input array for the segment tree + * @param callable|null $callback Optional callback function for custom aggregation logic. + * @throws InvalidArgumentException if the array is empty, contains non-numeric values, or is associative. + */ + public function __construct(array $arr, callable $callback = null) + { + $this->currentArray = $arr; + $this->arraySize = count($this->currentArray); + $this->callback = $callback; + + if ($this->isUnsupportedArray()) { + throw new InvalidArgumentException("Array must not be empty, must contain numeric values + and must be non-associative."); + } + + $this->root = $this->buildTree($this->currentArray, 0, $this->arraySize - 1); + } + + private function isUnsupportedArray(): bool + { + return empty($this->currentArray) || $this->isNonNumeric() || $this->isAssociative(); + } + + /** + * @return bool True if any element is non-numeric, false otherwise. + */ + private function isNonNumeric(): bool + { + return !array_reduce($this->currentArray, fn($carry, $item) => $carry && is_numeric($item), true); + } + + /** + * @return bool True if the array is associative, false otherwise. + */ + private function isAssociative(): bool + { + return array_keys($this->currentArray) !== range(0, $this->arraySize - 1); + } + + /** + * @return SegmentTreeNode The root node of the segment tree. + */ + public function getRoot(): SegmentTreeNode + { + return $this->root; + } + + /** + * @return array The original or the current array (after any update) stored in the segment tree. + */ + public function getCurrentArray(): array + { + return $this->currentArray; + } + + /** + * Builds the segment tree recursively. Takes O(n log n) in total. + * + * @param array $arr The input array. + * @param int $start The starting index of the segment. + * @param int $end The ending index of the segment. + * @return SegmentTreeNode The root node of the constructed segment. + */ + private function buildTree(array $arr, int $start, int $end): SegmentTreeNode + { + // Leaf node + if ($start == $end) { + return new SegmentTreeNode($start, $end, $arr[$start]); + } + + $mid = $start + (int)(($end - $start) / 2); + + // Recursively build left and right children + $leftChild = $this->buildTree($arr, $start, $mid); + $rightChild = $this->buildTree($arr, $mid + 1, $end); + + $node = new SegmentTreeNode($start, $end, $this->callback + ? ($this->callback)($leftChild->value, $rightChild->value) + : $leftChild->value + $rightChild->value); + + // Link the children to the parent node + $node->left = $leftChild; + $node->right = $rightChild; + + return $node; + } + + /** + * Queries the aggregated value over a specified range. Takes O(log n). + * + * @param int $start The starting index of the range. + * @param int $end The ending index of the range. + * @return int|float The aggregated value for the range. + * @throws OutOfBoundsException if the range is invalid. + */ + public function query(int $start, int $end) + { + if ($start > $end || $start < 0 || $end > ($this->root->end)) { + throw new OutOfBoundsException("Index out of bounds: start = $start, end = $end. + Must be between 0 and " . ($this->arraySize - 1)); + } + return $this->queryTree($this->root, $start, $end); + } + + /** + * Recursively queries the segment tree for a specific range. + * + * @param SegmentTreeNode $node The current node. + * @param int $start The starting index of the query range. + * @param int $end The ending index of the query range. + * @return int|float The aggregated value for the range. + */ + private function queryTree(SegmentTreeNode $node, int $start, int $end) + { + if ($node->start == $start && $node->end == $end) { + return $node->value; + } + + $mid = $node->start + (int)(($node->end - $node->start) / 2); + + // Determine which segment of the tree to query + if ($end <= $mid) { + return $this->queryTree($node->left, $start, $end); // Query left child + } elseif ($start > $mid) { + return $this->queryTree($node->right, $start, $end); // Query right child + } else { + // Split query between left and right children + $leftResult = $this->queryTree($node->left, $start, $mid); + $rightResult = $this->queryTree($node->right, $mid + 1, $end); + + return $this->callback + ? ($this->callback)($leftResult, $rightResult) + : $leftResult + $rightResult; // Default sum if no callback + } + } + + /** + * Updates the value at a specified index in the segment tree. Takes O(log n). + * + * @param int $index The index to update. + * @param int|float $value The new value to set. + * @throws OutOfBoundsException if the index is out of bounds. + */ + public function update(int $index, int $value): void + { + if ($index < 0 || $index >= $this->arraySize) { + throw new OutOfBoundsException("Index out of bounds: $index. Must be between 0 and " + . ($this->arraySize - 1)); + } + + $this->updateTree($this->root, $index, $value); + $this->currentArray[$index] = $value; // Reflect the update in the original array + } + + /** + * Recursively updates the segment tree. + * + * @param SegmentTreeNode $node The current node. + * @param int $index The index to update. + * @param int|float $value The new value. + */ + private function updateTree(SegmentTreeNode $node, int $index, $value): void + { + // Leaf node + if ($node->start == $node->end) { + $node->value = $value; + return; + } + + $mid = $node->start + (int)(($node->end - $node->start) / 2); + + // Decide whether to go to the left or right child + if ($index <= $mid) { + $this->updateTree($node->left, $index, $value); + } else { + $this->updateTree($node->right, $index, $value); + } + + // Recompute the value of the current node after the update + $node->value = $this->callback + ? ($this->callback)($node->left->value, $node->right->value) + : $node->left->value + $node->right->value; + } + + /** + * Performs a range update on a specified segment. + * + * @param int $start The starting index of the range. + * @param int $end The ending index of the range. + * @param int|float $value The value to set for the range. + * @throws OutOfBoundsException if the range is invalid. + */ + public function rangeUpdate(int $start, int $end, $value): void + { + if ($start < 0 || $end >= $this->arraySize || $start > $end) { + throw new OutOfBoundsException("Invalid range: start = $start, end = $end."); + } + $this->rangeUpdateTree($this->root, $start, $end, $value); + + // Update the original array to reflect the range update + $this->currentArray = array_replace($this->currentArray, array_fill_keys(range($start, $end), $value)); + } + + /** + * Recursively performs a range update in the segment tree. + * + * @param SegmentTreeNode $node The current node. + * @param int $start The starting index of the range. + * @param int $end The ending index of the range. + * @param int|float $value The new value for the range. + */ + private function rangeUpdateTree(SegmentTreeNode $node, int $start, int $end, $value): void + { + // Leaf node + if ($node->start == $node->end) { + $node->value = $value; + return; + } + + $mid = $node->start + (int)(($node->end - $node->start) / 2); + + // Determine which segment of the tree to update (Left, Right, Split respectively) + if ($end <= $mid) { + $this->rangeUpdateTree($node->left, $start, $end, $value); // Entire range is in the left child + } elseif ($start > $mid) { + $this->rangeUpdateTree($node->right, $start, $end, $value); // Entire range is in the right child + } else { + // Range is split between left and right children + $this->rangeUpdateTree($node->left, $start, $mid, $value); + $this->rangeUpdateTree($node->right, $mid + 1, $end, $value); + } + + // Recompute the value of the current node after the update + $node->value = $this->callback + ? ($this->callback)($node->left->value, $node->right->value) + : $node->left->value + $node->right->value; + } + + /** + * Serializes the segment tree into a JSON string. + * + * @return string The serialized segment tree as a JSON string. + */ + public function serialize(): string + { + return json_encode($this->serializeTree($this->root)); + } + + /** + * Recursively serializes the segment tree. + * + * @param SegmentTreeNode|null $node The current node. + * @return array The serialized representation of the node. + */ + private function serializeTree(?SegmentTreeNode $node): array + { + if ($node === null) { + return []; + } + return [ + 'start' => $node->start, + 'end' => $node->end, + 'value' => $node->value, + 'left' => $this->serializeTree($node->left), + 'right' => $this->serializeTree($node->right), + ]; + } + + /** + * Deserializes a JSON string into a SegmentTree object. + * + * @param string $data The JSON string to deserialize. + * @return SegmentTree The deserialized segment tree. + */ + public static function deserialize(string $data): self + { + $array = json_decode($data, true); + + $initialiseArray = array_fill(0, $array['end'] + 1, 0); + $segmentTree = new self($initialiseArray); + + $segmentTree->root = $segmentTree->deserializeTree($array); + return $segmentTree; + } + + /** + * Recursively deserializes a segment tree from an array representation. + * + * @param array $data The serialized data for the node. + * @return SegmentTreeNode|null The deserialized node. + */ + private function deserializeTree(array $data): ?SegmentTreeNode + { + if (empty($data)) { + return null; + } + $node = new SegmentTreeNode($data['start'], $data['end'], $data['value']); + + $node->left = $this->deserializeTree($data['left']); + $node->right = $this->deserializeTree($data['right']); + return $node; + } +} diff --git a/DataStructures/SegmentTree/SegmentTreeNode.php b/DataStructures/SegmentTree/SegmentTreeNode.php new file mode 100644 index 00000000..bb017c67 --- /dev/null +++ b/DataStructures/SegmentTree/SegmentTreeNode.php @@ -0,0 +1,38 @@ +start = $start; + $this->end = $end; + $this->value = $value; + $this->left = null; + $this->right = null; + } +} diff --git a/DataStructures/Trie/Trie.php b/DataStructures/Trie/Trie.php index 062d7f33..32c8c6d9 100644 --- a/DataStructures/Trie/Trie.php +++ b/DataStructures/Trie/Trie.php @@ -1,5 +1,13 @@ testArray = [1, 3, 5, 7, 9, 11, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]; + } + + public static function sumQueryProvider(): array + { + return [ + // Format: [expectedResult, startIndex, endIndex] + [24, 1, 4], + [107, 5, 11], + [91, 2, 9], + [23, 15, 15], + ]; + } + + /** + * Test sum queries using data provider. + * @dataProvider sumQueryProvider + */ + public function testSegmentTreeSumQuery(int $expected, int $startIndex, int $endIndex): void + { + // Test the default case: sum query + $segmentTree = new SegmentTree($this->testArray); + $this->assertEquals( + $expected, + $segmentTree->query($startIndex, $endIndex), + "Query sum between index $startIndex and $endIndex should return $expected." + ); + } + + public static function maxQueryProvider(): array + { + return [ + [26, 0, 18], + [13, 2, 6], + [22, 8, 14], + [11, 5, 5], + ]; + } + + /** + * Test max queries using data provider. + * @dataProvider maxQueryProvider + */ + public function testSegmentTreeMaxQuery(int $expected, int $startIndex, int $endIndex): void + { + $segmentTree = new SegmentTree($this->testArray, fn($a, $b) => max($a, $b)); + $this->assertEquals( + $expected, + $segmentTree->query($startIndex, $endIndex), + "Max query between index $startIndex and $endIndex should return $expected." + ); + } + + public static function minQueryProvider(): array + { + return [ + [1, 0, 18], + [5, 2, 7], + [18, 10, 17], + [17, 9, 9], + ]; + } + + /** + * Test min queries using data provider. + * @dataProvider minQueryProvider + */ + public function testSegmentTreeMinQuery(int $expected, int $startIndex, int $endIndex): void + { + $segmentTree = new SegmentTree($this->testArray, function ($a, $b) { + return min($a, $b); + }); + $this->assertEquals( + $expected, + $segmentTree->query($startIndex, $endIndex), + "Query min between index $startIndex and $endIndex should return $expected." + ); + } + + /** + * Test update functionality for different query types. + */ + public function testSegmentTreeUpdate(): void + { + // Sum Query + $segmentTreeSum = new SegmentTree($this->testArray); + $segmentTreeSum->update(2, 10); // Update index 2 to 10 + $this->assertEquals( + 29, + $segmentTreeSum->query(1, 4), + "After update, sum between index 1 and 4 should return 29." + ); + + // Max Query: with callback + $segmentTreeMax = new SegmentTree($this->testArray, fn($a, $b) => max($a, $b)); + $segmentTreeMax->update(12, -1); // Update index 12 to -1 + $this->assertEquals( + 19, + $segmentTreeMax->query(5, 12), + "After update, max between index 5 and 12 should return 19." + ); + + // Min Query: with callback + $segmentTreeMin = new SegmentTree($this->testArray, fn($a, $b) => min($a, $b)); + $segmentTreeMin->update(9, -5); // Update index 9 to -5 + $this->assertEquals( + -5, + $segmentTreeMin->query(9, 13), + "After update, min between index 9 and 13 should return -5." + ); + } + + /** + * Test range update functionality for different query types. + */ + public function testSegmentTreeRangeUpdate(): void + { + // Sum Query + $segmentTreeSum = new SegmentTree($this->testArray); + $segmentTreeSum->rangeUpdate(3, 7, 0); // Set indices 3 to 7 to 0 + $this->assertEquals( + 55, + $segmentTreeSum->query(2, 10), + "After range update, sum between index 2 and 10 should return 55." + ); + + // Max Query: with callback + $segmentTreeMax = new SegmentTree($this->testArray, fn($a, $b) => max($a, $b)); + $segmentTreeMax->rangeUpdate(3, 7, 0); // Set indices 3 to 7 to 0 + $this->assertEquals( + 5, + $segmentTreeMax->query(2, 7), + "After range update, max between index 2 and 7 should return 5." + ); + + // Min Query: with callback + $segmentTreeMin = new SegmentTree($this->testArray, fn($a, $b) => min($a, $b)); + $segmentTreeMin->rangeUpdate(3, 9, 0); // Set indices 3 to 9 to 0 + $this->assertEquals( + 0, + $segmentTreeMin->query(2, 9), + "After range update, min between index 2 and 9 should return 0." + ); + } + + /** + * Test array updates reflections. + */ + public function testGetCurrentArray(): void + { + $segmentTree = new SegmentTree($this->testArray); + + // Ensure the initial array matches the input array + $this->assertEquals( + $this->testArray, + $segmentTree->getCurrentArray(), + "getCurrentArray() should return the initial array." + ); + + // Perform an update and test again + $segmentTree->update(2, 10); // Update index 2 to 10 + $updatedArray = $this->testArray; + $updatedArray[2] = 10; + + $this->assertEquals( + $updatedArray, + $segmentTree->getCurrentArray(), + "getCurrentArray() should return the updated array." + ); + } + + /** + * Test serialization and deserialization of the segment tree. + */ + public function testSegmentTreeSerialization(): void + { + $segmentTree = new SegmentTree($this->testArray); + $serialized = $segmentTree->serialize(); + + $deserializedTree = SegmentTree::deserialize($serialized); + $this->assertEquals( + $segmentTree->query(1, 4), + $deserializedTree->query(1, 4), + "Serialized and deserialized trees should have the same query results." + ); + } + + /** + * Testing EdgeCases: first and last indices functionality on the Segment Tree + */ + public function testEdgeCases(): void + { + $segmentTree = new SegmentTree($this->testArray); + $firstIndex = 0; + $lastIndex = count($this->testArray) - 1; + + // Test querying the first and last indices + $this->assertEquals( + $this->testArray[$firstIndex], + $segmentTree->query($firstIndex, $firstIndex), + "Query at the first index should return {$this->testArray[$firstIndex]}." + ); + $this->assertEquals( + $this->testArray[$lastIndex], + $segmentTree->query($lastIndex, $lastIndex), + "Query at the last index should return {$this->testArray[$lastIndex]}." + ); + + + // Test updating the first index + $segmentTree->update($firstIndex, 100); // Update first index to 100 + $this->assertEquals( + 100, + $segmentTree->query($firstIndex, $firstIndex), + "After update, query at the first index should return {$this->testArray[$firstIndex]}." + ); + + // Test updating the last index + $segmentTree->update($lastIndex, 200); // Update last index to 200 + $this->assertEquals( + 200, + $segmentTree->query($lastIndex, $lastIndex), + "After update, query at the last index should return {$this->testArray[$lastIndex]}." + ); + + // Test range update that includes the first index + $segmentTree->rangeUpdate($firstIndex, 2, 50); // Set indices 0 to 2 to 50 + $this->assertEquals( + 50, + $segmentTree->query($firstIndex, $firstIndex), + "After range update, query at index $firstIndex should return 50." + ); + $this->assertEquals(50, $segmentTree->query(1, 1), "After range update, query at index 1 should return 50."); + $this->assertEquals(50, $segmentTree->query(2, 2), "After range update, query at index 2 should return 50."); + + // Test range update that includes the last index + $segmentTree->rangeUpdate($lastIndex - 3, $lastIndex, 10); // Set indices to 10 + $this->assertEquals( + 10, + $segmentTree->query($lastIndex, $lastIndex), + "After range update, query at the last index should return 10." + ); + $this->assertEquals( + 10, + $segmentTree->query(count($this->testArray) - 2, count($this->testArray) - 2), + "After range update, query at the second last index should return 10." + ); + } + + /** + * Test empty or unsupported arrays. + */ + public function testUnsupportedOrEmptyArrayInitialization(): void + { + // Test empty array + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Array must not be empty, must contain numeric values + and must be non-associative."); + + $segmentTreeEmpty = new SegmentTree([]); // expecting an exception + + // Test unsupported array (e.g., with non-numeric values) + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Array must not be empty, must contain numeric values + and must be non-associative."); + + $segmentTreeUnsupported = new SegmentTree([1, "two", 3]); // Mix of numeric and non-numeric + } + + + /** + * Test exception for invalid update index. + */ + public function testInvalidUpdateIndex(): void + { + $segmentTree = new SegmentTree($this->testArray); + + $index = count($this->testArray) + 5; + + // Expect an exception for range update with invalid indices + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage("Index out of bounds: $index. Must be between 0 and " + . (count($this->testArray) - 1)); + + $segmentTree->update($index, 100); // non-existing index, should trigger exception + } + + /** + * Test exception for invalid update index. + */ + public function testOutOfBoundsQuery(): void + { + $segmentTree = new SegmentTree($this->testArray); + + $start = 0; + $end = count($this->testArray); + + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage("Index out of bounds: start = $start, end = $end. + Must be between 0 and " . (count($this->testArray) - 1)); + + $segmentTree->query(0, count($this->testArray)); // expecting an exception + } + + /** + * Test exception for invalid range update. + */ + public function testInvalidRangeUpdate(): void + { + $segmentTree = new SegmentTree($this->testArray); + + $start = -1; + $end = 5; + + // Expect an exception for range update with invalid indices + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage("Invalid range: start = $start, end = $end."); + + $segmentTree->rangeUpdate(-1, 5, 0); // Negative index, should trigger exception + } +} diff --git a/tests/DataStructures/TrieTest.php b/tests/DataStructures/TrieTest.php index d2fa39d5..7733ed80 100644 --- a/tests/DataStructures/TrieTest.php +++ b/tests/DataStructures/TrieTest.php @@ -1,5 +1,13 @@ Date: Tue, 1 Oct 2024 05:28:14 +0000 Subject: [PATCH 74/95] updating DIRECTORY.md --- DIRECTORY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 34964991..8e2c1b5b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -27,9 +27,9 @@ * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) - * SegmentTree - * [SegmentTree](./DataStructures/SegmentTree/SegmentTree.php) - * [SegmentTreeNode](./DataStructures/SegmentTree/SegmentTreeNode.php) + * Segmenttree + * [Segmenttree](./DataStructures/SegmentTree/SegmentTree.php) + * [Segmenttreenode](./DataStructures/SegmentTree/SegmentTreeNode.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) * [Stack](./DataStructures/Stack.php) * Trie @@ -128,7 +128,7 @@ * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) - * [SegmentTreeTest](./tests/DataStructures/SegmentTreeTest.php) + * [Segmenttreetest](./tests/DataStructures/SegmentTreeTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * [Trietest](./tests/DataStructures/TrieTest.php) From e0be0fdf54fcbe5782eda594bed41e20b4042547 Mon Sep 17 00:00:00 2001 From: VISHAL V NAIR <122366926+vishalvnair124@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:01:10 +0530 Subject: [PATCH 75/95] Add Odd and Even Check Functions (#165) * Add CheckOdd and CheckEven functions * code style improved * code corrected --- DIRECTORY.md | 2 ++ Maths/CheckEven.php | 13 +++++++++++++ Maths/CheckOdd.php | 13 +++++++++++++ tests/Maths/MathsTest.php | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 Maths/CheckEven.php create mode 100644 Maths/CheckOdd.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 8e2c1b5b..353edbda 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -46,8 +46,10 @@ * [Absolutemin](./Maths/AbsoluteMin.php) * [Armstrongnumber](./Maths/ArmstrongNumber.php) * [Basex](./Maths/BaseX.php) + * [CheckEven](./Maths/CheckEven.php) * [Checkpalindrome](./Maths/CheckPalindrome.php) * [Checkprime](./Maths/CheckPrime.php) + * [CheckOdd](./Maths/CheckOdd.php) * [Eratosthenessieve](./Maths/EratosthenesSieve.php) * [Factorial](./Maths/Factorial.php) * [Fastexponentiation](./Maths/FastExponentiation.php) diff --git a/Maths/CheckEven.php b/Maths/CheckEven.php new file mode 100644 index 00000000..a53b8861 --- /dev/null +++ b/Maths/CheckEven.php @@ -0,0 +1,13 @@ +assertTrue(isEven(2)); + $this->assertTrue(isEven(0)); + $this->assertFalse(isEven(3)); + $this->assertFalse(isEven(17)); + $this->assertTrue(isEven(-4)); + } + public function testIsNumberArmstrong() { $this->assertTrue(isNumberArmstrong(153)); @@ -56,6 +67,15 @@ public function testIsNumberPalindromic() $this->assertFalse(isNumberPalindromic(2468)); } + public function testIsOdd() + { + $this->assertTrue(isOdd(3)); + $this->assertTrue(isOdd(17)); + $this->assertFalse(isOdd(4)); + $this->assertFalse(isOdd(0)); + $this->assertTrue(isOdd(-5)); + } + public function testIsPrime() { $this->assertTrue(isPrime(73)); From 5a8418c389c033c87346bd77c284c2864de56ce2 Mon Sep 17 00:00:00 2001 From: darwinz Date: Tue, 1 Oct 2024 05:31:23 +0000 Subject: [PATCH 76/95] updating DIRECTORY.md --- DIRECTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 353edbda..9b673e8d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -46,10 +46,10 @@ * [Absolutemin](./Maths/AbsoluteMin.php) * [Armstrongnumber](./Maths/ArmstrongNumber.php) * [Basex](./Maths/BaseX.php) - * [CheckEven](./Maths/CheckEven.php) + * [Checkeven](./Maths/CheckEven.php) + * [Checkodd](./Maths/CheckOdd.php) * [Checkpalindrome](./Maths/CheckPalindrome.php) * [Checkprime](./Maths/CheckPrime.php) - * [CheckOdd](./Maths/CheckOdd.php) * [Eratosthenessieve](./Maths/EratosthenesSieve.php) * [Factorial](./Maths/Factorial.php) * [Fastexponentiation](./Maths/FastExponentiation.php) From 45f57eae2d14f7c5804cdc31cf91497f54e32af5 Mon Sep 17 00:00:00 2001 From: Ramy Date: Thu, 3 Oct 2024 23:40:41 +0200 Subject: [PATCH 77/95] Implemented Splay Tree Data Structure (#168) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md * updating DIRECTORY.md * Implemented AVLTree DataStructure * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented SegmentTreeNode.php * Implementing SegmentTree * Implementing SegmentTree with updateTree * Implementing SegmentTree with rangeUpdateTree * Implementing SegmentTree with query and queryTree * Added serializing and deserializing of the SegmentTree * Adding unit tests SegmentTree implementation * Added unit tests for SegmentTree updates and range updates * considering PHPCS for Added unit tests for SegmentTree updates and range updates * Added unit tests for SegmentTree serialization/deserialization and array updates reflections * Added unit tests for SegmentTree Edge Cases * Added unit tests for SegmentTree Exceptions (OutOfBoundsException, InvalidArgumentException) * Added SegmentTree to DIRECTORY.md * Implemented Segment Tree Data Structure * updating DIRECTORY.md * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added comments time complexity for query(), update() and buildTree() * Implemented Splay Tree Data Structure * Update tests/DataStructures/SplayTreeTest.php Co-authored-by: Brandon Johnson --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DIRECTORY.md | 5 + DataStructures/SplayTree/SplayTree.php | 446 +++++++++++++ DataStructures/SplayTree/SplayTreeNode.php | 51 ++ .../SplayTree/SplayTreeRotations.php | 174 ++++++ tests/DataStructures/SplayTreeTest.php | 588 ++++++++++++++++++ 5 files changed, 1264 insertions(+) create mode 100644 DataStructures/SplayTree/SplayTree.php create mode 100644 DataStructures/SplayTree/SplayTreeNode.php create mode 100644 DataStructures/SplayTree/SplayTreeRotations.php create mode 100644 tests/DataStructures/SplayTreeTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 9b673e8d..42b09322 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -31,6 +31,10 @@ * [Segmenttree](./DataStructures/SegmentTree/SegmentTree.php) * [Segmenttreenode](./DataStructures/SegmentTree/SegmentTreeNode.php) * [Singlylinkedlist](./DataStructures/SinglyLinkedList.php) + * Splaytree + * [Splaytree](./DataStructures/SplayTree/SplayTree.php) + * [Splaytreenode](./DataStructures/SplayTree/SplayTreeNode.php) + * [Splaytreerotations](./DataStructures/SplayTree/SplayTreeRotations.php) * [Stack](./DataStructures/Stack.php) * Trie * [Trie](./DataStructures/Trie/Trie.php) @@ -132,6 +136,7 @@ * [Queuetest](./tests/DataStructures/QueueTest.php) * [Segmenttreetest](./tests/DataStructures/SegmentTreeTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) + * [Splaytreetest](./tests/DataStructures/SplayTreeTest.php) * [Stacktest](./tests/DataStructures/StackTest.php) * [Trietest](./tests/DataStructures/TrieTest.php) * Graphs diff --git a/DataStructures/SplayTree/SplayTree.php b/DataStructures/SplayTree/SplayTree.php new file mode 100644 index 00000000..ac8e80b4 --- /dev/null +++ b/DataStructures/SplayTree/SplayTree.php @@ -0,0 +1,446 @@ + $value) { + $this->insert($key, $value); + } + } + + /** + * @return SplayTreeNode|NULL The root node of the Splay Tree. + */ + public function getRoot(): ?SplayTreeNode + { + return $this->root; + } + + /** + * Set the root node of the Splay Tree. + * @param SplayTreeNode $node + */ + public function setRoot(SplayTreeNode $node): void + { + $this->root = $node; + } + + /** + * Get the number of nodes in the Splay Tree. + */ + public function size(): int + { + return $this->counter; + } + + /** + * Check if current splay tree is empty + */ + public function isEmpty(): bool + { + return $this->root === null; + } + + /** + * Splay the given node to the root of the tree. + * Keep rotating until the node becomes the root. + * + * Time complexity: Amortized O(log n) + * + * @param SplayTreeNode|NULL $node The current node being splayed + * @param int $key The node's key being searched for and splayed + * @return SplayTreeNode|NULL Returns the new root after splaying + */ + protected function splay(?SplayTreeNode $node, int $key): ?SplayTreeNode + { + if ($node === null || $node->key === $key) { + return $node; + } + + return $node->key > $key + ? $this->splayLeft($node, $key) // Key is in the Left subtree + : $this->splayRight($node, $key); // Key is in the Right subtree + } + + /** + * Handles the splay operation when the key is located in the left subtree. + * This includes Zig-Zig (Left-Left), Zig-Zag (Left-Right), and Zig (Left) cases. + * + * Time complexity: Amortized O(log n). + * + * @param SplayTreeNode|null $node The root node of the subtree to splay + * @param int $key The key to splay to the root + * @return SplayTreeNode|null Returns the new root after splaying + */ + private function splayLeft(?SplayTreeNode $node, int $key): ?SplayTreeNode + { + if ($node->left === null) { + return $node; // Key not found in the left subtree + } + + if ($node->left->key > $key) { // Zig-Zig (Left-Left case) + $node->left->left = $this->splay($node->left->left, $key); + return $this->zigZig($node); + } elseif ($node->left->key < $key) { // Zig-Zag (Left-Right case) + $node->left->right = $this->splay($node->left->right, $key); + + if ($node->left->right !== null) { + return $this->zigZag($node); + } + } + // Zig (Left case) + return $node->left === null + ? $node + : $this->zig($node); + } + + /** + * Handles the splay operation when the key is located in the right subtree. + * This includes Zag-Zag (Right-Right), Zag-Zig (Right-Left), and Zag (Right) cases. + * + * Time complexity: Amortized O(log n). + * + * @param SplayTreeNode|null $node The root node of the subtree to splay + * @param int $key The key to splay to the root + * @return SplayTreeNode|null Returns the new root after splaying + */ + private function splayRight(?SplayTreeNode $node, int $key): ?SplayTreeNode + { + if ($node->right === null) { + return $node; + } + + if ($node->right->key < $key) { // Zag-Zag (Right-Right case) + $node->right->right = $this->splay($node->right->right, $key); + + return $this->zagZag($node); + } elseif ($node->right->key > $key) { // Zag-Zig (Right-Left case) + $node->right->left = $this->splay($node->right->left, $key); + + if ($node->right->left !== null) { + return $this->zagZig($node); + } + } + + // Zag (Right case) + return $node->right === null + ? $node + : $this->zag($node); + } + + /** + * Insert a new element into the splay tree following binary search tree insertion. + * Then, apply rotations to bring the newly inserted node to the root of the tree. + * + * Time complexity: O(log n) for the splay operation. + * + * @param int $key The node's key to insert + * @param mixed $value The value associated with the key + * @return SplayTreeNode|null Returns the new root after insertion and splaying + * @throws LogicException If the key already exists + */ + public function insert(int $key, $value): ?SplayTreeNode + { + $this->root = $this->insertNode($this->root, $key, $value); + $this->counter++; + + return $this->splay($this->root, $key); + } + + /** + * Insert a key-value pair into the Splay Tree. + * Recursively inserts a node in the BST fashion and update parent references. + * + * Time complexity: O(log n) for binary search tree insertion. + * + * @param SplayTreeNode|null $node The (sub)tree's root node to start inserting after + * @param int $key The node's key to insert + * @param mixed $value The value associated with the key + * @return SplayTreeNode|null Returns the new root after insertion + * @throws LogicException If the key already exists + */ + private function insertNode(?SplayTreeNode $node, int $key, $value): SplayTreeNode + { + if ($node === null) { + return new SplayTreeNode($key, $value); + } + + if ($key < $node->key) { + $node->left = $this->insertNode($node->left, $key, $value); + $node->left->parent = $node; + } elseif ($key > $node->key) { + $node->right = $this->insertNode($node->right, $key, $value); + $node->right->parent = $node; + } else { + throw new LogicException("Duplicate key: " . $key); + } + + return $node; + } + + /** + * Search for an element in the tree by performing a binary search tree search. + * If the node is found, apply rotations to bring it to the root of the tree. + * Otherwise, apply rotations to the last node visited in the search. + * + * Time complexity: O(log n) for the splay operation. + * + * @param int $key The node's key being searched + * @return SplayTreeNode|null Returns the new root of either the found node or the last visited + */ + public function search(int $key): ?SplayTreeNode + { + if ($this->isEmpty()) { + return null; + } + + // + $lastVisited = null; + + $node = $this->searchNode($this->root, $key, $lastVisited); + + $this->root = $node !== null + ? $this->splay($this->root, $key) + : $this->splay($this->root, $lastVisited->key); + + return $this->root; + } + + /** + * Recursively searches for a node in the BST fashion. + * + * Time complexity: O(log n) for binary search tree search. + * + * @param SplayTreeNode|null $node The (sub)tree's root node to start searching from + * @param int $key The node's key being searched + * @param SplayTreeNode|null $lastVisited Keep track of the last visited node + * @return SplayTreeNode|null Returns the new root of the found node or Null + */ + private function searchNode(?SplayTreeNode $node, int $key, ?SplayTreeNode &$lastVisited): ?SplayTreeNode + { + if ($node === null) { + return null; + } + + $lastVisited = $node; + + if ($key < $node->key) { + return $this->searchNode($node->left, $key, $lastVisited); + } elseif ($key > $node->key) { + return $this->searchNode($node->right, $key, $lastVisited); + } else { + return $node; + } + } + + /** + * Check if a node with the given key exists in the tree. + * Apply rotations to the searched node or to the last visited node to the root of the tree. + * + * Time complexity: O(log n) for the splay operation. + * + * @param int $key The key of the node being searched + * @return bool True if the node exists, false otherwise + */ + public function isFound(int $key): bool + { + $foundNode = $this->search($key); + return $foundNode && $foundNode->key === $key; + } + + /** + * Updates the value of the node with the given key. + * If the node is found, update its value and apply rotations to bring it to the root of the tree. + * Otherwise, apply rotations to the last node visited in the search. + * + * Time complexity: O(log n) for the splay operation. + * + * @param int $key The key of the node to update + * @param mixed $value The new value to set + * @return SplayTreeNode|null Returns the root of the tree after the update or the last visited + */ + public function update(int $key, $value): ?SplayTreeNode + { + if ($this->isFound($key)) { + $this->root->value = $value; + } + return $this->root; + } + + /** + * Deletes the node with the given key from the tree. + * Splays the node to be deleted to the root, then isolates it and restructures the tree. + * If the key doesn't exist, apply rotations to bring the closest node to the root. + * + * Time complexity: O(log n) for the splay operation and O(log n) for restructuring the tree. + * + * @param int $key The key of the node to delete + * @return SplayTreeNode|null The new root after the deletion + * @throws LogicException If the key is not found in the tree + */ + public function delete(int $key): ?SplayTreeNode + { + if (!$this->isFound($key)) { + throw new LogicException("Key: " . $key . " not found in tree. Splayed the last visited node."); + } + + $leftSubtree = $this->root->left; + $rightSubtree = $this->root->right; + + $this->isolateRoot(); + $this->counter--; + return $this->restructureAfterDeletion($leftSubtree, $rightSubtree); + } + + /** + * Isolates the root node by breaking its connections. + */ + private function isolateRoot(): void + { + if ($this->root->left !== null) { + $this->root->left->parent = null; + } + if ($this->root->right !== null) { + $this->root->right->parent = null; + } + + $this->root->left = null; + $this->root->right = null; + } + + /** + * Restructures the tree after the root node has been isolated for deletion. + * + * @return SplayTreeNode|null The new root after restructuring + */ + private function restructureAfterDeletion(?SplayTreeNode $leftSubtree, ?SplayTreeNode $rightSubtree): ?SplayTreeNode + { + if ($leftSubtree === null) { + return $this->handleEmptyLeftSubtree($rightSubtree); + } + + return $this->mergeSubtrees($leftSubtree, $rightSubtree); + } + + /** + * Handles the case when the left subtree is empty after deletion. + * + * @param SplayTreeNode|null $rightSubtreeRoot The root of the right subtree + * @return SplayTreeNode|null The new root + */ + private function handleEmptyLeftSubtree(?SplayTreeNode $rightSubtreeRoot): ?SplayTreeNode + { + $this->root = $rightSubtreeRoot; + if ($this->root !== null) { + $this->root->parent = null; + } + return $this->root; + } + + /** + * Merges the left and right subtrees after deletion. + * + * @param SplayTreeNode $leftSubtreeRoot The root of the left subtree + * @param SplayTreeNode|null $rightSubtreeRoot The root of the right subtree + * @return SplayTreeNode The new root after merging + */ + private function mergeSubtrees(SplayTreeNode $leftSubtreeRoot, ?SplayTreeNode $rightSubtreeRoot): SplayTreeNode + { + $maxLeftNode = $this->maxNode($leftSubtreeRoot); + $this->root = $maxLeftNode; + + $this->detachMaxNodeFromLeftSubtree($maxLeftNode, $leftSubtreeRoot); + $this->attachRightSubtree($rightSubtreeRoot); + + return $this->root; + } + + /** + * Detaches the max node from the left subtree and reattaches the left subtree to the new root. + * + * @param SplayTreeNode $maxLeftNode The max node of the left subtree + * @param SplayTreeNode $leftSubtreeRoot The root of the left subtree + */ + private function detachMaxNodeFromLeftSubtree(SplayTreeNode $maxLeftNode, SplayTreeNode $leftSubtreeRoot): void + { + $maxLeftNodeParent = $maxLeftNode->parent; + if ($maxLeftNodeParent !== null) { + $maxLeftNodeParent->right = null; + $this->root->left = $leftSubtreeRoot; + $leftSubtreeRoot->parent = $this->root; + } + $maxLeftNode->parent = null; + } + + /** + * Attaches the right subtree to the new root. + * + * @param SplayTreeNode|null $rightSubtreeRoot The root of the right subtree + */ + private function attachRightSubtree(?SplayTreeNode $rightSubtreeRoot): void + { + $this->root->right = $rightSubtreeRoot; + if ($rightSubtreeRoot !== null) { + $rightSubtreeRoot->parent = $this->root; + } + } + + /** + * Finds the node with the maximum key in the given subtree. + * + * Time complexity: O(log n) for finding the maximum node in the subtree. + * + * @param SplayTreeNode|null $node The subtree to search for the maximum node + * @return SplayTreeNode|null The node with the maximum key, or null if the subtree is empty + */ + public function maxNode(?SplayTreeNode $node): ?SplayTreeNode + { + if ($node === null) { + return null; + } + return $node->right === null + ? $node + : $this->maxNode($node->right); + } + + /** + * Perform an in-order traversal of the Splay Tree. + * + * @param SplayTreeNode|null $node The root node to start traversing from + * @return array Representation of the traversed nodes + */ + public function inOrderTraversal(?SplayTreeNode $node): array + { + $result = []; + if ($node !== null) { + $result = array_merge($result, $this->inOrderTraversal($node->left)); + $result[] = [$node->key => $node->value]; + $result = array_merge($result, $this->inOrderTraversal($node->right)); + } + return $result; + } +} diff --git a/DataStructures/SplayTree/SplayTreeNode.php b/DataStructures/SplayTree/SplayTreeNode.php new file mode 100644 index 00000000..a11df33d --- /dev/null +++ b/DataStructures/SplayTree/SplayTreeNode.php @@ -0,0 +1,51 @@ +key = $key; + $this->value = $value; + + // Set all node pointers to null initially + $this->left = null; + $this->right = null; + $this->parent = null; + } + + public function isLeaf(): bool + { + return $this->left === null && $this->right === null; + } + + public function isRoot(): bool + { + return $this->parent === null; + } +} diff --git a/DataStructures/SplayTree/SplayTreeRotations.php b/DataStructures/SplayTree/SplayTreeRotations.php new file mode 100644 index 00000000..ec4ad36b --- /dev/null +++ b/DataStructures/SplayTree/SplayTreeRotations.php @@ -0,0 +1,174 @@ +rotateRight($node); + } + + /** + * Zag rotation (single left rotation). + * Performs a left rotation on the given node. + * A case where the node is directly a right child of its parent. + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after rotation. + */ + protected function zag(SplayTreeNode $node): SplayTreeNode + { + return $this->rotateLeft($node); + } + + /** + * Zig-Zig rotation (double right rotation). + * Performs two consecutive right rotations on the given node. The first right rotation is applied to + * the node’s parent, and the second one to the node’s new parent (the previous grandparent). + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotations. + */ + protected function zigZig(SplayTreeNode $node): SplayTreeNode + { + $node = $this->rotateRight($node); + return $this->rotateRight($node); + } + + /** + * Zag-Zag rotation (double left rotation). + * Performs two consecutive left rotations on the given node. The first left rotation is applied to + * the node’s parent, and the second one to the node’s new parent (the previous grandparent). + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotations. + */ + protected function zagZag(SplayTreeNode $node): SplayTreeNode + { + $node = $this->rotateLeft($node); + return $this->rotateLeft($node); + } + + /** + * Zig-Zag rotation (left-right rotation). + * Performs a left rotation on the left child followed by a right rotation on the node itself. + * + * A case when the target key is in the right subtree of the left child. + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotations. + */ + protected function zigZag(SplayTreeNode $node): SplayTreeNode + { + $node->left = $this->rotateLeft($node->left); + return $this->rotateRight($node); + } + + /** + * Zag-Zig rotation (right-left rotation). + * Performs a right rotation on the right child followed by a left rotation on the node itself. + * + * A case when the target key is in the left subtree of the right child. + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotations. + */ + protected function zagZig(SplayTreeNode $node): SplayTreeNode + { + $node->right = $this->rotateRight($node->right); + return $this->rotateLeft($node); + } + + /** + * Rotates the given node to the left, bringing its right child up to take its place. + * The left subtree of the node's right child will become the new right subtree of the node. + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotation (the former right child). + */ + private function rotateLeft(SplayTreeNode $node): SplayTreeNode + { + $rightChild = $node->right; + + if ($rightChild === null) { + return $node; // No rotation possible + } + + $node->right = $rightChild->left; + + if ($rightChild->left !== null) { + $rightChild->left->parent = $node; + } + + $rightChild->parent = $node->parent; + + if ($node->parent === null) { + static::setRoot($rightChild); + } elseif ($node === $node->parent->left) { + $node->parent->left = $rightChild; + } else { + $node->parent->right = $rightChild; + } + + $rightChild->left = $node; + $node->parent = $rightChild; + + return $rightChild; + } + + /** + * Rotates the given node to the right, bringing its left child up to take its place. + * The right subtree of the node's left child will become the new left subtree of the node. + * + * @param SplayTreeNode $node The node to be rotated. + * @return SplayTreeNode The new root of the subtree after the rotation (the former left child). + */ + private function rotateRight(SplayTreeNode $node): SplayTreeNode + { + $leftChild = $node->left; + + if ($leftChild === null) { + return $node; // No rotation possible + } + + $node->left = $leftChild->right; + + if ($leftChild->right !== null) { + $leftChild->right->parent = $node; + } + + $leftChild->parent = $node->parent; + + if ($node->parent === null) { + static::setRoot($leftChild); + } elseif ($node === $node->parent->right) { + $node->parent->right = $leftChild; + } else { + $node->parent->left = $leftChild; + } + + $leftChild->right = $node; + $node->parent = $leftChild; + + return $leftChild; + } +} diff --git a/tests/DataStructures/SplayTreeTest.php b/tests/DataStructures/SplayTreeTest.php new file mode 100644 index 00000000..9c33ffa7 --- /dev/null +++ b/tests/DataStructures/SplayTreeTest.php @@ -0,0 +1,588 @@ +tree = new SplayTree(); + } + + private function populateTree(): void + { + $this->tree->insert(20, "Value 20"); + $this->tree->insert(15, "Value 15"); + $this->tree->insert(17, "Value 17"); + $this->tree->insert(25, "Value 25"); + $this->tree->insert(30, "Value 30"); + $this->tree->insert(36, "Value 36"); + $this->tree->insert(23, "Value 23"); + $this->tree->insert(24, "Value 24"); + $this->tree->insert(22, "Value 22"); + $this->tree->insert(5, "Value 5"); + } + + public function testTreeInitialization() + { + $tree = new SplayTree(); + + $this->assertNull($tree->getRoot(), "Tree root should be null upon initialization."); + $this->assertEquals(0, $tree->size(), "Tree size should be 0 upon initialization."); + $this->assertTrue($tree->isEmpty(), "Tree should be empty upon initialization."); + } + + /** + * Checks if the root node is correctly set after one insertion. + */ + public function testInsertSingleElement() + { + $this->tree->insert(10, "Value 10"); + $root = $this->tree->getRoot(); + + $this->assertFalse($this->tree->isEmpty(), "Tree cannot be empty. Insertion failed."); + $this->assertNotNull($root, "Tree has one node and its root cannot be Null"); + + $this->assertEquals(10, $root->key, "The key must match the key of the inserted node"); + $this->assertEquals("Value 10", $root->value, "The value must match the value of the inserted node"); + + $this->assertTrue($root->isRoot(), "Tree root must not have a parent"); + $this->assertTrue($root->isLeaf(), "Root node has no children yet"); + } + + /** + * Inserts multiple nodes and checks if the last inserted node is splayed to the root. + */ + public function testInsertMultiple() + { + $this->populateTree(); + + $root = $this->tree->getRoot(); + + $this->assertFalse($this->tree->isEmpty(), "Tree was not populated correctly"); + $this->assertSame(10, $this->tree->size(), "Failed to insert all 10 nodes"); + + $this->assertEquals(5, $root->key, "After splay, the last inserted node should be the new root."); + $this->assertEquals("Value 5", $root->value, "The value of the new root must match the last inserted node"); + + $this->assertTrue($root->isRoot(), "The last inserted node has no longer a parent. Failed to splay correctly."); + $this->assertFalse($root->isLeaf(), "The last inserted node is no longer a leaf. Failed to splay correctly."); + } + + /** + * Inserts multiple nodes from an associative array and checks if the last inserted node is splayed to the root. + */ + public function testInsertMultipleFromArray() + { + $arrayData = [200 => "Value 200", 150 => "Value 150", 170 => "Value 170", + 250 => "Value 250", 300 => "Value 300", 360 => "Value 360", 230 => "Value 230", + 240 => "Value 240", 220 => "Value 220", 50 => "Value 50" + ]; + + $splayTree = new SplayTree($arrayData); + $root = $splayTree->getRoot(); + + $this->assertFalse($splayTree->isEmpty(), "Tree was not populated correctly"); + $this->assertSame( + count($arrayData), + $splayTree->size(), + "Failed to insert all " . count($arrayData) . " nodes" + ); + + $this->assertEquals(50, $root->key, "After splay, the new root should be the last inserted node"); + $this->assertEquals("Value 50", $root->value, "The value of the new root must match the last inserted node"); + + $this->assertTrue($root->isRoot(), "The last inserted node has no longer a parent. Failed to splay correctly."); + $this->assertFalse($root->isLeaf(), "The last inserted node is no longer a leaf. Failed to splay correctly."); + } + + /** + * Checks the empty state of the tree before and after insertions. + */ + public function testIsEmpty() + { + $this->assertTrue($this->tree->isEmpty(), "Tree should be empty."); + $this->tree->insert(120, "Value 120"); + $this->assertFalse($this->tree->isEmpty(), "Tree should not be empty."); + } + + /** + * Data provider for splay insertion and inOrder traversal test. + * Provides different sets of insertions and expected results. + * Format: [nodesInserted, InOrderNodeKeys, rootNodeKey] + */ + public static function splayInsertionDataProvider(): array + { + return [ + // Test case 1: Insert 20 + [ + 'insertions' => [20 => "Value 20"], + 'expectedInOrderKeys' => [20], + 'expectedRootKey' => 20, + ], + // Test case 2: Insert 20, 15 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15"], + 'expectedInOrderKeys' => [15, 20], + 'expectedRootKey' => 15, + ], + // Test case 3: Insert 20, 15, 17 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17"], + 'expectedInOrderKeys' => [15, 17, 20], + 'expectedRootKey' => 17, + ], + // Test case 25: Insert 20, 15, 17, 25 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25"], + 'expectedInOrderKeys' => [15, 17, 20, 25], + 'expectedRootKey' => 25, + ], + // Test case 30: Insert 20, 15, 17, 25, 30 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", + 30 => "Value 30"], + 'expectedInOrderKeys' => [15, 17, 20, 25, 30], + 'expectedRootKey' => 30, + ], + // Test case 36: Insert 20, 15, 17, 25, 30, 36 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", + 30 => "Value 30", 36 => "Value 36"], + 'expectedInOrderKeys' => [15, 17, 20, 25, 30, 36], + 'expectedRootKey' => 36, + ], + // Test case 23: Insert 20, 15, 17, 25, 30, 36, 23 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", + 30 => "Value 30", 36 => "Value 36", 23 => "Value 23"], + 'expectedInOrderKeys' => [15, 17, 20, 23, 25, 30, 36], + 'expectedRootKey' => 23, + ], + // Test case 24: Insert 20, 15, 17, 25, 30, 36, 23, 24 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", + 30 => "Value 30", 36 => "Value 36", 23 => "Value 23", 24 => "Value 24"], + 'expectedInOrderKeys' => [15, 17, 20, 23, 24, 25, 30, 36], + 'expectedRootKey' => 24, + ], + // Test case 22: Insert 20, 15, 17, 25, 30, 36, 23, 24, 22 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", + 30 => "Value 30", 36 => "Value 36", 23 => "Value 23", 24 => "Value 24", 22 => "Value 22"], + 'expectedInOrderKeys' => [15, 17, 20, 22, 23, 24, 25, 30, 36], + 'expectedRootKey' => 22, + ], + // Test case 5: Insert 20, 15, 17, 25, 30, 36, 23, 24, 22, 5 + [ + 'insertions' => [20 => "Value 20", 15 => "Value 15", 17 => "Value 17", 25 => "Value 25", 30 => + "Value 30", 36 => "Value 36", 23 => "Value 23", 24 => "Value 24", 22 => "Value 22", 5 => "Value 5"], + 'expectedInOrderKeys' => [5, 15, 17, 20, 22, 23, 24, 25, 30, 36], + 'expectedRootKey' => 5, + ], + ]; + } + + /** + * Test tree structure with inOrder Traversal after insertion and splaying nodes. + * @dataProvider splayInsertionDataProvider + */ + public function testSplayWithInOderTraversal($insertions, $expectedInOrderKeys, $expectedRootKey): void + { + $tree = new SplayTree(); + + // Insert nodes and splay + foreach ($insertions as $key => $value) { + $tree->insert($key, $value); + } + + // Traverse the tree structure wit inOrder Traversal + $inOrderArray = $tree->inOrderTraversal($tree->getRoot()); + $inOrderArrayKeys = $this->getInOrderKeys($inOrderArray); + + // Assert the in-order traversal keys match the expected keys for every dataProvider case + $this->assertEquals( + $expectedInOrderKeys, + $inOrderArrayKeys, + 'Tree structure after splay is not correct. The in-order traversal is not correct.' + ); + + // Assert the root key matches the expected root after the last insertion for every dataProvider case + $this->assertTrue( + $tree->getRoot()->key === $inOrderArrayKeys[array_search($expectedRootKey, $expectedInOrderKeys)], + "Node was not splayed to root successfully" + ); + // Assert the new root is correctly set + $this->assertTrue($tree->getRoot()->isRoot(), "Node with key $expectedRootKey should be the new tree root"); + } + + /** + * Helper function to extract keys from the in-order traversal array. + */ + private function getInOrderKeys(array $inOrderArray): array + { + $inOrderArrayKeys = []; + foreach ($inOrderArray as $node) { + $inOrderArrayKeys = array_merge($inOrderArrayKeys, array_keys($node)); + } + return $inOrderArrayKeys; + } + + // ------------- Test Operations on Splay Tree ------------- + + /** + * Verifies that searching for an existing key returns the correct node + * and ensures that it is splayed to the root. + */ + public function testSearchExistingKey() + { + $this->populateTree(); + + $node = $this->tree->search(22); + + $this->assertNotNull($node, "Returned node cannot be Null."); + $this->assertNull($node->parent, "The searched node must have become the new root which has no parent"); + $this->assertTrue( + $node->isRoot(), + "The searched node must have become the new root. Failed to splay it correctly." + ); + + $this->assertEquals(22, $node->key, "Node with key 22 should be returned. Got a non-expected key: $node->key"); + $this->assertEquals( + "Value 22", + $node->value, + "Value of Node with key 22 does not match. Got a non-expected value: $node->value" + ); + } + + /** + * Verifies that checking for an existing key returns true + * and ensures that its node is splayed to the root. + */ + public function testIsFoundExistingKey() + { + $this->populateTree(); + + $isFound = $this->tree->isFound(25); + $node = $this->tree->getRoot(); + + $this->assertTrue($isFound, "Node with key 25 exists."); + $this->assertEquals(25, $node->key, "Node with key 25 should be returned. Got a non-expected key: $node->key"); + + $this->assertTrue( + $node->isRoot(), + "The searched node must have become the new root. Failed to splay it correctly." + ); + } + + /** + * Ensures that searching for a non-existing key returns the last visit node + * and ensures that it is splayed to the root. + */ + public function testSearchNonExistingKey() + { + $this->populateTree(); + + $node = $this->tree->search(250); // Search for a non-existing key + + $this->assertNotNull($node, "Returned node cannot be Null."); + $this->assertEquals( + 36, + $node->key, + "Node key: 36 should be returned. Got a Non-expected key: $node->key + . Failed to splay the last visited node." + ); + + $this->assertEquals( + "Value 36", + $node->value, + "Value of node 36 does not match. Got a Non-expected value: $node->value" + ); + + $this->assertNull( + $node->parent, + "The last visited node must have become the new root with has no parent. Failed to splay correctly." + ); + } + + /** + * Verifies that checking for a non-existing key returns false + * and ensures that the last visited node is splayed to the root. + */ + public function testIsFoundNonExistingKey() + { + $this->populateTree(); + + $isFound = $this->tree->isFound(18); + $node = $this->tree->getRoot(); + + $this->assertFalse($isFound, "Node with key 18 does not exist."); + $this->assertEquals( + 17, + $node->key, + "Node key: 17 should be returned. Got a Non-expected key: $node->key + . Failed to splay the last visited node." + ); + $this->assertTrue( + $node->isRoot(), + "The last visited node must have become the new root. Failed to splay it correctly." + ); + } + + /** + * Tests the update functionality on an existing key and ensures its node is splayed to the root. + */ + public function testUpdateExistingKey() + { + $this->populateTree(); + + $this->tree->update(36, 360); + + $node = $this->tree->search(36); + + $this->assertNotNull($node, "Node with key 36 should exist after update."); + $this->assertEquals(360, $node->value, "Node with key 36 should have the updated value."); + $this->assertEquals(36, $node->key, "Node with key 36 should be returned. Got a non-expected key: $node->key"); + $this->assertTrue( + $node->isRoot(), + "The updated node must have become the new root. Failed to splay it correctly." + ); + } + + /** + * Checks that updating a non-existing key splays the last visited node only. + */ + public function testUpdateNonExistingKey() + { + $this->populateTree(); + + $node = $this->tree->update(60, "Value 60"); // Update a non-existing key + + $this->assertNotNull($node, "Returned node cannot be Null"); + $this->assertEquals( + 36, + $node->key, + "Node key: 36 should be returned. Got a Non-expected key: $node->key + . Failed to splay the last visited node." + ); + $this->assertEquals( + "Value 36", + $node->value, + "Value of node 36 does not match. Got a Non-expected value: $node->value" + ); + $this->assertNull( + $node->parent, + "The last visited node must have become the new root with has no parent. Failed to splay correctly." + ); + } + + /** + * Tests deletion of a node and checks if the correct new root is set after merging the two subtrees. + */ + public function testDeleteExistingKey() + { + $this->populateTree(); + + $node = $this->tree->delete(22); + $isFound = $this->tree->isFound(22); + + $this->assertFalse($isFound, "Node with key 22 was not deleted."); + $this->assertEquals( + 20, + $node->key, + "After deleting 22, the new root should be the node with key 20." + ); + } + + /** + * Tests correct subtree merging after deletion of a splayed node to the root. + */ + public function testMergeAfterDeleteExistingKey() + { + $this->populateTree(); + + $node = $this->tree->delete(20); + + $inOrderTraversalNodes = $this->tree->inOrderTraversal($this->tree->getRoot()); + $inOrderArrayKeys = $this->getInOrderKeys($inOrderTraversalNodes); + + $expectedInOrderKeys = [5, 15, 17, 22, 23, 24, 25, 30, 36]; + + $this->assertEquals( + 17, + $node->key === $inOrderArrayKeys[array_search($node->key, $expectedInOrderKeys)], + "After deleting 20, the new root should be the node with key 17." + ); + + // Assert the in-order traversal keys match the expected keys + $this->assertEquals( + $expectedInOrderKeys, + $inOrderArrayKeys, + 'Tree structure after splay is not correct. + The in-order traversal is not correct. Failed to merge subtrees.' + ); + } + + /** + * Ensures that attempting to delete a non-existing key throws an exception and keeps the tree intact. + */ + public function testDeleteNonExistingKey() + { + $this->populateTree(); + + $root = $this->tree->getRoot(); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage("Key: 90 not found in tree. Splayed the last visited node."); + + $this->tree->delete(90); // Delete a non-existing key + $this->assertEquals(5, $root->key, "The tree root should not have been changed."); + } + + /** + * Tests update, search, size, isFound and delete operations on an empty tree. + */ + public function testOperationsOnEmptyTree() + { + $this->assertEquals(0, $this->tree->size(), "Tree should be empty."); + + $rootNode1 = $this->tree->search(100); + $this->assertNull($rootNode1, "Searching for a key in an empty tree should return null."); + + $rootNode2 = $this->tree->isFound(200); + $this->assertFalse($rootNode2, "Searching for a key in an empty tree should return null."); + + $rootNode3 = $this->tree->update(100, "Value 100"); + $this->assertNull($rootNode3, "Updating a key in an empty tree should return null."); + + $this->expectException(LogicException::class); + $rootNode4 = $this->tree->delete(100); + $this->assertNull($rootNode4, "Deleting a key in an empty tree should return null."); + } + + + // ------------- Test 6 Rotation types of the Splay Tree ------------- + + /** + * Verify the structure after the Zig rotation + */ + public function testZigRotation(): void + { + $tree = new SplayTree(); + $this->populateTree(); + + $tree->insert(20, 'A'); + $tree->insert(10, 'B'); // Trigger a Zig rotation when 10 is splayed + + $root = $tree->getRoot(); + $this->assertSame(10, $root->key, 'Root should be 10 after Zig rotation'); + $this->assertNull($root->parent, "Root parent is Null after Zig rotation"); + $this->assertSame(20, $root->right->key, '20 should be the right child of 10 after Zig rotation'); + } + + /** + * Verify the structure after the Zag rotation + */ + public function testZagRotation(): void + { + $tree = new SplayTree(); + + $tree->insert(10, 'A'); + $tree->insert(20, 'B'); // Trigger a Zag rotation when 20 is splayed + + $root = $tree->getRoot(); + $this->assertSame(20, $root->key, 'Root should be 20 after Zag rotation'); + $this->assertNull($root->parent, "Root parent is Null after Zig rotation"); + $this->assertSame(10, $root->left->key, '10 should be the left child of 20 after Zag rotation'); + } + + /** + * Verify the structure after the Zig-Zig rotation + */ + public function testZigZigRotation(): void + { + $tree = new SplayTree(); + + $tree->insert(30, 'A'); + $tree->insert(20, 'B'); + $tree->insert(10, 'C'); // Trigger a Zig-Zig rotation when 10 is splayed + + $root = $tree->getRoot(); + $this->assertSame(10, $root->key, 'Root should be 10 after Zig-Zig rotation'); + $this->assertTrue($root->isRoot(), "Root parent should be Null after Zig-Zig rotation"); + $this->assertSame(20, $root->right->key, '20 should be the right child of 10 after Zig-Zig rotation'); + $this->assertSame(30, $root->right->right->key, '30 should be the right child of 20 after Zig-Zig rotation'); + } + + /** + * Verify the structure after the Zag-Zag rotation + */ + public function testZagZagRotation(): void + { + $tree = new SplayTree(); + + $tree->insert(10, 'A'); + $tree->insert(20, 'B'); + $tree->insert(30, 'C'); // Trigger a Zag-Zag rotation when 30 is splayed + + $root = $tree->getRoot(); + $this->assertSame(30, $root->key, 'Root should be 30 after Zag-Zag rotation'); + $this->assertTrue($root->isRoot(), "Root parent should be Null after Zag-Zag rotation"); + $this->assertSame(20, $root->left->key, '20 should be the left child of 30 after Zag-Zag rotation'); + $this->assertSame(10, $root->left->left->key, '10 should be the left child of 20 after Zag-Zag rotation'); + } + + /** + * Verify the structure after the Zig-Zag rotation + */ + public function testZigZagRotation(): void + { + $tree = new SplayTree(); + + $tree->insert(30, 'A'); + $tree->insert(10, 'B'); + $tree->insert(20, 'C'); // Trigger Zig-Zag rotation when 20 is splayed + + $root = $tree->getRoot(); + $this->assertSame(20, $root->key, 'Root should be 20 after Zig-Zag rotation'); + $this->assertTrue($root->isRoot(), "Root parent should be Null after Zig-Zag rotation"); + $this->assertSame(10, $root->left->key, '10 should be the left child of 20 after Zig-Zag rotation'); + $this->assertSame(30, $root->right->key, '30 should be the right child of 20 after Zig-Zag rotation'); + } + + /** + * Verify the structure after the Zag-Zig rotation + */ + public function testZagZigRotation(): void + { + $tree = new SplayTree(); + + $tree->insert(10, 'A'); + $tree->insert(30, 'B'); + $tree->insert(20, 'C'); // Trigger a Zag-Zig rotation when 20 is splayed + + $root = $tree->getRoot(); + $this->assertSame(20, $root->key, 'Root should be 20 after Zag-Zig rotation'); + $this->assertTrue($root->isRoot(), "Root parent should be Null after Zag-Zag rotation"); + $this->assertSame(10, $root->left->key, '10 should be the left child of 20 after Zag-Zig rotation'); + $this->assertSame(30, $root->right->key, '30 should be the right child of 20 after Zag-Zig rotation'); + } +} From 9e0964a1bbcce0e5efcda8f76124417798b2cbb4 Mon Sep 17 00:00:00 2001 From: Brandon Johnson Date: Mon, 7 Oct 2024 00:12:24 -0600 Subject: [PATCH 78/95] updating contribution guide to include requirement for unit tests to be passing and code is linted with phpcs (#170) --- CONTRIBUTING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8917aa2e..0b0abd8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,9 +62,12 @@ We want your work to be readable by others; therefore, we encourage you to note If you used a Wikipedia article or some other source material to create your algorithm, please add the URL in a docstring or comment to help your reader -- Write tests +- Write proper unit tests (see examples in [tests](https://github.com/TheAlgorithms/PHP/tree/master/tests) and ensure all unit tests are passing (composer run-script test) + - Avoid importing external libraries for basic algorithms. Only use them for complicated algorithms +- Ensure code is linted with phpcs, and passing all linting checks (vendor/bin/phpcs -n) + #### Other Standard While Submitting Your Work - File extension for code should be `.php` From ea4d3b0fdda1dc596cd8cd9a0270f2873c4e3e7b Mon Sep 17 00:00:00 2001 From: Ramy Date: Tue, 15 Oct 2024 22:43:04 +0200 Subject: [PATCH 79/95] Enhanced AVL Tree Data Structure with (de-)serialization Feature (#173) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md * updating DIRECTORY.md * Implemented AVLTree DataStructure * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented SegmentTreeNode.php * Implementing SegmentTree * Implementing SegmentTree with updateTree * Implementing SegmentTree with rangeUpdateTree * Implementing SegmentTree with query and queryTree * Added serializing and deserializing of the SegmentTree * Adding unit tests SegmentTree implementation * Added unit tests for SegmentTree updates and range updates * considering PHPCS for Added unit tests for SegmentTree updates and range updates * Added unit tests for SegmentTree serialization/deserialization and array updates reflections * Added unit tests for SegmentTree Edge Cases * Added unit tests for SegmentTree Exceptions (OutOfBoundsException, InvalidArgumentException) * Added SegmentTree to DIRECTORY.md * Implemented Segment Tree Data Structure * updating DIRECTORY.md * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added comments time complexity for query(), update() and buildTree() * Implemented Splay Tree Data Structure * Update tests/DataStructures/SplayTreeTest.php Co-authored-by: Brandon Johnson * Implemented AVL Tree Data Structure. Added serialization/deserialization feature. Added corresponding unit test. --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DataStructures/AVLTree/AVLTree.php | 82 +++++++++++++++++++++++++++- tests/DataStructures/AVLTreeTest.php | 67 ++++++++++++++++++++++- 2 files changed, 145 insertions(+), 4 deletions(-) diff --git a/DataStructures/AVLTree/AVLTree.php b/DataStructures/AVLTree/AVLTree.php index ff05ff86..28f08734 100644 --- a/DataStructures/AVLTree/AVLTree.php +++ b/DataStructures/AVLTree/AVLTree.php @@ -1,8 +1,9 @@ serializeTree($this->root)); + } + + /** + * Recursively serializes the AVL Tree. + * + * @param AVLTreeNode|null $node + * @return array + */ + private function serializeTree(?AVLTreeNode $node): array + { + if ($node === null) { + return []; + } + return [ + 'key' => $node->key, + 'value' => $node->value, + 'left' => $this->serializeTree($node->left), + 'right' => $this->serializeTree($node->right), + 'height' => $node->height, + ]; + } + + /** + * Deserializes a JSON string into an AVL Tree object + * + * @param string $data The JSON representation of an AVL Tree to deserialize. + */ + public function deserialize(string $data): void + { + $this->root = $this->deserializeTree(json_decode($data, true)); + $this->counter = 0; + $this->updateNodeCount($this->root); + } + + /** + * Recursively deserializes an AVL Tree from an array representation. + * + * @param array $data The serialized data for the node. + * @return AVLTreeNode|null The root node of the deserialized tree. + */ + private function deserializeTree(array $data): ?AVLTreeNode + { + if (empty($data)) { + return null; + } + + $node = new AVLTreeNode($data['key'], $data['value']); + $node->height = $data['height']; + + $node->left = $this->deserializeTree($data['left']); + $node->right = $this->deserializeTree($data['right']); + + return $node; + } + + /** + * Updates the deserialized tree size. + * + * @param AVLTreeNode|null $node The root node of the deserialized tree. + */ + private function updateNodeCount(?AVLTreeNode $node): void + { + if ($node !== null) { + $this->counter++; + $this->updateNodeCount($node->left); + $this->updateNodeCount($node->right); + } + } } diff --git a/tests/DataStructures/AVLTreeTest.php b/tests/DataStructures/AVLTreeTest.php index 0f1c9955..26729115 100644 --- a/tests/DataStructures/AVLTreeTest.php +++ b/tests/DataStructures/AVLTreeTest.php @@ -1,8 +1,9 @@ tree->insert(15, 'Value 15'); } + /** + * Tests the insert and search operations in the AVLTree. + */ public function testInsertAndSearch(): void { $this->populateTree(); @@ -47,6 +51,10 @@ public function testInsertAndSearch(): void $this->assertNull($this->tree->search(25), 'Value for non-existent key 25 should be null'); } + /** + * Tests the deletion of nodes and ensures the AVLTree maintains + * its integrity after deletions. + */ public function testDelete(): void { $this->populateTree(); @@ -167,6 +175,9 @@ public function testInsertDuplicateKeys(): void ); } + /** + * Tests the insertion and deletion of a large number of nodes. + */ public function testLargeTree(): void { // Inserting a large number of nodes @@ -186,6 +197,9 @@ public function testLargeTree(): void } } + /** + * Tests whether the AVLTree remains balanced after insertions. + */ public function testBalance(): void { $this->populateTree(); @@ -300,4 +314,53 @@ public function testSizeOnEmptyTree(): void $this->tree = new AVLTree(); $this->assertEquals(0, $this->tree->size(), 'Size should be 0 for an empty tree'); } + + /** + * Test serialization and deserialization + */ + public function testAVLTreeSerialization(): void + { + $avlTree = new AVLTree(); + + $avlTree->insert(100, 'Value 100'); + $avlTree->insert(200, 'Value 200'); + $avlTree->insert(50, 'Value 50'); + $avlTree->insert(150, 'Value 150'); + $avlTree->insert(350, 'Value 350'); + $avlTree->insert(40, 'Value 40'); + $avlTree->insert(90, 'Value 90'); + + $avlTreeRoot = $avlTree->getRoot(); + $serializedAVLTree = $avlTree->serialize(); + + $deserializedTree = new AVLTree(); + $deserializedTree->deserialize($serializedAVLTree); + + $deserializedTreeRoot = $deserializedTree->getRoot(); + + $this->assertEquals($deserializedTreeRoot->key, $avlTreeRoot->key, 'The two roots key should match'); + $this->assertEquals($deserializedTreeRoot->value, $avlTreeRoot->value, 'The two roots value should match'); + $this->assertEquals( + $deserializedTreeRoot->left->key, + $avlTreeRoot->left->key, + 'Left child of the two roots should match' + ); + $this->assertEquals( + $deserializedTreeRoot->right->key, + $avlTreeRoot->right->key, + 'Left child of the two roots should match' + ); + $this->assertEquals( + $deserializedTreeRoot->height, + $avlTreeRoot->height, + 'The two trees should match in height' + ); + $this->assertEquals($deserializedTree->size(), $avlTree->size(), 'The two trees should match in size'); + + $this->assertSame( + $deserializedTree->inOrderTraversal(), + $avlTree->inOrderTraversal(), + 'Tree structure was not retained' + ); + } } From 33604749b6122eedd75b00e7b8ba1cfa7de075c5 Mon Sep 17 00:00:00 2001 From: Ramy Date: Tue, 15 Oct 2024 22:46:33 +0200 Subject: [PATCH 80/95] Enhanced Trie Data Structure with Case-Insensitive Feature and additional Test Cases (#172) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md * updating DIRECTORY.md * Implemented AVLTree DataStructure * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented SegmentTreeNode.php * Implementing SegmentTree * Implementing SegmentTree with updateTree * Implementing SegmentTree with rangeUpdateTree * Implementing SegmentTree with query and queryTree * Added serializing and deserializing of the SegmentTree * Adding unit tests SegmentTree implementation * Added unit tests for SegmentTree updates and range updates * considering PHPCS for Added unit tests for SegmentTree updates and range updates * Added unit tests for SegmentTree serialization/deserialization and array updates reflections * Added unit tests for SegmentTree Edge Cases * Added unit tests for SegmentTree Exceptions (OutOfBoundsException, InvalidArgumentException) * Added SegmentTree to DIRECTORY.md * Implemented Segment Tree Data Structure * updating DIRECTORY.md * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added comments time complexity for query(), update() and buildTree() * Implemented Splay Tree Data Structure * Update tests/DataStructures/SplayTreeTest.php Co-authored-by: Brandon Johnson * Implemented Trie Data Structure. Added case-insensitive feature to the Trie implementation. Added corresponding unit testing. --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DataStructures/Trie/Trie.php | 4 +- DataStructures/Trie/TrieNode.php | 14 ++- tests/DataStructures/TrieTest.php | 161 +++++++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 4 deletions(-) diff --git a/DataStructures/Trie/Trie.php b/DataStructures/Trie/Trie.php index 32c8c6d9..03b353a0 100644 --- a/DataStructures/Trie/Trie.php +++ b/DataStructures/Trie/Trie.php @@ -1,8 +1,9 @@ root; for ($i = 0; $i < strlen($prefix); $i++) { $char = $prefix[$i]; diff --git a/DataStructures/Trie/TrieNode.php b/DataStructures/Trie/TrieNode.php index d9865f1a..303da902 100644 --- a/DataStructures/Trie/TrieNode.php +++ b/DataStructures/Trie/TrieNode.php @@ -1,8 +1,9 @@ normalizeChar($char); if (!isset($this->children[$char])) { $this->children[$char] = new TrieNode(); } @@ -38,6 +40,7 @@ public function addChild(string $char): TrieNode */ public function hasChild(string $char): bool { + $char = $this->normalizeChar($char); return isset($this->children[$char]); } @@ -46,6 +49,15 @@ public function hasChild(string $char): bool */ public function getChild(string $char): ?TrieNode { + $char = $this->normalizeChar($char); return $this->children[$char] ?? null; } + + /** + * Normalize the character to lowercase. + */ + private function normalizeChar(string $char): string + { + return strtolower($char); + } } diff --git a/tests/DataStructures/TrieTest.php b/tests/DataStructures/TrieTest.php index 7733ed80..a8ab2cdc 100644 --- a/tests/DataStructures/TrieTest.php +++ b/tests/DataStructures/TrieTest.php @@ -1,8 +1,9 @@ trie = new Trie(); } + /** + * Test insertion and search functionality of the Trie. + */ public function testInsertAndSearch() { $this->trie->insert('the'); @@ -42,6 +47,48 @@ public function testInsertAndSearch() ); } + /** + * Test insertion and search functionality with mixed case words. + */ + public function testInsertAndSearchMixedCase() + { + $this->trie->insert('Apple'); + $this->trie->insert('aPPle'); + $this->assertTrue($this->trie->search('apple'), 'Expected "apple" to be found in the Trie.'); + $this->assertTrue($this->trie->search('APPLE'), 'Expected "APPLE" to be found in the Trie.'); + } + + /** + * Test insertion and search functionality with special characters. + */ + public function testInsertAndSearchWithSpecialCharacters() + { + $this->trie->insert('hello123'); + $this->trie->insert('user@domain.com'); + $this->assertTrue($this->trie->search('hello123'), 'Expected "hello123" to be found in the Trie.'); + $this->assertTrue( + $this->trie->search('UseR@domain.CoM'), + 'Expected "user@domain.com" to be found in the Trie.' + ); + $this->assertTrue( + $this->trie->search('HELLO123'), + 'Expected "HELLO123" not to be found in the Trie (case-sensitive).' + ); + } + + /** + * Test insertion and search functionality with long strings. + */ + public function testInsertAndSearchLongStrings() + { + $longString = str_repeat('a', 1000); + $this->trie->insert($longString); + $this->assertTrue($this->trie->search($longString), 'Expected the long string to be found in the Trie.'); + } + + /** + * Test the startsWith functionality of the Trie. + */ public function testStartsWith() { $this->trie->insert('hello'); @@ -58,9 +105,31 @@ public function testStartsWith() ); } + /** + * Test startsWith functionality with mixed case prefixes. + */ + public function testStartsWithMixedCase() + { + $this->trie->insert('PrefixMatch'); + $this->trie->insert('PreFixTesting'); + $this->assertEquals( + ['prefixmatch', 'prefixtesting'], + $this->trie->startsWith('prefix'), + 'Expected words starting with "prefix" to be found in the Trie (case-insensitive).' + ); + + $this->assertEquals( + ['prefixmatch', 'prefixtesting'], + $this->trie->startsWith('PREFIX'), + 'Expected words starting with "PREFIX" to be found in the Trie (case-insensitive).' + ); + } + + /** + * Test deletion of existing words from the Trie. + */ public function testDelete() { - // Insert words into the Trie $this->trie->insert('the'); $this->trie->insert('universe'); $this->trie->insert('is'); @@ -80,12 +149,51 @@ public function testDelete() $this->assertTrue($this->trie->search('rather'), 'Expected "rather" to be found.'); } + /** + * Test deletion of mixed case words from the Trie. + */ + public function testDeleteMixedCase() + { + $this->trie->insert('MixedCase'); + $this->assertTrue($this->trie->search('mixedcase'), 'Expected "mixedcase" to be found before deletion.'); + + $this->trie->delete('MIXEDCASE'); + $this->assertFalse( + $this->trie->search('MixedCase'), + 'Expected "MixedCase" not to be found after deletion (case-insensitive).' + ); + } + + /** + * Test deletion of words with special characters. + */ + public function testDeleteWithSpecialCharacters() + { + $this->trie->insert('spec!@l#chars'); + $this->assertTrue( + $this->trie->search('spec!@l#chars'), + 'Expected "spec!@l#chars" to be found before deletion.' + ); + + $this->trie->delete('SPEC!@L#CHARS'); + $this->assertFalse( + $this->trie->search('spec!@l#chars'), + 'Expected "spec!@l#chars" not to be found after deletion.' + ); + } + + /** + * Test deletion of a non-existent word from the Trie. + */ public function testDeleteNonExistentWord() { $this->trie->delete('nonexistent'); $this->assertFalse($this->trie->search('nonexistent'), 'Expected "nonexistent" to not be found.'); } + /** + * Test traversal of the Trie and retrieval of words. + */ public function testTraverseTrieNode() { $this->trie->insert('hello'); @@ -99,11 +207,17 @@ public function testTraverseTrieNode() $this->assertCount(3, $words, 'Expected 3 words in the Trie.'); } + /** + * Test behavior of an empty Trie. + */ public function testEmptyTrie() { $this->assertEquals([], $this->trie->getWords(), 'Expected an empty Trie to return an empty array.'); } + /** + * Test retrieval of words from the Trie. + */ public function testGetWords() { $this->trie->insert('apple'); @@ -117,12 +231,18 @@ public function testGetWords() $this->assertCount(3, $words, 'Expected 3 words in the Trie.'); } + /** + * Test insertion of an empty string into the Trie. + */ public function testInsertEmptyString() { $this->trie->insert(''); $this->assertTrue($this->trie->search(''), 'Expected empty string to be found in the Trie.'); } + /** + * Test deletion of an empty string from the Trie. + */ public function testDeleteEmptyString() { $this->trie->insert(''); @@ -130,6 +250,9 @@ public function testDeleteEmptyString() $this->assertFalse($this->trie->search(''), 'Expected empty string not to be found after deletion.'); } + /** + * Test the startsWith functionality with a common prefix. + */ public function testStartsWithWithCommonPrefix() { $this->trie->insert('trie'); @@ -142,4 +265,38 @@ public function testStartsWithWithCommonPrefix() $this->assertContains('trier', $words, 'Expected "trier" to be found with prefix "tri".'); $this->assertCount(3, $words, 'Expected 3 words with prefix "tri".'); } + + /** + * Test retrieval of the root node of the Trie. + */ + public function testGetRoot() + { + $root = $this->trie->getRoot(); + $this->assertInstanceOf(TrieNode::class, $root, 'Expected root to be an instance of TrieNode.'); + $this->assertFalse($root->isEndOfWord, 'Expected the root node not to be the end of a word.'); + $this->assertCount(0, $root->children, 'Expected the root node to have no children initially.'); + } + + /** + * Test retrieval of the root node after populating the Trie with words. + */ + public function testGetRootAfterPopulation() + { + $this->trie->insert('TheAlgorithms'); + $this->trie->insert('PHP'); + $this->trie->insert('DSA'); + + $root = $this->trie->getRoot(); + + $this->assertInstanceOf(TrieNode::class, $root, 'Expected root to be an instance of TrieNode.'); + + // Assert that the root node is not marked as the end of a word + $this->assertFalse($root->isEndOfWord, 'Expected the root node not to be the end of a word.'); + + // Assert that the root node has children corresponding to the inserted words + $this->assertCount(3, $root->children, 'Expected the root node to have 3 children after inserting words.'); + $this->assertTrue($root->hasChild('t'), 'Expected root to have a child for "t".'); + $this->assertTrue($root->hasChild('p'), 'Expected root to have a child for "p".'); + $this->assertTrue($root->hasChild('D'), 'Expected root to have a child for "D".'); + } } From cb2bbf948cc35955da79577e242212487eb9452c Mon Sep 17 00:00:00 2001 From: Ramy Date: Tue, 15 Oct 2024 22:55:42 +0200 Subject: [PATCH 81/95] Added Extra Tests for Splay Tree Operations (#171) * Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented Trie DataStructure * Added Trie to DIRECTORY.md * updating DIRECTORY.md * Implemented AVLTree DataStructure * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented SegmentTreeNode.php * Implementing SegmentTree * Implementing SegmentTree with updateTree * Implementing SegmentTree with rangeUpdateTree * Implementing SegmentTree with query and queryTree * Added serializing and deserializing of the SegmentTree * Adding unit tests SegmentTree implementation * Added unit tests for SegmentTree updates and range updates * considering PHPCS for Added unit tests for SegmentTree updates and range updates * Added unit tests for SegmentTree serialization/deserialization and array updates reflections * Added unit tests for SegmentTree Edge Cases * Added unit tests for SegmentTree Exceptions (OutOfBoundsException, InvalidArgumentException) * Added SegmentTree to DIRECTORY.md * Implemented Segment Tree Data Structure * updating DIRECTORY.md * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added some comments to my files in: #160, #162, #163, #166. Implemented Segment Tree Data Structure. * Added comments time complexity for query(), update() and buildTree() * Implemented Splay Tree Data Structure * Update tests/DataStructures/SplayTreeTest.php Co-authored-by: Brandon Johnson * Implemented Splay Tree Data Structure. Added counter test for deletion. * Implemented Splay Tree Data Structure. Added counter test for multiple deletions. * Implemented Splay Tree Data Structure. Added counter test for multiple deletions. * Implemented Splay Tree Data Structure. Added abstract setRoot() declaration to the SplayTreeRotations.php * Implemented Splay Tree Data Structure. Fix for array_rand for non-array result. Rewriting. * Implemented Splay Tree Data Structure. Fix for multiple deletion test. * Implemented Splay Tree Data Structure. Added test for large splay tree operations. --------- Co-authored-by: Brandon Johnson Co-authored-by: Ramy-Badr-Ahmed --- DataStructures/SplayTree/SplayTree.php | 2 +- .../SplayTree/SplayTreeRotations.php | 1 + tests/DataStructures/SplayTreeTest.php | 71 +++++++++++++++++-- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/DataStructures/SplayTree/SplayTree.php b/DataStructures/SplayTree/SplayTree.php index ac8e80b4..c14b64ab 100644 --- a/DataStructures/SplayTree/SplayTree.php +++ b/DataStructures/SplayTree/SplayTree.php @@ -44,7 +44,7 @@ public function getRoot(): ?SplayTreeNode * Set the root node of the Splay Tree. * @param SplayTreeNode $node */ - public function setRoot(SplayTreeNode $node): void + protected function setRoot(SplayTreeNode $node): void { $this->root = $node; } diff --git a/DataStructures/SplayTree/SplayTreeRotations.php b/DataStructures/SplayTree/SplayTreeRotations.php index ec4ad36b..3375caa1 100644 --- a/DataStructures/SplayTree/SplayTreeRotations.php +++ b/DataStructures/SplayTree/SplayTreeRotations.php @@ -13,6 +13,7 @@ abstract class SplayTreeRotations { abstract protected function splay(?SplayTreeNode $node, int $key): ?SplayTreeNode; + abstract protected function setRoot(SplayTreeNode $node): void; /** * Zig rotation (single right rotation). diff --git a/tests/DataStructures/SplayTreeTest.php b/tests/DataStructures/SplayTreeTest.php index 9c33ffa7..82348207 100644 --- a/tests/DataStructures/SplayTreeTest.php +++ b/tests/DataStructures/SplayTreeTest.php @@ -1,8 +1,9 @@ assertNull( $node->parent, - "The last visited node must have become the new root with has no parent. Failed to splay correctly." + "The last visited node must have become the new root which has no parent. Failed to splay correctly." ); } @@ -390,7 +391,7 @@ public function testUpdateNonExistingKey() ); $this->assertNull( $node->parent, - "The last visited node must have become the new root with has no parent. Failed to splay correctly." + "The last visited node must have become the new root which has no parent. Failed to splay correctly." ); } @@ -401,10 +402,18 @@ public function testDeleteExistingKey() { $this->populateTree(); + $nodesNumber = $this->tree->size(); $node = $this->tree->delete(22); - $isFound = $this->tree->isFound(22); + $isFound = $this->tree->isFound(22); $this->assertFalse($isFound, "Node with key 22 was not deleted."); + + $this->assertEquals( + $nodesNumber - 1, + $this->tree->size(), + "After deletion, total nodes count was not updated correctly." + ); + $this->assertEquals( 20, $node->key, @@ -441,6 +450,36 @@ public function testMergeAfterDeleteExistingKey() ); } + /** + * Tests deletion of multiple nodes and checks if the tree size is updated. + */ + public function testDeleteMultipleKeys() + { + $arrayData = [200 => "Value 200", 150 => "Value 150", 170 => "Value 170", + 250 => "Value 250", 300 => "Value 300", 360 => "Value 360", 230 => "Value 230", + 240 => "Value 240", 220 => "Value 220", 50 => "Value 50", 28 => "Value 28", + 164 => "Value 164", 321 => "Value 321", 40 => "Value 40" + ]; + + $splayTree = new SplayTree($arrayData); + $treeSize = $splayTree->size(); + + $nodesToDelete = [150, 300, 50, 240, 170]; + $expectedSize = $treeSize - count($nodesToDelete); + + foreach ($nodesToDelete as $key) { + $splayTree->delete($key); + $isFound = $this->tree->isFound($key); + $this->assertFalse($isFound, "Node with key $key was not deleted."); + } + + $this->assertEquals( + $expectedSize, + $splayTree->size(), + "After deletion, total nodes count was not updated correctly." + ); + } + /** * Ensures that attempting to delete a non-existing key throws an exception and keeps the tree intact. */ @@ -478,6 +517,28 @@ public function testOperationsOnEmptyTree() $this->assertNull($rootNode4, "Deleting a key in an empty tree should return null."); } + /** + * Test insert, search, delete on large trees + */ + public function testLargeTree(): void + { + // Inserting a large number of nodes + for ($i = 1; $i <= 1000; $i++) { + $this->tree->insert($i, "Value $i"); + } + + // Verify that all inserted nodes can be searched + for ($i = 1; $i <= 1000; $i++) { + $this->assertEquals("Value $i", $this->tree->search($i)->value, "Value for key $i should be 'Value $i'"); + } + + // Verify that all inserted nodes can be deleted + for ($i = 1; $i <= 5; $i++) { + $this->tree->delete($i); + $this->assertFalse($this->tree->isFound($i), "Node was not deleted correctly"); + } + } + // ------------- Test 6 Rotation types of the Splay Tree ------------- From 57e772a1e0331dd2679006efc70ffa9b1bd1da87 Mon Sep 17 00:00:00 2001 From: Ramy Date: Sat, 19 Oct 2024 08:59:47 +0200 Subject: [PATCH 82/95] Implemented Binary Search Tree as Data Structure. Implemented with Iterator Interface. (#174) Implemented Binary Search Tree as Data Structure. Implemented with Iterator Interface --- DIRECTORY.md | 6 + DataStructures/BinarySearchTree/BSTNode.php | 66 ++ DataStructures/BinarySearchTree/BSTree.php | 378 +++++++++++ .../BinarySearchTree/BinaryTreeTraversal.php | 207 ++++++ .../DuplicateKeyException.php | 14 + tests/DataStructures/BSTreeTest.php | 598 ++++++++++++++++++ 6 files changed, 1269 insertions(+) create mode 100644 DataStructures/BinarySearchTree/BSTNode.php create mode 100644 DataStructures/BinarySearchTree/BSTree.php create mode 100644 DataStructures/BinarySearchTree/BinaryTreeTraversal.php create mode 100644 DataStructures/BinarySearchTree/DuplicateKeyException.php create mode 100644 tests/DataStructures/BSTreeTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 42b09322..bbfcedfe 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -21,6 +21,11 @@ * [Avltree](./DataStructures/AVLTree/AVLTree.php) * [Avltreenode](./DataStructures/AVLTree/AVLTreeNode.php) * [Treetraversal](./DataStructures/AVLTree/TreeTraversal.php) + * Binarysearchtree + * [Binarytreetraversal](./DataStructures/BinarySearchTree/BinaryTreeTraversal.php) + * [Bstnode](./DataStructures/BinarySearchTree/BSTNode.php) + * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) + * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) @@ -131,6 +136,7 @@ * [Conversionstest](./tests/Conversions/ConversionsTest.php) * Datastructures * [Avltreetest](./tests/DataStructures/AVLTreeTest.php) + * [Bstreetest](./tests/DataStructures/BSTreeTest.php) * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) diff --git a/DataStructures/BinarySearchTree/BSTNode.php b/DataStructures/BinarySearchTree/BSTNode.php new file mode 100644 index 00000000..1b180718 --- /dev/null +++ b/DataStructures/BinarySearchTree/BSTNode.php @@ -0,0 +1,66 @@ +key = $key; + $this->value = $value; + $this->left = null; + $this->right = null; + $this->parent = null; + } + + public function isRoot(): bool + { + return $this->parent === null; + } + + public function isLeaf(): bool + { + return $this->left === null && $this->right === null; + } + + public function getChildren(): array + { + if ($this->isLeaf()) { + return []; + } + + $children = []; + if ($this->left !== null) { + $children['left'] = $this->left; + } + if ($this->right !== null) { + $children['right'] = $this->right; + } + return $children; + } + public function getChildrenCount(): int + { + return count($this->getChildren()); + } +} diff --git a/DataStructures/BinarySearchTree/BSTree.php b/DataStructures/BinarySearchTree/BSTree.php new file mode 100644 index 00000000..0fded5e7 --- /dev/null +++ b/DataStructures/BinarySearchTree/BSTree.php @@ -0,0 +1,378 @@ + $value) { + $this->insert($key, $value); // Build the tree from an array of key-value pairs + } + parent::setTraversalType($traversalType); + } + /** + * Get the root of the Splay Tree. + * + * @return BSTNode|NULL The root node. + */ + public function getRoot(): ?BSTNode + { + return $this->root; + } + + public function size(): int + { + return $this->counter; + } + + public function isEmpty(): bool + { + return $this->root === null; + } + + /** + * Inserts a new node into binary search tree and updates parent reference. + * + * Time complexity: O(log n) for binary search tree insertion. + */ + public function insert(int $key, $value): ?BSTNode + { + $this->insertNode($this->root, $key, $value); + $this->counter++; + return $this->root; + } + + private function insertNode(?BSTNode &$rootPtr, int $key, $value): void + { + if ($rootPtr === null) { + $rootPtr = new BSTNode($key, $value); + return; + } + + if ($key < $rootPtr->key) { + $this->insertNode($rootPtr->left, $key, $value); + $rootPtr->left->parent = $rootPtr; + } elseif ($key > $rootPtr->key) { + $this->insertNode($rootPtr->right, $key, $value); + $rootPtr->right->parent = $rootPtr; + } else { + throw new DuplicateKeyException($key); + } + } + + /** + * Removes and isolates an existing node from the BST and update structure references. + * + * Time complexity: O(log n) for binary search tree node removal. + */ + public function remove(int $key): ?BSTNode + { + $discardedNode = $this->removeNode($this->root, $key); + if ($discardedNode !== null) { + $this->counter--; + } + return $discardedNode; + } + + private function removeNode(?BSTNode &$rootPtr, int $key): ?BSTNode + { + if ($rootPtr === null) { + return null; + } + + if ($key < $rootPtr->key) { + $discardedNode = $this->removeNode($rootPtr->left, $key); + } elseif ($key > $rootPtr->key) { + $discardedNode = $this->removeNode($rootPtr->right, $key); + } else { + // Key found, proceed to delete + $discardedNode = $rootPtr->getChildrenCount() === 2 + ? $this->handleNodeWithTwoChildren($rootPtr) + : $this->handleNodeWithSingleOrZeroChild($rootPtr); + } + return $discardedNode; + } + + /** + * handle deletion when found node has 2 children. + */ + private function handleNodeWithTwoChildren(BSTNode $rootPtr): ?BSTNode + { + $minRightNode = $this->minNode($rootPtr->right); + + $discarded = clone $rootPtr; + + $rootPtr->key = $minRightNode->key; + $rootPtr->value = $minRightNode->value; + + $this->removeNode($rootPtr->right, $minRightNode->key); + + // Isolate the target node + $discarded->left = null; + $discarded->right = null; + $discarded->parent = null; + + return $discarded; + } + + /** + * Handle deletion when found node has 1 or 0 child. + */ + private function handleNodeWithSingleOrZeroChild(BSTNode &$rootPtr): ?BSTNode + { + $discard = $rootPtr; + + if ($discard->isLeaf()) { // Case 1: Node to be removed is a leaf node + $rootPtr = null; + } elseif ($rootPtr->left != null) { // Case 2: Node has only a left child + $rootPtr = $rootPtr->left; + $discard->left = null; + } else { // Case 3: Node has only a right child + $rootPtr = $rootPtr->right; + $discard->right = null; + } + + // Update the parent reference for the new child node + if ($rootPtr !== null) { + $rootPtr->parent = $discard->parent; + } + + // Unlink the discarded node from its parent + $discard->parent = null; + return $discard; + } + + /** + * Return the minimum node in the BST. + */ + public function minNode(?BSTNode $node): ?BSTNode + { + if ($node === null) { + return null; + } + + return $node->left === null + ? $node + : $this->minNode($node->left); + } + + /** + * Search for a node by its key and return the node, otherwise Null. + * + * Time complexity: O(log n) for the splay operation. + */ + public function search(int $key): ?BSTNode + { + if ($this->isEmpty()) { + return null; + } + + return $this->searchNode($this->root, $key); + } + + private function searchNode(?BSTNode $node, int $key): ?BSTNode + { + if ($node === null) { + return null; + } + + if ($key === $node->key) { + return $node; + } elseif ($key < $node->key) { + return $this->searchNode($node->left, $key); + } else { + return $this->searchNode($node->right, $key); + } + } + + /** + * Check if a node with the given key exists in the BST. + * + * Time complexity: O(log n) for the splay operation. + * + * @param BSTNode|null $node The root of the (sub)tree to start propagating from. + * @param int $key The key of the node being searched. + */ + public function isFound(?BSTNode $node, int $key): bool + { + if ($node === null) { + return false; + } + + if ($key === $node->key) { + return true; + } elseif ($key < $node->key) { + return $this->isFound($node->left, $key); + } else { + return $this->isFound($node->right, $key); + } + } + + /** + * Get the depth of the given node relative to the root of the tree. + * Traverses from the given node up to the root of the tree by recursively following the parent references + * + * Time complexity: O(d_p) where d_p: depth of node p in tree + * + * @param BSTNode $node The node whose depth is to be calculated. + * @return int The depth of the node relative to the root. + */ + public function getDepth(BSTNode $node): int + { + return $node->parent === null + ? 0 + : 1 + $this->getDepth($node->parent); + } + + /** + * Get the height of the given node relative to the farthest leaf node. + * Recursively visits all children of the given node determining the maximum height of its subtrees + * + * Time Complexity: O(n) + * + * @param BSTNode $node The node whose height is to be calculated. + * @return int The height of the node relative to its farthest descendant. + */ + public function getHeight(BSTNode $node): int + { + if ($node->isLeaf()) { + return 0; + } + $height = 0; + $childrenList = $node->getChildren(); + + foreach ($childrenList as $childNode) { + $height = max($height, $this->getHeight($childNode)); + } + return 1 + $height; + } + + /** + * Returns a flat associative array structure for inOrder Traversal + */ + public function inOrderTraversal(): array + { + return $this->inOrder($this->root); + } + + /** + * Returns a flat associative array structure for preOrder Traversal + */ + public function preOrderTraversal(): array + { + return $this->preOrder($this->root); + } + + /** + * Returns a flat associative array structure for postOrder Traversal + */ + public function postOrderTraversal(): array + { + return $this->postOrder($this->root); + } + + /** + * Returns a flat associative array structure for Breadth-first Traversal + */ + public function breadthFirstTraversal(): array + { + return $this->breadthFirst($this->root); + } + + /** + * Serializes the BST into a JSON string. + * + * @return string The BST as a JSON string. + */ + public function serialize(): string + { + return json_encode($this->serializeTree($this->root)); + } + + /** + * Recursively serializes the Binary Search Tree. + * + * @param BSTNode|null $node The current node to serialize from. + * @return array The serialized representation of the node. + */ + private function serializeTree(?BSTNode $node): array + { + if ($node === null) { + return []; + } + return [ + 'key' => $node->key, + 'value' => $node->value, + 'left' => $this->serializeTree($node->left), + 'right' => $this->serializeTree($node->right), + ]; + } + + /** + * Deserializes a JSON string into a BST object. + * + * @param string $data The JSON string to deserialize. + * @return BSTree The deserialized Binary Search Tree. + */ + public function deserialize(string $data): BSTree + { + $arrayRepresentation = json_decode($data, true); + + $binarySearchTree = new self(); + $binarySearchTree->root = $binarySearchTree->deserializeTree($arrayRepresentation, null); + + $binarySearchTree->updateNodeCount($binarySearchTree->root); + return $binarySearchTree; + } + + /** + * Recursively deserializes a BST from an array representation. + * + * @param array $data The serialized data for the node. + * @return BSTNode|null The deserialized node. + */ + private function deserializeTree(array $data, ?BSTNode $parent): ?BSTNode + { + if (empty($data)) { + return null; + } + + $node = new BSTNode($data['key'], $data['value']); + $node->parent = $parent; + + $node->left = $this->deserializeTree($data['left'], $node); + $node->right = $this->deserializeTree($data['right'], $node); + + return $node; + } + /** + * Recursively updates the BST size after deserialization. + */ + private function updateNodeCount(?BSTNode $node): void + { + if ($node !== null) { + $this->counter++; + $this->updateNodeCount($node->left); + $this->updateNodeCount($node->right); + } + } +} diff --git a/DataStructures/BinarySearchTree/BinaryTreeTraversal.php b/DataStructures/BinarySearchTree/BinaryTreeTraversal.php new file mode 100644 index 00000000..a312dacc --- /dev/null +++ b/DataStructures/BinarySearchTree/BinaryTreeTraversal.php @@ -0,0 +1,207 @@ +traversalType = $traversalType; + + // Reset iterator with new traversal + $this->rewind(); + } + + /** + * Helper to populate iteration nodes per traversal ype + */ + private function loadTraversedNodes(): void + { + switch ($this->traversalType) { + case self::PRE_ORDER: + $this->preOrderIterator(static::getRoot()); + break; + + case self::POST_ORDER: + $this->postOrderIterator(static::getRoot()); + break; + + case self::IN_ORDER: + default: + $this->inOrderIterator(static::getRoot()); + } + } + + /** + * Returns a flat associative array structure for inOrder Traversal + */ + protected function inOrder(?BSTNode $node): array + { + $result = []; + if ($node !== null) { + $result += $this->inOrder($node->left); + $result[$node->key] = $node->value; + $result += $this->inOrder($node->right); + } + return $result; + } + + /** + * Returns a flat associative array structure for preOrder Traversal + */ + protected function preOrder(?BSTNode $node): array + { + $result = []; + if ($node !== null) { + $result[$node->key] = $node->value; + $result += $this->preOrder($node->left); + $result += $this->preOrder($node->right); + } + return $result; + } + + /** + * Returns a flat associative array structure for postOrder Traversal + */ + protected function postOrder(?BSTNode $node): array + { + $result = []; + if ($node !== null) { + $result += $this->postOrder($node->left); + $result += $this->postOrder($node->right); + $result[$node->key] = $node->value; + } + return $result; + } + + /** + * Returns a flat associative array structure for BFT Traversal + */ + protected function breadthFirst(?BSTNode $node): array + { + $result = []; + if (!isset($node)) { + return $result; + } + + $queue = []; + $queue[] = $node; + + while (!empty($queue)) { + $currentNode = array_shift($queue); + $result[$currentNode->key] = $currentNode->value; + + if ($currentNode->left !== null) { + $queue[] = $currentNode->left; + } + + if ($currentNode->right !== null) { + $queue[] = $currentNode->right; + } + } + return $result; + } + + /** + * Rewind the iterator to the first element. + */ + public function rewind(): void + { + $this->iterationNodes = []; + $this->currentPosition = 0; + + // Load nodes based on traversal type + $this->loadTraversedNodes(); + } + + /** + * Return the current element if exists + */ + public function current(): ?BSTNode + { + return $this->iterationNodes[$this->currentPosition] ?? null; + } + + /** + * Return the key of the current element. + */ + public function key(): int + { + return $this->currentPosition; + } + + /** + * Move forward to the next element. + */ + public function next(): void + { + $this->currentPosition++; + } + + /** + * Check if the current position is valid. + */ + public function valid(): bool + { + return isset($this->iterationNodes[$this->currentPosition]); + } + + /** + * Helper function to traverse the tree in-order and fill the $inOrderNodes array. + */ + private function inOrderIterator(?BSTNode $node): void + { + if ($node !== null) { + $this->inOrderIterator($node->left); + $this->iterationNodes[] = $node; + $this->inOrderIterator($node->right); + } + } + /** + * Helper function to traverse the tree in-order and fill the $preOrderNodes array. + */ + private function preOrderIterator(?BSTNode $node): void + { + if ($node !== null) { + $this->iterationNodes[] = $node; + $this->preOrderIterator($node->left); + $this->preOrderIterator($node->right); + } + } + /** + * Helper function to traverse the tree in-order and fill the $postOrderNodes array. + */ + private function postOrderIterator(?BSTNode $node): void + { + if ($node !== null) { + $this->postOrderIterator($node->left); + $this->postOrderIterator($node->right); + $this->iterationNodes[] = $node; + } + } +} diff --git a/DataStructures/BinarySearchTree/DuplicateKeyException.php b/DataStructures/BinarySearchTree/DuplicateKeyException.php new file mode 100644 index 00000000..37cf1669 --- /dev/null +++ b/DataStructures/BinarySearchTree/DuplicateKeyException.php @@ -0,0 +1,14 @@ +tree = new BSTree(); + } + + public function testTreeInitialization() + { + $this->assertNull($this->tree->getRoot(), "Tree root should be null upon initialization."); + $this->assertEquals(0, $this->tree->size(), "Tree size should be 0 upon initialization."); + $this->assertTrue($this->tree->isEmpty(), "Tree should be empty upon initialization."); + } + + /** + * Test: Insert a single node + */ + public function testInsertSingleNode(): void + { + $this->tree->insert(10, 'value10'); + $this->assertNotNull($this->tree->getRoot(), "Tree root should not be null after inserting."); + $this->assertEquals(10, $this->tree->getRoot()->key, "Node key does not match the key inserted in node"); + $this->assertEquals( + 'value10', + $this->tree->getRoot()->value, + "Node value does not match the value inserted in node" + ); + } + + /** + * Test: Insert multiple nodes and validate small structure + */ + public function testInsertMultipleNodes(): void + { + $this->tree->insert(20, 'value20'); + $this->tree->insert(10, 'value10'); + $this->tree->insert(30, 'value30'); + + $this->assertEquals(20, $this->tree->getRoot()->key, "Root node was not set properly."); + $this->assertEquals(10, $this->tree->getRoot()->left->key, "Left node was not inserted properly"); + $this->assertEquals(30, $this->tree->getRoot()->right->key, "Right node was not inserted properly"); + } + + /** + * Test: Insert multiple nodes from array and validate structure + * + */ + public function testInsertMultipleNodesFromArray() + { + $arrayData = [200 => "Value 200", 150 => "Value 150", 170 => "Value 170", + 250 => "Value 250", 300 => "Value 300", 360 => "Value 360", 230 => "Value 230", + 240 => "Value 240", 220 => "Value 220", 50 => "Value 50", 28 => "Value 28", 164 => "Value 164", + 321 => "Value 321", 40 => "Value 40", 9 => "Value 9", 32 => "Value 32", 64 => "Value 64", + ]; + + $bsTree = new BSTree($arrayData); + $root = $bsTree->getRoot(); + + $this->assertFalse($bsTree->isEmpty(), "Tree was not populated correctly"); + $this->assertSame(count($arrayData), $bsTree->size(), "Failed to insert all " . count($arrayData) . " nodes"); + + $this->assertEquals(200, $root->key, "The root should be the first inserted node"); + $this->assertEquals("Value 200", $root->value, "The value of the new root must match the first inserted node"); + } + + /** + * Test: Duplicate key insertion should throw an exception + */ + public function testInsertDuplicateKey(): void + { + $this->tree->insert(15, 'value15'); + $this->expectException(DuplicateKeyException::class); + $this->tree->insert(15, 'valueNew'); + } + + /** + * Checks the empty state of the tree before and after insertions. + */ + public function testIsEmpty() + { + $this->assertTrue($this->tree->isEmpty(), "Tree should be empty."); + $this->tree->insert(120, "Value 120"); + $this->assertFalse($this->tree->isEmpty(), "Tree should not be empty."); + } + + /** + * Helper to populate the initialized tree for further tests + * + * The structure of the Binary Search Tree (BST) after insertion: + * * + * * 200 + * * / \ + * * 150 250 + * * / \ / \ + * * 140 170 220 300 + * * / / \ / \ \ + * * 130 160 180 215 230 360 + * * / \ / \ + * * 110 185 225 240 + * * / \ + * * 50 115 + * * / + * * 70 + */ + private function populateTree(): void + { + $this->tree->insert(200, "Value 200"); + $this->tree->insert(150, "Value 150"); + $this->tree->insert(250, "Value 250"); + $this->tree->insert(170, "Value 170"); + $this->tree->insert(140, "Value 140"); + $this->tree->insert(130, "Value 130"); + $this->tree->insert(110, "Value 110"); + $this->tree->insert(115, "Value 115"); + $this->tree->insert(160, "Value 160"); + $this->tree->insert(180, "Value 180"); + $this->tree->insert(185, "Value 185"); + $this->tree->insert(220, "Value 220"); + $this->tree->insert(230, "Value 230"); + $this->tree->insert(300, "Value 300"); + $this->tree->insert(360, "Value 360"); + $this->tree->insert(215, "Value 215"); + $this->tree->insert(240, "Value 240"); + $this->tree->insert(225, "Value 225"); + $this->tree->insert(50, "Value 50"); + $this->tree->insert(70, "Value 70"); + } + + /** + * Test: Search for an existing node + */ + public function testSearchNodeExists(): void + { + $this->populateTree(); + $node = $this->tree->search(115); + $isFound = $this->tree->isFound($this->tree->getRoot(), 230); + + $this->assertNotNull($node, "The node with key 115 exists. Should not be null."); + $this->assertEquals(115, $node->key, "The node key does not match"); + $this->assertEquals('Value 115', $node->value, "The node value does not match"); + $this->assertTrue($isFound, "Node with key 230 exists."); + } + + /** + * Test: Search for a non-existing node + */ + public function testSearchNodeNotExists(): void + { + $this->populateTree(); + + $node = $this->tree->search(444); + $isFound = $this->tree->isFound($this->tree->getRoot(), 1500); + + $this->assertNull($node, "Node with key 444 does not exist"); + $this->assertFalse($isFound, "Node with key 1500 does not exist."); + } + + /** + * Test: Remove a leaf node + */ + public function testRemoveLeafNode(): void + { + $this->populateTree(); + + $this->assertTrue($this->tree->search(360)->isLeaf(), "The node with key 360 is leaf."); + $parentNode = $this->tree->search(360)->parent->key; // 300 + + $removedNode = $this->tree->remove(360); + $this->assertNull($this->tree->search(360), "Node with key 360 should be gone"); + $this->assertNotNull( + $this->tree->search($parentNode), + "Parent node with key 300 should still exist. Tree was not merged correctly." + ); + + $this->assertEquals(360, $removedNode->key, "The key of the removed node does not match"); + $this->assertEquals('Value 360', $removedNode->value, "The value of the removed node does not match"); + $this->assertNull( + $removedNode->left, + "The left pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->right, + "The right pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->parent, + "The parent pointer was not broken from tree correctly. Node isolation failed." + ); + } + + /** + * Test: Remove a node with one child + */ + public function testRemoveNodeWithOneChild(): void + { + $this->populateTree(); + + $this->assertEquals(1, $this->tree->search(140)->getChildrenCount(), "The node with key 140 has one child."); + $parentNode = $this->tree->search(140)->parent->key; // 150 + $leftNode = $this->tree->search(140)->left->key; // 130 + + $removedNode = $this->tree->remove(140); + $this->assertNull($this->tree->search(140), "Node with key 140 should be gone"); + $this->assertNotNull( + $this->tree->search($parentNode), + "Parent node with key 150 should still exist. Tree was not merged correctly." + ); + $this->assertNotNull( + $this->tree->search($leftNode), + "Left Node with key 130 should still exist. Tree was not merged correctly." + ); + + $this->assertEquals(140, $removedNode->key, "The key of the removed node does not match"); + $this->assertEquals('Value 140', $removedNode->value, "The value of the removed node does not match"); + $this->assertNull( + $removedNode->left, + "The left pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->right, + "The right pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->parent, + "The parent pointer was not broken from tree correctly. Node isolation failed." + ); + } + + /** + * Test: Remove a node with two children + */ + public function testRemoveNodeWithTwoChildren(): void + { + $this->populateTree(); + + $this->assertEquals( + 2, + $this->tree->search(230)->getChildrenCount(), + "The node with key 230 has two children." + ); + + $parentNode = $this->tree->search(230)->parent->key; // 220 + $leftNode = $this->tree->search(230)->left->key; // 225 + $rightNode = $this->tree->search(230)->right->key; // 240 + + $removedNode = $this->tree->remove(230); + $this->assertNull($this->tree->search(230), "Node with key 230 should be gone"); + $this->assertNotNull( + $this->tree->search($parentNode), + "Parent Node with key 220 should still exist. Tree was not merged correctly." + ); + $this->assertNotNull( + $this->tree->search($leftNode), + "Left Node with key 225 should still exist. Tree was not merged correctly." + ); + $this->assertNotNull( + $this->tree->search($rightNode), + "Parent Node with key 240 should still exist. Tree was not merged correctly." + ); + + $this->assertEquals(230, $removedNode->key, "The key of the removed node does not match"); + $this->assertEquals('Value 230', $removedNode->value, "The value of the removed node does not match"); + $this->assertNull( + $removedNode->left, + "The left pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->right, + "The right pointer was not broken from tree correctly. Node isolation failed." + ); + $this->assertNull( + $removedNode->parent, + "The parent pointer was not broken from tree correctly. Node isolation failed." + ); + } + + public function testRemoveNonExistingNode(): void + { + $this->populateTree(); + $removedNode = $this->tree->remove(3333); + $this->assertNull($removedNode, "Node not found, Null should be returned."); + } + + /** + * Test: Verify all operations on a large tree. + */ + public function testOperationsOnLargeTree(): void + { + for ($i = 1; $i <= 1000; $i++) { + $this->tree->insert($i, "Value $i"); + } + + for ($i = 1; $i <= 1000; $i++) { + $this->assertEquals("Value $i", $this->tree->search($i)->value, "Value for key $i should be 'Value $i'"); + } + + for ($i = 1; $i <= 1000; $i++) { + $this->assertTrue($this->tree->isFound($this->tree->getRoot(), $i), "Node with key $i should exist"); + } + + for ($i = 1; $i <= 5; $i++) { + $this->tree->remove($i); + $this->assertFalse( + $this->tree->isFound($this->tree->getRoot(), $i), + "Value for key $i should be not exist after deletion" + ); + } + } + + /** + * Test: Check tree size + */ + public function testTreeSize(): void + { + $this->assertEquals(0, $this->tree->size()); + + $arrayData = [200 => "Value 200", 150 => "Value 150", 170 => "Value 170", + 250 => "Value 250", 300 => "Value 300", 360 => "Value 360", 230 => "Value 230", + 240 => "Value 240", 220 => "Value 220", 50 => "Value 50", 28 => "Value 28", + 164 => "Value 164", 321 => "Value 321", 40 => "Value 40", 9 => "Value 9", + 32 => "Value 32", 64 => "Value 64", 116 => "Value 116" + ]; + + $bsTree = new BSTree($arrayData); + $this->assertEquals( + count($arrayData), + $bsTree->size(), + "Tree size should be size of array. Failed to insert all nodes." + ); + } + + /** + * Test depth for various nodes + */ + public function testGetDepth(): void + { + $this->populateTree(); + + $root = $this->tree->getRoot(); + + $node150 = $this->tree->search(150); + $node110 = $this->tree->search(110); + $node70 = $this->tree->search(70); + + $this->assertEquals(0, $this->tree->getdepth($root), "The root node should have a depth of 0."); + $this->assertEquals(1, $this->tree->getdepth($node150), "Node 150 should have a depth of 1."); + $this->assertEquals(4, $this->tree->getdepth($node110), "Node 110 should have a depth of 4."); + $this->assertEquals(6, $this->tree->getdepth($node70), "Node 70 should have a depth of 6."); + } + + /** + * Test height for various nodes + */ + public function testGetHeight(): void + { + $this->populateTree(); + + $root = $this->tree->getRoot(); + + $node150 = $this->tree->search(150); + $node110 = $this->tree->search(110); + $node70 = $this->tree->search(70); + $node360 = $this->tree->search(360); + + $this->assertEquals(6, $this->tree->getheight($root), "The root node should have a height of 6."); + $this->assertEquals(5, $this->tree->getheight($node150), "Node 150 should have a height of 5."); + $this->assertEquals(0, $this->tree->getheight($node70), "Node 70 should have a height of 0 (it's a leaf)."); + $this->assertEquals(0, $this->tree->getheight($node360), "Node 360 should have a height of 0 (it's a leaf)."); + $this->assertEquals(2, $this->tree->getheight($node110), "Node 110 should have a height of 2."); + } + + /** + * Test: In-order traversal + */ + public function testInOrderTraversal(): void + { + $this->assertSame( + $this->getExpectedInOrder(), + $this->tree->inOrderTraversal(), + "Did not match the expected inOrder nodes. Failed inOrder traversal." + ); + } + + private function getExpectedInOrder(): array + { + $this->populateTree(); + + return [ + 50 => 'Value 50', 70 => 'Value 70', 110 => 'Value 110', + 115 => 'Value 115', 130 => 'Value 130', 140 => 'Value 140', + 150 => 'Value 150', 160 => 'Value 160', 170 => 'Value 170', + 180 => 'Value 180', 185 => 'Value 185', 200 => 'Value 200', 215 => 'Value 215', + 220 => 'Value 220', 225 => 'Value 225', 230 => 'Value 230', 240 => 'Value 240', + 250 => 'Value 250', 300 => 'Value 300', 360 => 'Value 360' + ]; + } + + /** + * Test: Pre-order traversal + */ + public function testPreOrderTraversal(): void + { + $this->assertSame( + $this->getExpectedPreOrder(), + $this->tree->preOrderTraversal(), + "Did not match the expected preOrder nodes. Failed preOrder traversal." + ); + } + + private function getExpectedPreOrder(): array + { + $this->populateTree(); + + return [ + 200 => 'Value 200', 150 => 'Value 150', 140 => 'Value 140', + 130 => 'Value 130', 110 => 'Value 110', 50 => 'Value 50', + 70 => 'Value 70', 115 => 'Value 115', 170 => 'Value 170', + 160 => 'Value 160', 180 => 'Value 180', 185 => 'Value 185', 250 => 'Value 250', + 220 => 'Value 220', 215 => 'Value 215', 230 => 'Value 230', 225 => 'Value 225', + 240 => 'Value 240', 300 => 'Value 300', 360 => 'Value 360' + ]; + } + + /** + * Test: Post-order traversal + */ + public function testPostOrderTraversal(): void + { + $this->assertSame( + $this->getExpectedPostOrder(), + $this->tree->postOrderTraversal(), + "Did not match the expected postOrder nodes. Failed postOrder traversal." + ); + } + + private function getExpectedPostOrder(): array + { + $this->populateTree(); + + return [ + 70 => 'Value 70', 50 => 'Value 50', 115 => 'Value 115', + 110 => 'Value 110', 130 => 'Value 130', 140 => 'Value 140', + 160 => 'Value 160', 185 => 'Value 185', 180 => 'Value 180', + 170 => 'Value 170', 150 => 'Value 150', 215 => 'Value 215', 225 => 'Value 225', + 240 => 'Value 240', 230 => 'Value 230', 220 => 'Value 220', 360 => 'Value 360', + 300 => 'Value 300', 250 => 'Value 250', 200 => 'Value 200' + ]; + } + + /** + * Test: Breadth-first traversal + */ + public function testBreadthFirstTraversal(): void + { + $this->assertSame( + $this->getExpectedBFT(), + $this->tree->breadthFirstTraversal(), + "Did not match the expected breadth-first nodes. Failed BFT traversal." + ); + } + + private function getExpectedBFT(): array + { + $this->populateTree(); + + return [ + 200 => 'Value 200', 150 => 'Value 150', 250 => 'Value 250', + 140 => 'Value 140', 170 => 'Value 170', 220 => 'Value 220', + 300 => 'Value 300', 130 => 'Value 130', 160 => 'Value 160', + 180 => 'Value 180', 215 => 'Value 215', 230 => 'Value 230', 360 => 'Value 360', + 110 => 'Value 110', 185 => 'Value 185', 225 => 'Value 225', 240 => 'Value 240', + 50 => 'Value 50', 115 => 'Value 115', 70 => 'Value 70' + ]; + } + + /** + * Test: Serialize and deserialize + */ + public function testSerializationAndDeserialization(): void + { + $this->populateTree(); + + $serializedData = $this->tree->serialize(); + $deserializedTree = $this->tree->deserialize($serializedData); + + $this->assertEquals( + $this->tree->inOrderTraversal(), + $deserializedTree->inOrderTraversal(), + "Deserialized tree should match the original tree." + ); + $this->assertEquals( + $this->tree->size(), + $deserializedTree->size(), + "Deserialized tree size was not updated correctly." + ); + } + + /** + * Provides traversal types and expected results for the iterator test. + */ + public static function traversalProvider(): array + { + return [ + // Test case for In-Order traversal + 'InOrder' => [ + 'traversalType' => 'inOrder', + 'expected' => [ + 50 => 'Value 50', 70 => 'Value 70', 110 => 'Value 110', + 115 => 'Value 115', 130 => 'Value 130', 140 => 'Value 140', + 150 => 'Value 150', 160 => 'Value 160', 170 => 'Value 170', + 180 => 'Value 180', 185 => 'Value 185', 200 => 'Value 200', 215 => 'Value 215', + 220 => 'Value 220', 225 => 'Value 225', 230 => 'Value 230', 240 => 'Value 240', + 250 => 'Value 250', 300 => 'Value 300', 360 => 'Value 360', + ], + ], + // Test case for Pre-Order traversal + 'PreOrder' => [ + 'traversalType' => 'preOrder', + 'expected' => [ + 200 => 'Value 200', 150 => 'Value 150', 140 => 'Value 140', + 130 => 'Value 130', 110 => 'Value 110', 50 => 'Value 50', + 70 => 'Value 70', 115 => 'Value 115', 170 => 'Value 170', + 160 => 'Value 160', 180 => 'Value 180', 185 => 'Value 185', 250 => 'Value 250', + 220 => 'Value 220', 215 => 'Value 215', 230 => 'Value 230', 225 => 'Value 225', + 240 => 'Value 240', 300 => 'Value 300', 360 => 'Value 360', + ], + ], + // Test case for Post-Order traversal + 'PostOrder' => [ + 'traversalType' => 'postOrder', + 'expected' => [ + 70 => 'Value 70', 50 => 'Value 50', 115 => 'Value 115', + 110 => 'Value 110', 130 => 'Value 130', 140 => 'Value 140', + 160 => 'Value 160', 185 => 'Value 185', 180 => 'Value 180', + 170 => 'Value 170', 150 => 'Value 150', 215 => 'Value 215', 225 => 'Value 225', + 240 => 'Value 240', 230 => 'Value 230', 220 => 'Value 220', 360 => 'Value 360', + 300 => 'Value 300', 250 => 'Value 250', 200 => 'Value 200', + ], + ], + ]; + } + + /** + * Test: Iterating over the tree with inOrder, preOrder, and postOrder Traversals. + * + * @dataProvider traversalProvider + */ + public function testIteratorWithTraversalTypes(string $traversalType, array $expected): void + { + $this->tree->setTraversalType($traversalType); + $this->populateTree(); + + $expectedKeys = array_keys($expected); + $expectedValues = array_values($expected); + + $index = 0; + + foreach ($this->tree as $node) { + $this->assertEquals( + $expectedKeys[$index], + $node->key, + "Did not match the expected $traversalType key. Failed tree iteration." + ); + $this->assertEquals( + $expectedValues[$index], + $node->value, + "Did not match the expected $traversalType value. Failed tree iteration." + ); + $index++; + } + $this->assertEquals(count($expected), $index, "Tree iteration did not visit the expected number of nodes."); + } +} From 416caa61d5143675f73b310d4296eafdbb6796ff Mon Sep 17 00:00:00 2001 From: darwinz Date: Sat, 19 Oct 2024 06:59:58 +0000 Subject: [PATCH 83/95] updating DIRECTORY.md --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index bbfcedfe..861f2d22 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -25,7 +25,7 @@ * [Binarytreetraversal](./DataStructures/BinarySearchTree/BinaryTreeTraversal.php) * [Bstnode](./DataStructures/BinarySearchTree/BSTNode.php) * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) - * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) + * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) From de42d6ad062aae106f667f1b7920a314260306ad Mon Sep 17 00:00:00 2001 From: Marco Date: Sat, 19 Oct 2024 09:02:40 +0200 Subject: [PATCH 84/95] temperature conversions (#169) * temperature conversions * perfect number * added unit tests and ran phpcs * unit test * fixed style issue in ConversionsTest.php * fixed comments --- Conversions/TemperatureConversions.php | 115 +++++++++++++++++++++++++ DIRECTORY.md | 2 + Maths/PerfectNumber.php | 39 +++++++++ tests/Conversions/ConversionsTest.php | 57 ++++++++++++ tests/Maths/MathsTest.php | 17 ++++ 5 files changed, 230 insertions(+) create mode 100644 Conversions/TemperatureConversions.php create mode 100644 Maths/PerfectNumber.php diff --git a/Conversions/TemperatureConversions.php b/Conversions/TemperatureConversions.php new file mode 100644 index 00000000..1be1fb52 --- /dev/null +++ b/Conversions/TemperatureConversions.php @@ -0,0 +1,115 @@ +expectException(\Exception::class); convertSpeed(1, 'km/h', 'miles'); } + + public function testCelsiusToFahrenheit() + { + $this->assertEquals(32.0, CelsiusToFahrenheit(0)); + $this->assertEquals(212.0, CelsiusToFahrenheit(100)); + $this->assertEquals(98.6, CelsiusToFahrenheit(37)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Celsius) must be a number'); + CelsiusToFahrenheit("non-numeric"); + } + + public function testFahrenheitToCelsius() + { + $this->assertEquals(0.0, FahrenheitToCelsius(32)); + $this->assertEquals(100.0, FahrenheitToCelsius(212)); + $this->assertEquals(37.0, FahrenheitToCelsius(98.6)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Fahrenheit) must be a number'); + FahrenheitToCelsius("non-numeric"); + } + + public function testCelsiusToKelvin() + { + $this->assertEquals(273.15, CelsiusToKelvin(0)); + $this->assertEquals(373.15, CelsiusToKelvin(100)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Celsius) must be a number'); + CelsiusToKelvin("non-numeric"); + } + + public function testKelvinToCelsius() + { + $this->assertEquals(0.0, KelvinToCelsius(273.15)); + $this->assertEquals(100.0, KelvinToCelsius(373.15)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Kelvin) must be a number'); + KelvinToCelsius("non-numeric"); + } + + public function testKelvinToFahrenheit() + { + $this->assertEquals(32.0, KelvinToFahrenheit(273.15)); + $this->assertEquals(212.0, KelvinToFahrenheit(373.15)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Kelvin) must be a number'); + KelvinToFahrenheit("non-numeric"); + } + + public function testFahrenheitToKelvin() + { + $this->assertEquals(273.15, FahrenheitToKelvin(32)); + $this->assertEquals(373.15, FahrenheitToKelvin(212)); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Temperature (Fahrenheit) must be a number'); + FahrenheitToKelvin("non-numeric"); + } } diff --git a/tests/Maths/MathsTest.php b/tests/Maths/MathsTest.php index 66a86347..a9428b7f 100644 --- a/tests/Maths/MathsTest.php +++ b/tests/Maths/MathsTest.php @@ -22,6 +22,7 @@ require_once __DIR__ . '/../../Maths/Mode.php'; require_once __DIR__ . '/../../Maths/FastInverseSquareRoot.php'; require_once __DIR__ . '/../../Maths/BaseX.php'; +require_once __DIR__ . '/../../Maths/PerfectNumber.php'; class MathsTest extends TestCase { @@ -185,6 +186,22 @@ public function testGreatestCommonDivisor() $this->assertEquals(3, gcd(9, 12)); } + public function testPerfectNumber() + { + $this->assertTrue(perfect_number(6)); + $this->assertTrue(perfect_number(28)); + $this->assertTrue(perfect_number(496)); + + $this->assertFalse(perfect_number(10)); + $this->assertFalse(perfect_number(15)); + + $this->assertFalse(perfect_number(-6)); + $this->assertFalse(perfect_number(0)); + $this->assertFalse(perfect_number(1)); + $this->assertFalse(perfect_number(2.5)); + $this->assertFalse(perfect_number("string")); + } + public function testFastInverseSquareRoot() { $this->assertEqualsWithDelta(0.31568579235273, fastInvSqrt(10), 0.00001); From b90af128d5924db3c7790bc65af44d99bbdda03b Mon Sep 17 00:00:00 2001 From: Michal Zarnecki Date: Thu, 15 Aug 2024 22:51:20 +0200 Subject: [PATCH 85/95] Sieve of Eratosthenes algorithm --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 30aa2c7f..0215db5b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -85,6 +85,7 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) + * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## Searches * [Binarysearch](./Searches/BinarySearch.php) @@ -155,6 +156,7 @@ * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) + * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting From b1290daa543014c15fbfd5159191a2e2d0145a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Mon, 16 Dec 2024 18:29:22 +0100 Subject: [PATCH 86/95] PHP implementation fo Dijkstra's algorithm for finding shortest paths in a graph --- Graphs/BellmanFord.php | 5 ++-- Graphs/Dijkstras.php | 50 ++++++++++++++++++++++++++++++++ tests/Graphs/BellmanFordTest.php | 19 +++++++----- tests/Graphs/DijkstrasTest.php | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 Graphs/Dijkstras.php create mode 100644 tests/Graphs/DijkstrasTest.php diff --git a/Graphs/BellmanFord.php b/Graphs/BellmanFord.php index 37dbcd7d..d1b070e4 100644 --- a/Graphs/BellmanFord.php +++ b/Graphs/BellmanFord.php @@ -13,9 +13,10 @@ class Edge * (https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm). * * @author Michał Żarnecki https://github.com/rzarno - * @param array $verticies An array of verticies names + * @param array $verticesNames An array of verticies names * @param Edge[] $edges An array of edges - * @return string $start The starting vertex + * @param string $start The starting vertex + * @return array An array of shortest paths from $start to all other vertices */ function bellmanFord(array $verticesNames, array $edges, string $start, bool $verbose = false) { diff --git a/Graphs/Dijkstras.php b/Graphs/Dijkstras.php new file mode 100644 index 00000000..95bb3278 --- /dev/null +++ b/Graphs/Dijkstras.php @@ -0,0 +1,50 @@ +start == $nextVertex) { //consider only nodes connected to current one + $vertices[$edge->end] = min($vertices[$edge->end], $vertices[$nextVertex] + $edge->weight); + } + } + + // find vertex with current lowest value to be starting point in next iteration + $minVertexName = null; + $minVertexWeight = PHP_INT_MAX; + foreach ($vertices as $name => $weight) { + if (in_array($name, $visitedNodes) || $name == $nextVertex) { + continue; + } + if ($weight <= $minVertexWeight) { + $minVertexName = $name; + $minVertexWeight = $weight; + } + } + $visitedNodes[] = $nextVertex; + $nextVertex = $minVertexName; + } + return $vertices; +} diff --git a/tests/Graphs/BellmanFordTest.php b/tests/Graphs/BellmanFordTest.php index c483cb34..aabaaa85 100644 --- a/tests/Graphs/BellmanFordTest.php +++ b/tests/Graphs/BellmanFordTest.php @@ -36,13 +36,16 @@ public function testBellmanFord() $result = bellmanFord($vertices, $edges, 'S'); - $this->assertEquals($result, [ - 'S' => 0, - 'A' => 5, - 'B' => 5, - 'C' => 7, - 'D' => 9, - 'E' => 8 - ]); + $this->assertEquals( + [ + 'S' => 0, + 'A' => 5, + 'B' => 5, + 'C' => 7, + 'D' => 9, + 'E' => 8 + ], + $result + ); } } diff --git a/tests/Graphs/DijkstrasTest.php b/tests/Graphs/DijkstrasTest.php new file mode 100644 index 00000000..6618f2de --- /dev/null +++ b/tests/Graphs/DijkstrasTest.php @@ -0,0 +1,50 @@ +start = $edgeRaw[0]; + $edge->end = $edgeRaw[2]; + $edge->weight = $edgeRaw[1]; + $edges[] = $edge; + } + + $result = dijkstras($vertices, $edges, 'S'); + + $this->assertEquals( + [ + 'S' => 0, + 'A' => 5, + 'B' => 5, + 'C' => 7, + 'D' => 9, + 'E' => 8 + ], + $result + ); + } +} From d2fb4603e8d42a5a4eff0adc37afa3733fabffc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Mon, 16 Dec 2024 19:39:15 +0100 Subject: [PATCH 87/95] recuurent algorithm for comparing binary trees --- DIRECTORY.md | 4 + .../CompareBinaryTree/CompareBinaryTree.php | 34 +++++++ DataStructures/CompareBinaryTree/Node.php | 16 ++++ .../DataStructures/CompareBinaryTreeTest.php | 91 +++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 DataStructures/CompareBinaryTree/CompareBinaryTree.php create mode 100644 DataStructures/CompareBinaryTree/Node.php create mode 100644 tests/DataStructures/CompareBinaryTreeTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 0215db5b..3e2e35b2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -27,6 +27,9 @@ * [Bstnode](./DataStructures/BinarySearchTree/BSTNode.php) * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) + * CompareBinaryTree + * [CompareBinaryTree](./DataStructures/CompareBinaryTree/CompareBinaryTree.php) + * [Node](./DataStructures/CompareBinaryTree/Node.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) @@ -50,6 +53,7 @@ * [Bellmanford](./Graphs/BellmanFord.php) * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) * [Depthfirstsearch](./Graphs/DepthFirstSearch.php) + * [Dijkstra's](./Graphs/Dijkstras.php) ## Maths * [Absolutemax](./Maths/AbsoluteMax.php) diff --git a/DataStructures/CompareBinaryTree/CompareBinaryTree.php b/DataStructures/CompareBinaryTree/CompareBinaryTree.php new file mode 100644 index 00000000..787f4f2b --- /dev/null +++ b/DataStructures/CompareBinaryTree/CompareBinaryTree.php @@ -0,0 +1,34 @@ +value !== $b->value) { + return false; + } + return $this->areTreesEqual($a->left, $b->left) + && $this->areTreesEqual($a->right, $b->right); + } +} diff --git a/DataStructures/CompareBinaryTree/Node.php b/DataStructures/CompareBinaryTree/Node.php new file mode 100644 index 00000000..fb194f7f --- /dev/null +++ b/DataStructures/CompareBinaryTree/Node.php @@ -0,0 +1,16 @@ +value = $value; + $this->left = $left; + $this->right = $right; + } + + public $value; + public ?Node $left; + public ?Node $right; +} diff --git a/tests/DataStructures/CompareBinaryTreeTest.php b/tests/DataStructures/CompareBinaryTreeTest.php new file mode 100644 index 00000000..5e680e70 --- /dev/null +++ b/tests/DataStructures/CompareBinaryTreeTest.php @@ -0,0 +1,91 @@ +assertTrue($sut->areTreesEqual($tree1, $tree2)); + } + + public function testBinaryTreesAreNotEqualWhenAreNotEqualInReality() + { + + $tree1 = new Node( + 'A', + new Node( + 'B', + new Node( + 'F' + ), + new Node( + 'E', + null, + new Node( + 'D' + ) + ) + ), + new Node( + 'C', + new Node('G') + ) + ); + + $tree2 = new Node( + 'A', + new Node( + 'B', + new Node( + 'F' + ), + new Node( + 'E', + null, + new Node( + 'D' + ) + ) + ), + new Node( + 'C' + ) + ); + + $sut = new CompareBinaryTree(); + $this->assertFalse($sut->areTreesEqual($tree1, $tree2)); + } +} From 7edf1247c3672d7b119598e04c24b03338e5ff93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Mon, 16 Dec 2024 20:42:02 +0100 Subject: [PATCH 88/95] implementation of algorithms: 1. compare binary trees, 2. inverse binary tree, 3. reverse linked list --- DIRECTORY.md | 10 +++- DataStructures/BinaryTree.php | 40 ++++++++++++++++ DataStructures/BinaryTreeNode.php | 16 +++++++ .../CompareBinaryTree.php | 9 ++-- DataStructures/CompareBinaryTree/Node.php | 16 ------- DataStructures/InvertBinaryTree.php | 26 +++++++++++ DataStructures/LinkedListItem.php | 40 ++++++++++++++++ DataStructures/ReverseLinkedList.php | 26 +++++++++++ .../DataStructures/CompareBinaryTreeTest.php | 46 +++++++++---------- tests/DataStructures/InvertBinaryTreeTest.php | 36 +++++++++++++++ .../DataStructures/ReverseLinkedListTest.php | 36 +++++++++++++++ 11 files changed, 255 insertions(+), 46 deletions(-) create mode 100644 DataStructures/BinaryTree.php create mode 100644 DataStructures/BinaryTreeNode.php rename DataStructures/{CompareBinaryTree => }/CompareBinaryTree.php (78%) delete mode 100644 DataStructures/CompareBinaryTree/Node.php create mode 100644 DataStructures/InvertBinaryTree.php create mode 100644 DataStructures/LinkedListItem.php create mode 100644 DataStructures/ReverseLinkedList.php create mode 100644 tests/DataStructures/InvertBinaryTreeTest.php create mode 100644 tests/DataStructures/ReverseLinkedListTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 3e2e35b2..b76ce3fa 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -28,14 +28,20 @@ * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) * CompareBinaryTree - * [CompareBinaryTree](./DataStructures/CompareBinaryTree/CompareBinaryTree.php) - * [Node](./DataStructures/CompareBinaryTree/Node.php) + * [CompareBinaryTree](./DataStructures/CompareBinaryTree.php) + * [BinaryTreeNode](./DataStructures/BinaryTreeNode.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) + * InvertBinaryTree + * [InvertBinaryTree](./DataStructures/InvertBinaryTree.php) + * [BinaryTree](./DataStructures/BinaryTree.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) + * ReverseLinkedList + * [ReverseLinkedList.php](DataStructures/ReverseLinkedList.php) + * [LinkedListItem.php](DataStructures/LinkedListItem.php) * Segmenttree * [Segmenttree](./DataStructures/SegmentTree/SegmentTree.php) * [Segmenttreenode](./DataStructures/SegmentTree/SegmentTreeNode.php) diff --git a/DataStructures/BinaryTree.php b/DataStructures/BinaryTree.php new file mode 100644 index 00000000..099e889b --- /dev/null +++ b/DataStructures/BinaryTree.php @@ -0,0 +1,40 @@ +left = $left; + return $this; + } + + public function getLeft(): ?BinaryTree + { + return $this->left; + } + + public function setRight(?BinaryTree $right) + { + $this->right = $right; + return $this; + } + + public function getRight(): ?BinaryTree + { + return $this->right; + } + + public function setValue($value) + { + $this->value = $value; + return $this; + } + + public function getValue() + { + return $this->value; + } +} diff --git a/DataStructures/BinaryTreeNode.php b/DataStructures/BinaryTreeNode.php new file mode 100644 index 00000000..88ce68ec --- /dev/null +++ b/DataStructures/BinaryTreeNode.php @@ -0,0 +1,16 @@ +value = $value; + $this->left = $left; + $this->right = $right; + } + + public $value; + public ?BinaryTreeNode $left; + public ?BinaryTreeNode $right; +} diff --git a/DataStructures/CompareBinaryTree/CompareBinaryTree.php b/DataStructures/CompareBinaryTree.php similarity index 78% rename from DataStructures/CompareBinaryTree/CompareBinaryTree.php rename to DataStructures/CompareBinaryTree.php index 787f4f2b..53fed2a9 100644 --- a/DataStructures/CompareBinaryTree/CompareBinaryTree.php +++ b/DataStructures/CompareBinaryTree.php @@ -1,5 +1,6 @@ value = $value; - $this->left = $left; - $this->right = $right; - } - - public $value; - public ?Node $left; - public ?Node $right; -} diff --git a/DataStructures/InvertBinaryTree.php b/DataStructures/InvertBinaryTree.php new file mode 100644 index 00000000..e4002091 --- /dev/null +++ b/DataStructures/InvertBinaryTree.php @@ -0,0 +1,26 @@ +getLeft(); + $b->setLeft($b->getRight()); + $b->setRight($tmp); + $this->invert($b->getLeft()); + $this->invert($b->getRight()); + } +} diff --git a/DataStructures/LinkedListItem.php b/DataStructures/LinkedListItem.php new file mode 100644 index 00000000..02f91bd9 --- /dev/null +++ b/DataStructures/LinkedListItem.php @@ -0,0 +1,40 @@ +next = $next; + return $this; + } + + public function getNext(): ?LinkedListItem + { + return $this->next; + } + + public function setPrev(?LinkedListItem $prev) + { + $this->prev = $prev; + return $this; + } + + public function getPrev(): ?LinkedListItem + { + return $this->prev; + } + + public function setValue($value) + { + $this->value = $value; + return $this; + } + + public function getValue() + { + return $this->value; + } +} diff --git a/DataStructures/ReverseLinkedList.php b/DataStructures/ReverseLinkedList.php new file mode 100644 index 00000000..04577140 --- /dev/null +++ b/DataStructures/ReverseLinkedList.php @@ -0,0 +1,26 @@ +getNext(); + $item->setNext(null); + while (true) { + $item->setPrev($next); + if (! $next) { + return $item; + } + $nextNext = $next->getNext(); + $next->setNext($item); + $item = $next; + $next = $nextNext; + } + } +} diff --git a/tests/DataStructures/CompareBinaryTreeTest.php b/tests/DataStructures/CompareBinaryTreeTest.php index 5e680e70..a241bb89 100644 --- a/tests/DataStructures/CompareBinaryTreeTest.php +++ b/tests/DataStructures/CompareBinaryTreeTest.php @@ -3,35 +3,33 @@ namespace DataStructures; require_once __DIR__ . '/../../vendor/autoload.php'; -require_once __DIR__ . '/../../DataStructures/CompareBinaryTree/Node.php'; -require_once __DIR__ . '/../../DataStructures/CompareBinaryTree/CompareBinaryTree.php'; +require_once __DIR__ . '/../../DataStructures/BinaryTreeNode.php'; +require_once __DIR__ . '/../../DataStructures/CompareBinaryTree.php'; -use DataStructures\CompareBinaryTree\CompareBinaryTree; -use DataStructures\CompareBinaryTree\Node; use PHPUnit\Framework\TestCase; class CompareBinaryTreeTest extends TestCase { public function testBinaryTreesAreEqualWhenAreEqualInReality() { - $tree1 = new Node( + $tree1 = new BinaryTreeNode( 'A', - new Node( + new BinaryTreeNode( 'B', - new Node( + new BinaryTreeNode( 'D' ), - new Node( + new BinaryTreeNode( 'E', null, - new Node( + new BinaryTreeNode( 'F' ) ) ), - new Node( + new BinaryTreeNode( 'C', - new Node('G') + new BinaryTreeNode('G') ) ); @@ -44,43 +42,43 @@ public function testBinaryTreesAreEqualWhenAreEqualInReality() public function testBinaryTreesAreNotEqualWhenAreNotEqualInReality() { - $tree1 = new Node( + $tree1 = new BinaryTreeNode( 'A', - new Node( + new BinaryTreeNode( 'B', - new Node( + new BinaryTreeNode( 'F' ), - new Node( + new BinaryTreeNode( 'E', null, - new Node( + new BinaryTreeNode( 'D' ) ) ), - new Node( + new BinaryTreeNode( 'C', - new Node('G') + new BinaryTreeNode('G') ) ); - $tree2 = new Node( + $tree2 = new BinaryTreeNode( 'A', - new Node( + new BinaryTreeNode( 'B', - new Node( + new BinaryTreeNode( 'F' ), - new Node( + new BinaryTreeNode( 'E', null, - new Node( + new BinaryTreeNode( 'D' ) ) ), - new Node( + new BinaryTreeNode( 'C' ) ); diff --git a/tests/DataStructures/InvertBinaryTreeTest.php b/tests/DataStructures/InvertBinaryTreeTest.php new file mode 100644 index 00000000..18b90e05 --- /dev/null +++ b/tests/DataStructures/InvertBinaryTreeTest.php @@ -0,0 +1,36 @@ +setValue(1); + $bl = (new BinaryTree())->setValue(3); + $b->setLeft($bl); + $br = (new BinaryTree())->setValue(2); + $b->setRight($br); + $br->setLeft((new BinaryTree())->setValue(4)); + $br->setRight((new BinaryTree())->setValue(5)); + + $expected = (new BinaryTree())->setValue(1); + $expectedBr = (new BinaryTree())->setValue(3); + $expected->setRight($expectedBr); + $expectedBl = (new BinaryTree())->setValue(2); + $expected->setLeft($expectedBl); + $expectedBl->setRight((new BinaryTree())->setValue(4)); + $expectedBl->setLeft((new BinaryTree())->setValue(5)); + + (new InvertBinaryTree())->invert($b); + + $this->assertEquals($expected, $b); + } +} diff --git a/tests/DataStructures/ReverseLinkedListTest.php b/tests/DataStructures/ReverseLinkedListTest.php new file mode 100644 index 00000000..c22ffdcf --- /dev/null +++ b/tests/DataStructures/ReverseLinkedListTest.php @@ -0,0 +1,36 @@ +setValue(0); + + $prevItem = $firstItem; + + foreach ($list as $value) { + $item = new LinkedListItem(); + $item->setValue($value); + $item->setPrev($prevItem); + $prevItem->setNext($item); + $prevItem = $item; + } + + $newFirstItem = (new ReverseLinkedList())->reverse($firstItem); + do { + $this->assertEquals($newFirstItem->getValue(), array_pop($list)); + } while ($newFirstItem = $newFirstItem->getNext()); + } +} From d588cd1455bc6168ca32ab0646fe468e0869f097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Tue, 17 Dec 2024 06:54:18 +0100 Subject: [PATCH 89/95] code styling and refactoring for algorithms: Bellman-Ford, Dijkstras, ReverseLinkedList, CompareBinaryTree and InvertBinaryTree --- DIRECTORY.md | 13 +++++++------ .../{ => CompareBinaryTree}/BinaryTreeNode.php | 3 ++- .../CompareBinaryTree.php | 2 +- .../{ => InvertBinaryTree}/BinaryTree.php | 5 ++++- .../InvertBinaryTree.php | 4 +--- .../{ => ReverseLinkedList}/LinkedListItem.php | 5 ++++- .../ReverseLinkedList.php | 2 ++ Graphs/BellmanFord.php | 18 ++++++------------ Graphs/Dijkstras.php | 9 +-------- Graphs/GraphEdge.php | 8 ++++++++ tests/DataStructures/CompareBinaryTreeTest.php | 6 ++++-- tests/DataStructures/InvertBinaryTreeTest.php | 7 ++++--- tests/DataStructures/ReverseLinkedListTest.php | 9 +++++---- tests/Graphs/BellmanFordTest.php | 3 ++- tests/Graphs/DijkstrasTest.php | 5 +++-- 15 files changed, 54 insertions(+), 45 deletions(-) rename DataStructures/{ => CompareBinaryTree}/BinaryTreeNode.php (87%) rename DataStructures/{ => CompareBinaryTree}/CompareBinaryTree.php (94%) rename DataStructures/{ => InvertBinaryTree}/BinaryTree.php (91%) rename DataStructures/{ => InvertBinaryTree}/InvertBinaryTree.php (92%) rename DataStructures/{ => ReverseLinkedList}/LinkedListItem.php (91%) rename DataStructures/{ => ReverseLinkedList}/ReverseLinkedList.php (92%) create mode 100644 Graphs/GraphEdge.php diff --git a/DIRECTORY.md b/DIRECTORY.md index b76ce3fa..63e21210 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -28,20 +28,20 @@ * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) * CompareBinaryTree - * [CompareBinaryTree](./DataStructures/CompareBinaryTree.php) - * [BinaryTreeNode](./DataStructures/BinaryTreeNode.php) + * [CompareBinaryTree](./DataStructures/CompareBinaryTree/CompareBinaryTree.php) + * [BinaryTreeNode](./DataStructures/CompareBinaryTree/BinaryTreeNode.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) * InvertBinaryTree - * [InvertBinaryTree](./DataStructures/InvertBinaryTree.php) - * [BinaryTree](./DataStructures/BinaryTree.php) + * [InvertBinaryTree](./DataStructures/InvertBinaryTree/InvertBinaryTree.php) + * [BinaryTree](./DataStructures/InvertBinaryTree/BinaryTree.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) * ReverseLinkedList - * [ReverseLinkedList.php](DataStructures/ReverseLinkedList.php) - * [LinkedListItem.php](DataStructures/LinkedListItem.php) + * [ReverseLinkedList.php](DataStructures/ReverseLinkedList/ReverseLinkedList.php) + * [LinkedListItem.php](DataStructures/ReverseLinkedList/LinkedListItem.php) * Segmenttree * [Segmenttree](./DataStructures/SegmentTree/SegmentTree.php) * [Segmenttreenode](./DataStructures/SegmentTree/SegmentTreeNode.php) @@ -60,6 +60,7 @@ * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) * [Depthfirstsearch](./Graphs/DepthFirstSearch.php) * [Dijkstra's](./Graphs/Dijkstras.php) + * [Edge.php](Graphs/Edge.php) ## Maths * [Absolutemax](./Maths/AbsoluteMax.php) diff --git a/DataStructures/BinaryTreeNode.php b/DataStructures/CompareBinaryTree/BinaryTreeNode.php similarity index 87% rename from DataStructures/BinaryTreeNode.php rename to DataStructures/CompareBinaryTree/BinaryTreeNode.php index 88ce68ec..d0146aea 100644 --- a/DataStructures/BinaryTreeNode.php +++ b/DataStructures/CompareBinaryTree/BinaryTreeNode.php @@ -1,5 +1,6 @@ $minWeight) { if ($verbose) { - echo "checking vertice $vertice\n"; + echo "checking vertex $vertice\n"; } if ($start === $vertice) { $vertices[$vertice] = 0; @@ -40,7 +33,8 @@ function bellmanFord(array $verticesNames, array $edges, string $start, bool $ve foreach ($edges[$vertice] as $edge) { if ($vertices[$edge->end] > $vertices[$vertice] + $edge->weight) { if ($verbose) { - echo "replace $vertice " . $vertices[$edge->end] . " with " . $vertices[$vertice] + $edge->weight . "\n "; + echo "replace $vertice " . $vertices[$edge->end] . " with " + . ($vertices[$vertice] + $edge->weight) . "\n "; } $vertices[$edge->end] = $vertices[$vertice] + $edge->weight; $change = true; diff --git a/Graphs/Dijkstras.php b/Graphs/Dijkstras.php index 95bb3278..f6ba42f1 100644 --- a/Graphs/Dijkstras.php +++ b/Graphs/Dijkstras.php @@ -1,19 +1,12 @@ start = $edgeRaw[0]; $edge->end = $edgeRaw[2]; $edge->weight = $edgeRaw[1]; diff --git a/tests/Graphs/DijkstrasTest.php b/tests/Graphs/DijkstrasTest.php index 6618f2de..2ddf9536 100644 --- a/tests/Graphs/DijkstrasTest.php +++ b/tests/Graphs/DijkstrasTest.php @@ -2,9 +2,10 @@ namespace Graphs; require_once __DIR__ . '/../../vendor/autoload.php'; +require_once __DIR__ . '/../../Graphs/GraphEdge.php'; require_once __DIR__ . '/../../Graphs/Dijkstras.php'; -use Edge; +use GraphEdge; use PHPUnit\Framework\TestCase; class DijkstrasTest extends TestCase @@ -26,7 +27,7 @@ public function testDijkstras() #prepare array of edges listed by edge start to simplify Dijkstra's updating weights of other edges $edges = []; foreach ($edgesRaw as $edgeRaw) { - $edge = new Edge(); + $edge = new GraphEdge(); $edge->start = $edgeRaw[0]; $edge->end = $edgeRaw[2]; $edge->weight = $edgeRaw[1]; From beb386c21ed5eff82529002458bd911f99c4dc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Tue, 17 Dec 2024 07:09:39 +0100 Subject: [PATCH 90/95] code style fix --- tests/Graphs/DijkstrasTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Graphs/DijkstrasTest.php b/tests/Graphs/DijkstrasTest.php index 2ddf9536..f00ce0b7 100644 --- a/tests/Graphs/DijkstrasTest.php +++ b/tests/Graphs/DijkstrasTest.php @@ -1,6 +1,7 @@ Date: Fri, 3 Jan 2025 20:26:52 +0000 Subject: [PATCH 91/95] updating DIRECTORY.md --- DIRECTORY.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 63e21210..3554092d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -27,21 +27,21 @@ * [Bstnode](./DataStructures/BinarySearchTree/BSTNode.php) * [Bstree](./DataStructures/BinarySearchTree/BSTree.php) * [Duplicatekeyexception](./DataStructures/BinarySearchTree/DuplicateKeyException.php) - * CompareBinaryTree - * [CompareBinaryTree](./DataStructures/CompareBinaryTree/CompareBinaryTree.php) - * [BinaryTreeNode](./DataStructures/CompareBinaryTree/BinaryTreeNode.php) + * Comparebinarytree + * [Binarytreenode](./DataStructures/CompareBinaryTree/BinaryTreeNode.php) + * [Comparebinarytree](./DataStructures/CompareBinaryTree/CompareBinaryTree.php) * Disjointsets * [Disjointset](./DataStructures/DisjointSets/DisjointSet.php) * [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php) * [Doublylinkedlist](./DataStructures/DoublyLinkedList.php) - * InvertBinaryTree - * [InvertBinaryTree](./DataStructures/InvertBinaryTree/InvertBinaryTree.php) - * [BinaryTree](./DataStructures/InvertBinaryTree/BinaryTree.php) + * Invertbinarytree + * [Binarytree](./DataStructures/InvertBinaryTree/BinaryTree.php) + * [Invertbinarytree](./DataStructures/InvertBinaryTree/InvertBinaryTree.php) * [Node](./DataStructures/Node.php) * [Queue](./DataStructures/Queue.php) - * ReverseLinkedList - * [ReverseLinkedList.php](DataStructures/ReverseLinkedList/ReverseLinkedList.php) - * [LinkedListItem.php](DataStructures/ReverseLinkedList/LinkedListItem.php) + * Reverselinkedlist + * [Linkedlistitem](./DataStructures/ReverseLinkedList/LinkedListItem.php) + * [Reverselinkedlist](./DataStructures/ReverseLinkedList/ReverseLinkedList.php) * Segmenttree * [Segmenttree](./DataStructures/SegmentTree/SegmentTree.php) * [Segmenttreenode](./DataStructures/SegmentTree/SegmentTreeNode.php) @@ -59,8 +59,8 @@ * [Bellmanford](./Graphs/BellmanFord.php) * [Breadthfirstsearch](./Graphs/BreadthFirstSearch.php) * [Depthfirstsearch](./Graphs/DepthFirstSearch.php) - * [Dijkstra's](./Graphs/Dijkstras.php) - * [Edge.php](Graphs/Edge.php) + * [Dijkstras](./Graphs/Dijkstras.php) + * [Graphedge](./Graphs/GraphEdge.php) ## Maths * [Absolutemax](./Maths/AbsoluteMax.php) @@ -96,7 +96,6 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) - * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## Searches * [Binarysearch](./Searches/BinarySearch.php) @@ -151,9 +150,12 @@ * Datastructures * [Avltreetest](./tests/DataStructures/AVLTreeTest.php) * [Bstreetest](./tests/DataStructures/BSTreeTest.php) + * [Comparebinarytreetest](./tests/DataStructures/CompareBinaryTreeTest.php) * [Disjointsettest](./tests/DataStructures/DisjointSetTest.php) * [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php) + * [Invertbinarytreetest](./tests/DataStructures/InvertBinaryTreeTest.php) * [Queuetest](./tests/DataStructures/QueueTest.php) + * [Reverselinkedlisttest](./tests/DataStructures/ReverseLinkedListTest.php) * [Segmenttreetest](./tests/DataStructures/SegmentTreeTest.php) * [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php) * [Splaytreetest](./tests/DataStructures/SplayTreeTest.php) @@ -163,11 +165,11 @@ * [Bellmanfordtest](./tests/Graphs/BellmanFordTest.php) * [Breadthfirstsearchtest](./tests/Graphs/BreadthFirstSearchTest.php) * [Depthfirstsearchtest](./tests/Graphs/DepthFirstSearchTest.php) + * [Dijkstrastest](./tests/Graphs/DijkstrasTest.php) * Maths * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) - * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting From 6b52263d4a06d6727ac5adace4d3a80c7b45244b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBarnecki?= Date: Tue, 17 Dec 2024 14:22:03 +0100 Subject: [PATCH 92/95] Neural networks perceptron classifier --- DIRECTORY.md | 9 + .../NeuralNetworkPerceptronClassifier.php | 192 ++++++++++++++++++ NeuralNetworks/PerceptronClassifier/README.md | 100 +++++++++ .../PerceptronClassifier/chart/dataset.png | Bin 0 -> 20630 bytes .../chart/linear-separated.png | Bin 0 -> 17021 bytes .../PerceptronClassifier/chart/sigmoid.png | Bin 0 -> 18089 bytes .../NeuralNetworkPerceptronClassifierTest.php | 23 +++ 7 files changed, 324 insertions(+) create mode 100644 NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php create mode 100644 NeuralNetworks/PerceptronClassifier/README.md create mode 100644 NeuralNetworks/PerceptronClassifier/chart/dataset.png create mode 100644 NeuralNetworks/PerceptronClassifier/chart/linear-separated.png create mode 100644 NeuralNetworks/PerceptronClassifier/chart/sigmoid.png create mode 100644 tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php diff --git a/DIRECTORY.md b/DIRECTORY.md index 3554092d..bc7c6156 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -97,6 +97,15 @@ * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) +## NeuralNetworks + * PerceptronClassifier + * [NeuralNetworkPerceptronClassifier.php](NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php) + * [README.md](NeuralNetworks/PerceptronClassifier/README.md) + * chart + * [dataset.png](NeuralNetworks/PerceptronClassifier/chart/dataset.png) + * [linear-separated.png](NeuralNetworks/PerceptronClassifier/chart/linear-separated.png) + * [sigmoid.png](NeuralNetworks/PerceptronClassifier/chart/sigmoid.png) + ## Searches * [Binarysearch](./Searches/BinarySearch.php) * [Exponentialsearch](./Searches/ExponentialSearch.php) diff --git a/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php b/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php new file mode 100644 index 00000000..6b94e543 --- /dev/null +++ b/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php @@ -0,0 +1,192 @@ +initParams(count($X)); + + for ($i = 0; $i < $iterations; $i++) { + // Forward propagation + $A = $this->forwardPropagation($X, $W, $b); + + // Compute cost + $cost = $this->computeCost($A, $Y); + + // Backward propagation + [$dW, $db] = $this->backwardPropagation($A, $X, $Y); + + // Update parameters + [$W, $b] = $this->updateParams($W, $b, $dW, $db, $learningRate); + + if ($i % 100 == 0) { + echo "Iteration {$i} - Cost: {$cost}\n"; + } + } + + return [$W, $b]; + } + + /** + * @param array $X + * @param array $W + * @param float $b + * @return array + */ + public function predict(array $X, array $W, float $b): array + { + $A = $this->forwardPropagation($X, $W, $b); + return array_map(fn($a) => $a > 0.5 ? 1 : 0, $A); + } + + /** + * Stage 1. Prepare dataset + * @return array[] + */ + public function generateTrainingSet(): array + { + $m = 50; + + // Generate a 2 x m matrix with binary values (0 or 1) + $X = []; + for ($i = 0; $i < 2; $i++) { + for ($j = 0; $j < $m; $j++) { + $X[$i][$j] = rand(0, 1); + } + } + + // Compute Y: Logical AND condition (X[0] == 1 and X[1] == 0) + $Y = []; + for ($j = 0; $j < $m; $j++) { + $Y[$j] = ($X[0][$j] == 1 && $X[1][$j] == 0) ? 1 : 0; + } + + return [$X, $Y]; + } + + /** + * Stage 2. Initialize model parameters + * @param int $n Number of features + * @return array [$W, $b] Weight and bias arrays + */ + private function initParams(int $n): array + { + $W = []; + for ($i = 0; $i < $n; $i++) { + $W[$i] = mt_rand() / mt_getrandmax(); // Small random values + } + $b = 0.0; // Bias initialized to zero + return [$W, $b]; + } + + /** + * Sigmoid Activation Function + * @param float $z + * @return float + */ + private function sigmoid(float $z): float + { + return 1 / (1 + exp(-$z)); + } + + /** + * Stage 3. Forward Propagation + * @param array $X + * @param array $W + * @param float $b + * @return array + */ + private function forwardPropagation(array $X, array $W, float $b): array + { + $Z = []; + for ($j = 0; $j < count($X[0]); $j++) { + $sum = $b; + for ($i = 0; $i < count($W); $i++) { + $sum += $W[$i] * $X[$i][$j]; + } + $Z[$j] = $this->sigmoid($sum); + } + return $Z; + } + + /** + * Stage 4. Compute Cost Function (Binary Cross-Entropy Loss) + * @param array $A + * @param array $Y + * @return float + */ + private function computeCost(array $A, array $Y): float + { + $m = count($Y); + $cost = 0.0; + for ($i = 0; $i < $m; $i++) { + $cost += -($Y[$i] * log($A[$i]) + (1 - $Y[$i]) * log(1 - $A[$i])); + } + return $cost / $m; + } + + /** + * Stage 5. Backward Propagation + * @param array $A + * @param array $X + * @param array $Y + * @return array + */ + private function backwardPropagation(array $A, array $X, array $Y): array + { + $m = count($Y); + $dW = array_fill(0, count($X), 0.0); + $db = 0.0; + + for ($j = 0; $j < $m; $j++) { + $dZ = $A[$j] - $Y[$j]; + for ($i = 0; $i < count($X); $i++) { + $dW[$i] += $dZ * $X[$i][$j]; + } + $db += $dZ; + } + + // Average gradients + for ($i = 0; $i < count($dW); $i++) { + $dW[$i] /= $m; + } + $db /= $m; + + return [$dW, $db]; + } + + /** + * STage 6. Update Parameters + * @param array $W + * @param float $b + * @param array $dW + * @param float $db + * @param float $learningRate + * @return array + */ + private function updateParams(array $W, float $b, array $dW, float $db, float $learningRate): array + { + for ($i = 0; $i < count($W); $i++) { + $W[$i] -= $learningRate * $dW[$i]; + } + $b -= $learningRate * $db; + + return [$W, $b]; + } +} diff --git a/NeuralNetworks/PerceptronClassifier/README.md b/NeuralNetworks/PerceptronClassifier/README.md new file mode 100644 index 00000000..870c9727 --- /dev/null +++ b/NeuralNetworks/PerceptronClassifier/README.md @@ -0,0 +1,100 @@ +## Maths behind the single Perceptron Neural Network with Activation Function + +This work is based on examples from course https://www.coursera.org/learn/machine-learning-calculus prepared by author Luis Serrano. + +Linear separation refers to data points in binary classification problems that can be separated by a linear decision boundary. +If the data points can be separated by a line, linear function, or flat hyperplane, they are said to be linearly separable. + +If separate points in an n-dimensional space exist, then it is said to be linearly separable + +$$w_1x_1 + w_2x_2 + w_nx_n + b = 0$$ + +For two-dimensional input data, if there is a line, whose equation is $$w_1x_1 + w_2x_2 + b = 0$$ + +that separates all samples of one class from the other class, then the corresponding observation can be derived from the equation of the separating line. +Such classification problems are called "linearly separable", i.e. separating by linear combination. + + + +The input layer contains two nodes $x_1$ and $x_2$. Weight vector $W = \begin{bmatrix} w_1 & w_2\end{bmatrix}$ and bias ($b$) are the parameters to be updated during the model training. + +$$z^{(i)} = w_1x_1^{(i)} + w_2x_2^{(i)} + b = Wx^{(i)} + b.\tag{1}$$ + +To be able to perform classification we need nonlinear approach. This can achieved with sigmoid activation function which roughly replace values with nearly 0 or nearly 1 for most cases and some values between for small range near 0. + +$$\hat{y} = \begin{cases} 1 & \mbox{if } a > 0.5 \\ 0 & \mbox{otherwise } \end{cases}\tag{10}$$ + +Sigmoid activation function is defined as + +$$a = \sigma\left(z\right) = \frac{1}{1+e^{-z}}.\tag{2}$$ + + + +Threshold value of $0.5$ can be used for predictions: $1$ (red) if $a > 0.5$ and $0$ (blue) otherwise. + +The single perceptron neural network with sigmoid activation function can be expressed as: + +\begin{align} +z^{(i)} &= W x^{(i)} + b,\\ +a^{(i)} &= \sigma\left(z^{(i)}\right).\\\tag{3} +\end{align} + + +With $m$ training examples organised in the columns of ($2 \times m$) matrix $X$, you can apply the activation function element-wise. So the model can be written as: + + +\begin {align} +Z &= W X + b,\\ +A &= \sigma\left(Z\right),\\\tag{4} +\end{align} + +When dealing with classification problems, the most commonly used cost function is the **log loss**, which is described by the following equation + +$$\mathcal{L}\left(W, b\right) = \frac{1}{m}\sum_{i=1}^{m} L\left(W, b\right) = \frac{1}{m}\sum_{i=1}^{m} \large\left(\small -y^{(i)}\log\left(a^{(i)}\right) - (1-y^{(i)})\log\left(1- a^{(i)}\right) \large \right) \small,\tag{5}$$ + +where $y^{(i)} \in \{0,1\}$ are the original labels and $a^{(i)}$ are the continuous output values of the forward propagation step (elements of array $A$). + + +We want to minimize the cost function during the training. To implement gradient descent, calculate partial derivatives using chain rule + + +\begin{align} +\frac{\partial \mathcal{L} }{ \partial w_1 } &= +\frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right)x_1^{(i)},\\ +\frac{\partial \mathcal{L} }{ \partial w_2 } &= +\frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right)x_2^{(i)},\tag{7}\\ +\frac{\partial \mathcal{L} }{ \partial b } &= +\frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right). +\end{align} + +Equations above can be rewritten in a matrix form + + +\begin{align} +\frac{\partial \mathcal{L} }{ \partial W } &= +\begin{bmatrix} \frac{\partial \mathcal{L} }{ \partial w_1 } & +\frac{\partial \mathcal{L} }{ \partial w_2 }\end{bmatrix} = \frac{1}{m}\left(A - Y\right)X^T,\\ +\frac{\partial \mathcal{L} }{ \partial b } &= \frac{1}{m}\left(A - Y\right)\mathbf{1}. +\tag{8} +\end{align} + +where $\left(A - Y\right)$ is an array of a shape ($1 \times m$), $X^T$ is an array of a shape ($m \times 2$) and $\mathbf{1}$ is just a ($m \times 1$) vector of ones. + +Then you can update the parameters: + +\begin{align} +W &= W - \alpha \frac{\partial \mathcal{L} }{ \partial W },\\ +b &= b - \alpha \frac{\partial \mathcal{L} }{ \partial b }, +\tag{9}\end{align} + +where $\alpha$ is the learning rate. Repeat the process in a loop until the cost function stops decreasing. + +in last step apply activation +$$\hat{y} = \begin{cases} 1 & \mbox{if } a > 0.5 \\ 0 & \mbox{otherwise } \end{cases}\tag{10}$$ + + +### Dataset + +As a dataset we will generate $m=50$ data points $(x_1, x_2)$, where $x_1, x_2 \in \{0,1\}$ and save them in the `NumPy` array `X` of a shape $(2 \times m)$. The labels ($0$: blue, $1$: red) will be calculated so that $y = 1$ if $x_1 = 1$ and $x_2 = 0$, in the rest of the cases $y=0$. The labels will be saved in the array `Y` of a shape $(1 \times m)$. + + diff --git a/NeuralNetworks/PerceptronClassifier/chart/dataset.png b/NeuralNetworks/PerceptronClassifier/chart/dataset.png new file mode 100644 index 0000000000000000000000000000000000000000..95a5042ae01a6328044a02567fe41f8144c947f4 GIT binary patch literal 20630 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 literal 0 HcmV?d00001 diff --git a/NeuralNetworks/PerceptronClassifier/chart/linear-separated.png b/NeuralNetworks/PerceptronClassifier/chart/linear-separated.png new file mode 100644 index 0000000000000000000000000000000000000000..061be4bc6949e893f4a9cedbab7c140181300a40 GIT binary patch literal 17021 zcma)kc|4Wh*Y+)yQc@|BP$8lCO2~ASiVm5l%$1Ni^N=YT5h4khr_3_XLqdjQ9x@-9 zj#-Z3nBR5#{)XrIz0dP`-#>ihoO|zk4{NV=t!rJocM5V+RFr2a5d@*a-n*lOAp0-~ zLY8&tAbgVi&@vQ$3EAF#V5@9tXzTFE+5nM#Wc$p_($>uQ@p*d#Ya3%r3qI}}+<#p; z|J2s@nT;?HkNLkI;I_0j;^F_6mkOO6es)j820^GFp+96_#gmK?H68@sd|S`T8i2fD{q0$GfRl6MM6Pt+R_;D;g%E!R&c`$DtHc9lWQwWz zv4yk+l=&WNsj69&(pF@54$!P37knPoOP=o^+uqcFeZ2(xIh3!FXr)u+!Dn};E4V1q7zNBw@|J2ZF=9vUfPfuUBpM9XJrY4X^{K?`^_*2K+^ZnJ4JhyG< zUNZq5mp?TrU$f>}IT2(lf>-j@D<0#~+AFGRZmF;cwhbH$#*6s6)921vtT@WKhCN=-|WKShnb3#CN4GGepH#&U;z@W+pxSp$YSSnG4^*n6af z3;Dl)|E86CarOu`wYp=WLxkf2#6!?z2*>y2XNR|c#n6&PWbaN_IKa9{o%t0s>!)Ku!Yap<|!#D+kNTzu3HX?CL|1Pdpqsy1<1Y1>6? z`vE%b)+g(~Ykc_4`>tc#UDpPa^y~blR{Jg0vDkObm`$^E?II<3EmI^df(#q_Dk`?= z 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 literal 0 HcmV?d00001 diff --git a/NeuralNetworks/PerceptronClassifier/chart/sigmoid.png b/NeuralNetworks/PerceptronClassifier/chart/sigmoid.png new file mode 100644 index 0000000000000000000000000000000000000000..985603111c7e6f9b98c242c4896af0a371327d99 GIT binary patch literal 18089 zcma)k2RzmPzyG1^dF&NA%3e`qhhvLKWMqbtmAxtJ5TUG6Dw0jvvPVW%ij=)6gk)s? zUmw5Uz4v$T|K9t!--mDIe8zjd-mmd|KHn$mijEouDGMnIg`&_<$Lga{_>w3T-dkcq z_~e==_A2}(<)L!j#f2{liwd5;<>BG# zE-fPB^zSzaySUklTzFTW4=*8cRX27=p{T5o|M7AZvTvhMstg)fMFXE_D`VaP2DbHt zYxXAGfv4p-Im1}|G)~^fkljL|Kd27)fBe{@zgX*q)!HNR}Z*4)zz)UwB6pwPW0K!Kk;3V<7sHo;HJv1E!HOHyoAMK zXEC$-jL1K3>XJ($zw0wO<8Zi+EYVSFG&(`cs)`eX!K5_1D#JzL@Tmy6Xcn#i6W=85 zyJK6DfYIc_0?`v~Zci!X9R!>wHMO+RXf$4R?GGDqXGZ5Dv+DafIlQ@Dr{OiMH|_1Y z#KcNE<>AWy{yxg!Xl7?`Ps@w~9x+zVAH?wUfBfxT%LkVobXETD?TI73kYc_g%r`~w z;s4q_(iY@>)pQZ8(54&Te;$`Qi?i3taQNMsHW(Pb*c0+J38(jSVlv*pux4g#vT}0$ z?`+fp_Sf5mEO_B9QWP33mwkPucDGg@oYhLcKByorPP;wk%@IL)D!Q!feC^iooz!zS zgkOs-CYqCIV9HA4J*K#q;=7c{SBLVc?9_hyq1U|Ox)W@q=E zO<>4-gu~@*5_4KINCt$5P?xXb*>RqB*o}HBF;E|DGx1b@=fDZ~baU*R&y0UE%i7+9 z_bU`b{^H?g5w5W*o~)zz&m0Wb4)(UW&YgRwGm@j1`POYtlZl)##~{zIYtcg*e!A`y z8jehR(x4MO{-}z7^i)m!5tH*N?aCYRjE|Q0dvD$EwN$;_ z$tPCluPI2Tc8kJ~$uNPoQsYeq(WeC3^FJj@a{`4d&yx`AjTKA0PVFA->zV&qKR+C= zPb=XZutKV|Kd`g8>3?SLP>0_6_wV1-EG#D3Lg&xZsb-=H2ndL&sERAQ?-P)%&8N9k z7)F8B5D`9RCa{&JOIo^H*Bmk({j+#T;16&856>sPIJf5{bADXVZ6#b|8*NBez9t>|=usI+r(BrWVpJGbX@cjHMB|Xe=8(mf&QCC) zDK(&}aybn@onK0p&Mn7*T{E1V{es0c((1WtCG!Il&J;z?$BM7w6^-}IghTZ?Lu==& z%?vls&|6h@d7I>-LT;T8#Dl2S;!?*4LM?ww1S zS{~GY)+eIeR_?ZVdEQjGII-(f+sFy0x@)(VR_;lZTLk7cRBRe5&dlK{=$;K2(A>$R zU)#zF`Ze<^M8BzDXEf<*cv@K4$^Eq!D$n5x=dtLWogKpBN={71*H_|CUH9EcDD4=g z2Iz%)&4pC#6s2P3Bd8jDcxQ>f_+V=1(QnpEWT}RHIJIY@E^$zG%>D8nxaa)h_0*e! z0`i5v7nQZW-QBG&4F?e+A*eE!33m5|UKJf3%KDvYGE`&P?JsX{cG7gG%dz&!?+I?L zPQ-yDnND-5PjH~c;YRh7n!>n@KkQs6B_VOsWzj0`Pi0hVcB!0^t%%#@Efkwvp!NNhnjCt-9G*P#RxZ(P^o!g{s?E68 zw?$2BSAJ8TaSK4aob$3@ID413c_ZVV@T-_wlgh9GLd&K zFcP;%{GCeD8xzGygbjU)*Rn+JIbm1YNY!3ssrS*?jK2Vd!V>sQ&H2|@nN1M$`qW5N zQzKrOkvpvMVd>D?uRi(=nBKH7NcVaDsv zArj;K0c$?NGv35%x!wL&=zt$Q(0E`O75nm# z&d(AY8zC}Hv}mQt)y^N^;*(E67??YfLxIU;2|ws~H6f$6v*U#QZSF=7mfl6onitZa9)0>XQ zu`wcTh3>%?{L&zg|JZo)U6pz_^C6W^4Pts1&4EU8_J)Ud#bYic^PC!S;d&u zPH{UR(KjLVqi|m1>uU`eWgl@e(q$hW@$4z*PPR+*7p_%?;X3f>6Mu-_ol`Uu803`b zrh0Mt{fTsb%1>%HDUc;3%9MP%-rYbsWo<=(8S$IiuqRZSDSY%^5R=`6Ord46vqGFr ziq@yErr{#lSve&PgV8yj1I2(6ffGD#WuJU<*BWO0*OOf^tmlwtLN@+p?O2XTp&D!N z%27|ZFFE2Ik!3t{4yGh&HBQk*%*LnEQg)!>Insp-NEf0C$J3(93YONc3aooSyrk_h zf~@PAMB^(DgB_(#pnL)yy~FO*9(N3BM5EP_!N>F9++vK~=yxe<4R5T*4Vr}G-Sw!$ zh)84#+>(z!@7uL0dD&AcH+hhqPSXMNd%T|A%(M@3P%cpd7`Ic%l|pUed$5|BN-oWf zlz2O{A}1@UcFc9_UbN>>g2ofg(hPvE0SVe^PW)(Mn3^I)vI z8oC?>L&-4w)7eX&_%mjaHn-`EcbobR$^>>++LjXt047dG?H@)$04^9 zBpEWlb<^cWS?=O`+1|$G&>wf^r3jUol13KZsn9@Ii&fX$E>1TxWQv4dE4j4V*W||y z*PhQ1Z!hO`ID7o@&7OOLkc6O5Eymsr#Jm=c>~K1MPY8b~iGY_a3T4hV&a6qG;2dQ_ z7CeH(nKVi^*u8qG9obP~?voQNgESz3yvfVZR&)xSD+<+j^orI%)|Z{t}F{99}K`b64^`-AvH^OQJ_MlN)*rzXok1aqpLX(Tap=eofRueG~jI2 zjeRYXf%Hj8gWWk<$TT7~mC47?4wIxTypgE!ktf*E*3ui{#>%MY`O{u=Sj)P zOmj^z8Ewqkc7kJifdliwzkU(x(#H)`YbbtpEp;A~?tUgkTikFc{rU4}QIEwdG0ftG z5HBgVCQ9w8xGz5q{sr`{!~@G$QC>oCf--;;W*m|S%R=RbaO`d6V5Cxh{1bod{}tqhj+9+Y`ZY?`+y7b^|@6PnDTCJtsmZ)ZGx|ZExzvbljXg zrH`d~EI3L>N7r~$jPv~YG9CY0w|F5jWihksi08asYDe+t(WA+!se5nVib85a$PxHQ zFLT1a={~`yTj!J24CqAGxG;Nr@|$8P%Uday*s}FURVd#Ei$HX2YmAhaH|v zdyIU#1|l{dpPb~EHSv0ZeUSP!xZ_9A(VY`ujH<*?c;>WrMw1^+F_ zfUWQL*UKH~KY#gh?#@@io3^&bI^sRgWf&iGUoQE>A?eH};rJ~C7MF{Q>xBRIuMc27 zqOOxjF{xh8jGb+RJ+Ch1OOIM(($zpqo$S=n!n>rePXlIoF8DBDc78qsmJilZ)x%?R zhq}7D+Ow_J_h)YQ6%&(asI?~NQY=)VIuo^SC9d7*mC<*?M^{$|A0?lt>x@Z9A1*S# zN&p9gf11|WOI|I1P${$j6~ii&J;!991yqj{pCl?=C(36 zRAZPAC8g%9I^xb9k@&31PeDB7GhNiHh_on)MB0+3%E)|O4LKG@lJMFoy}>AT7Q`d; zc+B~o^?Ff581ncgjT8p)t#&3a{Fs$cp)!Q0r9Y|tbi*xjAKz&yK^Bl=BBQ}Aa)#Rt zd;HPb(g(~<`fH6wg{`UPn>ncY%5=c*Dk9(@W!k0_3o)F)8v4SEY6Y<9-&42gxU)$b@R1$7c zKJe9y?npq|F@CkRwTgbnNxXFxDb+6^D8M2^81+uyx-6;@5=RaAbPS36{X)-r1yl_6 zklHE2lMM0y?+K8>ntpRD+0E%0yAe9I@+uLw&wC_VBOm|fM?p9ijS}F*+(EqU12xDFF!tg%&w zc`Z{~A5bA!a~I{=^TGEgaeSRwe59`q z&uTMNdN(zF@@A`QwBCX@PKZ7J+_6x~LA>COXss+AGHDe^C-e0D>nM;hNWvH}C`Pi6 zcjq1W65W_p+uZ)GD`680k61>}QM0%twGLAEuOQuMj-|EzokNjOij)SGDaD1&2?a~2 z5P$zQKUR!=I}E_tWhwV3S=63txngSD)oBOZq8FG?!bm=ar0iLhSr+J0N8z=_*o&^O zs8R9XLZ>1+kU*VaDwC>r685y0Q|%|dZ$`)xQ6AsBBI{3q5-l(ly(#tt`jH@ae0OPB zTG2DD=#Fd1ZzCW13ob!@3jL4pAWcNf1zAuxQ<=VN9OcZ^V+=v*Y)PsAym{C+BKtMY z3=IZVeYUPYCXkU71#hWlK_1bH6rq2m}s@d8teokb38+(`SXbeFT7Yz?&h5ZSF(Kh zw`-~6cd^#i*2XViXz$*=`wh^b#|NI$r_6&7Jh5tO%7uoRSF?4%F==tMTKxCM zhShl%(`q-2n<11VzuYejjd(zx+*HIk!`|8|hBk%N*2)?f7@WR%kqHkEugARorDCY4 zWg|+^qJH%ASLjy^hXOkpgJ90k5d^ZfnK*@qF)`%%4KH-ELv^xCYri^_$Z=*aW@vFQi#6N3O+{?^u*t(B3XNwG9Z7uA3O z+3~>b8)bKfC~wqweRy&BhsVIs5TZVV;B|PRjW1S3#kipfJL`sciWQnxOXb;&g2SGm zql4}KSIR^IyFap-&RL&Cg4RUP9)*4Hv!=s?y>l+(vP`k6qyDRPOmcqbd!Ne;&F+$t zl3I#AIYzGJ0J-w@^`#JgoRDx&&VOsNHH!9I)m<(I5%ZYUh9h>7`W~fS)=6-C|^Yj9wAE*$O2MkmFY8)RvXK78%&0TY0E;pel zf9kDFjlN=5)ZV`gTd;=N8E2L}I5l6hT3?#?B0HPM+-EA>bc>9X^uE%o9PdPK1%-z3 z{%0mrQ&YEwik_eSh8i4NB<6Je``b3ZnGRiDEVn>r$#AI>V2(RKkL;mH4!z9ttk}G6 zc+tQ8;i*!GQ|#>QxCalEH8nM#ZzU$uku!_EZnFJVyP;!N?M7g0YnxnAYTu_MoC4_X z=e$)d?5}*U0M>EX^uvy%l6C&uZSy^8OXUsQV-QDB_$=IPQ&M+YhBt;&v$-4>g0KWwX{5)9<+P=8Dv*AUMtN4#kKcg`rb5M)yHDX z!>Akjcm^RFbtHX1-`kU4Tp#`V^%?DSS!P%m`RM3q(Yw>wXR^LDOJ561{p2(>G->^uNi^PlDKw2UI=VTSo1!boV@tL88gUSZ)1?^rChGRw@qbxKPG z1wM21-sLH~RvcPgQ?nV2yC1ePl8oQJeY+~)uUqEKB$S)BN8w6L+W0FcYWq1mki;V= z{JV$Z`qx6!bB^EMN;^R++ng@vzqU2vGLj{V!T3+*v|@jKc*7+rNyOUqan@}gkhQwP z`#9XP2JyX9_8WMJ3nj~M=@~A+Q)qtM%S&SG*Vvt_idgKH$Gf-q-&K;X@ZD5jq~nEp zXMnVTQWC6R&CyYC+;`#B>+I|y{{~3gIy*akes}Vq85tupGB7m^n%J8UhV`OW7pYMk z<(KWJqdGdTAEed8hS>*5eP=Npa3c8JNrI%vsxc?Qc@BzDXG%Y;X!{xaB#<* z1l4n8>`f!-Op-kjI{Zn&N$juIH#TMW?%nfQYa&{?L55Dak!3EZn$;9$sc^x%WgluX zoCyh4DRn~Ea9}+OC0gcA{f~I1cjw)1$K{aml{`IM60o+OW|XfO_! z4I|S&JYY0UXJlYN()wn=@8jd+QK)YX!3q_Q-<$73yyT=yS-Lx|NrIXz>V=#E`eNL~ z;X8{a=a6UnlC4|(r5=z}$B_yONR&&A0=?Reo1#Wrblct5r-#%&T^L|AwZ9;{#*3DU zV|YOy(3LC%PR^rlOD|K^3OZI%@CesYwDqjGo?ySIw-$ox*f%<7{1YeM0YBom0`F9^ zJgtU0i@8n0`L<@>FYOiL)>giG21R#4ZWv>BpVm*q2StpcBd;&LzGq z`$!y{LRvR+8(GCOmY!9Aq3EEdz7l9P4w1*S;4~&(g@Wn?ZNmAF9;$4e{Ddg- zxyeARbJrWieM%1b7@guEkgB^v_8J)NfQCVz}7*BhPZ(NDzft%r(kcnQH`veZCgg0r-# zk_y;yeQtRIrI|$&e7Nxf&B5VPIgP_TwbAXF3{;jPyO>~F`lBqdp+Ny0@6U33d;6BY zzSW8cfJ+yRKGFnoA=HeJ{0F43v%TQM!Fi^N*^+{jh+JNFG^-C_zB5$xVIcSFdMmB& zw;EN*<@4&a^1N@d-m1NZDGCClmS0|dhnsIpjp+YB4A{U zLm?T<|+T7Yw zT^+Amxdzs7S!W?Bh0DZ4;8Ie1=A~EN3s_;l z`S%+2;mZRak030NP7c7h`eZlA_4W1Fz@J!8Hr}Jq$9|9yx@ERd4(b*kQ#BbNj3?Q+A(D4aE0bn=ES$Q&+hI)DlH=?;P z?~~-7=|fBHWQf`ECU%7ser`_g3>ESvcf7 zuo2N?t#4ev6k9ZKw^!)p8X)m5nqFY?N9TxmR7X9f#lJb|_mS@WOqjf`H#J!j&Z9Wt z8}BC{>&iDF)D*C{0T6K(zZSCAAMV~MFsr6%*#Av-_&cTHTa7se)5Vu-bt6;?$BQ1c z`I3-UIT6H)7^!M3i|y^DFWFQWF0S>VhC}*>!(D-ZfPl5%T_Q7e+vBCC@32_Z+l}s3 zq)fJxI8`kbw0|)pGjpk2O-*fPXMF~hr+Z^=etz8C6KcRtq1@Id`C0K@7hzBry64f+ zjKXHbb>=R&#hJ*jSGjV+a^az*q@-Zg%XO2m*b5FWc&n^~gie972t>h_a5+0GS@=dY z5xWm6At7NAdInAi){*dWXBbEDJ{lDdnJc5YPgBtc8uW^^sT}{g)klB|f~SulXTJY{ zQDhoYr{?da_MBmjnQ`*bJTC%hf`SIUg|(ku>@NzZK{H>rTb4I+ue*$tj1H+iEq=SZ zyVk>Hx3>zyaXA`lA{LPq$Bs0RYXmT2v=lES1Ca5Zv+HKcG2nL`udVK^r;=T3bYfJ+ zdXx^=ypHf9xvzxOQ#n=;1=nZVxkN;$z)>?P1O;RsT)fMTE>C1_2RhOrX3$&fNsnDv zSlIP+$*o(?Z;dM)nXRp@Cx85is;auUJYF}FHwXC!S(biO(82D7r9E_lJ3s%>%yk_I zn$Advl}eon6nxfB!#+G3xV<<1K!lo>HZ(q-dcW^zpUOk}`QVairSmDf-e*xm7Qv?= zSz?m*;BAXx4lO7s;7$?X<7;}Y@mSPtwqrg~7pvpAU44fdA4jpF49X2p&WyGT0;9F- zZLFT$J+iRUZ?vD>_y1K*jQ-57Cc7?d(8@met@_ehc6}voAS=>KoSZp2(j?{9z*4HqQ%Pbf^i`3$WP|NwBO}7dT?LtRIMBxE{+lb+uU4x zNF^aR>*(%2c_namt|NiVQ->lcIaw)7|3ZF9n*ASorSliNiaT#M!;XxSoCu(Wx~|;s z0O5)?G@|CLoOUpnc0GSh2ledJKD*L;fs zNAg>7?~)6GP4HNL8P#WrebclR5;A{dF(olEthrgK`-K7r;IE5=`HYVqKSrNAMS<$* z>8YDbmBh{tU*tcBzK{ZXq1VD07jBoE+&a&an3&i&F=2t!4kSv%qno{6vu4GuKeK-| zu3b-0#<&^v{N3vrp~Q7q+0rF)E%==U{RH{X4%qJ<(vZc@zICw2<6xpixlpC`X3!?E zBDhrKhb1Rm2Lp0BK98K6t%LVu<6nqQz6cb=d8%6w#44&)ZL~L(3OKBx_yF(9roXZ& z;q6W0?5fEC4h_MBl!rZw=KWaH!NJ)bztz3E3V&gv>t%eGtG#_Mj|#R231^hqTf(`A zdQLCb^%3w=iWLEXE}H6^6QCZdYhxu4E;llxP|R{793Rz~lWv8iG`+`de%$)Y&-z$gr#&rJfL>fXn)*7ZufEhm4&XR4Nr&8;Nx2F?r-&#DGd==9p1In(Ezp^4Pnc zJN~>p`}IE|L2gXRDFsEGy4x`%i1k7S_(;ZA<-xP1q&0)iyWWhw_&V51glRfs1l#HQ zkfo0=FA_YSodPsVqex_^nKo+A0izW|aQ5X9=%n#Tx5o&ZKbZCS+ArP6Dn-%GK>?3b13|PZ%%OWtC83EEr zgtrD-@0j7#NCc+uNa+}O#cm;7;iGRs(j2O#mr|%L0DC>oqBvn<&X6E$nv4k13X1@c ze<(hLKz9iTfWhhIj|3K>k_c%r=hGJldl9?r_y8C6^7G|DVj)g0_Cy=mW`oC{i9PM! zY54PV-HFCGh3+3jr^B@db3vuM;dmgRVfw~-33&m*@r#GuGIkROI)1&n!oP!tg0@|k z0poB1s>8H6b39bc3av1}xwCmN0Xc*F7Jl;40_4OS+pS|a?jr!A86Awa{HY6-{RtF% z)E%PmXUM?2VDd5y6HN7^ZrJA&{qh$S)Sj01pYL|mqw+a1MhMy}^b2@lkX(esMdEL7&fn0-4wN9>_z>v7BZithlt-`W2^>#3 zvdHB*V$ac??e2Ie+Fz4=d>}r7IUUFdlJ+8id6OpG`-eyZK->_dIfG+vK<^peGn@&s zV&$nDe{B($1)77%y~@Xkf2T#>U|p|OSbyLT9C+A{zb0&@YBI1+2Q(;0mac-vG46vq z)*9m=hun+tO0SN97=a%s3lb76GRd*Xiaf|Um9X>baOI``S|+qjrEnM5@r(4M$IhIG zJPKL$ugG}4D*1$nz%C|oBo-Owd5}G1cZNz5N?K)YZE_IqAh=FAyOxU2BG5PB?{8h?rM+KWd|8#TnD)bSrhq;L`_XzE*sQve`hfdV_+s%$VGwD?!fN@DObHrC&1=59csC>hnJOXT+BT@A>k zs&VuC2g%DFvF62U4i5Y@w6sfQJlx#Y6ZL_Hg)NQ}nPp{kfZ@h&_5bkrzAx``<%Hh?9?ZW+I|#5>OtJBOzKAR~!0`Pj$f zu9qi{;cp}hwZTVhc{FjBk#0gZA)UH8uXD5h?>sA#^jwF;NlA%?W;L z1R#9Es}4I*HlU?~h`{*6W1vh?*MD@XdVOu}638^6V*6Q^{ra^f5V*0z+dYyKleyU- z>9Gio1tz${;|ca=7S++JENU_VF9COJ<}9*Fd(X(Yoj?Ij*#;0U)WdP0e?tlYSnFos zeNhH4^gniXHZfWR;JuXo1{fp>lI{~yu{%oroSB;=h*jnK?nPZ)Gf`4(v+(`Sl@42< zDXQnkQ}gWg6gM9qNqBg8szR{5o4b2|zLCfo9v&`fX%2+MERDEWrk%WQLvT`vci-e5P?BraCUE5N@;-1L6RZmorEm zwe#g2Ha@GfBYi-EQcp?10s&JGzH-e;t7A=;GS+|!yo>S5$4$)u?~AOcH5#PDL`WPk zG<~;$PtJX>>C_2PQEtQ~<5&*K+1>Y(&j0BBnCvoI}l%TSuBmf{6;YOH9~P3X~OAW!a(3+u>)t2 zo`05%EP1PtNewVBgZ$?-B)&}{o;lO4$jDmp_ia?8K<0Z+plp%1IU{lvmNQ|EqZI9W zZ^SXe?}fOjew+qj5!Miy9l}5$d24#jLnHMcdULFt2!EEHiR+L;yq=s<`L&%14qZHy z-NXp}^ZS8ki$Hhn2aeJpk{Z9*MV8Vj2_49FA=S?)2DC-!3uG`;gYW(>(mQJPMQU=k z)WmIt>o>)bVBCcF2QZiice12w-~Bxk@-ymOP5568Gc;On16j=vtZBL#Rm~ynQ-?l> zRL>Els{^5$PojXpHQV=)WhAX8UzHyw;!l4F0VD5SM^eC$dUNWfT(_vOAe4-V(Ff9lE`%Z%(-|>AYHW2^Ux7RhNO(aErV}Pz ziVsw8h!~VASc-@-xZ=ZgIRC)f1h!K zRV25I3*)8^W2NM6C?n-q?PxS25(c5(36$zKdCLn`Peei-l@B*LA~$8|yEK2jdMvLU zTL9XR40TDV{Zg$oH^V{3u)67Kbi!lk)}wfk37^iSW_>sdV%%yV`2|-$s8oLF_J0q^ zydsDwm^c7a2KzeWDy{n!Bj*J zz1g7Ql%*Y-(vEKWk}9XLnLuT2HcYzX3s>3EhEvlSBfoh;QsD$;j00ZE0|8Je6E}b; z+tQ9xn89$s*CH$43hPMB{*xnW>Rl6CKpg1Epnd1iPQmya2KU_m+Fneth%WWby6Ewa z3E;VZT%vfP%==uMsnpK-eL85ojyS#wAJ8`PH zTx>CR-tU0kjoGjAi{+Je6nQksrGQA(j=xwp=5db5AGVVLf9+x`6wN$LcXG}iZNhO5tw%w-MW&UM;`Y6*(6;Nsb8l;) zm6^ot$N^IiNBH8U7f+r<=z0fNocddWGiNzZdcbiA z4^@LjZP<}|MUok2_Sov)o4)2Iw%EJj>_Ya7^B;l` zi(_3llqiS>y~6xThc8+bosbz%!Wm{T5%B4yB9o{kDp|zB`S#LyolILCM;zcl14~Cg z^NX!F=6lq2bt}s*r%6x%1^xjb+rr)Sf7O&X9K_~WiIL4F?*~0sxq6f*S4kMTGxl5S|OGBD&Mf3|EAMJKiG|#xTQe*7D#rQCHn%Q;v~76Tpuu@&1rI7={&2IOm*GT_%zvBOvY2s; zh!i&+{x=6shKtv@eezfiPw*)pTxF1?w8i9q5ze0w-aF7**#kA7Jdi^X3k?lij|YW~ zZnUo!yZBfTcG!x+PnSEhrLWNA&eO#|vg12Xrx%sdrEhr$3a0BlXP> z>Xo=dl{iE7;XsIRq$XdeCj7}636~?4K(bC~+hbI&kEe^AF~J8JuEVuWBds(|N|9@x zsZ+P%L{i?_zw~jERM)w~Zn;sHM(j!snBRfS8g^2}T)APsih)65;hY42Yaea)k$244 zkQS)zdbo_MNd6V^8t;E%Vr6ATv1P=cL`+WZ=z1&~FmZa30b8+SO2g~by?=+T^t0uF z@>Y%@zwP=2Kvutl%6~Zwg28Na{kWEhF8*<9YodOz4$5%X!r)X$!L>c_k*87gqr#(!$;tSrjEsyk#}}UFLzxAz8Jt{`tW5mZ zlW%rIo_ESkKzqUo;?A=Rg>IWGC(MFbs{?!c1}hUr7WPdXSi`A#g+O$OJvsaPO>y4c z8ZJq?g?8ok4-Rf@MKZV^{MVqa>LmFpDy`?fE`55XTxYV-{p8E6m=ZBkE5Zbaq@*w&u0<*!qT)h> zyBi1tfAx*FB2@6TwY8seukv)KNv zXl?EmD8#;52CGd3BjS>(yE9-Fc)(YJV;=OK_}bU9xipjs9{NiDAW&aKKp@>3de7|q zRIzwy=3P5`%SNBV#N;0LV-lP8hbnMLaBKN{X!)HX>x11*A0U#`%vQlK0+p^>aHEF7 ze`{IM*qHfO!%b33)iuHgDRd>-BOR**Tw z#VH&z71wPI3#E9r23GwqQtv)EsTH_A770-sBP2ut&#)jGZ5Vo`+uYah)$Tmz0qZJk z7hO>xBo<|fRoB+W`uhGAN|A#(ets|r+uksz>aX9{vT=6rOnaQ<@X+@y4iu=la(cJ2 zUcW}6*!`E^ai_Qs92l9KKLk0Hb?2kA6!k7$3sVsJ5&(gYqUD)}2~NFu7lTOILW!t` zi(M6aD*QGbUiIJlSa8{>gYU- z@0vrD*A@ONmCD6K4}jtUrSU%Z-sN;zW7(a_(88+&PSFn^GJrm)e(<2ju9TRJj24KJ zva)N}u8}~2FMqf*!;tgo5K)?;X=%UQ>BC4! zROpPj_sQ`9Mlfo9VfboLP(A9j*RRp#?J7ucI(!d&4jXBIEu*wXB zB*bs?XEBD46`z{=ZL#41mT+m4M*p7~4fLF)@~vC72V~PY4Am z-Y4tc74`JF?a^2V?7p_Gd^rVWX6I#IAP0d-@*Ezna3Z6kqJkb?c#kkI@VjX?DD=wu zZC{tb(bpK33#58_dY89YAsvJu5{fV{NBav3cj53t4AVJ$>E}gB{N>{uM>}fBo`i0- z|M~IdozJ>;?OHQg>{js@kL0So+NcFZwd)kV{O%%`cA7+CXVAkcDp1pbbmZx#%fzSE zn{a{v#1}v_nG_iMQ!81*&aYZ3s`&&eOx3;c$jBLlWZvCeLR2muOJ6B~G;ako(d3=U z5<=ry1gsN)!sk9BU>^v7V&NL1b3te{pfSfL#d_Z#h5S6Ce#k8Trni4T zP3p_xofx4PkwOQ|KewjrzA^vBOAAn1vA`={@HaLc?UgUA<5~n6 zAVoylu#5~M8e?a7zTF3V!h@qSY~FbWPNRx~dT}3K|eZt;H(gcx$Vqx8%t2K-ST*VR%D)V5Q|U1larc?=P1W!8Fw#Y>fa1SH8Sh zXj-XMXvifjT-0O>RO3tiT!d`|Wg-d%NaNkQDG}AhDH$)=@@;Q6fq>O`UtEv{-YY%*AyOyq)R(R_uol94A?&}5mt$#)XYzsl z(t;K6oCwUzCN=TiGe7W1*`-qJ@mM278?1@RQ=g3<$?)RhVs$MoTHxz5%E}CgIm7yI z(bf5F(!ge;10@w21a7pDWm$t2C3}Ko$ryIxWj0x#PoH6nokue>^Hn&Fa6e>|!l=eD zal#3Iff!Yrd>0#KLqVk2EATaCJ2nyCPY9+`Bqf;z`jDcd;y=i9`4KDrx47|VS zhRmx$2h-AJ)8R^&7Z9NnkX|t}Gh2gh8dJD0d7g6K3(KkBC;}6;2KDANq*oY8$)Vp>38l*|kWr8(X`VGKGUJ1waTa6>e2|8< zf`lNPS_u*iHAJTIS%Hg78T3OB;D~b+^gIe|7SH(d}l;E!m9J}-dA*?RfgyXLMgEeJTcA78Q` z1oG)C3vTPmu7`mSRnJ^KK@AqmphOHo zfD@<^6<61{S8@$90{6D8f6sPOmdlmHfwB+4&xAp7n4X@V0m2V!P)TG!_NmjrTB(cs zIl{5flg88B+#CiKx%Wh1<;9?V9|0qm=Y#o1k~`x58%KAZ-R1xxkpXD-k!W@bJT{x(R1(OW}+26Z&@4Tqo#W)>`df{CdH@sT@ zqL$F%9guiUt*%m4n>`e1*r$QG*91E?OE?Z^!W{RHl5#2msa5~$myZ0i-uyq#DwNCr e_AM;?3R?;4lM}|KQSh}fC=C@IY>|@X{r?BT{`W8d literal 0 HcmV?d00001 diff --git a/tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php b/tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php new file mode 100644 index 00000000..61eacc48 --- /dev/null +++ b/tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php @@ -0,0 +1,23 @@ +generateTrainingSet(); + // Train the model + [$W, $b] = $nnClassifier->trainModel($X, $Y, 1000, 0.1); + + // Make predictions + $predictions = $nnClassifier->predict([[0, 0, 1, 1], [0, 1, 1, 0]], $W, $b); + $this->assertEquals([0, 0, 0, 1], $predictions); + } +} From 334f87601e9459e324c4f133216e1b831135239b Mon Sep 17 00:00:00 2001 From: Michal Zarnecki Date: Thu, 15 Aug 2024 22:51:20 +0200 Subject: [PATCH 93/95] Sieve of Eratosthenes algorithm --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index bc7c6156..d60918a9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -96,6 +96,7 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) + * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## NeuralNetworks * PerceptronClassifier @@ -179,6 +180,7 @@ * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) + * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting From 5a335fa98dbad231425aecdf1f1648ce480d94a7 Mon Sep 17 00:00:00 2001 From: darwinz Date: Fri, 3 Jan 2025 20:26:52 +0000 Subject: [PATCH 94/95] updating DIRECTORY.md --- DIRECTORY.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index d60918a9..bc7c6156 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -96,7 +96,6 @@ * [Problem7](./Maths/ProjectEuler/Problem7.php) * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) - * [Eratosthenessieve](./Maths/EratosthenesSieve.php) ## NeuralNetworks * PerceptronClassifier @@ -180,7 +179,6 @@ * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) - * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting From 81b5386af390fd2b44fecf162ade3b1a940c8500 Mon Sep 17 00:00:00 2001 From: darwinz Date: Sat, 11 Jan 2025 01:25:05 +0000 Subject: [PATCH 95/95] updating DIRECTORY.md --- DIRECTORY.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index bc7c6156..18c67420 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -97,14 +97,9 @@ * [Problem8](./Maths/ProjectEuler/Problem8.php) * [Problem9](./Maths/ProjectEuler/Problem9.php) -## NeuralNetworks - * PerceptronClassifier - * [NeuralNetworkPerceptronClassifier.php](NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php) - * [README.md](NeuralNetworks/PerceptronClassifier/README.md) - * chart - * [dataset.png](NeuralNetworks/PerceptronClassifier/chart/dataset.png) - * [linear-separated.png](NeuralNetworks/PerceptronClassifier/chart/linear-separated.png) - * [sigmoid.png](NeuralNetworks/PerceptronClassifier/chart/sigmoid.png) +## Neuralnetworks + * Perceptronclassifier + * [Neuralnetworkperceptronclassifier](./NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifier.php) ## Searches * [Binarysearch](./Searches/BinarySearch.php) @@ -179,6 +174,9 @@ * [Eratosthenessievetest](./tests/Maths/EratosthenesSieveTest.php) * [Mathstest](./tests/Maths/MathsTest.php) * [Projecteulertest](./tests/Maths/ProjectEulerTest.php) + * Neuralnetworks + * Perceptronclassifier + * [Neuralnetworkperceptronclassifiertest](./tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php) * Searches * [Searchestest](./tests/Searches/SearchesTest.php) * Sorting