├── .github └── workflows │ ├── ci.yml │ ├── code-style.yml │ └── directory_workflow.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── CONTRIBUTING.md ├── Ciphers ├── AtbashCipher.php ├── CaesarCipher.php ├── MonoAlphabeticCipher.php ├── MorseCode.php ├── RailfenceCipher.php ├── VignereCipher.php └── XORCipher.php ├── Conversions ├── BinaryToDecimal.php ├── DecimalToBinary.php ├── HexadecimalToDecimal.php ├── OctalToDecimal.php ├── SpeedConversion.php └── TemperatureConversions.php ├── DIRECTORY.md ├── DataStructures ├── .keep ├── AVLTree │ ├── AVLTree.php │ ├── AVLTreeNode.php │ └── TreeTraversal.php ├── BinarySearchTree │ ├── BSTNode.php │ ├── BSTree.php │ ├── BinaryTreeTraversal.php │ └── DuplicateKeyException.php ├── CompareBinaryTree │ ├── BinaryTreeNode.php │ └── CompareBinaryTree.php ├── DisjointSets │ ├── DisjointSet.php │ └── DisjointSetNode.php ├── DoublyLinkedList.php ├── InvertBinaryTree │ ├── BinaryTree.php │ └── InvertBinaryTree.php ├── Node.php ├── Queue.php ├── ReverseLinkedList │ ├── LinkedListItem.php │ └── ReverseLinkedList.php ├── SegmentTree │ ├── SegmentTree.php │ └── SegmentTreeNode.php ├── SinglyLinkedList.php ├── SplayTree │ ├── SplayTree.php │ ├── SplayTreeNode.php │ └── SplayTreeRotations.php ├── Stack.php └── Trie │ ├── Trie.php │ └── TrieNode.php ├── Graphs ├── BellmanFord.php ├── BreadthFirstSearch.php ├── DepthFirstSearch.php ├── Dijkstras.php └── GraphEdge.php ├── LICENSE ├── Maths ├── AbsoluteMax.php ├── AbsoluteMin.php ├── ArmstrongNumber.php ├── BaseX.php ├── CheckEven.php ├── CheckOdd.php ├── CheckPalindrome.php ├── CheckPrime.php ├── EratosthenesSieve.php ├── Factorial.php ├── FastExponentiation.php ├── FastInverseSquareRoot.php ├── Fibonacci.php ├── Fibonacci2.php ├── GreatestCommonDivisor.php ├── Mean.php ├── Median.php ├── Mode.php ├── NeonNumber.php ├── PerfectNumber.php ├── PerfectSquare.php └── ProjectEuler │ ├── Problem1.php │ ├── Problem10.php │ ├── Problem11.php │ ├── Problem2.php │ ├── Problem3.php │ ├── Problem4.php │ ├── Problem5.php │ ├── Problem6.php │ ├── Problem7.php │ ├── Problem8.php │ └── Problem9.php ├── NeuralNetworks └── PerceptronClassifier │ ├── NeuralNetworkPerceptronClassifier.php │ ├── README.md │ └── chart │ ├── dataset.png │ ├── linear-separated.png │ └── sigmoid.png ├── README.md ├── Searches ├── BinarySearch.php ├── ExponentialSearch.php ├── FibonacciSearch.php ├── InterpolationSearch.php ├── JumpSearch.php ├── LinearSearch.php ├── LowerBound.php ├── SentinelSearch.php ├── TernarySearch.php ├── TwoPointers.php └── UpperBound.php ├── Sorting ├── ArrayKeysSort.php ├── BubbleSort.php ├── BubbleSort2.php ├── CountSort.php ├── GnomeSort.php ├── HeapSort.php ├── InsertionSort.php ├── MergeSort.php ├── QuickSort.php ├── RadixSort.php ├── SelectionSort.php └── ShellSort.php ├── Strings ├── CheckAnagram.php ├── CheckPalindrome.php ├── CheckPalindrome2.php ├── CountConsonants.php ├── CountHomogenous.php ├── CountSentences.php ├── CountVowels.php ├── Distance.php ├── MaxCharacter.php ├── ReverseString.php └── ReverseWords.php ├── Utils ├── ArrayHelpers.php └── ExecutionTime.php ├── composer.json ├── phpcs.xml.dist └── tests ├── Ciphers ├── AtbashCipherTest.php ├── CiphersTest.php ├── MonoAlphabeticCipherTest.php ├── MorseCodeTest.php ├── RailfenceCipherTest.php └── VignereCipherTest.php ├── Conversions └── ConversionsTest.php ├── DataStructures ├── .keep ├── AVLTreeTest.php ├── BSTreeTest.php ├── CompareBinaryTreeTest.php ├── DisjointSetTest.php ├── DoublyLinkedListTest.php ├── InvertBinaryTreeTest.php ├── QueueTest.php ├── ReverseLinkedListTest.php ├── SegmentTreeTest.php ├── SinglyLinkedListTest.php ├── SplayTreeTest.php ├── StackTest.php └── TrieTest.php ├── Graphs ├── BellmanFordTest.php ├── BreadthFirstSearchTest.php ├── DepthFirstSearchTest.php └── DijkstrasTest.php ├── Maths ├── EratosthenesSieveTest.php ├── MathsTest.php └── ProjectEulerTest.php ├── NeuralNetworks └── PerceptronClassifier │ └── NeuralNetworkPerceptronClassifierTest.php ├── Searches └── SearchesTest.php ├── Sorting ├── ArrayKeysSortTest.php ├── GnomeSortTest.php ├── ShellSortTest.php └── SortingTest.php └── Strings └── StringsTest.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: '7.4' 21 | ini-values: xdebug.max_nesting_level=512 22 | 23 | - name: Validate composer.json and composer.lock 24 | run: composer validate 25 | 26 | - name: Cache Composer packages 27 | id: composer-cache 28 | uses: actions/cache@v3 29 | with: 30 | path: vendor 31 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 32 | restore-keys: | 33 | ${{ runner.os }}-php- 34 | 35 | - name: Install dependencies 36 | if: steps.composer-cache.outputs.cache-hit != 'true' 37 | run: composer install --prefer-dist --no-progress --no-suggest 38 | 39 | - name: Run PHPUnit 40 | run: composer run-script test -------------------------------------------------------------------------------- /.github/workflows/code-style.yml: -------------------------------------------------------------------------------- 1 | name: Code style 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | phpcs: 10 | name: PHPCS 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup PHP 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: '7.4' 20 | 21 | - name: Install dependencies 22 | run: composer update --prefer-dist --no-progress --no-suggest 23 | 24 | - name: Run script 25 | run: vendor/bin/phpcs -n -------------------------------------------------------------------------------- /.github/workflows/directory_workflow.yml: -------------------------------------------------------------------------------- 1 | name: directory_md 2 | on: 3 | push: 4 | branches: [ "master" ] 5 | 6 | jobs: 7 | MainSequence: 8 | name: DIRECTORY.md 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-python@v5 13 | - name: Setup Git Specs 14 | run: | 15 | git config --global user.name "$GITHUB_ACTOR" 16 | git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" 17 | git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY 18 | - name: Update DIRECTORY.md 19 | shell: python 20 | run: | 21 | import os 22 | from typing import Iterator 23 | g_output = [] 24 | def good_filepaths(top_dir: str = ".") -> Iterator[str]: 25 | fs_exts = tuple(".php".split()) 26 | for dirpath, dirnames, filenames in os.walk(top_dir): 27 | dirnames[:] = [d for d in dirnames if d[0] not in "._"] 28 | for filename in filenames: 29 | if os.path.splitext(filename)[1].lower() in fs_exts: 30 | yield os.path.join(dirpath, filename).lstrip("./") 31 | def md_prefix(i): 32 | return f"{i * ' '}*" if i else "\n##" 33 | def print_path(old_path: str, new_path: str) -> str: 34 | global g_output 35 | old_parts = old_path.split(os.sep) 36 | for i, new_part in enumerate(new_path.split(os.sep)): 37 | if i + 1 > len(old_parts) or old_parts[i] != new_part: 38 | if new_part: 39 | g_output.append(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") 40 | return new_path 41 | def build_directory_md(top_dir: str = ".") -> str: 42 | global g_output 43 | old_path = "" 44 | for filepath in sorted(good_filepaths(), key=str.lower): 45 | filepath, filename = os.path.split(filepath) 46 | if filepath != old_path: 47 | old_path = print_path(old_path, filepath) 48 | indent = (filepath.count(os.sep) + 1) if filepath else 0 49 | url = "/".join((".", filepath, filename)).replace(" ", "%20") 50 | filename = os.path.splitext(filename.replace("_", " ").title())[0] 51 | g_output.append(f"{md_prefix(indent)} [{filename}]({url})") 52 | return "# List of all files\n" + "\n".join(g_output) 53 | with open("DIRECTORY.md", "w") as out_file: 54 | out_file.write(build_directory_md(".") + "\n") 55 | - name: Commit DIRECTORY.md 56 | run: | 57 | git commit -m "updating DIRECTORY.md" DIRECTORY.md || true 58 | git diff DIRECTORY.md 59 | git push --force origin HEAD:$GITHUB_REF || true 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .idea 3 | .vscode 4 | .phan 5 | composer.lock 6 | 7 | /.phpcs-cache 8 | /phpcs.xml 9 | 10 | .phpunit.result.cache -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full:2022-05-08-14-31-53 2 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | tasks: 5 | - init: | 6 | echo "Welcome to TheAlgorithms/PHP" 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | 3 | ## Before contributing 4 | 5 | Welcome to [TheAlgorithms/PHP](https://github.com/TheAlgorithms/PHP)! Before sending your pull requests, make sure that you **read the entire guide**. If you have any question about the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/PHP/issues/new) or ask the community in [Gitter](https://gitter.im/TheAlgorithms). 6 | 7 | ## Contributing 8 | 9 | ### Contributor 10 | 11 | We are very happy that you would consider contributing! As a contributor, you agree and confirm that: 12 | 13 | - You did your work - no plagiarism allowed 14 | - Any plagiarized work will not be merged 15 | - Your work will be distributed under [MIT License](LICENSE) once your pull request is merged 16 | - Your submitted work follows (or mostly follows) the styles and standards already found in this repo 17 | 18 | **New implementations** are welcome! For example, new solutions for an existing problem, different representations for a graph data structure or an algorithm design with different complexity. 19 | 20 | **Improving comments** and **writing proper tests** are also highly welcome. 21 | 22 | ### Contribution 23 | 24 | We appreciate any contribution, from fixing a grammar mistake in a comment to implementing complex algorithms. Please read this section if you are contributing your work. 25 | 26 | Please help us keep our issue list small by adding fixes: #{$ISSUE_NO} to the commit message of pull requests that resolve open issues. GitHub will use this tag to auto close the issue when the PR is merged. 27 | 28 | #### What is an Algorithm? 29 | 30 | An Algorithm is one or more functions (or classes) that: 31 | * take one or more inputs, 32 | * perform some internal calculations or data manipulations, 33 | * return one or more outputs, 34 | * have minimal side effects (Ex. print(), plot(), read(), write()). 35 | 36 | Algorithms should be packaged in a way that would make it easy for readers to put them into larger programs. 37 | 38 | Algorithms should: 39 | * have intuitive class and function names that make their purpose clear to readers 40 | * use PHP naming conventions and intuitive variable names to ease comprehension 41 | * be flexible to take different input values 42 | * have PHP type hints for their input parameters and return values 43 | * raise PHP exceptions (UnexpectedValueException, etc.) on erroneous input values 44 | * have docstrings with clear explanations and/or URLs to source materials 45 | * contain doctests that test both valid and erroneous input values 46 | * return all calculation results instead of printing or plotting them 47 | 48 | Algorithms in this repo should not be how-to examples for existing PHP packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing PHP packages but each algorithm in this repo should add unique value. 49 | 50 | #### Coding Style 51 | 52 | We want your work to be readable by others; therefore, we encourage you to note the following: 53 | 54 | - Please write in PHP 7.1+ 55 | - Please put thought into naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments 56 | - Single letter variable names are _old school_ so please avoid them unless their life only spans a few lines 57 | - Please follow the [PHP Basic Coding Standard](https://www.php-fig.org/psr/psr-12/) style guide. So functionNames should be camelCase, CONSTANTS in UPPER_CASE, Name\Spaces and ClassNames should follow an "autoloading" PSR, etc. 58 | 59 | - Original code submission require docstrings or comments to describe your work 60 | 61 | - More on docstrings and comments: 62 | 63 | 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 64 | 65 | - 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) 66 | 67 | - Avoid importing external libraries for basic algorithms. Only use them for complicated algorithms 68 | 69 | - Ensure code is linted with phpcs, and passing all linting checks (vendor/bin/phpcs -n) 70 | 71 | #### Other Standard While Submitting Your Work 72 | 73 | - File extension for code should be `.php` 74 | - After adding a new File/Directory, please make sure to update the [DIRECTORY.md](DIRECTORY.md) file with the details. 75 | - If possible, follow the standard *within* the folder you are submitting to 76 | - If you have modified/added code work, make sure the code compiles before submitting 77 | - If you have modified/added documentation work, ensure your language is concise and contains no grammar errors 78 | - Add a corresponding explanation to [Algorithms-Explanation](https://github.com/TheAlgorithms/Algorithms-Explanation) (Optional but recommended). 79 | 80 | - Most importantly, 81 | - **Be consistent in the use of these guidelines when submitting.** 82 | - **Join** [Gitter](https://gitter.im/TheAlgorithms) **now!** 83 | - Happy coding! 84 | 85 | Writer [@darwinz](https://github.com/darwinz), Aug 2020 (based on [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md) by [@poyea](https://github.com/poyea)) 86 | -------------------------------------------------------------------------------- /Ciphers/AtbashCipher.php: -------------------------------------------------------------------------------- 1 | ".-", 15 | "B" => "-...", 16 | "C" => "-.-.", 17 | "D" => "-..", 18 | "E" => ".", 19 | "F" => "..-.", 20 | "G" => "--.", 21 | "H" => "....", 22 | "I" => "..", 23 | "J" => ".---", 24 | "K" => "-.-", 25 | "L" => ".-..", 26 | "M" => "--", 27 | "N" => "-.", 28 | "O" => "---", 29 | "P" => ".--.", 30 | "Q" => "--.-", 31 | "R" => ".-.", 32 | "S" => "...", 33 | "T" => "-", 34 | "U" => "..-", 35 | "V" => "...-", 36 | "W" => ".--", 37 | "X" => "-..-", 38 | "Y" => "-.--", 39 | "Z" => "--..", 40 | "1" => ".----", 41 | "2" => "..---", 42 | "3" => "...--", 43 | "4" => "....-", 44 | "5" => ".....", 45 | "6" => "-....", 46 | "7" => "--...", 47 | "8" => "---..", 48 | "9" => "----.", 49 | "0" => "-----", 50 | " " => "/" 51 | ); 52 | 53 | $encodedText = ""; // Stores the encoded text 54 | foreach (str_split($text) as $c) { // Going through each character 55 | if (array_key_exists($c, $MORSE_CODE)) { // Checks if it is a valid character 56 | $encodedText .= $MORSE_CODE[$c] . " "; // Appends the correct character 57 | } else { 58 | throw new \Exception("Invalid character: $c"); 59 | } 60 | } 61 | substr_replace($encodedText, "", -1); // Removes trailing space 62 | return $encodedText; 63 | } 64 | 65 | /** 66 | * Decode Morse Code to text. 67 | * @param string $text text to decode 68 | * @throws \Exception 69 | */ 70 | function decode(string $text): string 71 | { 72 | $MORSE_CODE = array( // An array containing morse code to text translations 73 | ".-" => "A", 74 | "-..." => "B", 75 | "-.-." => "C", 76 | "-.." => "D", 77 | "." => "E", 78 | "..-." => "F", 79 | "--." => "G", 80 | "...." => "H", 81 | ".." => "I", 82 | ".---" => "J", 83 | "-.-" => "K", 84 | ".-.." => "L", 85 | "--" => "M", 86 | "-." => "N", 87 | "---" => "O", 88 | ".--." => "P", 89 | "--.-" => "Q", 90 | ".-." => "R", 91 | "..." => "S", 92 | "-" => "T", 93 | "..-" => "U", 94 | "...-" => "V", 95 | ".--" => "W", 96 | "-..-" => "X", 97 | "-.--" => "Y", 98 | "--.." => "Z", 99 | ".----" => "1", 100 | "..---" => "2", 101 | "...--" => "3", 102 | "....-" => "4", 103 | "....." => "5", 104 | "-...." => "6", 105 | "--..." => "7", 106 | "---.." => "8", 107 | "----." => "9", 108 | "-----" => "0", 109 | "/" => " " 110 | ); 111 | 112 | $decodedText = ""; // Stores the decoded text 113 | foreach (explode(" ", $text) as $c) { // Going through each group 114 | if (array_key_exists($c, $MORSE_CODE)) { // Checks if it is a valid character 115 | $decodedText .= $MORSE_CODE[$c]; // Appends the correct character 116 | } else { 117 | if ($c) { // Makes sure that the string is not empty to prevent trailing spaces or extra spaces from breaking this 118 | throw new \Exception("Invalid character: $c"); 119 | } 120 | } 121 | } 122 | return $decodedText; 123 | } 124 | -------------------------------------------------------------------------------- /Ciphers/RailfenceCipher.php: -------------------------------------------------------------------------------- 1 | $rowIndex) { 59 | $lengths[$rowIndex]++; 60 | } 61 | if ($balance > ($rails + ($rails - $rowIndex) - 2)) { 62 | $lengths[$rowIndex]++; 63 | } 64 | $strings[] = substr($cipherMessage, $totalLengths, $lengths[$rowIndex]); 65 | $totalLengths += $lengths[$rowIndex]; 66 | } 67 | // Convert the rows of characters to plain message 68 | $plainText = ''; 69 | while (strlen($plainText) < $textLength) { 70 | for ($charIndex = 0; $charIndex < $position; $charIndex++) { 71 | if (isset($strings[$charIndex])) { 72 | $index = $charIndex; 73 | } else { 74 | $index = $position - $charIndex; 75 | } 76 | $plainText .= substr($strings[$index], 0, 1); 77 | $strings[$index] = substr($strings[$index], 1); 78 | } 79 | } 80 | return $plainText; 81 | } 82 | -------------------------------------------------------------------------------- /Ciphers/VignereCipher.php: -------------------------------------------------------------------------------- 1 | $digit) { 28 | $decimalNumber += $digit * pow(2, $index); 29 | } 30 | 31 | return $decimalNumber; 32 | } 33 | -------------------------------------------------------------------------------- /Conversions/DecimalToBinary.php: -------------------------------------------------------------------------------- 1 | 0) { 21 | $binaryNumber = ($decimalNumber % 2) . $binaryNumber; 22 | $decimalNumber /= 2; 23 | } 24 | 25 | return $binaryNumber; 26 | } 27 | -------------------------------------------------------------------------------- /Conversions/HexadecimalToDecimal.php: -------------------------------------------------------------------------------- 1 | 10, 30 | 'B' => 11, 31 | 'C' => 12, 32 | 'D' => 13, 33 | 'E' => 14, 34 | 'F' => 15, 35 | ]; 36 | 37 | $hexDigits = str_split($hexNumber); 38 | $hexDigits = array_reverse($hexDigits); 39 | 40 | foreach ($hexDigits as $power => $digit) { 41 | $hexDigit = $digit; 42 | if (!is_numeric($digit)) { 43 | $hexDigit = $decimalDigitMappings[$digit]; 44 | } 45 | $decimalNumber += (pow(16, $power) * $hexDigit); 46 | } 47 | return $decimalNumber; 48 | } 49 | 50 | /** 51 | * This function converts the 52 | * submitted Decimal Number to 53 | * Hexadecimal Number. 54 | * 55 | * @param string $decimalNumber 56 | * @return string 57 | */ 58 | function decimalToHex($decimalNumber) 59 | { 60 | $hexDigits = []; 61 | 62 | // Mapping for HexaDecimal Digits after 9 63 | $hexDigitMappings = [ 64 | 10 => 'A', 65 | 11 => 'B', 66 | 12 => 'C', 67 | 13 => 'D', 68 | 14 => 'E', 69 | 15 => 'F', 70 | ]; 71 | if (!is_numeric($decimalNumber)) { 72 | throw new \Exception('Please pass a valid Decimal Number for Converting it to a Hexadecimal Number.'); 73 | } 74 | 75 | while ($decimalNumber > 0) { 76 | $remainder = ($decimalNumber % 16); 77 | $decimalNumber /= 16; 78 | if (empty($hexDigits) && 0 === $remainder) { 79 | continue; 80 | } 81 | $hexDigits[] = $remainder; 82 | } 83 | 84 | $hexDigits = array_reverse($hexDigits); 85 | 86 | foreach ($hexDigits as $index => $digit) { 87 | if ($digit > 9) { 88 | $hexDigits[$index] = $hexDigitMappings[$digit]; 89 | } 90 | } 91 | 92 | $hexNumber = ltrim(implode('', $hexDigits), '0'); // Connecting all the digits and removing leading zeroes. 93 | 94 | return $hexNumber; 95 | } 96 | -------------------------------------------------------------------------------- /Conversions/OctalToDecimal.php: -------------------------------------------------------------------------------- 1 | $digit) { 27 | $decimalNumber += $digit * pow(8, $index); 28 | } 29 | 30 | return $decimalNumber; 31 | } 32 | 33 | /** 34 | * This function converts the 35 | * submitted Decimal Number to 36 | * Octal Number. 37 | * 38 | * @param string $decimalNumber 39 | * @return string 40 | * @throws \Exception 41 | */ 42 | function decimalToOctal($decimalNumber) 43 | { 44 | if (!is_numeric($decimalNumber)) { 45 | throw new \Exception('Please pass a valid Decimal Number for Converting it to an Octal Number.'); 46 | } 47 | 48 | $octalNumber = ''; 49 | 50 | while ($decimalNumber > 0) { 51 | $octalNumber = ($decimalNumber % 8) . $octalNumber; 52 | $decimalNumber /= 8; 53 | } 54 | 55 | return $octalNumber; 56 | } 57 | -------------------------------------------------------------------------------- /Conversions/SpeedConversion.php: -------------------------------------------------------------------------------- 1 | miles per hour 11 | * km/h -> kilometers per hour 12 | * m/s -> meters per second 13 | * ft/s -> feet per second 14 | * kn -> 1 knot which is equal to 1 nautical mile (1852 km/h) 15 | * The conversion is made using kilometers as base 16 | * 17 | * @param float $speed 18 | * @param string $unitFrom 19 | * @param string $unitTo 20 | * @return float 21 | * @throws \Exception 22 | */ 23 | function convertSpeed(float $speed, string $unitFrom, string $unitTo) 24 | { 25 | $speedUnitsFrom = [ 26 | 'mph' => 1.609344, 27 | 'km/h' => 1, 28 | 'm/s' => 3.6, 29 | 'ft/s' => 1.097, 30 | 'kn' => 1.852, 31 | ]; 32 | $speedUnitsTo = [ 33 | 'mph' => 0.6213712, 34 | 'km/h' => 1, 35 | 'm/s' => 0.277778, 36 | 'ft/s' => 0.911344, 37 | 'kn' => 0.539957, 38 | ]; 39 | $availableUnits = array_keys($speedUnitsFrom); 40 | 41 | if (!is_numeric($speed)) { 42 | throw new \Exception("Please pass a valid speed number for converting it from one unit to another."); 43 | } 44 | if (!in_array($unitFrom, $availableUnits) || !in_array($unitTo, $availableUnits)) { 45 | throw new \Exception("Please pass a valid speed unit.\n\nAvailable units: " . implode(', ', $availableUnits)); 46 | } 47 | 48 | return round($speed * $speedUnitsFrom[$unitFrom] * $speedUnitsTo[$unitTo], 2); 49 | } 50 | -------------------------------------------------------------------------------- /Conversions/TemperatureConversions.php: -------------------------------------------------------------------------------- 1 | key = $key; 30 | $this->value = $value; 31 | $this->left = $left; 32 | $this->right = $right; 33 | $this->height = 1; // New node is initially at height 1 34 | } 35 | 36 | public function updateHeight(): void 37 | { 38 | $leftHeight = $this->left ? $this->left->height : 0; 39 | $rightHeight = $this->right ? $this->right->height : 0; 40 | $this->height = max($leftHeight, $rightHeight) + 1; 41 | } 42 | 43 | public function balanceFactor(): int 44 | { 45 | $leftHeight = $this->left ? $this->left->height : 0; 46 | $rightHeight = $this->right ? $this->right->height : 0; 47 | return $leftHeight - $rightHeight; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DataStructures/AVLTree/TreeTraversal.php: -------------------------------------------------------------------------------- 1 | left)); 24 | $result[] = [$node->key => $node->value]; 25 | $result = array_merge($result, self::inOrder($node->right)); 26 | } 27 | return $result; 28 | } 29 | 30 | /** 31 | * Perform a pre-order traversal of the subtree. 32 | * Recursively traverses the subtree rooted at the given node. 33 | */ 34 | public static function preOrder(?AVLTreeNode $node): array 35 | { 36 | $result = []; 37 | if ($node !== null) { 38 | $result[] = [$node->key => $node->value]; 39 | $result = array_merge($result, self::preOrder($node->left)); 40 | $result = array_merge($result, self::preOrder($node->right)); 41 | } 42 | return $result; 43 | } 44 | 45 | /** 46 | * Perform a post-order traversal of the subtree. 47 | * Recursively traverses the subtree rooted at the given node. 48 | */ 49 | public static function postOrder(?AVLTreeNode $node): array 50 | { 51 | $result = []; 52 | if ($node !== null) { 53 | $result = array_merge($result, self::postOrder($node->left)); 54 | $result = array_merge($result, self::postOrder($node->right)); 55 | $result[] = [$node->key => $node->value]; 56 | } 57 | return $result; 58 | } 59 | 60 | /** 61 | * Perform a breadth-first traversal of the AVL Tree. 62 | */ 63 | public static function breadthFirst(?AVLTreeNode $root): array 64 | { 65 | $result = []; 66 | if ($root === null) { 67 | return $result; 68 | } 69 | 70 | $queue = []; 71 | $queue[] = $root; 72 | 73 | while (!empty($queue)) { 74 | $currentNode = array_shift($queue); 75 | $result[] = [$currentNode->key => $currentNode->value]; 76 | 77 | if ($currentNode->left !== null) { 78 | $queue[] = $currentNode->left; 79 | } 80 | 81 | if ($currentNode->right !== null) { 82 | $queue[] = $currentNode->right; 83 | } 84 | } 85 | 86 | return $result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /DataStructures/BinarySearchTree/BSTNode.php: -------------------------------------------------------------------------------- 1 | key = $key; 31 | $this->value = $value; 32 | $this->left = null; 33 | $this->right = null; 34 | $this->parent = null; 35 | } 36 | 37 | public function isRoot(): bool 38 | { 39 | return $this->parent === null; 40 | } 41 | 42 | public function isLeaf(): bool 43 | { 44 | return $this->left === null && $this->right === null; 45 | } 46 | 47 | public function getChildren(): array 48 | { 49 | if ($this->isLeaf()) { 50 | return []; 51 | } 52 | 53 | $children = []; 54 | if ($this->left !== null) { 55 | $children['left'] = $this->left; 56 | } 57 | if ($this->right !== null) { 58 | $children['right'] = $this->right; 59 | } 60 | return $children; 61 | } 62 | public function getChildrenCount(): int 63 | { 64 | return count($this->getChildren()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DataStructures/BinarySearchTree/DuplicateKeyException.php: -------------------------------------------------------------------------------- 1 | value = $value; 10 | $this->left = $left; 11 | $this->right = $right; 12 | } 13 | 14 | public $value; 15 | public ?BinaryTreeNode $left; 16 | public ?BinaryTreeNode $right; 17 | } 18 | -------------------------------------------------------------------------------- /DataStructures/CompareBinaryTree/CompareBinaryTree.php: -------------------------------------------------------------------------------- 1 | value !== $b->value) { 30 | return false; 31 | } 32 | return $this->areTreesEqual($a->left, $b->left) 33 | && $this->areTreesEqual($a->right, $b->right); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DataStructures/DisjointSets/DisjointSet.php: -------------------------------------------------------------------------------- 1 | parent) { 21 | // Path compression: make the parent point directly to the root 22 | $node->parent = $this->findSet($node->parent); 23 | } 24 | return $node->parent; 25 | } 26 | 27 | /** 28 | * Unites the sets that contain x and y. 29 | */ 30 | public function unionSet(DisjointSetNode $nodeX, DisjointSetNode $nodeY): void 31 | { 32 | $rootX = $this->findSet($nodeX); 33 | $rootY = $this->findSet($nodeY); 34 | 35 | if ($rootX === $rootY) { 36 | return; // They are already in the same set 37 | } 38 | 39 | // Union by rank: attach the smaller tree under the larger tree 40 | if ($rootX->rank > $rootY->rank) { 41 | $rootY->parent = $rootX; 42 | } else { 43 | $rootX->parent = $rootY; 44 | if ($rootX->rank === $rootY->rank) { 45 | $rootY->rank += 1; 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DataStructures/DisjointSets/DisjointSetNode.php: -------------------------------------------------------------------------------- 1 | data = $data; 26 | $this->rank = 0; 27 | $this->parent = $this; // Initialize parent to itself 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DataStructures/InvertBinaryTree/BinaryTree.php: -------------------------------------------------------------------------------- 1 | left = $left; 14 | return $this; 15 | } 16 | 17 | public function getLeft(): ?BinaryTree 18 | { 19 | return $this->left; 20 | } 21 | 22 | public function setRight(?BinaryTree $right) 23 | { 24 | $this->right = $right; 25 | return $this; 26 | } 27 | 28 | public function getRight(): ?BinaryTree 29 | { 30 | return $this->right; 31 | } 32 | 33 | public function setValue($value) 34 | { 35 | $this->value = $value; 36 | return $this; 37 | } 38 | 39 | public function getValue() 40 | { 41 | return $this->value; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DataStructures/InvertBinaryTree/InvertBinaryTree.php: -------------------------------------------------------------------------------- 1 | getLeft(); 19 | $b->setLeft($b->getRight()); 20 | $b->setRight($tmp); 21 | $this->invert($b->getLeft()); 22 | $this->invert($b->getRight()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DataStructures/Node.php: -------------------------------------------------------------------------------- 1 | data = $data; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DataStructures/Queue.php: -------------------------------------------------------------------------------- 1 | elements = []; 15 | $this->count = 0; 16 | $this->lowestCount = 0; 17 | } 18 | 19 | public function enqueue($element): void 20 | { 21 | $this->elements[$this->count] = $element; 22 | $this->count++; 23 | } 24 | 25 | public function dequeue() 26 | { 27 | if ($this->isEmpty()) { 28 | return null; 29 | } 30 | 31 | $element = $this->elements[$this->lowestCount]; 32 | 33 | unset($this->elements[$this->lowestCount]); 34 | 35 | $this->lowestCount++; 36 | 37 | return $element; 38 | } 39 | 40 | public function isEmpty(): bool 41 | { 42 | return $this->count - $this->lowestCount === 0; 43 | } 44 | 45 | public function size(): int 46 | { 47 | return $this->count - $this->lowestCount; 48 | } 49 | 50 | public function peek() 51 | { 52 | if ($this->isEmpty()) { 53 | return null; 54 | } 55 | 56 | return $this->elements[$this->lowestCount]; 57 | } 58 | 59 | public function clear(): void 60 | { 61 | $this->elements = []; 62 | $this->count = 0; 63 | $this->lowestCount = 0; 64 | } 65 | 66 | public function toString(string $delimiter = ''): string 67 | { 68 | if ($this->isEmpty()) { 69 | return ''; 70 | } 71 | 72 | $result = "{$this->elements[$this->lowestCount]}"; 73 | 74 | for ($i = $this->lowestCount + 1; $i < $this->count; $i++) { 75 | $result .= "{$delimiter}{$this->elements[$i]}"; 76 | } 77 | 78 | return $result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /DataStructures/ReverseLinkedList/LinkedListItem.php: -------------------------------------------------------------------------------- 1 | next = $next; 14 | return $this; 15 | } 16 | 17 | public function getNext(): ?LinkedListItem 18 | { 19 | return $this->next; 20 | } 21 | 22 | public function setPrev(?LinkedListItem $prev) 23 | { 24 | $this->prev = $prev; 25 | return $this; 26 | } 27 | 28 | public function getPrev(): ?LinkedListItem 29 | { 30 | return $this->prev; 31 | } 32 | 33 | public function setValue($value) 34 | { 35 | $this->value = $value; 36 | return $this; 37 | } 38 | 39 | public function getValue() 40 | { 41 | return $this->value; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DataStructures/ReverseLinkedList/ReverseLinkedList.php: -------------------------------------------------------------------------------- 1 | getNext(); 16 | $item->setNext(null); 17 | while (true) { 18 | $item->setPrev($next); 19 | if (! $next) { 20 | return $item; 21 | } 22 | $nextNext = $next->getNext(); 23 | $next->setNext($item); 24 | $item = $next; 25 | $next = $nextNext; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DataStructures/SegmentTree/SegmentTreeNode.php: -------------------------------------------------------------------------------- 1 | start = $start; 33 | $this->end = $end; 34 | $this->value = $value; 35 | $this->left = null; 36 | $this->right = null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /DataStructures/SinglyLinkedList.php: -------------------------------------------------------------------------------- 1 | data = $data; 14 | } 15 | 16 | public function append($data): void 17 | { 18 | $current = $this; 19 | while ($current instanceof SinglyLinkedList && isset($current->next)) { 20 | $current = $current->next; 21 | } 22 | 23 | $current->next = new SinglyLinkedList($data); 24 | } 25 | 26 | public function delete($data): SinglyLinkedList 27 | { 28 | $current = $this; 29 | if ($current->data == $data) { 30 | return $current->next; 31 | } 32 | 33 | while ($current instanceof SinglyLinkedList && isset($current->next)) { 34 | if ($current->next->data === $data) { 35 | $current->next = $current->next->next; 36 | return $this; 37 | } 38 | 39 | $current = $current->next; 40 | } 41 | 42 | return $this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DataStructures/SplayTree/SplayTreeNode.php: -------------------------------------------------------------------------------- 1 | key = $key; 34 | $this->value = $value; 35 | 36 | // Set all node pointers to null initially 37 | $this->left = null; 38 | $this->right = null; 39 | $this->parent = null; 40 | } 41 | 42 | public function isLeaf(): bool 43 | { 44 | return $this->left === null && $this->right === null; 45 | } 46 | 47 | public function isRoot(): bool 48 | { 49 | return $this->parent === null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /DataStructures/Stack.php: -------------------------------------------------------------------------------- 1 | stack = $array; 13 | } 14 | 15 | public function __destruct() 16 | { 17 | unset($this->stack); 18 | } 19 | 20 | 21 | public function push($data): void 22 | { 23 | array_push($this->stack, $data); 24 | } 25 | 26 | public function pop() 27 | { 28 | return array_pop($this->stack); 29 | } 30 | 31 | public function peek() 32 | { 33 | return $this->stack[count($this->stack) - 1]; 34 | } 35 | 36 | public function isEmpty(): bool 37 | { 38 | return empty($this->stack); 39 | } 40 | 41 | public function print(): void 42 | { 43 | echo implode(', ', $this->stack); 44 | } 45 | 46 | public function __toString(): string 47 | { 48 | return implode(', ', $this->stack); 49 | } 50 | 51 | public function length(): int 52 | { 53 | return count($this->stack); 54 | } 55 | 56 | public function clear(): void 57 | { 58 | $this->stack = []; 59 | } 60 | 61 | public function search($data): int 62 | { 63 | return array_search($data, $this->stack); 64 | } 65 | 66 | public function toArray(): array 67 | { 68 | return $this->stack; 69 | } 70 | 71 | public function fromArray(array $array): void 72 | { 73 | $this->stack = $array; 74 | } 75 | 76 | public function reverse(): void 77 | { 78 | $this->stack = array_reverse($this->stack); 79 | } 80 | 81 | public function sort(): void 82 | { 83 | sort($this->stack); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /DataStructures/Trie/Trie.php: -------------------------------------------------------------------------------- 1 | root = new TrieNode(); 21 | } 22 | 23 | /** 24 | * Get the root node of the Trie. 25 | */ 26 | public function getRoot(): TrieNode 27 | { 28 | return $this->root; 29 | } 30 | 31 | /** 32 | * Insert a word into the Trie. 33 | */ 34 | public function insert(string $word): void 35 | { 36 | $node = $this->root; 37 | for ($i = 0; $i < strlen($word); $i++) { 38 | $char = $word[$i]; 39 | $node = $node->addChild($char); 40 | } 41 | $node->isEndOfWord = true; 42 | } 43 | 44 | /** 45 | * Search for a word in the Trie. 46 | */ 47 | public function search(string $word): bool 48 | { 49 | $node = $this->root; 50 | for ($i = 0; $i < strlen($word); $i++) { 51 | $char = $word[$i]; 52 | if (!$node->hasChild($char)) { 53 | return false; 54 | } 55 | $node = $node->getChild($char); 56 | } 57 | return $node->isEndOfWord; 58 | } 59 | 60 | /** 61 | * Find all words that start with a given prefix. 62 | */ 63 | public function startsWith(string $prefix): array 64 | { 65 | $prefix = strtolower($prefix); // Normalize the prefix to lowercase 66 | $node = $this->root; 67 | for ($i = 0; $i < strlen($prefix); $i++) { 68 | $char = $prefix[$i]; 69 | if (!$node->hasChild($char)) { 70 | return []; 71 | } 72 | $node = $node->getChild($char); 73 | } 74 | return $this->findWordsFromNode($node, $prefix); 75 | } 76 | 77 | /** 78 | * Helper function to find all words from a given node. 79 | */ 80 | private function findWordsFromNode(TrieNode $node, string $prefix): array 81 | { 82 | $words = []; 83 | if ($node->isEndOfWord) { 84 | $words[] = $prefix; 85 | } 86 | 87 | foreach ($node->children as $char => $childNode) { 88 | $words = array_merge($words, $this->findWordsFromNode($childNode, $prefix . $char)); 89 | } 90 | 91 | return $words; 92 | } 93 | 94 | /** 95 | * Delete a word from the Trie. 96 | * Recursively traverses the Trie and removes nodes 97 | * 98 | * @param string $word The word to delete. 99 | * @return bool Returns true if the word was successfully deleted, otherwise false. 100 | */ 101 | public function delete(string $word): bool 102 | { 103 | return $this->deleteHelper($this->root, $word, 0); 104 | } 105 | 106 | /** 107 | * Helper function for deleting a word. 108 | * Recursively traverse the Trie and removes nodes. 109 | * 110 | * @param TrieNode $node The current node in the Trie. 111 | * @param string $word The word being deleted. 112 | * @param int $index The current index in the word. 113 | * @return bool Returns true if the current node should be deleted, otherwise false. 114 | */ 115 | private function deleteHelper(TrieNode $node, string &$word, int $index): bool 116 | { 117 | if ($index === strlen($word)) { 118 | if (!$node->isEndOfWord) { 119 | return false; 120 | } 121 | $node->isEndOfWord = false; 122 | return empty($node->children); 123 | } 124 | 125 | $char = $word[$index]; 126 | $childNode = $node->getChild($char); 127 | if ($childNode === null) { 128 | return false; 129 | } 130 | 131 | // Recursively delete the child node 132 | $shouldDeleteCurrentNode = $this->deleteHelper($childNode, $word, $index + 1); 133 | 134 | if ($shouldDeleteCurrentNode) { 135 | unset($node->children[$char]); 136 | return !$node->isEndOfWord; // true if current node is not the end of another word 137 | } 138 | 139 | return false; 140 | } 141 | 142 | /** 143 | * Recursively traverses the Trie starting from the given node and collects all words. 144 | * 145 | * @param TrieNode $node The starting node for traversal. 146 | * @param string $prefix The prefix of the current path in the Trie. 147 | * @return array An array of words found in the Trie starting from the given node. 148 | */ 149 | public function traverseTrieNode(TrieNode $node, string $prefix = ''): array 150 | { 151 | $words = []; 152 | 153 | if ($node->isEndOfWord) { 154 | $words[] = $prefix; 155 | } 156 | 157 | foreach ($node->children as $char => $childNode) { 158 | $words = array_merge($words, $this->traverseTrieNode($childNode, $prefix . $char)); 159 | } 160 | 161 | return $words; 162 | } 163 | 164 | /** 165 | * Gets all words stored in the Trie. 166 | * 167 | * @return array An array of all words in the Trie. 168 | */ 169 | public function getWords(): array 170 | { 171 | return $this->traverseTrieNode($this->root); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /DataStructures/Trie/TrieNode.php: -------------------------------------------------------------------------------- 1 | */ 17 | public array $children; 18 | public bool $isEndOfWord; 19 | 20 | public function __construct() 21 | { 22 | $this->children = []; // Associative array where [ char => TrieNode ] 23 | $this->isEndOfWord = false; 24 | } 25 | 26 | /** 27 | * Add a child node for a character. 28 | */ 29 | public function addChild(string $char): TrieNode 30 | { 31 | $char = $this->normalizeChar($char); 32 | if (!isset($this->children[$char])) { 33 | $this->children[$char] = new TrieNode(); 34 | } 35 | return $this->children[$char]; 36 | } 37 | 38 | /** 39 | * Check if a character has a child node. 40 | */ 41 | public function hasChild(string $char): bool 42 | { 43 | $char = $this->normalizeChar($char); 44 | return isset($this->children[$char]); 45 | } 46 | 47 | /** 48 | * Get the child node corresponding to a character. 49 | */ 50 | public function getChild(string $char): ?TrieNode 51 | { 52 | $char = $this->normalizeChar($char); 53 | return $this->children[$char] ?? null; 54 | } 55 | 56 | /** 57 | * Normalize the character to lowercase. 58 | */ 59 | private function normalizeChar(string $char): string 60 | { 61 | return strtolower($char); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Graphs/BellmanFord.php: -------------------------------------------------------------------------------- 1 | $minWeight) { 26 | if ($verbose) { 27 | echo "checking vertex $vertice\n"; 28 | } 29 | if ($start === $vertice) { 30 | $vertices[$vertice] = 0; 31 | } 32 | 33 | foreach ($edges[$vertice] as $edge) { 34 | if ($vertices[$edge->end] > $vertices[$vertice] + $edge->weight) { 35 | if ($verbose) { 36 | echo "replace $vertice " . $vertices[$edge->end] . " with " 37 | . ($vertices[$vertice] + $edge->weight) . "\n "; 38 | } 39 | $vertices[$edge->end] = $vertices[$vertice] + $edge->weight; 40 | $change = true; 41 | } 42 | } 43 | } 44 | $round++; 45 | } 46 | return $vertices; 47 | } 48 | -------------------------------------------------------------------------------- /Graphs/BreadthFirstSearch.php: -------------------------------------------------------------------------------- 1 | start == $nextVertex) { //consider only nodes connected to current one 23 | $vertices[$edge->end] = min($vertices[$edge->end], $vertices[$nextVertex] + $edge->weight); 24 | } 25 | } 26 | 27 | // find vertex with current lowest value to be starting point in next iteration 28 | $minVertexName = null; 29 | $minVertexWeight = PHP_INT_MAX; 30 | foreach ($vertices as $name => $weight) { 31 | if (in_array($name, $visitedNodes) || $name == $nextVertex) { 32 | continue; 33 | } 34 | if ($weight <= $minVertexWeight) { 35 | $minVertexName = $name; 36 | $minVertexWeight = $weight; 37 | } 38 | } 39 | $visitedNodes[] = $nextVertex; 40 | $nextVertex = $minVertexName; 41 | } 42 | return $vertices; 43 | } 44 | -------------------------------------------------------------------------------- /Graphs/GraphEdge.php: -------------------------------------------------------------------------------- 1 | $absoluteMax) { 22 | $absoluteMax = $numbers[$loopIndex]; 23 | } 24 | } 25 | 26 | return $absoluteMax; 27 | } 28 | -------------------------------------------------------------------------------- /Maths/AbsoluteMin.php: -------------------------------------------------------------------------------- 1 | > 1); 18 | // Convert the integer back to a float 19 | $y = unpack('f', pack('l', $i))[1]; 20 | // Perform the final calculation 21 | return $y * (1.5 - 0.5 * $x * $y * $y); 22 | } 23 | -------------------------------------------------------------------------------- /Maths/Fibonacci.php: -------------------------------------------------------------------------------- 1 | 0 && $set->valid()) { 14 | yield $set->current(); 15 | $set->next(); 16 | } 17 | } 18 | 19 | /* 20 | * Fibonacci generator 21 | */ 22 | function fib() 23 | { 24 | yield $i = 0; 25 | yield $j = 1; 26 | 27 | while (true) { 28 | yield $k = $i + $j; 29 | $i = $j; 30 | $j = $k; 31 | } 32 | } 33 | 34 | /* 35 | * Generate 100 Fibonacci numbers 36 | */ 37 | foreach (loop(100, fib()) as $item) { 38 | print($item . ','); 39 | } 40 | -------------------------------------------------------------------------------- /Maths/GreatestCommonDivisor.php: -------------------------------------------------------------------------------- 1 | 6 6 | * @param int $a 7 | * @param int $b 8 | * @return int 9 | */ 10 | function gcd(int $a, int $b): int 11 | { 12 | if ($b == 0) { 13 | return $a; 14 | } 15 | return gcd($b, $a % $b); 16 | } 17 | -------------------------------------------------------------------------------- /Maths/Mean.php: -------------------------------------------------------------------------------- 1 | $largest) { 21 | $largest = $product; 22 | } 23 | } 24 | } 25 | 26 | return $largest; 27 | } 28 | -------------------------------------------------------------------------------- /Maths/ProjectEuler/Problem5.php: -------------------------------------------------------------------------------- 1 | 23 | * 24 | * @return int Difference between the sum of the squares and 25 | * the square of the sum 26 | * 27 | * @link https://projecteuler.net/problem=6 28 | */ 29 | 30 | /** 31 | * @param int $maxNumber Range from 1 to 32 | * 33 | * @return int Difference between the sum of the squares and 34 | * the square of the sum 35 | */ 36 | function problem6(int $maxNumber = 100): int 37 | { 38 | $sumOfSquares = 0; 39 | $sums = 0; 40 | for ($i = 1; $i <= $maxNumber; $i++) { 41 | $sumOfSquares += $i ** 2; // add squares to the sum of squares 42 | $sums += $i; // add number to sum to square later 43 | } 44 | return ($sums ** 2) - $sumOfSquares; // difference of square of the total sum and sum of squares 45 | } 46 | -------------------------------------------------------------------------------- /Maths/ProjectEuler/Problem7.php: -------------------------------------------------------------------------------- 1 | initParams(count($X)); 24 | 25 | for ($i = 0; $i < $iterations; $i++) { 26 | // Forward propagation 27 | $A = $this->forwardPropagation($X, $W, $b); 28 | 29 | // Compute cost 30 | $cost = $this->computeCost($A, $Y); 31 | 32 | // Backward propagation 33 | [$dW, $db] = $this->backwardPropagation($A, $X, $Y); 34 | 35 | // Update parameters 36 | [$W, $b] = $this->updateParams($W, $b, $dW, $db, $learningRate); 37 | 38 | if ($i % 100 == 0) { 39 | echo "Iteration {$i} - Cost: {$cost}\n"; 40 | } 41 | } 42 | 43 | return [$W, $b]; 44 | } 45 | 46 | /** 47 | * @param array $X 48 | * @param array $W 49 | * @param float $b 50 | * @return array 51 | */ 52 | public function predict(array $X, array $W, float $b): array 53 | { 54 | $A = $this->forwardPropagation($X, $W, $b); 55 | return array_map(fn($a) => $a > 0.5 ? 1 : 0, $A); 56 | } 57 | 58 | /** 59 | * Stage 1. Prepare dataset 60 | * @return array[] 61 | */ 62 | public function generateTrainingSet(): array 63 | { 64 | $m = 50; 65 | 66 | // Generate a 2 x m matrix with binary values (0 or 1) 67 | $X = []; 68 | for ($i = 0; $i < 2; $i++) { 69 | for ($j = 0; $j < $m; $j++) { 70 | $X[$i][$j] = rand(0, 1); 71 | } 72 | } 73 | 74 | // Compute Y: Logical AND condition (X[0] == 1 and X[1] == 0) 75 | $Y = []; 76 | for ($j = 0; $j < $m; $j++) { 77 | $Y[$j] = ($X[0][$j] == 1 && $X[1][$j] == 0) ? 1 : 0; 78 | } 79 | 80 | return [$X, $Y]; 81 | } 82 | 83 | /** 84 | * Stage 2. Initialize model parameters 85 | * @param int $n Number of features 86 | * @return array [$W, $b] Weight and bias arrays 87 | */ 88 | private function initParams(int $n): array 89 | { 90 | $W = []; 91 | for ($i = 0; $i < $n; $i++) { 92 | $W[$i] = mt_rand() / mt_getrandmax(); // Small random values 93 | } 94 | $b = 0.0; // Bias initialized to zero 95 | return [$W, $b]; 96 | } 97 | 98 | /** 99 | * Sigmoid Activation Function 100 | * @param float $z 101 | * @return float 102 | */ 103 | private function sigmoid(float $z): float 104 | { 105 | return 1 / (1 + exp(-$z)); 106 | } 107 | 108 | /** 109 | * Stage 3. Forward Propagation 110 | * @param array $X 111 | * @param array $W 112 | * @param float $b 113 | * @return array 114 | */ 115 | private function forwardPropagation(array $X, array $W, float $b): array 116 | { 117 | $Z = []; 118 | for ($j = 0; $j < count($X[0]); $j++) { 119 | $sum = $b; 120 | for ($i = 0; $i < count($W); $i++) { 121 | $sum += $W[$i] * $X[$i][$j]; 122 | } 123 | $Z[$j] = $this->sigmoid($sum); 124 | } 125 | return $Z; 126 | } 127 | 128 | /** 129 | * Stage 4. Compute Cost Function (Binary Cross-Entropy Loss) 130 | * @param array $A 131 | * @param array $Y 132 | * @return float 133 | */ 134 | private function computeCost(array $A, array $Y): float 135 | { 136 | $m = count($Y); 137 | $cost = 0.0; 138 | for ($i = 0; $i < $m; $i++) { 139 | $cost += -($Y[$i] * log($A[$i]) + (1 - $Y[$i]) * log(1 - $A[$i])); 140 | } 141 | return $cost / $m; 142 | } 143 | 144 | /** 145 | * Stage 5. Backward Propagation 146 | * @param array $A 147 | * @param array $X 148 | * @param array $Y 149 | * @return array 150 | */ 151 | private function backwardPropagation(array $A, array $X, array $Y): array 152 | { 153 | $m = count($Y); 154 | $dW = array_fill(0, count($X), 0.0); 155 | $db = 0.0; 156 | 157 | for ($j = 0; $j < $m; $j++) { 158 | $dZ = $A[$j] - $Y[$j]; 159 | for ($i = 0; $i < count($X); $i++) { 160 | $dW[$i] += $dZ * $X[$i][$j]; 161 | } 162 | $db += $dZ; 163 | } 164 | 165 | // Average gradients 166 | for ($i = 0; $i < count($dW); $i++) { 167 | $dW[$i] /= $m; 168 | } 169 | $db /= $m; 170 | 171 | return [$dW, $db]; 172 | } 173 | 174 | /** 175 | * STage 6. Update Parameters 176 | * @param array $W 177 | * @param float $b 178 | * @param array $dW 179 | * @param float $db 180 | * @param float $learningRate 181 | * @return array 182 | */ 183 | private function updateParams(array $W, float $b, array $dW, float $db, float $learningRate): array 184 | { 185 | for ($i = 0; $i < count($W); $i++) { 186 | $W[$i] -= $learningRate * $dW[$i]; 187 | } 188 | $b -= $learningRate * $db; 189 | 190 | return [$W, $b]; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /NeuralNetworks/PerceptronClassifier/README.md: -------------------------------------------------------------------------------- 1 | ## Maths behind the single Perceptron Neural Network with Activation Function 2 | 3 | This work is based on examples from course https://www.coursera.org/learn/machine-learning-calculus prepared by author Luis Serrano. 4 | 5 | Linear separation refers to data points in binary classification problems that can be separated by a linear decision boundary. 6 | If the data points can be separated by a line, linear function, or flat hyperplane, they are said to be linearly separable. 7 | 8 | If separate points in an n-dimensional space exist, then it is said to be linearly separable 9 | 10 | $$w_1x_1 + w_2x_2 + w_nx_n + b = 0$$ 11 | 12 | For two-dimensional input data, if there is a line, whose equation is $$w_1x_1 + w_2x_2 + b = 0$$ 13 | 14 | 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. 15 | Such classification problems are called "linearly separable", i.e. separating by linear combination. 16 | 17 | 18 | 19 | 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. 20 | 21 | $$z^{(i)} = w_1x_1^{(i)} + w_2x_2^{(i)} + b = Wx^{(i)} + b.\tag{1}$$ 22 | 23 | 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. 24 | 25 | $$\hat{y} = \begin{cases} 1 & \mbox{if } a > 0.5 \\ 0 & \mbox{otherwise } \end{cases}\tag{10}$$ 26 | 27 | Sigmoid activation function is defined as 28 | 29 | $$a = \sigma\left(z\right) = \frac{1}{1+e^{-z}}.\tag{2}$$ 30 | 31 | 32 | 33 | Threshold value of $0.5$ can be used for predictions: $1$ (red) if $a > 0.5$ and $0$ (blue) otherwise. 34 | 35 | The single perceptron neural network with sigmoid activation function can be expressed as: 36 | 37 | \begin{align} 38 | z^{(i)} &= W x^{(i)} + b,\\ 39 | a^{(i)} &= \sigma\left(z^{(i)}\right).\\\tag{3} 40 | \end{align} 41 | 42 | 43 | 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: 44 | 45 | 46 | \begin {align} 47 | Z &= W X + b,\\ 48 | A &= \sigma\left(Z\right),\\\tag{4} 49 | \end{align} 50 | 51 | When dealing with classification problems, the most commonly used cost function is the **log loss**, which is described by the following equation 52 | 53 | $$\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}$$ 54 | 55 | 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$). 56 | 57 | 58 | We want to minimize the cost function during the training. To implement gradient descent, calculate partial derivatives using chain rule 59 | 60 | 61 | \begin{align} 62 | \frac{\partial \mathcal{L} }{ \partial w_1 } &= 63 | \frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right)x_1^{(i)},\\ 64 | \frac{\partial \mathcal{L} }{ \partial w_2 } &= 65 | \frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right)x_2^{(i)},\tag{7}\\ 66 | \frac{\partial \mathcal{L} }{ \partial b } &= 67 | \frac{1}{m}\sum_{i=1}^{m} \left(a^{(i)} - y^{(i)}\right). 68 | \end{align} 69 | 70 | Equations above can be rewritten in a matrix form 71 | 72 | 73 | \begin{align} 74 | \frac{\partial \mathcal{L} }{ \partial W } &= 75 | \begin{bmatrix} \frac{\partial \mathcal{L} }{ \partial w_1 } & 76 | \frac{\partial \mathcal{L} }{ \partial w_2 }\end{bmatrix} = \frac{1}{m}\left(A - Y\right)X^T,\\ 77 | \frac{\partial \mathcal{L} }{ \partial b } &= \frac{1}{m}\left(A - Y\right)\mathbf{1}. 78 | \tag{8} 79 | \end{align} 80 | 81 | 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. 82 | 83 | Then you can update the parameters: 84 | 85 | \begin{align} 86 | W &= W - \alpha \frac{\partial \mathcal{L} }{ \partial W },\\ 87 | b &= b - \alpha \frac{\partial \mathcal{L} }{ \partial b }, 88 | \tag{9}\end{align} 89 | 90 | where $\alpha$ is the learning rate. Repeat the process in a loop until the cost function stops decreasing. 91 | 92 | in last step apply activation 93 | $$\hat{y} = \begin{cases} 1 & \mbox{if } a > 0.5 \\ 0 & \mbox{otherwise } \end{cases}\tag{10}$$ 94 | 95 | 96 | ### Dataset 97 | 98 | 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)$. 99 | 100 | 101 | -------------------------------------------------------------------------------- /NeuralNetworks/PerceptronClassifier/chart/dataset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlgorithms/PHP/81b5386af390fd2b44fecf162ade3b1a940c8500/NeuralNetworks/PerceptronClassifier/chart/dataset.png -------------------------------------------------------------------------------- /NeuralNetworks/PerceptronClassifier/chart/linear-separated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlgorithms/PHP/81b5386af390fd2b44fecf162ade3b1a940c8500/NeuralNetworks/PerceptronClassifier/chart/linear-separated.png -------------------------------------------------------------------------------- /NeuralNetworks/PerceptronClassifier/chart/sigmoid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlgorithms/PHP/81b5386af390fd2b44fecf162ade3b1a940c8500/NeuralNetworks/PerceptronClassifier/chart/sigmoid.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Algorithms - PHP 2 | 3 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/PHP) 4 | 5 | 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. 6 | 7 | ### All algorithms implemented in PHP - for education 8 | 9 | The Implementations in this repo are examples and may be less efficient than the implementations in the PHP standard Library. 10 | 11 | 12 | ## Contribution Guidelines: 13 | 14 | To Contribute to this repo please take your time to read our 15 | [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 16 | 17 | ## Community Channels 18 | 19 | We're on [Discord](https://discord.gg/c7MnfGFGa6) and [Gitter](https://gitter.im/TheAlgorithms)! Community channels are great for you to ask questions and get help. Please join us! 20 | 21 | ## List of Algorithms 22 | 23 | See the [directory](DIRECTORY.md) for easier navigation and to view the current list of algorithms. 24 | 25 | Thanks for contributing!! 26 | -------------------------------------------------------------------------------- /Searches/BinarySearch.php: -------------------------------------------------------------------------------- 1 | > 1; 29 | 30 | 31 | if ($list[$mid] == $target) { 32 | return $mid; 33 | } elseif ($list[$mid] > $target) { 34 | $last = $mid - 1; 35 | } elseif ($list[$mid] < $target) { 36 | $first = $mid + 1; 37 | } 38 | } 39 | 40 | return null; 41 | } 42 | 43 | /** 44 | * Binary search algorithm in PHP by recursion 45 | * 46 | * Be careful collection must be ascending sorted, otherwise result will be unpredictable 47 | * 48 | * Examples: 49 | * binarySearchByRecursion([0, 5, 7, 10, 15], 0, 0, 4); 50 | * 0 51 | * binarySearchByRecursion([0, 5, 7, 10, 15], 15, 0, 4); 52 | * 4 53 | * binarySearchByRecursion([0, 5, 7, 10, 15], 5, 0, 4); 54 | * 1 55 | * binarySearchByRecursion([0, 5, 7, 10, 15], 6, 0, 4); 56 | * 57 | * @param Array $list a sorted array list of integers to search 58 | * @param integer $target an integer number to search for in the list 59 | * @param integer $start an integer number where to start searching in the list 60 | * @param integer $end an integer number where to end searching in the list 61 | * @return integer the index where the target is found (or null if not found) 62 | */ 63 | function binarySearchByRecursion($list, $target, $start, $end) 64 | { 65 | if (count($list) == 0) { 66 | return null; 67 | } 68 | 69 | if (count($list) < 2) { 70 | return $list[0] == $target ? 0 : null; 71 | } 72 | 73 | if ($start > $end) { 74 | return null; 75 | } 76 | 77 | 78 | $mid = ($start + $end) >> 1; 79 | 80 | 81 | if ($list[$mid] == $target) { 82 | return $mid; 83 | } elseif ($list[$mid] > $target) { 84 | return binarySearchByRecursion($list, $target, $start, $mid - 1); 85 | } elseif ($list[$mid] < $target) { 86 | return binarySearchByRecursion($list, $target, $mid + 1, $end); 87 | } 88 | 89 | return null; 90 | } 91 | -------------------------------------------------------------------------------- /Searches/ExponentialSearch.php: -------------------------------------------------------------------------------- 1 | $ceiling) { 31 | return -1; 32 | } 33 | 34 | // search the left part of the $array 35 | // If the $middle element is greater than the $value 36 | if ($arr[$mid] > $value) { 37 | return binarySearch($arr, $value, $floor, $mid - 1); 38 | } else { // search the right part of the $array If the $middle element is lower than the $value 39 | return binarySearch($arr, $value, $mid + 1, $ceiling); 40 | } 41 | } 42 | 43 | /** 44 | * @param Array $arr 45 | * @param int $value 46 | * @return int 47 | */ 48 | function exponentialSearch($arr, $value) 49 | { 50 | // If $value is the first element of the $array return this position 51 | if ($arr[0] === $value) { 52 | return 0; 53 | } 54 | 55 | // Find range for binary search 56 | $i = 1; 57 | $length = count($arr); 58 | while ($i < $length && $arr[$i] <= $value) { 59 | $i = $i * 2; 60 | } 61 | $floor = $i / 2; 62 | $ceiling = min($i, $length); 63 | 64 | // Call binary search for the range found above 65 | return binarySearch($arr, $value, $floor, $ceiling); 66 | } 67 | -------------------------------------------------------------------------------- /Searches/FibonacciSearch.php: -------------------------------------------------------------------------------- 1 | key decrease the high index 17 | * repeat the loop 18 | */ 19 | function interpolationSearch($arr, $key) 20 | { 21 | $length = count($arr) - 1; 22 | $low = 0; 23 | $high = $length; 24 | $position = -1; 25 | //loop, between low & high 26 | while ($low <= $high && $key >= $arr[$low] && $key <= $arr[$high]) { 27 | //GET INDEX 28 | $delta = ($key - $arr[$low]) / ($arr[$high] - $arr[$low]); 29 | $index = $low + floor(($high - $low) * $delta); 30 | 31 | //GET VALUE OF INDEX IN ARRAY... 32 | $indexValue = $arr[$index]; 33 | 34 | if ($indexValue === $key) { 35 | //index value equals key 36 | //FOUND TARGET 37 | //return index value 38 | $position = $index; 39 | return (int) $position; 40 | } elseif ($indexValue < $key) { 41 | //index value lower than key 42 | //increase low index 43 | $low = $index + 1; 44 | } elseif ($indexValue > $key) { 45 | //index value higher than key 46 | //decrease high index 47 | $high = $index - 1; 48 | } 49 | } 50 | 51 | //when key not found in array or array not sorted 52 | return null; 53 | } 54 | -------------------------------------------------------------------------------- /Searches/JumpSearch.php: -------------------------------------------------------------------------------- 1 | = $num) { 26 | return -1; 27 | } 28 | } 29 | 30 | /*Performing linear search for $key in block*/ 31 | while ($list[$prev] < $key) { 32 | $prev++; 33 | if ($prev == min($step, $num)) { 34 | return -1; 35 | } 36 | } 37 | 38 | return $list[$prev] === $key ? $prev : -1; 39 | } 40 | -------------------------------------------------------------------------------- /Searches/LinearSearch.php: -------------------------------------------------------------------------------- 1 | $arr[$mid2]) { 36 | // Key is in the right section, between $mid2 and $high. 37 | return ternarySearchByRecursion($arr, $key, $mid2 + 1, $high); 38 | } else { 39 | // Key is in the middle section, between $mid1 and $mid2. 40 | return ternarySearchByRecursion($arr, $key, $mid1 + 1, $mid2 - 1); 41 | } 42 | } 43 | 44 | function ternarySearchIterative($arr, $key) 45 | { 46 | // Initialize low and high pointers. 47 | $low = 0; 48 | $high = count($arr) - 1; 49 | 50 | // Continue searching while the high pointer is greater than or equal to the low pointer. 51 | while ($high >= $low) { 52 | // Calculate the first and second midpoints. 53 | $mid1 = floor($low + ($high - $low) / 3); 54 | $mid2 = floor($high - ($high - $low) / 3); 55 | 56 | // Check if the key is found at either midpoint. 57 | if ($arr[$mid1] === $key) { 58 | return $mid1; 59 | } 60 | 61 | if ($arr[$mid2] === $key) { 62 | return $mid2; 63 | } 64 | 65 | // Determine the section to continue the search in. 66 | if ($key < $arr[$mid1]) { 67 | // Key is in the left section, update the high pointer. 68 | $high = $mid1 - 1; 69 | } elseif ($key > $arr[$mid2]) { 70 | // Key is in the right section, update the low pointer. 71 | $low = $mid2 + 1; 72 | } else { 73 | // Key is in the middle section, update both pointers. 74 | $low = $mid1 + 1; 75 | $high = $mid2 - 1; 76 | } 77 | } 78 | 79 | // Key was not found. 80 | return null; 81 | } 82 | -------------------------------------------------------------------------------- /Searches/TwoPointers.php: -------------------------------------------------------------------------------- 1 | $target) { 50 | // the sum is greater than the target, so we need to decrease the sum 51 | // to decrease the sum we will move our $right pointer to the left 52 | $right--; 53 | } else if ($list[$left] + $list[$right] == $target) { 54 | // if it's true, we have found a pair 55 | $ans++; 56 | // now we will move one of our pointers, otherwise it'll run forever 57 | $left++; # doesn't matter which one we move 58 | } 59 | // The loop will go until the pointers point to the same element 60 | } 61 | return $ans; # returning the number of pairs 62 | } 63 | -------------------------------------------------------------------------------- /Searches/UpperBound.php: -------------------------------------------------------------------------------- 1 | $key) || !isset($b->$key)) { 39 | $errorMsg = 'The key "' . $key 40 | . '" does not exist in the collection'; 41 | throw new Exception($errorMsg); 42 | } 43 | $item1 = !$isCaseSensitive 44 | ? strtolower($a->$key) : $a->$key; 45 | $item2 = !$isCaseSensitive 46 | ? strtolower($b->$key) : $b->$key; 47 | } 48 | } while ($item1 === $item2 && !empty($keys[++$pos])); 49 | if ($item1 === $item2) { 50 | return 0; 51 | } elseif ($order === self::ORDER_ASC) { 52 | return ($item1 < $item2) ? -1 : 1; 53 | } else { 54 | return ($item1 > $item2) ? -1 : 1; 55 | } 56 | }); 57 | } catch (Exception $e) { 58 | echo $e->getMessage(); 59 | die(); 60 | } 61 | } 62 | 63 | return $collection; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sorting/BubbleSort.php: -------------------------------------------------------------------------------- 1 | 0; $i--) { 14 | $swapped = true; 15 | 16 | for ($j = 0; $j < $i - 1; $j++) { 17 | if ($array[$j] > $array[$j + 1]) { 18 | $temp = $array[$j]; 19 | $array[$j] = $array[$j + 1]; 20 | $array[$j + 1] = $temp; 21 | $swapped = false; 22 | } 23 | } 24 | 25 | if ($swapped) { 26 | break; 27 | } 28 | } 29 | 30 | return $array; 31 | } 32 | -------------------------------------------------------------------------------- /Sorting/BubbleSort2.php: -------------------------------------------------------------------------------- 1 | 0) { 25 | $array[$z++] = $i; 26 | } 27 | } 28 | 29 | return $array; 30 | } 31 | -------------------------------------------------------------------------------- /Sorting/GnomeSort.php: -------------------------------------------------------------------------------- 1 | = 0; $i--) { 26 | heapify($arr, $n, $i); 27 | } 28 | 29 | // Extract elements from the max heap and build the sorted array. 30 | for ($i = $n - 1; $i >= 0; $i--) { 31 | // Swap the root(maximum value) of the heap with the last element of the heap. 32 | [$arr[0], $arr[$i]] = [$arr[$i], $arr[0]]; 33 | 34 | // Heapify the reduced heap. 35 | heapify($arr, $i, 0); 36 | } 37 | 38 | // Return the sorted array. 39 | return $arr; 40 | } 41 | 42 | /** 43 | * Ensures that the array satisfies the heap property 44 | * 45 | * @param array $arr 46 | * @param int $n 47 | * @param int $i 48 | */ 49 | function heapify(array &$arr, int $n, int $i): void 50 | { 51 | $largest = $i; 52 | $left = 2 * $i + 1; 53 | $right = 2 * $i + 2; 54 | if ($left < $n && $arr[$left] > $arr[$largest]) { 55 | $largest = $left; 56 | } 57 | 58 | if ($right < $n && $arr[$right] > $arr[$largest]) { 59 | $largest = $right; 60 | } 61 | 62 | if ($largest !== $i) { 63 | [$arr[$i], $arr[$largest]] = [$arr[$largest], $arr[$i]]; 64 | heapify($arr, $n, $largest); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sorting/InsertionSort.php: -------------------------------------------------------------------------------- 1 | = 0 && $array[$j] > $currentVal; $j--) { 15 | $array[$j + 1] = $array[$j]; 16 | } 17 | 18 | $array[$j + 1] = $currentVal; 19 | } 20 | 21 | return $array; 22 | } 23 | -------------------------------------------------------------------------------- /Sorting/MergeSort.php: -------------------------------------------------------------------------------- 1 | $leftArray[$i]) { 35 | $result[] = $leftArray[$i]; 36 | $i++; 37 | } else { 38 | $result[] = $rightArray[$j]; 39 | $j++; 40 | } 41 | } 42 | 43 | while ($i < count($leftArray)) { 44 | $result[] = $leftArray[$i]; 45 | $i++; 46 | } 47 | 48 | while ($j < count($rightArray)) { 49 | $result[] = $rightArray[$j]; 50 | $j++; 51 | } 52 | 53 | return $result; 54 | } 55 | -------------------------------------------------------------------------------- /Sorting/QuickSort.php: -------------------------------------------------------------------------------- 1 | $shift], quickSort($gt)); 30 | } 31 | -------------------------------------------------------------------------------- /Sorting/RadixSort.php: -------------------------------------------------------------------------------- 1 | = $gap && $array[$j - $gap] > $temp) { 25 | $array[$j] = $array[$j - $gap]; 26 | $j -= $gap; 27 | } 28 | 29 | $array[$j] = $temp; 30 | } 31 | } 32 | 33 | return $array; 34 | } 35 | 36 | /** 37 | * Calculate Knuth's series 38 | * 39 | * @param int $n Size of the array 40 | * @return array 41 | */ 42 | function calculateKnuthSeries(int $n): array 43 | { 44 | $h = 1; 45 | $series = []; 46 | 47 | while ($h < $n) { 48 | array_unshift($series, $h); 49 | $h = 3 * $h + 1; 50 | } 51 | 52 | return $series; 53 | } 54 | -------------------------------------------------------------------------------- /Strings/CheckAnagram.php: -------------------------------------------------------------------------------- 1 | = 'a' && $string[$i] <= 'z' 26 | ) { 27 | $consonantCount++; 28 | } 29 | } 30 | 31 | return $consonantCount; 32 | } 33 | -------------------------------------------------------------------------------- /Strings/CountHomogenous.php: -------------------------------------------------------------------------------- 1 | = 0; $i--) { 18 | $reversedCharacters[] = $characters[$i]; 19 | } 20 | 21 | return implode('', $reversedCharacters); 22 | } 23 | -------------------------------------------------------------------------------- /Strings/ReverseWords.php: -------------------------------------------------------------------------------- 1 | = 0; $i--) { 17 | $reversedWords[] = $words[$i]; 18 | } 19 | 20 | return implode(' ', $reversedWords); 21 | } 22 | -------------------------------------------------------------------------------- /Utils/ArrayHelpers.php: -------------------------------------------------------------------------------- 1 | start_time = microtime(true); 17 | } 18 | 19 | public function __destruct() 20 | { 21 | $this->end_time = microtime(true); 22 | $this->execution_time = $this->end_time - $this->start_time; 23 | echo "Executed in $this->execution_time seconds\n"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thealgorithms/php", 3 | "description": "All Algorithms implemented in PHP", 4 | "config": { 5 | "platform": { 6 | "php": "7.4.0" 7 | } 8 | }, 9 | "license": "MIT", 10 | "require": { 11 | "php": "7.4", 12 | "phan/phan": "^2.7", 13 | "ext-json": "*" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^9", 17 | "squizlabs/php_codesniffer": "^3.7" 18 | }, 19 | "scripts": { 20 | "test": "vendor/bin/phpunit tests" 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | . 9 | 10 | vendor 11 | 12 | 13 | 0 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/Ciphers/AtbashCipherTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($plainText, $decryptedText); 16 | } 17 | 18 | public function testWithNonAlphabetCharacters() 19 | { 20 | $plainText = "HELLO, WORLD!"; 21 | $encryptedText = atbash_encrypt($plainText); 22 | $decryptedText = atbash_decrypt($encryptedText); 23 | $this->assertEquals($plainText, $decryptedText); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Ciphers/CiphersTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('Aopz pz h alza.', encrypt('This is a test.', 7)); 14 | $this->assertEquals('Aopz pz h alza.', encrypt('This is a test.', 7 + 26)); 15 | $this->assertEquals('This is a test.', decrypt('Aopz pz h alza.', 7)); 16 | $this->assertEquals('This is a test.', decrypt('Aopz pz h alza.', 7 + 26)); 17 | } 18 | 19 | public function testXorCipher() 20 | { 21 | $input_str = "test@string"; 22 | $key = "test-key"; 23 | $invalid_key = "wrong-key"; 24 | $this->assertEquals($input_str, xorCipher(xorCipher($input_str, $key), $key)); 25 | $this->assertNotEquals($input_str, xorCipher(xorCipher($input_str, $key), $invalid_key)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Ciphers/MonoAlphabeticCipherTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(maEncrypt($key, $alphabet, $text), $encryptedText); 17 | $this->assertEquals(maDecrypt($key, $alphabet, $encryptedText), "I love GitHub"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Ciphers/MorseCodeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('TEST 123', decode(encode('TEST 123'))); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Ciphers/RailfenceCipherTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($plainMessage, $decodedMessage); 17 | } 18 | public function testRailfenceCipherCase2() 19 | { 20 | $plainMessage = "THIS IS RAILFENCE"; 21 | $rails = 3; 22 | $cipherMessage = Railencode($plainMessage, $rails); 23 | $decodedMessage = Raildecode($cipherMessage, $rails); 24 | $this->assertEquals($plainMessage, $decodedMessage); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Ciphers/VignereCipherTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($plaintext, $decryptedText); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/DataStructures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlgorithms/PHP/81b5386af390fd2b44fecf162ade3b1a940c8500/tests/DataStructures/.keep -------------------------------------------------------------------------------- /tests/DataStructures/CompareBinaryTreeTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($sut->areTreesEqual($tree1, $tree2)); 42 | } 43 | 44 | public function testBinaryTreesAreNotEqualWhenAreNotEqualInReality() 45 | { 46 | 47 | $tree1 = new BinaryTreeNode( 48 | 'A', 49 | new BinaryTreeNode( 50 | 'B', 51 | new BinaryTreeNode( 52 | 'F' 53 | ), 54 | new BinaryTreeNode( 55 | 'E', 56 | null, 57 | new BinaryTreeNode( 58 | 'D' 59 | ) 60 | ) 61 | ), 62 | new BinaryTreeNode( 63 | 'C', 64 | new BinaryTreeNode('G') 65 | ) 66 | ); 67 | 68 | $tree2 = new BinaryTreeNode( 69 | 'A', 70 | new BinaryTreeNode( 71 | 'B', 72 | new BinaryTreeNode( 73 | 'F' 74 | ), 75 | new BinaryTreeNode( 76 | 'E', 77 | null, 78 | new BinaryTreeNode( 79 | 'D' 80 | ) 81 | ) 82 | ), 83 | new BinaryTreeNode( 84 | 'C' 85 | ) 86 | ); 87 | 88 | $sut = new CompareBinaryTree(); 89 | $this->assertFalse($sut->areTreesEqual($tree1, $tree2)); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/DataStructures/DisjointSetTest.php: -------------------------------------------------------------------------------- 1 | ds = new DisjointSet(); 28 | $this->nodes = []; 29 | 30 | // Create 20 nodes 31 | for ($i = 0; $i < 20; $i++) { 32 | $this->nodes[$i] = new DisjointSetNode($i); 33 | } 34 | 35 | // Perform union operations to form several disjoint sets 36 | $this->ds->unionSet($this->nodes[0], $this->nodes[1]); 37 | $this->ds->unionSet($this->nodes[1], $this->nodes[2]); 38 | 39 | $this->ds->unionSet($this->nodes[3], $this->nodes[4]); 40 | $this->ds->unionSet($this->nodes[4], $this->nodes[5]); 41 | 42 | $this->ds->unionSet($this->nodes[6], $this->nodes[7]); 43 | $this->ds->unionSet($this->nodes[7], $this->nodes[8]); 44 | 45 | $this->ds->unionSet($this->nodes[9], $this->nodes[10]); 46 | $this->ds->unionSet($this->nodes[10], $this->nodes[11]); 47 | 48 | $this->ds->unionSet($this->nodes[12], $this->nodes[13]); 49 | $this->ds->unionSet($this->nodes[13], $this->nodes[14]); 50 | 51 | $this->ds->unionSet($this->nodes[15], $this->nodes[16]); 52 | $this->ds->unionSet($this->nodes[16], $this->nodes[17]); 53 | 54 | $this->ds->unionSet($this->nodes[18], $this->nodes[19]); 55 | } 56 | 57 | public function testFindSet(): void 58 | { 59 | // Nodes in the same sets should have the same root 60 | for ($i = 0; $i < 6; $i++) { 61 | for ($j = 0; $j < 6; $j++) { 62 | $setI = $this->ds->findSet($this->nodes[$i]); 63 | $setJ = $this->ds->findSet($this->nodes[$j]); 64 | 65 | if ($this->inSameSet($i, $j)) { 66 | $this->assertSame($setI, $setJ, "Nodes $i and $j should be in the same set"); 67 | } else { 68 | $this->assertNotSame($setI, $setJ, "Nodes $i and $j should be in different sets"); 69 | } 70 | } 71 | } 72 | } 73 | 74 | private function inSameSet(int $i, int $j): bool 75 | { 76 | // Define which nodes should be in the same set based on union operations 77 | $sets = [ 78 | [0, 1, 2], // Set A 79 | [3, 4, 5], // Set B 80 | [6, 7, 8], // Set C 81 | [9, 10, 11], // Set D 82 | [12, 13, 14], // Set E 83 | [15, 16, 17], // Set F 84 | [18, 19] // Set G 85 | ]; 86 | 87 | foreach ($sets as $set) { 88 | if (in_array($i, $set) && in_array($j, $set)) { 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/DataStructures/DoublyLinkedListTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(0, $list->length()); 21 | } 22 | 23 | /** 24 | * Test for the append method 25 | */ 26 | public function testAppend() 27 | { 28 | $list = new DoublyLinkedList(); 29 | $list->append(1); 30 | $list->append(2); 31 | $list->append(3); 32 | $this->assertEquals(3, $list->length()); 33 | } 34 | 35 | /** 36 | * Test for the insert method 37 | */ 38 | public function testInsert() 39 | { 40 | $list = new DoublyLinkedList(); 41 | $list->append(1); 42 | $list->append(2); 43 | $list->append(3); 44 | $list->insert(1, 4); 45 | $this->assertEquals(4, $list->length()); 46 | } 47 | 48 | /** 49 | * Test for the delete method 50 | */ 51 | public function testDelete() 52 | { 53 | $list = new DoublyLinkedList(); 54 | $list->append(1); 55 | $list->append(2); 56 | $list->append(3); 57 | $list->delete(1); 58 | $this->assertEquals(2, $list->length()); 59 | } 60 | 61 | /** 62 | * Test for the deleteAt method 63 | */ 64 | public function testDeleteAt() 65 | { 66 | $list = new DoublyLinkedList(); 67 | $list->append(1); 68 | $list->append(2); 69 | $list->append(3); 70 | $list->deleteAt(1); 71 | $this->assertEquals(2, $list->length()); 72 | } 73 | 74 | /** 75 | * Test for printList method 76 | */ 77 | public function testPrintList() 78 | { 79 | $list = new DoublyLinkedList(); 80 | $list->append(1); 81 | $list->append(2); 82 | $list->append(3); 83 | $this->expectOutputString("1\n2\n3\n"); 84 | $list->printList(); 85 | } 86 | 87 | /** 88 | * Test for the printListReverse method 89 | */ 90 | public function testPrintListReverse() 91 | { 92 | $list = new DoublyLinkedList(); 93 | $list->append(1); 94 | $list->append(2); 95 | $list->append(3); 96 | $this->expectOutputString("3\n2\n1\n"); 97 | $list->printListReverse(); 98 | } 99 | 100 | /** 101 | * Test for the reverse method 102 | */ 103 | public function testReverse() 104 | { 105 | $list = new DoublyLinkedList(); 106 | $list->append(1); 107 | $list->append(2); 108 | $list->append(3); 109 | $list->reverse(); 110 | $this->expectOutputString("3\n2\n1\n"); 111 | $list->printList(); 112 | } 113 | 114 | /** 115 | * Test for the length method 116 | */ 117 | public function testLength() 118 | { 119 | $list = new DoublyLinkedList(); 120 | $list->append(1); 121 | $list->append(2); 122 | $list->append(3); 123 | $this->assertEquals(3, $list->length()); 124 | } 125 | /** 126 | * Test for the Search method 127 | */ 128 | public function testSearch() 129 | { 130 | $list = new DoublyLinkedList(); 131 | $list->append(1); 132 | $list->append(2); 133 | $list->append(3); 134 | $searchItem = $list->search(2); 135 | $this->assertEquals(2, $searchItem->data); 136 | } 137 | 138 | /** 139 | * Test for the isEmpty method 140 | */ 141 | public function testIsEmpty() 142 | { 143 | $list = new DoublyLinkedList(); 144 | $this->assertEquals(true, $list->isEmpty()); 145 | } 146 | 147 | /** 148 | * Test for __toString method 149 | */ 150 | public function testToString() 151 | { 152 | $list = new DoublyLinkedList(); 153 | $list->append(1); 154 | $list->append(2); 155 | $list->append(3); 156 | $this->expectOutputString("1, 2, 3"); 157 | echo $list; 158 | } 159 | 160 | /** 161 | * Test for the toArray method 162 | */ 163 | public function testToArray() 164 | { 165 | $list = new DoublyLinkedList(); 166 | $list->append(1); 167 | $list->append(2); 168 | $list->append(3); 169 | $this->assertEquals([1,2,3], $list->toArray()); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tests/DataStructures/InvertBinaryTreeTest.php: -------------------------------------------------------------------------------- 1 | setValue(1); 18 | $bl = (new BinaryTree())->setValue(3); 19 | $b->setLeft($bl); 20 | $br = (new BinaryTree())->setValue(2); 21 | $b->setRight($br); 22 | $br->setLeft((new BinaryTree())->setValue(4)); 23 | $br->setRight((new BinaryTree())->setValue(5)); 24 | 25 | $expected = (new BinaryTree())->setValue(1); 26 | $expectedBr = (new BinaryTree())->setValue(3); 27 | $expected->setRight($expectedBr); 28 | $expectedBl = (new BinaryTree())->setValue(2); 29 | $expected->setLeft($expectedBl); 30 | $expectedBl->setRight((new BinaryTree())->setValue(4)); 31 | $expectedBl->setLeft((new BinaryTree())->setValue(5)); 32 | 33 | (new InvertBinaryTree())->invert($b); 34 | 35 | $this->assertEquals($expected, $b); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/DataStructures/QueueTest.php: -------------------------------------------------------------------------------- 1 | enqueue(1); 17 | $queue->enqueue(2); 18 | $queue->enqueue(3); 19 | 20 | $this->assertEquals(3, $queue->size()); 21 | $this->assertEquals(1, $queue->peek()); 22 | } 23 | 24 | /** 25 | * @test 26 | */ 27 | public function shouldRemoveElementFromQueue(): void 28 | { 29 | $queue = new Queue(); 30 | $queue->enqueue(1); 31 | $queue->enqueue(2); 32 | $queue->enqueue(3); 33 | 34 | $this->assertEquals(1, $queue->dequeue()); 35 | $this->assertEquals(2, $queue->peek()); 36 | $this->assertEquals(2, $queue->size()); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function shouldReturnCorrectValueWhenDequeue(): void 43 | { 44 | $queue = new Queue(); 45 | $queue->enqueue(1); 46 | $queue->enqueue(2); 47 | $queue->enqueue(3); 48 | 49 | $this->assertEquals(1, $queue->dequeue()); 50 | $this->assertEquals(2, $queue->dequeue()); 51 | $this->assertEquals(3, $queue->dequeue()); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function shouldReturnNullWhenDequeueEmptyQueue(): void 58 | { 59 | $queue = new Queue(); 60 | 61 | $this->assertNull($queue->dequeue()); 62 | } 63 | 64 | /** 65 | * @test 66 | */ 67 | public function shouldReturnTrueIfQueueIsEmpty(): void 68 | { 69 | $queue = new Queue(); 70 | 71 | $this->assertTrue($queue->isEmpty()); 72 | } 73 | 74 | /** 75 | * @test 76 | */ 77 | public function shouldReturnFalseIfQueueIsNotEmpty(): void 78 | { 79 | $queue = new Queue(); 80 | $queue->enqueue(1); 81 | 82 | $this->assertFalse($queue->isEmpty()); 83 | } 84 | 85 | /** 86 | * @test 87 | */ 88 | public function shouldReturnQueueSize(): void 89 | { 90 | $queue = new Queue(); 91 | $queue->enqueue(1); 92 | $queue->enqueue(2); 93 | $queue->enqueue(3); 94 | 95 | $this->assertEquals(3, $queue->size()); 96 | $this->assertEquals(1, $queue->peek()); 97 | } 98 | 99 | /** 100 | * @test 101 | */ 102 | public function shouldReturnFrontElement(): void 103 | { 104 | $queue = new Queue(); 105 | $queue->enqueue(1); 106 | $queue->enqueue(2); 107 | $queue->enqueue(3); 108 | 109 | $this->assertEquals(1, $queue->peek()); 110 | $this->assertEquals(3, $queue->size()); 111 | } 112 | 113 | /** 114 | * @test 115 | */ 116 | public function shouldReturnNullWhenPeekEmptyQueue(): void 117 | { 118 | $queue = new Queue(); 119 | 120 | $this->assertNull($queue->peek()); 121 | } 122 | 123 | /** 124 | * @test 125 | */ 126 | public function shouldClearQueue(): void 127 | { 128 | $queue = new Queue(); 129 | $queue->enqueue(1); 130 | $queue->enqueue(2); 131 | $queue->enqueue(3); 132 | 133 | $queue->clear(); 134 | 135 | $this->assertTrue($queue->isEmpty()); 136 | $this->assertEquals(0, $queue->size()); 137 | $this->assertNull($queue->peek()); 138 | } 139 | 140 | /** 141 | * @test 142 | */ 143 | public function shouldReturnStringRepresentation(): void 144 | { 145 | $queue = new Queue(); 146 | $queue->enqueue(1); 147 | $queue->enqueue(2); 148 | $queue->enqueue(3); 149 | 150 | $this->assertIsString($queue->toString()); 151 | $this->assertEquals("1, 2, 3", $queue->toString(', ')); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tests/DataStructures/ReverseLinkedListTest.php: -------------------------------------------------------------------------------- 1 | setValue(0); 21 | 22 | $prevItem = $firstItem; 23 | 24 | foreach ($list as $value) { 25 | $item = new LinkedListItem(); 26 | $item->setValue($value); 27 | $item->setPrev($prevItem); 28 | $prevItem->setNext($item); 29 | $prevItem = $item; 30 | } 31 | 32 | $newFirstItem = (new ReverseLinkedList())->reverse($firstItem); 33 | do { 34 | $this->assertEquals($newFirstItem->getValue(), array_pop($list)); 35 | } while ($newFirstItem = $newFirstItem->getNext()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/DataStructures/SinglyLinkedListTest.php: -------------------------------------------------------------------------------- 1 | append($string[$i]); 22 | } 23 | 24 | return $node; 25 | } 26 | 27 | /** 28 | * Provider of SinglyLinkedList nodes 29 | */ 30 | public function provideNodes() 31 | { 32 | return [ 33 | 'IsPalindrome' => [ 34 | 'node' => $this->createNode('hannah'), 35 | 'expected' => true 36 | ], 37 | 'IsPalindrome2' => [ 38 | 'node' => $this->createNode('hanah'), 39 | 'expected' => true 40 | ], 41 | 'IsNotPalindrome' => [ 42 | 'node' => $this->createNode('hanbah'), 43 | 'expected' => false 44 | ], 45 | 'IsNotPalindrome2' => [ 46 | 'node' => $this->createNode('han1h'), 47 | 'expected' => false 48 | ], 49 | ]; 50 | } 51 | 52 | /** 53 | * Test the if a SinglyLinkedList is a palindrome 54 | * 55 | * @dataProvider provideNodes 56 | */ 57 | public function testIsPalindrome($node, $expected): void 58 | { 59 | $this->assertEquals($expected, $this->isPalindrome($node)); 60 | } 61 | 62 | /** 63 | * Supporting method for testing if a linked list is palindrome 64 | */ 65 | private function isPalindrome(SinglyLinkedList $node): bool 66 | { 67 | $length = $this->length($node); 68 | $pairs = 0; 69 | $curr = $node; 70 | 71 | while ($curr instanceof SinglyLinkedList) { 72 | if ($this->hasPair($curr->next, $curr->data)) { 73 | $pairs++; 74 | } 75 | 76 | $curr = $curr->next; 77 | } 78 | 79 | return $pairs == floor($length / 2); 80 | } 81 | 82 | /** 83 | * Supporting method for testing if a linked list is palindrome 84 | */ 85 | private function hasPair($node, string $data): bool 86 | { 87 | if (! $node instanceof SinglyLinkedList) { 88 | return false; 89 | } 90 | 91 | $curr = $node; 92 | 93 | while ($curr instanceof SinglyLinkedList) { 94 | if ($curr->data == $data) { 95 | return true; 96 | } 97 | 98 | $curr = $curr->next; 99 | } 100 | 101 | return false; 102 | } 103 | 104 | /** 105 | * Supporting method for testing if a linked list is palindrome 106 | */ 107 | private function length(SinglyLinkedList $node): int 108 | { 109 | $curr = $node; 110 | $counter = 0; 111 | 112 | while ($curr instanceof SinglyLinkedList) { 113 | $counter++; 114 | $curr = $curr->next; 115 | } 116 | 117 | return $counter; 118 | } 119 | 120 | /** 121 | * Test SinglyLinkedList's delete functionality 122 | */ 123 | public function testDelete(): void 124 | { 125 | $this->assertEquals( 126 | $this->createNode('hanah'), // expected node 127 | $this->createNode('hannah')->delete('n'), // actual node 128 | ); 129 | 130 | $this->assertNotEquals( 131 | $this->createNode('hanah'), // expected node 132 | $this->createNode('hannah')->delete('h'), // actual node 133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /tests/DataStructures/StackTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([1, 2, 3], $stack->toArray()); 14 | } 15 | 16 | public function testDestruct() 17 | { 18 | $stack = new Stack([1, 2, 3]); 19 | unset($stack); 20 | $this->expectNotToPerformAssertions(); 21 | } 22 | 23 | public function testPush() 24 | { 25 | $stack = new Stack(); 26 | $stack->push(1); 27 | $stack->push(2); 28 | $stack->push(3); 29 | $this->assertEquals([1, 2, 3], $stack->toArray()); 30 | } 31 | 32 | public function testPop() 33 | { 34 | $stack = new Stack([1, 2, 3]); 35 | $this->assertEquals(3, $stack->pop()); 36 | $this->assertEquals([1, 2], $stack->toArray()); 37 | } 38 | 39 | public function testPeek() 40 | { 41 | $stack = new Stack([1, 2, 3]); 42 | $this->assertEquals(3, $stack->peek()); 43 | } 44 | 45 | public function testIsEmpty() 46 | { 47 | $stack = new Stack(); 48 | $this->assertTrue($stack->isEmpty()); 49 | } 50 | 51 | public function testPrint() 52 | { 53 | $stack = new Stack([1, 2, 3]); 54 | $this->expectOutputString("1, 2, 3"); 55 | $stack->print(); 56 | } 57 | 58 | public function testToString() 59 | { 60 | $stack = new Stack([1, 2, 3]); 61 | $this->expectOutputString("1, 2, 3"); 62 | echo $stack; 63 | } 64 | 65 | public function testLength() 66 | { 67 | $stack = new Stack([1, 2, 3]); 68 | $this->assertEquals(3, $stack->length()); 69 | } 70 | 71 | public function testClear() 72 | { 73 | $stack = new Stack([1, 2, 3]); 74 | $stack->clear(); 75 | $this->assertEquals([], $stack->toArray()); 76 | } 77 | 78 | public function testSearch() 79 | { 80 | $stack = new Stack([1, 2, 3]); 81 | $this->assertEquals(2, $stack->search(3)); 82 | } 83 | 84 | public function testToArray() 85 | { 86 | $stack = new Stack([1, 2, 3]); 87 | $this->assertEquals([1, 2, 3], $stack->toArray()); 88 | } 89 | 90 | public function testFromArray() 91 | { 92 | $stack = new Stack(); 93 | $stack->fromArray([1, 2, 3]); 94 | $this->assertEquals([1, 2, 3], $stack->toArray()); 95 | } 96 | 97 | public function testReverse() 98 | { 99 | $stack = new Stack([1, 2, 3]); 100 | $stack->reverse(); 101 | $this->assertEquals([3, 2, 1], $stack->toArray()); 102 | } 103 | 104 | public function testSort() 105 | { 106 | $stack = new Stack([3, 2, 1]); 107 | $stack->sort(); 108 | $this->assertEquals([1, 2, 3], $stack->toArray()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/Graphs/BellmanFordTest.php: -------------------------------------------------------------------------------- 1 | start = $edgeRaw[0]; 30 | $edge->end = $edgeRaw[2]; 31 | $edge->weight = $edgeRaw[1]; 32 | if (!isset($edges[$edgeRaw[0]])) { 33 | $edges[$edgeRaw[0]] = []; 34 | } 35 | $edges[$edgeRaw[0]][] = $edge; 36 | } 37 | 38 | $result = bellmanFord($vertices, $edges, 'S'); 39 | 40 | $this->assertEquals( 41 | [ 42 | 'S' => 0, 43 | 'A' => 5, 44 | 'B' => 5, 45 | 'C' => 7, 46 | 'D' => 9, 47 | 'E' => 8 48 | ], 49 | $result 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Graphs/BreadthFirstSearchTest.php: -------------------------------------------------------------------------------- 1 | ["B", "C", "D"], 14 | "B" => ["A", "D", "E"], 15 | "C" => ["A", "F"], 16 | "D" => ["B", "D"], 17 | "E" => ["B", "F"], 18 | "F" => ["C", "E", "G"], 19 | "G" => ["F"], 20 | ); 21 | $ans = bfs($graph, "A", "F"); 22 | $this->assertTrue($ans); 23 | } 24 | public function testBreadthFirstSearch2() 25 | { 26 | $graph = array( 27 | [1, 2], 28 | [2], 29 | [0, 3], 30 | [3], 31 | ); 32 | 33 | $ans2 = bfs($graph, 3, 0); 34 | $this->assertFalse($ans2); 35 | } 36 | public function testBreadthFirstSearch3() 37 | { 38 | $graph = array( 39 | [2, 3, 1], 40 | [0], 41 | [0, 4], 42 | [0], 43 | [2], 44 | ); 45 | $ans = bfs($graph, 0, 3); 46 | $this->assertTrue($ans); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Graphs/DepthFirstSearchTest.php: -------------------------------------------------------------------------------- 1 | ["B", "C", "D"], 14 | "B" => ["A", "D", "E"], 15 | "C" => ["A", "F"], 16 | "D" => ["B", "D"], 17 | "E" => ["B", "F"], 18 | "F" => ["C", "E", "G"], 19 | "G" => ["F"], 20 | ); 21 | $visited = dfs($graph, "A"); 22 | $this->assertEquals(["A", "B", "D", "E", "F", "C", "G"], $visited); 23 | } 24 | public function testDepthFirstSearch2() 25 | { 26 | $graph = array( 27 | [1, 2], 28 | [2], 29 | [0, 3], 30 | [3], 31 | ); 32 | 33 | $visited = dfs($graph, 2); 34 | $this->assertEquals([2, 0, 1, 3], $visited); 35 | } 36 | public function testDepthFirstSearch3() 37 | { 38 | $graph = array( 39 | [2, 3, 1], 40 | [0], 41 | [0, 4], 42 | [0], 43 | [2], 44 | ); 45 | $visited = dfs($graph, 0); 46 | $this->assertEquals([0, 2, 4, 3, 1], $visited); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Graphs/DijkstrasTest.php: -------------------------------------------------------------------------------- 1 | start = $edgeRaw[0]; 33 | $edge->end = $edgeRaw[2]; 34 | $edge->weight = $edgeRaw[1]; 35 | $edges[] = $edge; 36 | } 37 | 38 | $result = dijkstras($vertices, $edges, 'S'); 39 | 40 | $this->assertEquals( 41 | [ 42 | 'S' => 0, 43 | 'A' => 5, 44 | 'B' => 5, 45 | 'C' => 7, 46 | 'D' => 9, 47 | 'E' => 8 48 | ], 49 | $result 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Maths/EratosthenesSieveTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($result, [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Maths/ProjectEulerTest.php: -------------------------------------------------------------------------------- 1 | assertSame(233168, problem1a()); 23 | $this->assertSame(233168, problem1b()); 24 | } 25 | 26 | public function testProblem2(): void 27 | { 28 | $this->assertSame(4613732, problem2()); 29 | } 30 | 31 | public function testProblem3(): void 32 | { 33 | $this->assertSame(6857, problem3()); 34 | } 35 | 36 | public function testProblem4(): void 37 | { 38 | $this->assertSame(906609, problem4()); 39 | } 40 | 41 | public function testProblem5(): void 42 | { 43 | $this->assertSame(232792560, problem5()); 44 | } 45 | 46 | public function testProblem6(): void 47 | { 48 | $this->assertSame(25164150, problem6()); 49 | } 50 | 51 | public function testProblem7(): void 52 | { 53 | $this->assertSame(104743, problem7()); 54 | } 55 | 56 | public function testProblem8(): void 57 | { 58 | $this->assertSame(23514624000, problem8()); 59 | } 60 | 61 | public function testProblem9(): void 62 | { 63 | $this->assertSame(31875000, problem9()); 64 | } 65 | 66 | public function testProblem10(): void 67 | { 68 | $this->assertSame(142913828922, problem10()); 69 | } 70 | 71 | public function testProblem11(): void 72 | { 73 | $this->assertSame(70600674, problem11()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/NeuralNetworks/PerceptronClassifier/NeuralNetworkPerceptronClassifierTest.php: -------------------------------------------------------------------------------- 1 | generateTrainingSet(); 16 | // Train the model 17 | [$W, $b] = $nnClassifier->trainModel($X, $Y, 1000, 0.1); 18 | 19 | // Make predictions 20 | $predictions = $nnClassifier->predict([[0, 0, 1, 1], [0, 1, 1, 0]], $W, $b); 21 | $this->assertEquals([0, 0, 0, 1], $predictions); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Sorting/ArrayKeysSortTest.php: -------------------------------------------------------------------------------- 1 | 'banana', 'color' => 'yellow', 'cant' => 5], 15 | ['fruit' => 'apple', 'color' => 'red', 'cant' => 2], 16 | ['fruit' => 'apple', 'color' => 'green', 'cant' => 7], 17 | ['fruit' => 'grapes', 'color' => 'purple', 'cant' => 4], 18 | ]; 19 | 20 | $result1 = [ 21 | ['fruit' => 'apple', 'color' => 'red', 'cant' => 2], 22 | ['fruit' => 'grapes', 'color' => 'purple', 'cant' => 4], 23 | ['fruit' => 'banana', 'color' => 'yellow', 'cant' => 5], 24 | ['fruit' => 'apple', 'color' => 'green', 'cant' => 7], 25 | ]; 26 | $result2 = [ 27 | ['fruit' => 'apple', 'color' => 'green', 'cant' => 7], 28 | ['fruit' => 'apple', 'color' => 'red', 'cant' => 2], 29 | ['fruit' => 'banana', 'color' => 'yellow', 'cant' => 5], 30 | ['fruit' => 'grapes', 'color' => 'purple', 'cant' => 4], 31 | ]; 32 | 33 | $test1 = arrayKeysSort::sort($array1, ['cant']); 34 | $test2 = arrayKeysSort::sort($array1, ['fruit', 'color']); 35 | 36 | $this->assertEquals($result1, $test1); 37 | $this->assertEquals($result2, $test2); 38 | 39 | //test array of objects 40 | $object1 = new \stdClass(); 41 | $object1->fruit = 'banana'; 42 | $object1->color = 'yellow'; 43 | $object1->cant = 5; 44 | 45 | $object2 = new \stdClass(); 46 | $object2->fruit = 'apple'; 47 | $object2->color = 'red'; 48 | $object2->cant = 2; 49 | 50 | $object3 = new \stdClass(); 51 | $object3->fruit = 'apple'; 52 | $object3->color = 'green'; 53 | $object3->cant = 7; 54 | 55 | $object4 = new \stdClass(); 56 | $object4->fruit = 'grapes'; 57 | $object4->color = 'purple'; 58 | $object4->cant = 4; 59 | 60 | $array2 = [$object1, $object2, $object3, $object4]; 61 | 62 | $result3 = [$object2, $object4, $object1, $object3]; 63 | $result4 = [$object3, $object2, $object1, $object4]; 64 | 65 | $test3 = arrayKeysSort::sort($array2, ['cant']); 66 | $test4 = arrayKeysSort::sort($array2, ['fruit', 'color']); 67 | 68 | $this->assertEquals($result3, $test3); 69 | $this->assertEquals($result4, $test4); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Sorting/GnomeSortTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([-1, 0, 1, 2, 3, 4, 5], $sorted); 15 | } 16 | 17 | public function testGnomeSort2() 18 | { 19 | $array = array(8, -50, 1, 90, 35, 47, 69, -5); 20 | $sorted = gnomeSort($array); 21 | $this->assertEquals([-50, -5, 1, 8, 35, 47, 69, 90], $sorted); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Sorting/ShellSortTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([ 39 | 272, 1392, 1689, 1807, 1856, 2246, 2811, 2847, 3720, 3938, 4204, 4337, 4605, 4696, 4813, 5239, 5257, 5520, 40 | 5523, 5676, 6466, 6506, 7018, 7515, 7762, 7853, 7873, 8181, 8350, 8459, 8935, 9358, 9508, 9592, 10786, 41 | 10867, 11122, 11754, 12029, 12440, 12973, 13720, 13732, 14098, 14123, 14202, 15003, 15352, 16122, 16387, 42 | 16544, 16677, 16868, 17860, 18158, 18362, 18456, 18492, 18708, 19074, 19713, 19756, 20620, 21165, 21746, 43 | 22192, 22296, 22479, 22874, 23232, 23606, 23954, 24105, 24689, 24714, 24932, 25236, 25385, 25774, 26277, 44 | 26281, 26993, 27230, 27760, 27892, 28229, 28302, 28309, 28493, 28651, 29373, 29411, 29724, 30110, 30258, 45 | 30298, 30819, 30948, 31159, 31263, 31577, 31805, 31919, 32074, 32158, 32178, 32392, 33446, 33679, 33963, 46 | 33982, 34201, 34649, 34671, 34925, 35245, 35374, 36141, 36625, 36828, 36852, 37348, 38265, 38386, 39583, 47 | 39621, 40171, 40206, 40372, 40459, 40565, 40742, 40789, 40858, 42310, 42348, 42422, 42685, 43340, 43688, 48 | 43780, 43836, 44044, 44518, 44628, 44637, 44654, 45344, 45519, 45755, 45799, 45894, 46184, 46186, 46528, 49 | 46574, 46704, 47432, 47489, 47596, 47615, 47662, 47910, 48208, 48964, 49048, 49052, 49168, 49709, 49809, 50 | 49935, 50209, 50596, 50800, 51158, 51433, 51830, 51886, 52068, 52328, 52905, 52928, 53635, 54211, 54616, 51 | 54955, 55295, 55416, 55515, 55549, 55908, 56063, 56301, 56494, 56841, 56963, 56992, 57402, 57954, 57981, 52 | 58213, 58223, 58336, 59748, 59973, 59992, 61349, 61591, 61968, 62310, 63015, 63178, 63245, 63395, 63771, 53 | 63968, 64204, 64208, 65764, 66174, 66178, 66206, 66570, 67025, 67338, 67452, 67957, 68125, 68514, 68756, 54 | 68831, 68975, 69091, 69858, 70466, 70474, 71526, 71774, 72472, 72686, 72849, 73437, 73907, 74474, 74813, 55 | 74872, 75043, 75045, 75190, 75197, 75786, 76201, 76277, 76474, 76818, 77026, 77849, 77977, 78095, 78105, 56 | 78533, 78629, 78828, 79028, 79488, 79935, 80188, 81097, 81960, 82300, 82656, 82785, 83246, 83638, 84076, 57 | 84402, 85799, 86070, 86198, 86309, 86358, 86805, 86814, 86897, 86968, 87450, 88246, 90235, 90728, 90876, 58 | 91503, 92106, 92215, 92559, 92837, 93070, 93144, 93729, 94083, 94257, 94706, 94807, 96145, 96407, 96782, 59 | 97156, 97222, 97577, 97799, 98758, 98845, 99156, 99211, 99272, 99519, 99711 60 | ], $sorted); 61 | } 62 | 63 | public function testShellSortWithEmptyInput() 64 | { 65 | $array = []; 66 | $sorted = shellSort($array); 67 | $this->assertEmpty($sorted); 68 | } 69 | 70 | public function testShellSortPerformance() 71 | { 72 | $array = range(1, 10000); 73 | $n = count($array); 74 | shuffle($array); // Randomize the order 75 | $start = microtime(true); 76 | shellSort($array); 77 | $end = microtime(true); 78 | $this->assertLessThan(pow($n, 3 / 2), $end - $start); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/Strings/StringsTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(isPalindrome('MaDam')); // true 24 | $this->assertFalse(isPalindrome('ThisIsTest')); // false 25 | $this->assertFalse(isPalindrome('')); // false 26 | $this->assertTrue(isPalindrome('Sator Arepo Tenet Opera Rotas')); // true 27 | $this->assertFalse(isPalindrome('Sator Arepo Tenet Opera Rotas', false)); // false since we are doing 28 | // case-sensitive check 29 | } 30 | 31 | public function testCountSentences() 32 | { 33 | $this->assertEquals(2, countSentences('Hi! Hello world.')); 34 | $this->assertEquals(2, countSentences('i am testing. test....')); 35 | $this->assertEquals(1, countSentences('How are you?')); 36 | } 37 | 38 | public function testReverseString() 39 | { 40 | $this->assertEquals('txet emos si sihT', reverseString('This is some text')); 41 | $this->assertEquals('mADaM', reverseString('MaDAm')); 42 | $this->assertNotEquals('MaDAm', reverseString('MaDAm')); 43 | } 44 | 45 | public function testReverseWords() 46 | { 47 | $this->assertEquals('Fun is Coding PHP', reverseWords('PHP Coding is Fun')); 48 | $this->assertEquals('OneWord', reverseWords('OneWord')); 49 | $this->assertEquals('Text different some is This', reverseWords('This is some different Text')); 50 | } 51 | 52 | public function testIsAnagram() 53 | { 54 | $this->assertTrue(isAnagram("php", "PHP")); // By default it's case-insensitive 55 | $this->assertFalse(isAnagram("php", "PHP", false)); // Make case-sensitive check 56 | $this->assertTrue(isAnagram("php is fun", "pin hpf us")); 57 | $this->assertFalse(isAnagram("Hello", " Hello")); //Extra space character 58 | $this->assertTrue(isAnagram("ScRamble", "SRlmcaeb", false)); // Check with a mixture of upper and lower case 59 | } 60 | 61 | public function testMaxCharacter() 62 | { 63 | $this->assertEquals('t', maxCharacter("this is test for max character repetition")); 64 | $this->assertEquals('t', maxCharacter("This is Test for max characTer repetition")); 65 | $this->assertEquals(' ', maxCharacter(" ")); 66 | } 67 | 68 | public function testCountVowels() 69 | { 70 | $this->assertEquals(7, countVowelsSimple("This is a string with 7 vowels")); 71 | $this->assertEquals(3, countVowelsSimple("hello world")); 72 | $this->assertEquals(16, countVowelsRegex("Just A list of somE aaaaaaaaaa")); 73 | 74 | $this->assertEquals(7, countVowelsRegex("This is a string with 7 vowels")); 75 | $this->assertEquals(3, countVowelsRegex("hello world")); 76 | $this->assertEquals(16, countVowelsRegex("Just A list of somE aaaaaaaaaa")); 77 | } 78 | 79 | public function testCountConsonants() 80 | { 81 | $this->assertEquals(19, countConsonants("This is a string with 19 consonants")); 82 | $this->assertEquals(7, countConsonants("hello world")); 83 | $this->assertEquals(9, countConsonants("Just A list of somE aaaaaaaaaa")); 84 | } 85 | public function testCountHomogenous() 86 | { 87 | $this->assertEquals(4, countHomogenous("abbcccaa")); 88 | $this->assertEquals(2, countHomogenous("xy")); 89 | } 90 | 91 | public function testFindDistance() 92 | { 93 | $this->assertEquals(1, findDistance("hello", "hallo")); 94 | $this->assertEquals(1, findDistance("hallo", "hello")); 95 | $this->assertEquals(0, findDistance("sunday", "sunday")); 96 | $this->assertEquals(3, findDistance("saturday", "sunday")); 97 | } 98 | } 99 | --------------------------------------------------------------------------------