Repository: mgechev/javascript-algorithms Branch: master Commit: 405279c52e6a Files: 150 Total size: 329.1 KB Directory structure: gitextract_q50stgoz/ ├── .eslintrc.json ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── doc-config.json ├── gulpfile.js ├── package.json ├── readme.md ├── src/ │ ├── combinatorics/ │ │ ├── cartesianproduct.js │ │ ├── combinations.js │ │ ├── permutations.js │ │ └── variations-repetition.js │ ├── compression/ │ │ ├── LZW/ │ │ │ └── LZW.js │ │ ├── burrows-wheeler/ │ │ │ └── burrows-wheeler.js │ │ └── runlength/ │ │ └── runlength.js │ ├── data-structures/ │ │ ├── avl-tree.js │ │ ├── binary-search-tree.js │ │ ├── bloomfilter.js │ │ ├── edge.js │ │ ├── hash-table.js │ │ ├── heap.js │ │ ├── interval-tree.js │ │ ├── linked-list.js │ │ ├── red-black-tree.js │ │ ├── segment-tree.js │ │ ├── size-balanced-tree.js │ │ ├── splay-tree.js │ │ ├── suffix-tree.js │ │ └── vertex.js │ ├── graphics/ │ │ ├── bezier.js │ │ ├── bresenham-line-drawing.js │ │ └── graham.js │ ├── graphs/ │ │ ├── others/ │ │ │ ├── tarjan-connected-components.js │ │ │ └── topological-sort.js │ │ ├── searching/ │ │ │ ├── bfs.js │ │ │ └── dfs.js │ │ ├── shortest-path/ │ │ │ ├── bellman-ford.js │ │ │ ├── dijkstra.js │ │ │ └── floyd-warshall.js │ │ └── spanning-trees/ │ │ ├── kruskal.js │ │ └── prim.js │ ├── others/ │ │ ├── fibonacci.js │ │ ├── fibonacciMemory.js │ │ ├── hanoi.js │ │ ├── levenshtein-distance.js │ │ ├── min-coins-change.js │ │ ├── minimax.js │ │ └── minkowski-distance.js │ ├── primes/ │ │ ├── is-prime.js │ │ ├── prime-factor-tree.js │ │ ├── sieve-of-atkins.js │ │ └── sieve-of-eratosthenes.js │ ├── searching/ │ │ ├── binarysearch.js │ │ ├── interpolation-search.js │ │ ├── jump-search.js │ │ ├── knuth-morris-pratt.js │ │ ├── linearSearch.js │ │ ├── longest-common-subsequence.js │ │ ├── longest-increasing-subsequence.js │ │ ├── maximum-subarray-divide-and-conquer.js │ │ ├── maximum-subarray.js │ │ ├── quickselect.js │ │ └── recursive-binarysearch.js │ ├── sets/ │ │ ├── quickfind.js │ │ ├── quickunion.js │ │ └── weightquickunion.js │ ├── shuffle/ │ │ ├── fisheryates.js │ │ └── richarddurstenfeld.js │ └── sorting/ │ ├── 3-way-string-quicksort.js │ ├── bubblesort.js │ ├── bucketsort.js │ ├── countingsort.js │ ├── heapsort.js │ ├── insertion-binary-sort.js │ ├── insertionsort.js │ ├── lsd.js │ ├── mergesort.js │ ├── msd.js │ ├── oddeven-sort.js │ ├── quicksort-declarative.js │ ├── quicksort-middle.js │ ├── quicksort.js │ ├── radixsort.js │ ├── readme.md │ ├── recursive-insertionsort.js │ ├── selectionsort.js │ └── shellsort.js └── test/ ├── compression/ │ └── burrows-wheeler/ │ └── burrows-wheeler.spec.js ├── data-structures/ │ ├── avl-tree.spec.js │ ├── binary-search-tree.spec.js │ ├── bloomfilter.spec.js │ ├── hash-table.spec.js │ ├── heap.spec.js │ ├── interval-tree.spec.js │ ├── linked-list.spec.js │ ├── red-black-tree.spec.js │ ├── segment-tree.spec.js │ ├── size-balanced-tree.spec.js │ └── splay-tree.spec.js ├── graphics/ │ ├── bezier.spec.js │ └── grapham.spec.js ├── graphs/ │ ├── others/ │ │ ├── tarjan-connected-components.spec.js │ │ └── topological-sort.spec.js │ ├── searching/ │ │ ├── bfs.spec.js │ │ └── dfs.spec.js │ ├── shortest-path/ │ │ ├── bellman-ford.spec.js │ │ └── dijkstra.spec.js │ └── spanning-trees/ │ └── kruskal.spec.js ├── others/ │ ├── fibonacci.spec.js │ ├── fibonacciMemory.spec.js │ ├── levenshtein-distance.spec.js │ ├── min-coins-sum.spec.js │ ├── minimax.spec.js │ └── minkowski-distance.spec.js ├── primes/ │ ├── is-prime.spec.js │ ├── prime-factor-tree.spec.js │ ├── sieve-of-atkins.spec.js │ └── sieve-of-eratosthenes.spec.js ├── searching/ │ ├── binarysearch.spec.js │ ├── interpolation-search.spec.js │ ├── jump-search.spec.js │ ├── knuth-morris-pratt.spec.js │ ├── linearSearch.spec.js │ ├── longest-common-subsequence.spec.js │ ├── longest-increasing-subsequence.spec.js │ ├── maximum-subarray-divide-and-conquer.spec.js │ ├── maximum-subarray.spec.js │ ├── quickselect.spec.js │ └── recursive-binarysearch.spec.js └── sorting/ ├── 3-way-string-quicksort.spec.js ├── bubblesort.spec.js ├── bucketsort.spec.js ├── countingsort.spec.js ├── heapsort.spec.js ├── insertionbinarysort.spec.js ├── insertionsort.spec.js ├── lsd.spec.js ├── mergesort.spec.js ├── msd.spec.js ├── oddeven-sort.spec.js ├── quicksort-declarative.spec.js ├── quicksort-middle.spec.js ├── quicksort.spec.js ├── radixsort.spec.js ├── recursiveinsertionsort.spec.js ├── selectionsort.spec.js ├── shellsort.spec.js └── sort.testcase.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "env": { "browser": true, "jquery": true, "node": true, "es6": true }, "globals": { "expect": true, "it": true, "describe": true, "beforeEach": true, "afterEach": true }, "parserOptions": { "ecmaVersion": 6 }, "rules": { "camelcase": 2, "eqeqeq": 2, "indent": [ 2, 2, { "SwitchCase": 1 } ], "no-use-before-define": [ 2, { "functions": false } ], "no-caller": 2, "new-cap": 0, "quotes": [ 2, "single" ], "no-unused-vars": 2, "strict": [ 2, "function" ], "no-extend-native": 2, "wrap-iife": [ 2, "any" ], "no-empty": 2, "no-plusplus": 2, "no-undef": 2, "linebreak-style": 0, "max-depth": [ 2, 4 ], "no-loop-func": 2, "complexity": [ 2, 13 ], "max-len": [ 2, { "code": 120, "ignoreComments": true } ], "max-params": [ 2, 5 ], "curly": [ 2, "all" ], "keyword-spacing": [ 2, {} ], "one-var": [ 2, "never" ], "array-bracket-spacing": [ 2, "never", {} ], "space-in-parens": [ 2, "never" ], "key-spacing": [ 2, { "beforeColon": false, "afterColon": true } ], "quote-props": [ 2, "as-needed" ], "space-infix-ops": 2, "space-unary-ops": [ 2, { "words": false, "nonwords": false } ], "no-implicit-coercion": [ 2, { "boolean": false, "string": true, "number": true } ], "no-with": 2, "no-multiple-empty-lines": 2, "brace-style": [ 2, "1tbs", { "allowSingleLine": true } ], "eol-last": 2, "no-trailing-spaces": 2 } } ================================================ FILE: .github/workflows/main.yml ================================================ name: Node.js CI on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v3 with: node-version: '17.7.x' - run: yarn --frozen-lockfile - run: npm test ================================================ FILE: .gitignore ================================================ node_modules .vscode npm-debug.log debug dist .idea ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "8" script: npm run build ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at minko@gechev.io. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Minko Gechev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: doc-config.json ================================================ { "tags": { "allowUnknownTags": true }, "source": { "include": [ "./src/combinatorics/", "./src/compression/", "./src/data-structures/", "./src/graphics/", "./src/graphs/others/", "./src/graphs/searching/", "./src/graphs/shortest-path/", "./src/graphs/spanning-trees/", "./src/others/", "./src/primes/", "./src/searching/", "./src/sets/", "./src/shuffle/", "./src/sorting/" ], "includePattern": ".+\\.js(doc)?$", "excludePattern": "docs" }, "plugins": [], "opts": { "template": "node_modules/@jeremyckahn/minami", "encoding": "utf8", "destination": "dist", "recurse": true, "private": false, "readme": "./readme.md" } } ================================================ FILE: gulpfile.js ================================================ const gulp = require('gulp'); const eslint = require('gulp-eslint'); const jasmine = require('gulp-jasmine'); gulp.task('test', () => { 'use strict'; return gulp.src('test/**/*.spec.js') .pipe(jasmine()); }); gulp.task('lint', ()=> { 'use strict'; return gulp.src(['src/**/*.js', 'test/**/*.js']) .pipe(eslint()) .pipe(eslint.format()) .pipe(eslint.failAfterError()); }); gulp.task('build', gulp.parallel(['lint', 'test'])); ================================================ FILE: package.json ================================================ { "name": "javascript-algorithms", "version": "0.0.0", "description": "Implementations of different computer science algorithms", "main": "index.js", "directories": { "test": "test" }, "devDependencies": { "@jeremyckahn/minami": "^1.3.1", "gh-pages": "^1.1.0", "gulp": "^4.0.2", "gulp-eslint": "^3.0.1", "gulp-jasmine": "^2.0.1", "jsdoc": "3.5.5", "live-server": "^1.2.0" }, "scripts": { "build": "gulp build", "test": "gulp test", "deploy": "npm run doc:build && gh-pages -d dist -b gh-pages", "doc": "npm run doc:build && npm run doc:view", "doc:build": "jsdoc -c doc-config.json", "doc:view": "live-server dist --port=9124" }, "repository": { "type": "git", "url": "git://github.com/mgechev/javascript-algorithms" }, "author": "@mgechev", "license": "MIT", "bugs": { "url": "https://github.com/mgechev/javascript-algorithms/issues" }, "homepage": "https://github.com/mgechev/javascript-algorithms" } ================================================ FILE: readme.md ================================================ ## About This repository contains JavaScript implementations of famous computer science algorithms. API reference with usage examples available here. ## Development **To install all dev dependencies** Call: ```bash npm install ``` **To setup repository with documentation** ```bash npm run doc ``` This will build the documentation and open it in your browser. **To update .html files with documentation** Just run `npm run doc` again. **To run tests** Call: ```bash npm run test ``` This will execute all `*.spec.js` files. **To deploy documentation site** ```bash npm run deploy ``` This requires you to have commit access to your Git remote. ## Contributions Fork the repo and make required changes. Afterwards, push your changes in branch. The name will be according to the changes you did. Initiate the pull request. Make sure your editor makes validations according to the `.jshintrc` in the root directory of the repository. Before pushing to the repository, run: ```bash npm run build ``` If the build is not successful, fix your code in order for the tests and jshint validation to run successfully. Then create a pull request. ## Contributors [mgechev](https://github.com/mgechev) |[AndriiHeonia](https://github.com/AndriiHeonia) |[Jakehp](https://github.com/Jakehp) |[lygstate](https://github.com/lygstate) |[mik-laj](https://github.com/mik-laj) |[krzysztof-grzybek](https://github.com/krzysztof-grzybek) | :---: |:---: |:---: |:---: |:---: |:---: | [mgechev](https://github.com/mgechev) |[AndriiHeonia](https://github.com/AndriiHeonia) |[Jakehp](https://github.com/Jakehp) |[lygstate](https://github.com/lygstate) |[mik-laj](https://github.com/mik-laj) |[krzysztof-grzybek](https://github.com/krzysztof-grzybek) | [pvoznenko](https://github.com/pvoznenko) |[jettcalleja](https://github.com/jettcalleja) |[filipefalcaos](https://github.com/filipefalcaos) |[kdamball](https://github.com/kdamball) |[lekkas](https://github.com/lekkas) |[infusion](https://github.com/infusion) | :---: |:---: |:---: |:---: |:---: |:---: | [pvoznenko](https://github.com/pvoznenko) |[jettcalleja](https://github.com/jettcalleja) |[filipefalcaos](https://github.com/filipefalcaos) |[kdamball](https://github.com/kdamball) |[lekkas](https://github.com/lekkas) |[infusion](https://github.com/infusion) | [deniskyashif](https://github.com/deniskyashif) |[brunohadlich](https://github.com/brunohadlich) |[designeng](https://github.com/designeng) |[Microfed](https://github.com/Microfed) |[Nirajkashyap](https://github.com/Nirajkashyap) |[pkerpedjiev](https://github.com/pkerpedjiev) | :---: |:---: |:---: |:---: |:---: |:---: | [deniskyashif](https://github.com/deniskyashif) |[brunohadlich](https://github.com/brunohadlich) |[designeng](https://github.com/designeng) |[Microfed](https://github.com/Microfed) |[Nirajkashyap](https://github.com/Nirajkashyap) |[pkerpedjiev](https://github.com/pkerpedjiev) | [duffman85](https://github.com/duffman85) |[Xuefeng-Zhu](https://github.com/Xuefeng-Zhu) |[emyarod](https://github.com/emyarod) |[alexjoverm](https://github.com/alexjoverm) |[amilajack](https://github.com/amilajack) |[BorislavBorisov22](https://github.com/BorislavBorisov22) | :---: |:---: |:---: |:---: |:---: |:---: | [duffman85](https://github.com/duffman85) |[Xuefeng-Zhu](https://github.com/Xuefeng-Zhu) |[emyarod](https://github.com/emyarod) |[alexjoverm](https://github.com/alexjoverm) |[amilajack](https://github.com/amilajack) |[BorislavBorisov22](https://github.com/BorislavBorisov22) | [brunob15](https://github.com/brunob15) |[BryanChan777](https://github.com/BryanChan777) |[ysharplanguage](https://github.com/ysharplanguage) |[jurassix](https://github.com/jurassix) |[fisenkodv](https://github.com/fisenkodv) |[contra](https://github.com/contra) | :---: |:---: |:---: |:---: |:---: |:---: | [brunob15](https://github.com/brunob15) |[BryanChan777](https://github.com/BryanChan777) |[ysharplanguage](https://github.com/ysharplanguage) |[jurassix](https://github.com/jurassix) |[fisenkodv](https://github.com/fisenkodv) |[contra](https://github.com/contra) | [liesislukas](https://github.com/liesislukas) |[marrcelo](https://github.com/marrcelo) |[wnvko](https://github.com/wnvko) |[millerrach](https://github.com/millerrach) |[xiedezhuo](https://github.com/xiedezhuo) |[DengYiping](https://github.com/DengYiping) | :---: |:---: |:---: |:---: |:---: |:---: | [liesislukas](https://github.com/liesislukas) |[marrcelo](https://github.com/marrcelo) |[wnvko](https://github.com/wnvko) |[millerrach](https://github.com/millerrach) |[xiedezhuo](https://github.com/xiedezhuo) |[DengYiping](https://github.com/DengYiping) | [fanixk](https://github.com/fanixk) |[wlx199x](https://github.com/wlx199x) |[shaunak1111](https://github.com/shaunak1111) | :---: |:---: |:---: | [fanixk](https://github.com/fanixk) |[wlx199x](https://github.com/wlx199x) |[shaunak1111](https://github.com/shaunak1111) | ## License The code in this repository is distributed under the terms of the MIT license. ================================================ FILE: src/combinatorics/cartesianproduct.js ================================================ (function (exports) { 'use strict'; var cartesianProduct = (function () { var result; function cartesianProduct(sets, index, current) { if (index === sets.length) { return result.push(current.slice()); } for (var i = 0; i < sets[index].length; i += 1) { current[index] = sets[index][i]; cartesianProduct(sets, index + 1, current); } } /** * Calculates Cartesian product of provided sets. * * @module combinatorics/cartesianproduct * @public * @param {Array} sets Array of sets. * @return {Array} Cartesian product of provided sets. * * @example * var product = require('path-to-algorithms/src/combinatorics/' + * 'cartesianproduct').cartesianProduct; * var result = product([[1, 2, 3], [3, 2, 1]]); * // [ [ 1, 3 ], * // [ 1, 2 ], * // [ 1, 1 ], * // [ 2, 3 ], * // [ 2, 2 ], * // [ 2, 1 ], * // [ 3, 3 ], * // [ 3, 2 ], * // [ 3, 1 ] ] * console.log(result); */ return function (sets) { result = []; cartesianProduct(sets, 0, []); return result; }; }()); exports.cartesianProduct = cartesianProduct; }((typeof window === 'undefined') ? module.exports : window)); ================================================ FILE: src/combinatorics/combinations.js ================================================ (function (exports) { 'use strict'; var combinations = (function () { var res = []; function combinations(arr, k, start, idx, current) { if (idx === k) { res.push(current.slice()); return; } for (var i = start; i < arr.length; i += 1) { current[idx] = arr[i]; combinations(arr, k, i + 1, idx + 1, current); } } /** * Finds all the combinations of given array.

* A combination is a way of selecting members from a grouping, * such that (unlike permutations) the order of selection does not matter. * For example given three fruits, say an apple, an orange and a pear, * there are three combinations of two that can be drawn from this set: * an apple and a pear; an apple and an orange; or a pear and an orange. * * @example * * var combinations = require('path-to-algorithms/src/' + * 'combinatorics/combinations').combinations; * var result = combinations(['apple', 'orange', 'pear'], 2); * // [['apple', 'orange'], * // ['apple', 'pear'], * // ['orange', 'pear']] * console.log(result); * * @module combinatorics/combinations * @public * @param arr {Array} Set of items. * @param k {Number} Size of each combination. * @return {Array} Returns all combinations. */ return function (arr, k) { res = []; combinations(arr, k, 0, 0, []); var temp = res; // Free the extra memory res = null; return temp; }; }()); exports.combinations = combinations; }((typeof window === 'undefined') ? module.exports : window)); ================================================ FILE: src/combinatorics/permutations.js ================================================ (function (exports) { 'use strict'; var permutations = (function () { var res; function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function permutations(arr, current) { if (current >= arr.length) { return res.push(arr.slice()); } for (var i = current; i < arr.length; i += 1) { swap(arr, i, current); permutations(arr, current + 1); swap(arr, i, current); } } /** * Finds all the permutations of given array.

* Permutation relates to the act of rearranging, or permuting, * all the members of a set into some sequence or order. * For example there are six permutations of the set {1,2,3}, namely: * (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), and (3,2,1).

* Complexity: O(N*N!). * * @example * * var permutations = require('path-to-algorithms/src/' + * 'combinatorics/permutations').permutations; * var result = permutations(['apple', 'orange', 'pear']); * * // [ [ 'apple', 'orange', 'pear' ], * // [ 'apple', 'pear', 'orange' ], * // [ 'orange', 'apple', 'pear' ], * // [ 'orange', 'pear', 'apple' ], * // [ 'pear', 'orange', 'apple' ], * // [ 'pear', 'apple', 'orange' ] ] * console.log(result); * * @module combinatorics/permutations * @public * @param {Array} arr Array to find the permutations of. * @returns {Array} Array containing all the permutations. */ return function (arr) { res = []; permutations(arr, 0); var temp = res; // Free the extra memory res = null; return temp; }; }()); exports.permutations = permutations; }((typeof window === 'undefined') ? module.exports : window)); ================================================ FILE: src/combinatorics/variations-repetition.js ================================================ (function (exports) { 'use strict'; var variationsWithRepetition = (function () { var res; function variations(arr, k, index, current) { if (k === index) { return res.push(current.slice()); } for (var i = 0; i < arr.length; i += 1) { current[index] = arr[i]; variations(arr, k, index + 1, current); } } /** * Finds all the variations with repetition of given array.

* Variations with repetition is the number of ways to sample k elements * from a set of elements (which may be repeated). * * @example * var variations = require('path-to-algorithms/src/combinatorics/' + * 'variations-repetition').variationsWithRepetition; * var result = variations(['apple', 'orange', 'pear'], 2); * * // [['apple', 'apple'], * // ['apple', 'orange'], * // ['apple', 'pear'], * // ['orange', 'apple'], * // ['orange', 'orange'], * // ['orange', 'pear'], * // ['pear', 'apple'], * // ['pear', 'orange'], * // ['pear', 'pear']] * console.log(result); * * @module combinatorics/variations-repetition * @public * @param arr {Array} Set of items. * @param k {Number} Size of each combination. * @return {Array} Returns all combinations. */ return function (arr, k) { res = []; variations(arr, k, 0, []); var temp = res; res = undefined; return temp; }; }()); exports.variationsWithRepetition = variationsWithRepetition; }((typeof window === 'undefined') ? module.exports : window)); ================================================ FILE: src/compression/LZW/LZW.js ================================================ /** * LZW Encoding/Decoding * * Lempel–Ziv–Welch (LZW) is a universal lossless data * compression algorithm. It is an improved implementation * of the LZ78 algorithm. * * @example * var lzwModule = require('path-to-algorithms/src/compression'+ * '/LZW/LZW'); * var lzw = new lzwModule.LZW(); * * var compressed = lzw.compress("ABCABCABCABCABCABC"); * console.log(compressed); * * var decompressed = lzw.decompress(compressed); * console.log(decompressed); * * @module compression/LZW/LZW */ (function (exports) { 'use strict'; exports.LZW = function () { this.dictionarySize = 256; }; exports.LZW.compress = function (data) { var i; var dictionary = {}; var character; var wc; var w = ''; var result = []; for (i = 0; i < this.dictionarySize; i = i + 1) { dictionary[String.fromCharCode(i)] = i; } for (i = 0; i < data.length; i = i + 1) { character = data.charAt(i); wc = w + character; if (dictionary.hasOwnProperty(wc)) { w = wc; } else { result.push(dictionary[w]); dictionary[wc] = this.dictionarySize; this.dictionarySize = this.dictionarySize + 1; w = String(character); } } if (w !== '') { result.push(dictionary[w]); } return result; }; exports.LZW.decompress = function (compressedData) { var i; var dictionary = []; var w; var result; var key; var entry = ''; for (i = 0; i < this.dictionarySize; i = i + 1) { dictionary[i] = String.fromCharCode(i); } w = String.fromCharCode(compressedData[0]); result = w; for (i = 1; i < compressedData.length; i = i + 1) { key = compressedData[i]; if (dictionary[key]) { entry = dictionary[key]; } else { if (key === this.dictionarySize) { entry = w + w.charAt(0); } else { return null; } } result += entry; dictionary[this.dictionarySize] = w + entry.charAt(0); this.dictionarySize = this.dictionarySize + 1; w = entry; } return result; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/compression/burrows-wheeler/burrows-wheeler.js ================================================ (function (exports) { 'use strict'; /** * Burrows Wheeler. * * This algorithm is commonly used as a step in the process of compressing data, * it rearranges a character string into runs of similar characters making algorithms * like move-to-front transform and run-length encoding reach higher compression * rates. This implementation only supports characters with ascii code greater than $(36) as * 36 is used at the process of encode and decode. * * @example * const burrows = require('path-to-algorithms/src/burrows-wheeler/burrows-wheeler').burrowsWheeler; * const s = 'ananabanana'; * const encodedStr = burrows.encode(s); * console.log(encodedStr); * const decodedStr = burrows.decode(encodedStr); * console.log(decodedStr); * * @module compression/burrows-wheeler/burrows-wheeler */ exports.burrowsWheeler = function() { } /** * Consumes n^2 space. */ exports.burrowsWheeler.encode = function(str) { str = '$' + str; var combinations = []; for (let i = 0; i < str.length; i += 1) { combinations.push(str.substring(i) + str.substring(0, i)); } var sorted = combinations.sort(); var result = []; for (let i = 0; i < sorted.length; i += 1) { result.push(combinations[i][str.length - 1]); } return result.join(''); } exports.burrowsWheeler.decode = function(encodedStr) { const sortedCharSequence = encodedStr.split('').sort().join(''); const leftSide = {}; const rightSide = {}; var maxEachCharLeft = {}; var maxEachCharRight = {}; for (let i = 0; i < encodedStr.length; i += 1) { var idLeft = sortedCharSequence[i]; if (idLeft in maxEachCharLeft) { maxEachCharLeft[idLeft] = maxEachCharLeft[idLeft] + 1; } else { maxEachCharLeft[idLeft] = 1; } idLeft += String(maxEachCharLeft[idLeft]); var idRight = encodedStr[i]; if (idRight in maxEachCharRight) { maxEachCharRight[idRight] = maxEachCharRight[idRight] + 1; } else { maxEachCharRight[idRight] = 1; } idRight += String(maxEachCharRight[idRight]); leftSide[idLeft] = {char: sortedCharSequence[i], right: idRight}; rightSide[idRight] = {char: encodedStr[i], left: idRight}; } var result = ''; var firstChar = sortedCharSequence[0]; var searchChar = firstChar + '1'; var endChar = searchChar; while (rightSide[leftSide[searchChar].right].left !== endChar) { result += leftSide[searchChar].char; searchChar = rightSide[leftSide[searchChar].right].left; } result += leftSide[searchChar].char; result = result.substring(1).split('').reverse().join(''); return result; } }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/compression/runlength/runlength.js ================================================ /** * Run-length encoding. * The idea of this algorithm is to remove the usless zeros and * give us representation of string in binary which in which the * zeros will be stripped and replaced with their count. */ (function (exports) { 'use strict'; var runLengthEncoding = (function () { /** * Converts a given string to sequence of numbers * This takes O(n). */ function convertToAscii(str) { var result = []; var currentChar = ''; var i = 0; for (; i < str.length; i += 1) { currentChar = str[i].charCodeAt(0).toString(2); currentChar = new Array(9 - currentChar.length).join('0') + currentChar; result.push(currentChar); } return result.join(''); } /** * Encodes the binary string to run-length encoding. * Takes O(n^2). */ function runLength(vector) { var result = []; var zeros = 0; var zerosTemp = ''; var i = 0; for (; i < vector.length; i += 1) { if (vector[i] === '0') { zeros += 1; } else { zerosTemp = zeros.toString(2); result.push(new Array(zerosTemp.length).join('1')); result.push('0' + zerosTemp); zeros = 0; } } return result.join(''); } /** * Accepts a string and returns it's run-length * encoded binary representation. * Takes O(n^2). */ return function (str) { var asciiString = convertToAscii(str); return runLength(asciiString); }; }()); exports.runLength = runLengthEncoding; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/data-structures/avl-tree.js ================================================ /** * AVL tree, a Binary Search Tree that satisfies the Height-Balance * Property. * * @example * var avlTree = require('path-to-algorithms/src/data-structures'+ * '/avl-tree'); * var avl = new avlTree.AVLTree(); * * avl.insert(2000); * avl.insert(1989); * avl.insert(1991); * avl.insert(2001); * avl.insert(1966); * * @module data-structures/avl-tree */ (function (exports) { 'use strict'; /** * Node of the tree. * * @public * @constructor * @param {Number|String} value Value of the node. * @param {Node} left Left sibling. * @param {Node} right Right sibling. * @param {Node} parent Parent of the node. * @param {Number} height Height of the node. */ exports.Node = function (value, left, right, parent, height) { /** * @member {Number|String} */ this.value = value; this._left = left; this._right = right; this._parent = parent; this._height = height; }; /** * AVL Tree. * * @public * @constructor */ exports.AVLTree = function () { this._root = null; }; /** * Calculates the height of a node based on height * property of children. * * @public * @method * @param {Node} node Given node's height is returned. */ exports.AVLTree.prototype._getHeightAtNode = function (node) { if (node._left !== null && node._right !== null){ var height = Math.max(node._left._height, node._right._height); height += 1; return height; } else if (node._left !== null){ return node._left._height + 1; } else if (node._right !== null){ return node._right._height + 1; } else { return 1; } }; /** * Checks if a given node has an imbalance. * * @public * @method * @param {Node} node Given node's children are checked for * imbalance. */ exports.AVLTree.prototype._isBalancedAtNode = function (node) { if (node._left !== null && node._right !== null){ return (Math.abs(node._left._height - node._right._height) <= 1); } if (node._right !== null && node._left === null){ return node._right._height < 2; } if (node._left !== null && node._right === null){ return node._left._height < 2; } return true; }; /** * Gets the nodes to be restructured during an AVL restructure * after a remove/delete takes place. * * @public * @method * @param {Array} traveledNodes Array of previously traveled nodes * that are used to help determine the nodes to be restructured. */ exports.AVLTree.prototype._getNodesToRestructureRemove = function (traveledNodes) { // z is last traveled node - imbalance found at z var zIndex = traveledNodes.length; zIndex -= 1; var z = traveledNodes[zIndex]; // y should be child of z with larger height // (cannot be ancestor of removed node) var y; if ((z._left !== null && z._right !== null) || (z._left !== null && z._right === null)){ y = z._left; } else if (z._right !== null && z._left === null){ y = z._right; } // x should be tallest child of y. // If children same height, x should be child of y // that has same orientation as z to y. var x; if (y._left !== null && y._right !== null){ if (y._left._height > y._right._height){ x = y._left; } else if (y._left._height < y._right._height){ x = y._right; } else if (y._left._height === y._right._height){ x = (z._left === y) ? y._left : y._right; } } else if (y._left !== null && y._right === null){ x = y._left; } else if (y._right !== null && y._left === null){ x = y._right; } return [x, y, z]; }; /** * Gets the nodes to be restructured during an AVL restructure * after an insert takes place. * * @public * @method * @param {Array} traveledNodes Array of previously traveled nodes * that are used to help determine the nodes to be restructured. */ exports.AVLTree.prototype._getNodesToRestructureInsert = function (traveledNodes) { // z is last traveled node - imbalance found at z var zIndex = traveledNodes.length; zIndex -= 1; var z = traveledNodes[zIndex]; // y should be child of z with larger height // (must be ancestor of inserted node) // therefore, last traveled node is correct. var yIndex = traveledNodes.length; yIndex -= 2; var y = traveledNodes[yIndex]; // x should be tallest child of y. // If children same height, x should be ancestor // of inserted node (in traveled path). var x; if (y._left !== null && y._right !== null){ if (y._left._height > y._right._height){ x = y._left; } else if (y._left._height < y._right._height){ x = y._right; } else if (y._left._height === y._right._height){ var xIndex = traveledNodes.length; xIndex -= 3; x = traveledNodes[xIndex]; } } else if (y._left !== null && y._right === null){ x = y._left; } else if (y._right !== null && y._left === null){ x = y._right; } return [x, y, z]; }; /** * Maintains the height balance property by * walking to root and checking for invalid height * differences between children and restructuring * appropriately. * * @public * @method * @param {Node} node Started node. * @param {Boolean} isRemove Represents if method was called after remove. */ exports.AVLTree.prototype._maintainHeightBalanceProperty = function (node, isRemove) { var current = node; var traveledNodes = []; while (current !== null){ traveledNodes.push(current); current._height = this._getHeightAtNode(current); if (!this._isBalancedAtNode(current)){ var nodesToBeRestructured = (isRemove) ? this._getNodesToRestructureRemove(traveledNodes) : this._getNodesToRestructureInsert(traveledNodes); this._restructure(nodesToBeRestructured); } current = current._parent; } }; /** * Identifies the pattern of given nodes, then calls * the appropriate pattern rotator. * * @public * @method * @param {Array} nodesToBeRestructured * array of nodes, in format, [x, y, z], to be restructured */ exports.AVLTree.prototype._restructure = function (nodesToBeRestructured) { var x = nodesToBeRestructured[0]; var y = nodesToBeRestructured[1]; var z = nodesToBeRestructured[2]; //Determine Rotation Pattern if (z._right === y && y._right === x){ this._rightRight(x, y, z); } else if (z._left === y && y._left === x){ this._leftLeft(x, y, z); } else if (z._right === y && y._left === x){ this._rightLeft(x, y, z); } else if (z._left === y && y._right === x){ this._leftRight(x, y, z); } }; /** * Rotates the given nodes from a right right pattern * to a parent, with 2 children. * * @public * @method * @param {Node} x node with lowest height to be restructured. * @param {Node} y parent of x parameter. * @param {Node} z grandparent of x, largest height. */ exports.AVLTree.prototype._rightRight = function (x, y, z) { /* z y => y x z x */ // pass z parent to y and move y's left to z's right if (z._parent !== null){ var orientation = (z._parent._left === z) ? '_left' : '_right'; z._parent[orientation] = y; y._parent = z._parent; } else { this._root = y; y._parent = null; } // z adopts y's left. z._right = y._left; if (z._right !== null){ z._right._parent = z; } // y adopts z y._left = z; z._parent = y; // Correct each nodes height - order matters, children first x._height = this._getHeightAtNode(x); z._height = this._getHeightAtNode(z); y._height = this._getHeightAtNode(y); }; /** * Rotates the given nodes from a left left pattern * to a parent, with 2 children. * * @public * @method * @param {Node} x node with lowest height to be restructured. * @param {Node} y parent of x parameter. * @param {Node} z grandparent of x, largest height. */ exports.AVLTree.prototype._leftLeft = function (x, y, z) { /* z y => y x x z */ //pass z parent to y and move y's right to z's left if (z._parent !== null){ var orientation = (z._parent._left === z) ? '_left' : '_right'; z._parent[orientation] = y; y._parent = z._parent; } else { this._root = y; y._parent = null; } z._left = y._right; if (z._left !== null) { z._left._parent = z; } //fix y right child y._right = z; z._parent = y; // Correct each nodes height - order matters, children first x._height = this._getHeightAtNode(x); z._height = this._getHeightAtNode(z); y._height = this._getHeightAtNode(y); }; /** * Rotates the given nodes from a right left pattern * to a parent, with 2 children. * * @public * @method * @param {Node} x node with lowest height to be restructured. * @param {Node} y parent of x parameter. * @param {Node} z grandparent of x, largest height. */ exports.AVLTree.prototype._rightLeft = function (x, y, z) { /* z y => x x z y */ //pass z parent to x if (z._parent !== null){ var orientation = (z._parent._left === z) ? '_left' : '_right'; z._parent[orientation] = x; x._parent = z._parent; } else { this._root = x; x._parent = null; } // Adoptions z._right = x._left; if (z._right !== null){ z._right._parent = z; } y._left = x._right; if (y._left !== null){ y._left._parent = y; } // Point to new children (x new parent) x._left = z; x._right = y; x._left._parent = x; x._right._parent = x; // Correct each nodes height - order matters, children first y._height = this._getHeightAtNode(y); z._height = this._getHeightAtNode(z); x._height = this._getHeightAtNode(x); }; /** * Rotates the given nodes from a left right pattern * to a parent, with 2 children. * * @public * @method * @param {Node} x node with lowest height to be restructured. * @param {Node} y parent of x parameter. * @param {Node} z grandparent of x, largest height. */ exports.AVLTree.prototype._leftRight = function (x, y, z) { /* z y => x x y z */ //pass z parent to x if (z._parent !== null){ var orientation = (z._parent._left === z) ? '_left' : '_right'; z._parent[orientation] = x; x._parent = z._parent; } else { this._root = x; x._parent = null; } // Adoptions z._left = x._right; if (z._left !== null){ z._left._parent = z; } y._right = x._left; if (y._right !== null){ y._right._parent = y; } // Point to new children (x new parent) x._right = z; x._left = y; x._left._parent = x; x._right._parent = x; // Correct each nodes height - order matters, children first y._height = this._getHeightAtNode(y); z._height = this._getHeightAtNode(z); x._height = this._getHeightAtNode(x); }; /** * Inserts a node into the AVL Tree.

* Time complexity: O(log N) in the average case * and O(N) in the worst case. * * @public * @method * @param {Number|String} value Node value. * @param {Node} current Current node. */ exports.AVLTree.prototype.insert = function (value, current) { if (this._root === null) { this._root = new exports.Node(value, null, null, null, 1); this._maintainHeightBalanceProperty(this._root); return; } var insertKey; current = current || this._root; if (current.value > value) { insertKey = '_left'; } else { insertKey = '_right'; } if (!current[insertKey]) { current[insertKey] = new exports.Node(value, null, null, current); this._maintainHeightBalanceProperty(current[insertKey], false); } else { this.insert(value, current[insertKey]); } }; /** * In-order traversal from the given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.AVLTree.prototype._inorder = function (current, callback) { if (!current) { return; } this._inorder(current._left, callback); if (typeof callback === 'function') { callback(current); } this._inorder(current._right, callback); }; /** * In-order traversal of the whole AVL tree. * * @public * @method * @param {Function} callback Callback which will be * called for each traversed node. */ exports.AVLTree.prototype.inorder = function (callback) { return this._inorder(this._root, callback); }; /** * Post-order traversal from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which will * be called for each traversed node */ exports.AVLTree.prototype._postorder = function (current, callback) { if (!current) { return; } if (typeof callback === 'function') { callback(current); } this._postorder(current._left, callback); this._postorder(current._right, callback); }; /** * Post-order traversal of the whole tree. * * @public * @param {Function} callback Callback which * will be called for each traversed node. */ exports.AVLTree.prototype.postorder = function (callback) { return this._postorder(this._root, callback); }; /** * Pre-order traversal of the tree from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.AVLTree.prototype._preorder = function (current, callback) { if (!current) { return; } if (typeof callback === 'function') { callback(current); } this._preorder(current._left, callback); this._preorder(current._right, callback); }; /** * Pre-order preorder traversal of the whole tree. * * @public * @param {Function} callback Callback which will * be called for each traversed node. */ exports.AVLTree.prototype.preorder = function (callback) { return this._preorder(this._root, callback); }; /** * Finds a node by it's value.

* Average time complexity: O(log N). * * @public * @param {Number|String} value of the node which should be found. */ exports.AVLTree.prototype.find = function (value) { return this._find(value, this._root); }; /** * Finds a node by it's value in a given sub-tree. * Average time complexity: O(log N). * * @private * @param {Number|String} value of the node which should be found. * @param {Node} current node to be checked. */ exports.AVLTree.prototype._find = function (value, current) { if (!current) { return null; } if (current.value === value) { return current; } if (current.value > value) { return this._find(value, current._left); } if (current.value < value) { return this._find(value, current._right); } }; /** * Replaces given child with new one, for given parent. * * @private * @param {Node} parent Parent node. * @param {Node} oldChild Child to be replaced. * @param {Node} newChild Child replacement. */ exports.AVLTree.prototype._replaceChild = function (parent, oldChild, newChild) { if (parent === null) { this._root = newChild; if (this._root !== null){ this._root._parent = null; } } else { if (parent._left === oldChild) { parent._left = newChild; } else { parent._right = newChild; } if (newChild) { newChild._parent = parent; } } }; /** * Removes node from the tree.

* Average runtime complexity: O(log N). * * @public * @param {Number|String} value of node to be removed * @returns {Boolean} True/false depending * on whether the given node is removed. */ exports.AVLTree.prototype.remove = function (value) { var node = this.find(value); if (!node) { return false; } if (node._left && node._right) { var min = this._findMin(node._right); var temp = node.value; node.value = min.value; min.value = temp; return this.remove(temp); } else { if (node._left) { this._replaceChild(node._parent, node, node._left); this._maintainHeightBalanceProperty(node._left, true); } else if (node._right) { this._replaceChild(node._parent, node, node._right); this._maintainHeightBalanceProperty(node._right, true); } else { this._replaceChild(node._parent, node, null); this._maintainHeightBalanceProperty(node._parent, true); } return true; } }; /** * Finds the node with minimum value in given sub-tree. * * @private * @param {Node} node Root of the sub-tree. * @param {Number|String} current Current minimum value of the sub-tree. * @returns {Node} Node with the minimum value in the sub-tree. */ exports.AVLTree.prototype._findMin = function (node, current) { current = current || { value: Infinity }; if (!node) { return current; } if (current.value > node.value) { current = node; } return this._findMin(node._left, current); }; /** * Finds the node with maximum value in given sub-tree. * * @private * @param {Node} node Root of the sub-tree. * @param {Number|String} current Current maximum value of the sub-tree. * @returns {Node} Node with the maximum value in the sub-tree. */ exports.AVLTree.prototype._findMax = function (node, current) { current = current || { value: -Infinity }; if (!node) { return current; } if (current.value < node.value) { current = node; } return this._findMax(node._right, current); }; /** * Finds the node with minimum value in the whole tree. * * @public * @returns {Node} The minimum node of the tree. */ exports.AVLTree.prototype.findMin = function () { return this._findMin(this._root); }; /** * Finds the node with maximum value in the whole tree. * * @public * @returns {Node} The maximum node of the tree. * */ exports.AVLTree.prototype.findMax = function () { return this._findMax(this._root); }; exports.AVLTree.prototype._isBalanced = function (current) { if (!current) { return true; } return this._isBalanced(current._left) && this._isBalanced(current._right) && Math.abs(this._getHeight(current._left) - this._getHeight(current._right)) <= 1; }; /** * Returns whether the AVL Tree is balanced. * * @public * @returns {Boolean} Whether the tree is balanced or not. */ exports.AVLTree.prototype.isBalanced = function () { return this._isBalanced(this._root); }; /** * Finds the diameter of the AVL tree. * * @public * @returns {Number} The longest path in the AVL Tree. */ exports.AVLTree.prototype.getDiameter = function () { var getDiameter = function (root) { if (!root) { return 0; } var leftHeight = this._getHeight(root._left); var rightHeight = this._getHeight(root._right); var path = leftHeight + rightHeight + 1; return Math.max(path, getDiameter(root._left), getDiameter(root._right)); }.bind(this); return getDiameter(this._root); }; /** * Returns the height of the tree. * * @public * @returns {Number} The height of the tree. */ exports.AVLTree.prototype.getTreeHeight = function () { return this._getHeight(this._root); }; exports.AVLTree.prototype._getHeight = function (node) { if (!node) { return 0; } return 1 + Math.max(this._getHeight(node._left), this._getHeight(node._right)); }; /** * Finds the lowest common ancestor of two nodes. * * @public * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.AVLTree.prototype.lowestCommonAncestor = function (firstNode, secondNode) { return this._lowestCommonAncestor(firstNode, secondNode, this._root); }; exports.AVLTree.prototype._lowestCommonAncestor = function (firstNode, secondNode, current) { var firstNodeInLeft = this._existsInSubtree(firstNode, current._left); var secondNodeInLeft = this._existsInSubtree(secondNode, current._left); var firstNodeInRight = this._existsInSubtree(firstNode, current._right); var secondNodeInRight = this._existsInSubtree(secondNode, current._right); if ((firstNodeInLeft && secondNodeInRight) || (firstNodeInRight && secondNodeInLeft)) { return current; } if (secondNodeInLeft && firstNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._left); } if (secondNodeInRight && secondNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._right); } return null; }; exports.AVLTree.prototype._existsInSubtree = function (node, root) { if (!root) { return false; } if (node === root.value) { return true; } return this._existsInSubtree(node, root._left) || this._existsInSubtree(node, root._right); }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/binary-search-tree.js ================================================ /** * Binary search tree. * * @example * var BST = require('path-to-algorithms/src/data-structures'+ * '/binary-search-tree'); * var bst = new BST.BinaryTree(); * * bst.insert(2000); * bst.insert(1989); * bst.insert(1991); * bst.insert(2001); * bst.insert(1966); * * var node = bst.find(1989); * console.log(node.value); // 1989 * * var minNode = bst.findMin(); * console.log(minNode.value); // 1966 * * var maxNode = bst.findMax(); * console.log(maxNode.value); //2001 * * @module data-structures/binary-search-tree */ (function (exports) { 'use strict'; /** * Node of the tree. * * @public * @constructor * @param {Number|String} value Value of the node. * @param {Node} left Left sibling. * @param {Node} right Right sibling. * @param {Node} parent Parent of the node. */ exports.Node = function (value, left, right, parent) { /** * @member {Number|String} */ this.value = value; this._left = left; this._right = right; this._parent = parent; }; /** * Binary tree. * * @public * @constructor */ exports.BinaryTree = function () { this._root = null; }; /** * Inserts a node into the binary search tree.

* Time complexity: O(log N) in the average case * and O(N) in the worst case. * * @public * @method * @param {Number|String} value Node value. * @param {Node} current Current node. */ exports.BinaryTree.prototype.insert = function (value, current) { if (this._root === null) { this._root = new exports.Node(value, null, null, null); return; } var insertKey; current = current || this._root; if (current.value > value) { insertKey = '_left'; } else { insertKey = '_right'; } if (!current[insertKey]) { current[insertKey] = new exports.Node(value, null, null, current); } else { this.insert(value, current[insertKey]); } }; /** * In-order traversal from the given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.BinaryTree.prototype._inorder = function (current, callback) { if (!current) { return; } this._inorder(current._left, callback); if (typeof callback === 'function') { callback(current); } this._inorder(current._right, callback); }; /** * In-order traversal of the whole binary search tree. * * @public * @method * @param {Function} callback Callback which will be * called for each traversed node. */ exports.BinaryTree.prototype.inorder = function (callback) { return this._inorder(this._root, callback); }; /** * Post-order traversal from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which will * be called for each traversed node */ exports.BinaryTree.prototype._postorder = function (current, callback) { if (!current) { return; } this._postorder(current._left, callback); this._postorder(current._right, callback); if (typeof callback === 'function') { callback(current); } }; /** * Post-order traversal of the whole tree. * * @public * @param {Function} callback Callback which * will be called for each traversed node. */ exports.BinaryTree.prototype.postorder = function (callback) { return this._postorder(this._root, callback); }; /** * Pre-order traversal of the tree from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.BinaryTree.prototype._preorder = function (current, callback) { if (!current) { return; } if (typeof callback === 'function') { callback(current); } this._preorder(current._left, callback); this._preorder(current._right, callback); }; /** * Pre-order preorder traversal of the whole tree. * * @public * @param {Function} callback Callback which will * be called for each traversed node. */ exports.BinaryTree.prototype.preorder = function (callback) { return this._preorder(this._root, callback); }; /** * Finds a node by it's value.

* Average time complexity: O(log N). * * @public * @param {Number|String} value of the node which should be found. */ exports.BinaryTree.prototype.find = function (value) { return this._find(value, this._root); }; /** * Finds a node by it's value in a given sub-tree. * Average time complexity: O(log N). * * @private * @param {Number|String} value of the node which should be found. * @param {Node} current node to be checked. */ exports.BinaryTree.prototype._find = function (value, current) { if (!current) { return null; } if (current.value === value) { return current; } if (current.value > value) { return this._find(value, current._left); } if (current.value < value) { return this._find(value, current._right); } }; /** * Replaces given child with new one, for given parent. * * @private * @param {Node} parent Parent node. * @param {Node} oldChild Child to be replaced. * @param {Node} newChild Child replacement. */ exports.BinaryTree.prototype._replaceChild = function (parent, oldChild, newChild) { if (!parent) { this._root = newChild; if (this._root !== null){ this._root._parent = null; } } else { if (parent._left === oldChild) { parent._left = newChild; } else { parent._right = newChild; } if (newChild) { newChild._parent = parent; } } }; /** * Removes node from the tree.

* Average runtime complexity: O(log N). * * @public * @param {Node} node to be removed * @returns {Boolean} True/false depending * on whether the given node is removed. */ exports.BinaryTree.prototype.remove = function (node) { if (!node) { return false; } if (node._left && node._right) { var min = this._findMin(node._right); var temp = node.value; node.value = min.value; min.value = temp; return this.remove(min); } else { if (node._left) { this._replaceChild(node._parent, node, node._left); } else if (node._right) { this._replaceChild(node._parent, node, node._right); } else { this._replaceChild(node._parent, node, null); } return true; } }; /** * Finds the node with minimum value in given sub-tree. * * @private * @param {Node} node Root of the sub-tree. * @param {Number|String} current Current minimum value of the sub-tree. * @returns {Node} Node with the minimum value in the sub-tree. */ exports.BinaryTree.prototype._findMin = function (node, current) { current = current || { value: Infinity }; if (!node) { return current; } if (current.value > node.value) { current = node; } return this._findMin(node._left, current); }; /** * Finds the node with maximum value in given sub-tree. * * @private * @param {Node} node Root of the sub-tree. * @param {Number|String} current Current maximum value of the sub-tree. * @returns {Node} Node with the maximum value in the sub-tree. */ exports.BinaryTree.prototype._findMax = function (node, current) { current = current || { value: -Infinity }; if (!node) { return current; } if (current.value < node.value) { current = node; } return this._findMax(node._right, current); }; /** * Finds the node with minimum value in the whole tree. * * @public * @returns {Node} The minimum node of the tree. */ exports.BinaryTree.prototype.findMin = function () { return this._findMin(this._root); }; /** * Finds the node with maximum value in the whole tree. * * @public * @returns {Node} The maximum node of the tree. * */ exports.BinaryTree.prototype.findMax = function () { return this._findMax(this._root); }; /** * Checks if a given node is balanced. * * @private * @param {Node} current Node to have balance checked. * @returns {Boolean} Boolean of whether or not provided node is balanced. */ exports.BinaryTree.prototype._isBalanced = function (current) { if (!current) { return true; } return this._isBalanced(current._left) && this._isBalanced(current._right) && Math.abs(this._getHeight(current._left) - this._getHeight(current._right)) <= 1; }; /** * Returns whether the BST is balanced. * * @public * @returns {Boolean} Whether the tree is balanced or not. */ exports.BinaryTree.prototype.isBalanced = function () { return this._isBalanced(this._root); }; /** * Finds the diameter of the binary tree. * * @public * @returns {Number} The longest path in the BST. */ exports.BinaryTree.prototype.getDiameter = function () { var getDiameter = function (root) { if (!root) { return 0; } var leftHeight = this._getHeight(root._left); var rightHeight = this._getHeight(root._right); var path = leftHeight + rightHeight + 1; return Math.max(path, getDiameter(root._left), getDiameter(root._right)); }.bind(this); return getDiameter(this._root); }; /** * Returns the height of the tree. * * @public * @returns {Number} The height of the tree. */ exports.BinaryTree.prototype.getHeight = function () { return this._getHeight(this._root); }; /** * Recursive worker function for getHeight() * * @private * @param {Node} node Node at current recursive frame. * @returns {Number} Height of the Node in the parameter. */ exports.BinaryTree.prototype._getHeight = function (node) { if (!node) { return 0; } return 1 + Math.max(this._getHeight(node._left), this._getHeight(node._right)); }; /** * Finds the lowest common ancestor of two nodes. * * @public * @param {Node} firstNode First node to be considered when checking * for ancestor. * @param {Node} secondNode Second node to be considered when checking * for ancestor. * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.BinaryTree.prototype.lowestCommonAncestor = function (firstNode, secondNode) { return this._lowestCommonAncestor(firstNode, secondNode, this._root); }; /** * Obtains the lowest common ancestor for the given nodes. * * @private * @param {Node} firstNode First node to be considered when checking * for ancestor. * @param {Node} secondNode Second node to be considered when checking * for ancestor. * @param {Node} current Current node. * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.BinaryTree.prototype._lowestCommonAncestor = function (firstNode, secondNode, current) { var firstNodeInLeft = this._existsInSubtree(firstNode, current._left); var secondNodeInLeft = this._existsInSubtree(secondNode, current._left); var firstNodeInRight = this._existsInSubtree(firstNode, current._right); var secondNodeInRight = this._existsInSubtree(secondNode, current._right); if ((firstNodeInLeft && secondNodeInRight) || (firstNodeInRight && secondNodeInLeft)) { return current; } if (secondNodeInLeft && firstNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._left); } if (secondNodeInRight && secondNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._right); } return null; }; /** * Checks if a given node exists in a subtree. * * @private * @param {Node} node Node to check for. * @param {Node} root Root node of a given subtree. * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.BinaryTree.prototype._existsInSubtree = function (node, root) { if (!root) { return false; } if (node.value === root.value) { return true; } return this._existsInSubtree(node, root._left) || this._existsInSubtree(node, root._right); }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/bloomfilter.js ================================================ /** * Bloomfilter and a bitmap. * Probablistic data structure useful for deduplication * * @example * // create a bloom filter with capacity of 1024 and 0.01 error rat * var bloomfilter = new Bloomfilter(1024, 0.01); * bloomfilter.set('hello'); * bloomfilter.get('hello') === true; * bloomfilter.get('world') === false; * @module data-structures/bloomfilter */ (function(exports) { 'use strict'; function randomUint32() { return Math.floor(Math.random() * Math.pow(2, 32)); } /** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ function hashFnv32a(str, asString, seed) { /*jshint bitwise:false */ var i; var l; var hval = seed === undefined ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i = i + 1) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if (asString) { // Convert to 8 digit hex string return ('0000000' + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; } // Make a hash function function mkHashFun(seed, limit) { return function(value) { return hashFnv32a(value, false, seed) % limit; }; } /** * Create a bitmap with a given size * Bit map is not resizeable * @public * @constructor * @param {Number} size the size of the bitmap */ exports.Bitmap = function(size) { size = size || 1024; if (size < 0) { throw new Error('The size cannot be negative'); } this.size = size; this.intSize = Math.ceil(size / 32); // number of underlying integers // Create a 0 initialized array this.intArray = Array.from({ length: this.intSize }, function() { return 0; }); }; /** * Get the size of the bit map * @public * @return {Number} size of the bit map */ exports.Bitmap.prototype.getSize = function() { return this.size; }; /** * Get if a bit is set at a particular index * @public * @return {Boolean} true or false value of the bit */ exports.Bitmap.prototype.exists = function(index) { // Necessary boundary check if (index < 0 || index > this.size) { throw new Error('Index out of bound') } // Calculate the offset within the int var intOffset = index % 32; var arrayOffset = Math.floor(index / 32); // the offset for the array var mask = 1 << intOffset; return (mask & this.intArray[arrayOffset]) !== 0; }; /** * Get the bit at a particular index * @public * @return {Number} true or false value of the bit */ exports.Bitmap.prototype.get = function(index) { // Necessary boundary check if (index < 0 || index > this.size) { throw new Error('Index out of bound') } // Calculate the offset within the int var intOffset = index % 32; var arrayOffset = Math.floor(index / 32); // the offset for the array var mask = 1 << intOffset; if ((mask & this.intArray[arrayOffset]) !== 0) { return 1; } else { return 0; } }; /** * Set the bit at a particular index * @public * @param {Number} index the index to set * @param {Boolean} value the value that is necessary */ exports.Bitmap.prototype.set = function(index, value) { // necessary boundary check if (index < 0 || index > this.size) { throw new Error('Index out of bound') } var intOffset = index % 32; //calculate the offset within the int var arrayOffset = Math.floor(index / 32); // the offset for the array var mask = 1 << intOffset; // Check trutyness if (value) { this.intArray[arrayOffset] |= mask; // or operation } else { this.intArray[arrayOffset] &= ~mask; // and opertation to set 0 } }; /** * Construct a bloom filter by given the capacity and the error rate, default error rate is 0.001 * @public * @constructor * @param {Number} capacity the maximum capacity to maintain the given error rate * @param {Number} errorRate the error rate expected under maximum capacity */ exports.Bloomfilter = function(capacity, errorRate) { errorRate = errorRate || 0.001; // default error rate if (errorRate > 1 || errorRate < 0) { throw new Error('The error rate range is outside of bound'); } if (capacity < 0) { throw new Error('The capacity cannot be negative'); } this.capacity = capacity; this.errorRate = errorRate; // Calculate the optimal size of the bitmap var numBit = Math.ceil( (capacity * Math.log(1.0 / errorRate)) / Math.pow(Math.log(2), 2) ); // Calculate the optimal number of hash functions this.numHashFunction = Math.ceil(Math.log(2), numBit / capacity); // Create a bitmap this.bitmap = new exports.Bitmap(numBit); // Generate an array of hash functions this.hashFunctions = Array.from( { length: this.numHashFunction }, function() { return mkHashFun(randomUint32(), numBit); }.bind(this) ); }; /** * To check if an item is in the filter. If it is in, it will return true, * if it is not in the filter, highly likely it will return false, but guaranteed * @param {Number | String} value the value that we are trying to check in the filter */ exports.Bloomfilter.prototype.get = function(value) { value = String(value); // make it string var hashes = this.hashFunctions.map(function(hashFct) { return hashFct(value); }); // if one of the bits is not set for (var i = 0; i < hashes.length; i = i + 1) { if (this.bitmap.exists(hashes[i]) === false) { return false; } } return true; }; /** * To set(put) an item in the bloom filter * @public * @param {Number | String} value the value that is been set in the filter */ exports.Bloomfilter.prototype.set = function(value) { value = String(value); // make it string var hashes = this.hashFunctions.map(function(hashFct) { return hashFct(value); }); // Set all the corresponding bits for (var i = 0; i < hashes.length; i = i + 1) { this.bitmap.set(hashes[i], true); } }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/edge.js ================================================ (function (exports) { 'use strict'; /** * Graph edge. * * @constructor * @public * @param {Vertex} e Vertex which this edge connects. * @param {Vertex} v Vertex which this edge connects. * @param {Number} distance Weight of the edge. * @module data-structures/edge */ exports.Edge = function (e, v, distance) { this.from = e; this.to = v; this.distance = distance; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/hash-table.js ================================================ /** * Hash Table * * An associative array, that can map keys * (strings and numbers) to values in O(1). * * @example * var hash = require('path-to-algorithms/src/data-structures'+ * '/hash-table'); * var hashTable = new hash.Hashtable(); * * hashTable.put(10, 'value'); * hashTable.put('key', 10); * * console.log(hashTable.get(10)); // 'value' * console.log(hashTable.get('key')); // 10 * * hashTable.remove(10); * hashTable.remove('key'); * * console.log(hashTable.get(10)); // undefined * console.log(hashTable.get('key')); // undefined * * @module data-structures/hash-table */ (function (exports) { 'use strict'; /** * Constructs a Node to store data and next/prev nodes in Hash table. * * @public * @constructor * @param {Number|String} key Key of the node. * @param {Number|String} data Data to be stored in hash table. */ exports.Node = function (key, data) { this.key = key; this.data = data; this.next = undefined; this.prev = undefined; }; /** * Construct a Hash table.. * * @public * @constructor */ exports.Hashtable = function () { this.buckets = []; // The higher the bucket count; less likely for collisions. this.maxBucketCount = 100; }; /** * Simple non-crypto hash used to hash keys, which determines * which bucket the value will be placed in. * A javascript implementation of Java's 32bitint hash. * * @public * @method * @param {Number|String} val Key to be hashed. */ exports.Hashtable.prototype.hashCode = function (val) { var i; var hashCode = 0; var character; // If value to be hashed is already an integer, return it. if (val.length === 0 || val.length === undefined) { return val; } for (i = 0; i < val.length; i += 1) { character = val.charCodeAt(i); /*jshint -W016 */ hashCode = ((hashCode << 5) - hashCode) + character; hashCode = hashCode & hashCode; /*jshint -W016 */ } return hashCode; }; /** * Puts data into the table based on hashed key value. * * @public * @method * @param {Number|String} key Key for data. * @param {Number|String} data Data to be stored in table. */ exports.Hashtable.prototype.put = function (key, data, hashCode) { /* Make collision testing easy with optional hashCode parameter. That should not be used! Only by spec/tests. */ if (hashCode === undefined) { // Typical use hashCode = this.hashCode(key); } else if (hashCode.length > 0) { // Testing/Spec - String hash passed, convert to int based hash. hashCode = this.hashCode(hashCode); } // Adjust hash to fit within buckets. hashCode = hashCode % this.maxBucketCount; var newNode = new exports.Node(key, data); // No element exists at hash/index for given key -> put in table. if (this.buckets[hashCode] === undefined) { this.buckets[hashCode] = newNode; return; } // Element exists at hash/index for given key, but, same key -> overwrite. if (this.buckets[hashCode].key === key) { this.buckets[hashCode].data = data; return; } /* Item exists at hash/index for key, but different key. Handle collision. */ var first = this.buckets[hashCode]; while (first.next !== undefined) { first = first.next; } first.next = newNode; newNode.prev = first; }; /** * Get's data from the table based on key. * * @public * @method * @param {Number|String} key Key for data to be retrieved. */ exports.Hashtable.prototype.get = function (key, hashCode) { /* Make collision testing easy with optional hashCode parameter. That should not be used! Only by spec/tests. */ if (hashCode === undefined) { // Typical use hashCode = this.hashCode(key); } else if (hashCode.length > 0) { // Testing/Spec - String hash passed, convert to int based hash. hashCode = this.hashCode(hashCode); } hashCode = hashCode % this.maxBucketCount; if (this.buckets[hashCode] === undefined) { return undefined; } else if ( this.buckets[hashCode].next === undefined && this.buckets[hashCode].key === key ) { return this.buckets[hashCode].data; } else { var first = this.buckets[hashCode]; while ( first !== undefined && first.next !== undefined && first.key !== key ) { first = first.next; } if (first.key === key) { return first.data; } else { return undefined; } } }; /** * Removes data from the table based on key. * * @public * @method * @param {Number|String} key Key of the data to be removed. */ exports.Hashtable.prototype.remove = function (key, hashCode) { /* Make collision testing easy with optional hashCode parameter. That should not be used! Only by spec/tests. */ if (hashCode === undefined) { // Typical use hashCode = this.hashCode(key); } else if (hashCode.length > 0) { // Testing/Spec - String hash passed, convert to int based hash. hashCode = this.hashCode(hashCode); } hashCode = hashCode % this.maxBucketCount; if (this.buckets[hashCode] === undefined) { return undefined; } else if (this.buckets[hashCode].next === undefined) { this.buckets[hashCode] = undefined; } else { var first = this.buckets[hashCode]; while ( first !== undefined && first.next !== undefined && first.key !== key ) { first = first.next; } var removedValue = first.data; // Removing (B) // (B) : only item in bucket if (first.prev === undefined && first.next === undefined) { first = undefined; return removedValue; } // (B) - A - C: start link in bucket if (first.prev === undefined && first.next !== undefined) { first.data = first.next.data; first.key = first.next.key; if (first.next.next !== undefined) { first.next = first.next.next; } else { first.next = undefined; } return removedValue; } // A - (B) : end link in bucket if (first.prev !== undefined && first.next === undefined) { first.prev.next = undefined; first = undefined; return removedValue; } // A - (B) - C : middle link in bucket if (first.prev !== undefined && first.next !== undefined) { first.prev.next = first.next; first.next.prev = first.prev; first = undefined; return removedValue; } } }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/heap.js ================================================ /** * A binary heap is a complete binary tree which * satisfies the heap ordering property. * * @example * var Heap = require('path-to-algorithms/src/data-structures/heap').Heap; * * var heap = new Heap(function(a, b) { * return a.birthyear - b.birthyear; * }); * * heap.add({ * name: 'John', * birthyear: 1981 * }); * heap.add({ * name: 'Pavlo', * birthyear: 2000 * }); * heap.add({ * name: 'Garry', * birthyear: 1989 * }); * heap.add({ * name: 'Derek', * birthyear: 1990 * }); * heap.add({ * name: 'Ivan', * birthyear: 1966 * }); * * console.log(heap.extract()); // { name: 'Pavlo', birthyear: 2000 } * console.log(heap.extract()); // { name: 'Derek', birthyear: 1990 } * console.log(heap.extract()); // { name: 'Garry', birthyear: 1989 } * console.log(heap.extract()); // { name: 'John', birthyear: 1981 } * console.log(heap.extract()); // { name: 'Ivan', birthyear: 1966 } * * @module data-structures/heap */ (function (exports) { 'use strict'; /** * Minimum heap constructor. * * @public * @constructor * @param {Function} cmp Function used for comparison between the elements. */ exports.Heap = function (cmp) { this._heap = []; if (typeof cmp === 'function') { this._cmp = cmp; } else { this._cmp = function (a, b) { return a - b; }; } }; /** * Exchange indexes with start index given as argument * to turn the tree into a valid heap. On a single call * this method maintains only a single "branch" of the heap.

* * Time complexity: O(log N). * * @private * @param {Number} index The parent. */ exports.Heap.prototype._heapify = function (index) { var extr = index; var left = 2 * index + 1; var right = 2 * index + 2; var temp; if (left < this._heap.length && this._cmp(this._heap[left], this._heap[index]) > 0) { extr = left; } if (right < this._heap.length && this._cmp(this._heap[right], this._heap[index]) > 0 && this._cmp(this._heap[right], this._heap[left]) > 0) { extr = right; } if (index !== extr) { temp = this._heap[index]; this._heap[index] = this._heap[extr]; this._heap[extr] = temp; this._heapify(extr); } }; /** * Changes the key.

* Complexity: O(log N). * * @public * @param {Number} index Index of the value which should be changed. * @param {Number|Object} value New value according to the index. * @return {Number} New position of the element. */ exports.Heap.prototype.changeKey = function (index, value) { this._heap[index] = value; var elem = this._heap[index]; var parent = Math.floor(index / 2); var temp; if (elem !== undefined) { while (parent >= 0 && this._cmp(elem, this._heap[parent]) > 0) { temp = this._heap[parent]; this._heap[parent] = elem; this._heap[index] = temp; index = parent; parent = Math.floor(parent / 2); } this._heapify(index); } return parent; }; /** * Updates a given node. This operation is useful * in algorithms like Dijkstra, A* where we need * to decrease/increase value of the given node. * * @public * @param {Number|Object} node Node which should be updated. */ exports.Heap.prototype.update = function (node) { var idx = this._heap.indexOf(node); if (idx >= 0) { this.changeKey(idx, node); } }; /** * Adds new element to the heap.

* Complexity: O(log N). * * @public * @param {Number|Object} value Value which will be inserted. * @return {Number} Index of the inserted value. */ exports.Heap.prototype.add = function (value) { this._heap.push(value); return this.changeKey(this._heap.length - 1, value); }; /** * Returns current value which is on the top of the heap.

* Complexity: O(1). * * @public * @return {Number|Object} Current top value. */ exports.Heap.prototype.top = function () { return this._heap[0]; }; /** * Removes and returns the current extremum value * which is on the top of the heap.

* Complexity: O(log N). * * @public * @returns {Number|Object} The extremum value. */ exports.Heap.prototype.extract = function () { if (!this._heap.length) { throw 'The heap is already empty!'; } var extr = this._heap.shift(); this._heapify(0); return extr; }; exports.Heap.prototype.getCollection = function () { return this._heap; }; /** * Checks or heap is empty. * * @public * @returns {Boolean} Returns true if heap is empty. */ exports.Heap.prototype.isEmpty = function () { return !this._heap.length; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/interval-tree.js ================================================ /** * Interval tree is an ordered tree data structure to hold intervals. * * @example * * var IT = require('path-to-algorithms/src/data-structures/interval-tree'); * var intervalTree = new IT.IntervalTree(); * * intervalTree.add([0, 100]); * intervalTree.add([101, 200]); * intervalTree.add([10, 50]); * intervalTree.add([120, 220]); * * console.log(intervalTree.contains(150)); // true * console.log(intervalTree.contains(250)); // false * console.log(intervalTree.intersects([210, 310])); // true * console.log(intervalTree.intersects([310, 320])); // false * * @module data-structures/interval-tree */ (function (exports) { 'use strict'; /** * Node which describes an interval. * * @public * @constructor * @param {Number} start Start of the interval. * @param {Number} end End of the interval. * @param {Node} left Left child node. * @param {Node} right Right child node. */ exports.Node = function (start, end, left, right) { /** * Node interval. * @member {Array} */ this.interval = [start, end]; /** * Max endpoint in subtree which starts from this node. * @member {Number} */ this.max = -Infinity; /** * Parent node. * @member {Node} */ this.parentNode = null; /** * Left child node. * @member {Node} */ this.left = left; /** * Right child node. * @member {Node} */ this.right = right; }; /** * Interval tree. * * @public * @constructor */ exports.IntervalTree = function () { /** * Root node of the tree. * @member {Node} */ this.root = null; }; function addNode(node, side, interval) { var child = new exports.Node(interval[0], interval[1]); child.max = interval[1]; child.parentNode = node; node[side] = child; if (node.max < interval[1]) { while (child) { if (child.max < interval[1]) { child.max = interval[1]; } child = child.parentNode; } } } function addHelper(node, interval) { if (node.interval[0] > interval[0]) { if (node.left) { addHelper(node.left, interval); } else { addNode(node, 'left', interval); } } else { if (node.right) { addHelper(node.right, interval); } else { addNode(node, 'right', interval); } } } /** * Add new interval to the tree. * * @public * @param {Array} intreval Array with start and end points of the interval. */ exports.IntervalTree.prototype.add = function (interval) { if (!this.root) { this.root = new exports.Node(interval[0], interval[1]); this.root.max = interval[1]; return; } addHelper(this.root, interval); }; function contains(point, node) { if (!node) { return false; } if (node.interval[0] <= point && node.interval[1] >= point) { return true; } var result = false; var temp; ['left', 'right'].forEach(function (key) { temp = node[key]; if (temp) { if (temp.max > point) { result = result || contains(point, temp); } } }); return result; } /** * Checks or point belongs to at least one intarval from the tree.

* Complexity: O(log N). * * @public * @method * @param {Number} point Point which should be checked. * @return {Boolean} True if point belongs to one of the intervals. */ exports.IntervalTree.prototype.contains = function (point) { return contains(point, this.root); }; function intersects(a, b) { return (a[0] <= b[0] && a[1] >= b[0]) || (a[0] <= b[1] && a[1] >= b[1]) || (b[0] <= a[0] && b[1] >= a[0]) || (b[0] <= a[1] && b[1] >= a[1]); } function intersectsHelper(interval, node) { if (!node) { return false; } if (intersects(node.interval, interval)) { return true; } var result = false; var temp; ['left', 'right'].forEach(function (side) { temp = node[side]; if (temp && temp.max >= interval[0]) { result = result || intersectsHelper(interval, temp); } }); return result; } /** * Checks or interval belongs to at least one intarval from the tree.

* Complexity: O(log N). * * @public * @method * @param {Array} interval Interval which should be checked. * @return {Boolean} True if interval intersects with one of the intervals. */ exports.IntervalTree.prototype.intersects = function (interval) { return intersectsHelper(interval, this.root); }; function heightHelper(node) { if (!node) { return 0; } return 1 + Math.max(heightHelper(node.left), heightHelper(node.right)); } /** * Returns height of the tree. * * @public * @method * @return {Number} Height of the tree. */ exports.IntervalTree.prototype.height = function () { return heightHelper(this.root); }; /** * Returns node with the max endpoint in subtree. * * @public * @method * @param {Node} node Root node of subtree. * @return {Node} Node with the largest endpoint. */ exports.IntervalTree.prototype.findMax = function (node) { var stack = [node]; var current; var max = -Infinity; var maxNode; while (stack.length) { current = stack.pop(); if (current.left) { stack.push(current.left); } if (current.right) { stack.push(current.right); } if (current.interval[1] > max) { max = current.interval[1]; maxNode = current; } } return maxNode; }; // adjust the max value exports.IntervalTree.prototype._removeHelper = function (interval, node) { if (!node) { return; } if (node.interval[0] === interval[0] && node.interval[1] === interval[1]) { // When left and right children exists if (node.left && node.right) { var replacement = node.left; while (replacement.left) { replacement = replacement.left; } var temp = replacement.interval; replacement.interval = node.interval; node.interval = temp; this._removeHelper(replacement.interval, node); } else { // When only left or right child exists var side = 'left'; if (node.right) { side = 'right'; } var parentNode = node.parentNode; if (parentNode) { if (parentNode.left === node) { parentNode.left = node[side]; } else { parentNode.right = node[side]; } if (node[side]) { node[side].parentNode = parentNode; } } else { this.root = node[side]; // last node removed if (this.root) { this.root.parentNode = null; } } } // Adjust the max value var p = node.parentNode; if (p) { var maxNode = this.findMax(p); var max = maxNode.interval[1]; while (maxNode) { if (maxNode.max === node.interval[1]) { maxNode.max = max; maxNode = maxNode.parentNode; } else { maxNode = false; } } } } else { // could be optimized this._removeHelper(interval, node.left); this._removeHelper(interval, node.right); } }; /** * Remove interval from the tree. * * @public * @method * @param {Array} intreval Array with start and end of the interval. */ exports.IntervalTree.prototype.remove = function (interval) { return this._removeHelper(interval, this.root); }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/linked-list.js ================================================ /** * Linked list. * * @example * * var LL = require('path-to-algorithms/src/data-structures/linked-list'); * * var linkedList = new LL.LinkedList(); * * linkedList.push({ * name: 'John', * birthyear: 1981 * }); * linkedList.push({ * name: 'Pavlo', * birthyear: 2000 * }); * linkedList.push({ * name: 'Garry', * birthyear: 1989 * }); * linkedList.push({ * name: 'Derek', * birthyear: 1990 * }); * linkedList.push({ * name: 'Ivan', * birthyear: 1966 * }); * * console.log(linkedList.shift().data); // { name: 'John', birthyear: 1981 } * console.log(linkedList.pop().data); // { name: 'Ivan', birthyear: 1966 } * * @module data-structures/linked-list */ (function (exports) { 'use strict'; /** * Linked list node. * * @public * @constructor * @param {Object} data Data of the node. */ exports.Node = function (data) { /** * Data of the node. * @member {Object} */ this.data = data; /** * Next node. * @member {Node} */ this.next = null; /** * Previous node. * @member {Node} */ this.prev = null; }; /** * Linked list. * * @public * @constructor */ exports.LinkedList = function () { this.first = null; this.last = null; }; /** * Add data to the end of linked list. * * @public * @method * @param {Object} data Data which should be added. */ exports.LinkedList.prototype.push = function (data) { var node = new exports.Node(data); if (this.first === null) { this.first = this.last = node; } else { var temp = this.last; this.last = node; node.prev = temp; temp.next = node; } }; /** * Add data to the beginning of linked list. * * @public * @method * @param {Object} data Data which should be added. */ exports.LinkedList.prototype.unshift = function (data) { var node = new exports.Node(data); if (this.first === null) { this.first = this.last = node; } else { var temp = this.first; this.first = node; node.next = temp; temp.prev = node; } }; /** * In order traversal of the linked list. * * @public * @method * @param {Function} cb Callback which should be executed on each node. */ exports.LinkedList.prototype.inorder = function (cb) { var temp = this.first; while (temp) { cb(temp); temp = temp.next; } }; /** * Remove data from the linked list. * * @public * @method * @param {Object} data Data which should be removed. * @return {Boolean} Returns true if data has been removed. */ exports.LinkedList.prototype.remove = function (data, equals) { if (this.first === null) { return false; } var temp = this.first; var next; var prev; while (temp) { var dataFound = equals ? equals(temp.data, data) : temp.data === data; if (dataFound) { next = temp.next; prev = temp.prev; if (next) { next.prev = prev; } if (prev) { prev.next = next; } if (temp === this.first) { this.first = next; } if (temp === this.last) { this.last = prev; } return true; } temp = temp.next; } return false; }; /** * Check if linked list contains cycle. * * @public * @method * @return {Boolean} Returns true if linked list contains cycle. */ exports.LinkedList.prototype.hasCycle = function () { var fast = this.first; var slow = this.first; while (true) { if (fast === null) { return false; } fast = fast.next; if (fast === null) { return false; } fast = fast.next; slow = slow.next; if (fast === slow) { return true; } } }; /** * Return last node from the linked list. * * @public * @method * @return {Node} Last node. */ exports.LinkedList.prototype.pop = function () { if (this.last === null) { return null; } var temp = this.last; this.last = this.last.prev; return temp; }; /** * Return first node from the linked list. * * @public * @method * @return {Node} First node. */ exports.LinkedList.prototype.shift = function () { if (this.first === null) { return null; } var temp = this.first; this.first = this.first.next; return temp; }; /** * Reverses the linked list recursively * * @public * @method */ exports.LinkedList.prototype.recursiveReverse = function () { function inverse(current, next) { if (!next) { return; } inverse(next, next.next); next.prev = next.next; next.next = current; } if (!this.first) { return; } inverse(this.first, this.first.next); this.first.prev = this.first.next; this.first.next = null; var temp = this.first; this.first = this.last; this.last = temp; }; /** * Reverses the linked list iteratively * * @public * @method */ exports.LinkedList.prototype.reverse = function () { if (!this.first || !this.first.next) { return; } var current = this.first var next do { next = current.next current.next = current.prev current.prev = next current = next } while (next) var tmp = this.first this.first = this.last this.last = tmp }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/red-black-tree.js ================================================ /** * Red-Black tree is a data structure which is * a type of self-balancing binary search tree. * * @example * * var RBTree = require('../src/data-structures/red-black-tree').RBTree; * var rbTree = new RBTree(); * * rbTree.put(1981, { * name: 'John', * surname: 'Smith' * }); * rbTree.put(2000, { * name: 'Pavlo', * surname: 'Popov' * }); * rbTree.put(1989, { * name: 'Garry', * surname: 'Fisher' * }); * rbTree.put(1990, { * name: 'Derek', * surname: 'Anderson' * }); * * console.log(rbTree.get(1989)); // { name: 'Garry', surname: 'Fisher' } * * @module data-structures/red-black-tree */ (function (exports) { 'use strict'; /** * Enum for the different colors */ var Colors = { RED: 0, BLACK: 1 }; exports.Colors = Colors; /** * Node of the Red-Black tree. * * @private * @constructor * @param {Number} key Key of the node. * @param {Object} value Value assigned to the node. * @param {Node} left Left node. * @param {Node} right Right node. * @param {Number} color Node color. */ function Node(key, value, left, right, color) { this._key = key; this._left = left; this._right = right; this._value = value; this._color = color; } /** * Check or node is red. * * @private * @method * @return {Boolean} Returns true if node is red. */ Node.prototype.isRed = function () { return this._color === Colors.RED; }; /** * Changes node color. * * @private * @method */ Node.prototype.flipColor = function () { if (this._color === Colors.RED) { this._color = Colors.BLACK; } else { this._color = Colors.RED; } }; /** * Creates getters and setters for the properties: * key, value, left, right and color. */ 'key value left right color' .split(' ') .forEach(function (key) { var valueName = key.substr(0, 1).toUpperCase() + key.substr(1, key.length); Node.prototype['get' + valueName] = function () { return this['_' + key]; }; Node.prototype['set' + valueName] = function (val) { this['_' + key] = val; }; }); exports.Node = Node; /** * Red-Black Tree. * * @public * @constructor */ exports.RBTree = function () { this._root = null; }; /** * Add value associated with a given key.

* Complexity: O(log N). * * @public * @method * @param {Number} key Key. * @param {Object} value Value. */ exports.RBTree.prototype.put = function (key, value) { this._root = this._put(key, value, this._root); this._root.setColor(Colors.BLACK); }; /** * Returns true or false depending on whether * given node is red. * * @private * @method * @param {Node} node Node which sould be checked. * @return Returns true if node is red. */ exports.RBTree.prototype.isRed = function (node) { if (!node) { return false; } return node.isRed(); }; /** * Helper function for insertion of given key, * value pair into the Red-Black tree. * * @private * @method * @param {Number} key Key. * @param {Object} value Value. * @param {Node} node Node. */ exports.RBTree.prototype._put = function (key, value, node) { var newRoot = node; if (node === null) { return new Node(key, value, null, null, Colors.RED); } if (node.getKey() > key) { node.setLeft(this._put(key, value, node.getLeft())); } else if (node.getKey() < key) { node.setRight(this._put(key, value, node.getRight())); } else { node.setValue(value); } if (this.isRed(node.getRight()) && !this.isRed(node.getLeft())) { newRoot = this._rotateLeft(node); } if (this.isRed(node.getLeft()) && this.isRed(node.getLeft().getLeft())) { newRoot = this._rotateRight(node); } if (this.isRed(node.getLeft()) && this.isRed(node.getRight())) { this._flipColors(node); } return newRoot; }; /** * Flip the colors of the both neighbours of given node.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. */ exports.RBTree.prototype._flipColors = function (node) { node.getLeft().flipColor(); node.getRight().flipColor(); }; /* * Rotates given node to the left.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. * @return {Node} Right node. */ exports.RBTree.prototype._rotateLeft = function (node) { var x = node.getRight(); if (x !== null) { var temp = x.getLeft(); node.setRight(temp); x.setLeft(node); x.setColor(node.getColor()); node.setColor(Colors.RED); } return x; }; /* * Rotates given node to the right.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. * @return {Node} Left node. */ exports.RBTree.prototype._rotateRight = function (node) { var x = node.getLeft(); if (x !== null) { var temp = x.getRight(); node.setLeft(temp); x.setRight(node); x.setColor(node.getColor()); node.setColor(Colors.RED); } return x; }; /** * Get value by the given key.

* Complexity: O(log N). * * @public * @param {Number} key A key to be searched for. * @return {Object} A value which will be returned based on the key. */ exports.RBTree.prototype.get = function (key) { return this._get(this._root, key); }; /** * Get value by the given key.

* * @private * @param {Node} node Node to start with. * @param {Number} key A key to be searched for. * @return {Object} A value which will be returned based on the key. */ exports.RBTree.prototype._get = function (node, key) { if (node === null) { return undefined; } if (node.getKey() === key) { return node.getValue(); } if (node.getKey() > key) { return this._get(node.getLeft(), key); } else { return this._get(node.getRight(), key); } }; /** * Get Level Order Traversal for the given Red Black Tree, * returns 'Tree is empty' string when tree has no Nodes. * Complexity: O(N). * * @public * @return {String} The keys of the tree in level order traversal. * */ exports.RBTree.prototype.levelOrderTraversal = function () { var queue = []; var levelOrderString = ''; if (this._root){ queue.push(this._root); } else { levelOrderString = ' Tree is empty'; } while (queue.length !== 0){ var tempNode = queue.shift(); levelOrderString += ' ' + tempNode.getKey(); if (tempNode.getLeft() !== null){ queue.push(tempNode.getLeft()); } if (tempNode.getRight() !== null){ queue.push(tempNode.getRight()); } } return 'Level Order Traversal -:' + levelOrderString; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/segment-tree.js ================================================ /** * Implementation of a segment tree. * * @example * var SegmentTree = require('path-to-algorithms/src/data-structures'+ * '/segment-tree').SegmentTree; * * var tree = SegmentTree.indexArray([-1, 2, 4, 0], Infinity, function (a, b) { * return Math.min(a, b); * }); * * @public * @constructor * @param {any} placeholder A placeholder value dpendent on the aggregate. * @param {Function} aggregate Generates the values for the intermediate nodes. * @module data-structures/segment-tree */ (function (exports) { 'use strict'; /** * SegmentTree constructor. * * @public * @constructor * @param {any} invalidValue Invalid value to be returned depending * on the aggregate. * @param {Function} aggregate Function to generate the intermediate * values in the tree. */ function SegmentTree(invalidValue, aggregate) { this._data = []; this._original = null; this._invalidValue = invalidValue; this._aggregate = aggregate; } /** * Creates a segment tree using an array passed as element. * * @static * @public * @param {Array} array Array to be indexed. * @param {Function} aggregate Function used for generation of * intermediate nodes. */ SegmentTree.indexArray = function (array, placeholder, aggregate) { var segmentize = function (original, data, lo, hi, idx) { if (lo === hi) { data[idx] = original[lo]; } else { var mid = Math.floor((lo + hi) / 2); var left = 2 * idx + 1; var right = 2 * idx + 2; segmentize(original, data, lo, mid, left); segmentize(original, data, mid + 1, hi, right); data[idx] = aggregate(data[left], data[right]); } }; var result = []; if (array && array.length) { segmentize(array, result, 0, array.length - 1, 0); } var tree = new SegmentTree(placeholder, aggregate); tree._data = result; tree._original = array; return tree; }; /** * Queries the SegmentTree in given range based on the set aggregate. * * @param {Number} start The start index of the interval. * @param {Number} end The end index of the interval. */ SegmentTree.prototype.query = function (start, end) { if (start > end) { throw new Error('The start index should be smaller by the end index'); } var findEl = function (originalArrayStart, originalArrayEnd, current) { if (start > originalArrayEnd) { return this._invalidValue; } if (end < originalArrayStart) { return this._invalidValue; } if (start === originalArrayStart && end === originalArrayEnd || originalArrayStart === originalArrayEnd) { return this._data[current]; } var originalArrayMid = Math.floor((originalArrayStart + originalArrayEnd) / 2); return this._aggregate( findEl(originalArrayStart, originalArrayMid, 2 * current + 1), findEl(originalArrayMid + 1, originalArrayEnd, 2 * current + 2) ); }.bind(this); return findEl(0, this._original.length - 1, 0, this._aggregate); }; exports.SegmentTree = SegmentTree; }(typeof window === 'undefined' ? module.exports : window)); ================================================ FILE: src/data-structures/size-balanced-tree.js ================================================ /** * Size balanced tree is a data structure which is * a type of self-balancing binary search tree that use * the tree size attribute for re-balancing the tree. * * @example * * var SBTree = require('../src/data-structures/size-balanced-tree').SBTree; * var sbTree = new SBTree(); * * var treeNode = sbTree.push({ * name: 'John', * surname: 'Smith' * }); * sbTree.insert(0, { * name: 'Pavlo', * surname: 'Popov' * }); * sbTree.insert(1, { * name: 'Garry', * surname: 'Fisher' * }); * sbTree.insert(0, { * name: 'Derek', * surname: 'Anderson' * }); * * console.log(sbTree.get(0)); // { name: 'Derek', surname: 'Anderson' } * * @module data-structures/size-balanced-tree */ function CreateSBTreeClass (Node, Nil, updateChild) { 'use strict'; function LeftRotate(node, childNode) { /* Before rotate: node / \ NL childNode / \ CL CR After rotate: childNode / \ node CR / \ NL CL */ node.right = childNode.left; if (node.right !== Nil) { node.right.parent = node; } childNode.left = node; // setting childNode's parent to node's parent updateChild(node, childNode); return childNode; } function RightRotate(node, childNode) { /* Before rotate: node / \ childNode NR / \ CL CR After rotate: childNode / \ CL node / \ CR NR */ node.left = childNode.right; if (node.left !== Nil) { node.left.parent = node; } childNode.right = node; // setting childNode's parent to node's parent updateChild(node, childNode); return childNode; } function maintain(node, leftChild) { if (node === Nil) { return node; } var savedNode = node; if (leftChild) { if (node.left.left.size > node.right.size) { node = RightRotate(node, node.left); } else if (node.left.right.size > node.right.size) { LeftRotate(node.left, node.left.right); node = RightRotate(node, node.left); } } else { if (node.right.right.size > node.left.size) { node = LeftRotate(node, node.right); } else if (node.right.left.size > node.left.size) { RightRotate(node.right, node.right.left); node = LeftRotate(node, node.right); } } if (node === savedNode) { return node; } maintain(node.left, false); maintain(node.right, true); node = maintain(node, true); node = maintain(node, false); return node; } function maintainSizeBalancedTree(node) { while (node.parent !== Nil) { var childNode = node; node = node.parent; if (node.left === childNode) { node = maintain(node, true); } else { node = maintain(node, false); } } return node; } function findNodeAtPos(node, pos) { while (pos !== node.left.size) { if (pos < node.left.size) { node = node.left; } else { pos -= node.left.size; pos -= 1; //The node element should be decrement by 1 node = node.right; } } return node; } /** * Size Balanced Tree. * * @public * @constructor */ var SBTree = function () {}; SBTree.prototype = { _root: Nil, get size() { return this._root.size; }, get root() { return this._root; }, binarySearch: function (cmp, value) { var left = -1; var right = this.size; while (left + 1 < right) { var middle = (left + right) >> 1; // jshint ignore:line var result = cmp(this.get(middle).value, value); if (result <= 0) { left = middle; } else { right = middle; } } return left + 1; }, }; SBTree.prototype.get = function (pos) { if (pos >= this.size) { return Nil; } return findNodeAtPos(this._root, pos); }; SBTree.prototype.getIndex = function (node) { var index = node.left.size; while (node !== this._root) { var parent = node.parent; if (parent.right === node) { index += parent.left.size + 1; } node = parent; } return index; }; SBTree.prototype.shiftDown = function (node) { var direction = 0; while (true) { if (node.left !== Nil && node.right !== Nil) { switch (direction) { case 0: RightRotate(node, node.left); break; case 1: LeftRotate(node, node.right); break; } direction = 1 - direction; } else if (node.left !== Nil) { RightRotate(node, node.left); } else if (node.right !== Nil) { LeftRotate(node, node.right); } else { break; // The node could be able to removed } } }; SBTree.prototype.insertLeafNode = function (node) { var parent = node.parent; while (parent !== Nil) { parent.size = parent.size + 1; parent = parent.parent; } }; SBTree.prototype.removeLeafNode = function (node) { var parent = node.parent; while (parent !== Nil) { parent.size = parent.size - 1; parent = parent.parent; } }; SBTree.prototype.insert = function (pos, value) { var node = Nil; var newNode = new Node(value, Nil, Nil, Nil, 1); if (pos === this.size) { if (pos > 0) { node = findNodeAtPos(this._root, pos - 1); node.right = newNode; } } else { node = findNodeAtPos(this._root, pos); if (node.left !== Nil) { this.shiftDown(node); } node.left = newNode; } newNode.parent = node; this.insertLeafNode(newNode); this._root = maintainSizeBalancedTree(newNode); return newNode; }; /** * Push a value to the end of tree. * Complexity: O(log N). * * @public * @method * @param {Object} value Value. */ SBTree.prototype.push = function (value) { this.insert(this.size, value); }; SBTree.prototype.removeNode = function (node) { this.shiftDown(node); var maintainNode = node.parent; if (maintainNode.left === node) { maintainNode.left = Nil; } else if (maintainNode.right === node) { maintainNode.right = Nil; } this.removeLeafNode(node); this._root = maintainSizeBalancedTree(maintainNode); return node; }; SBTree.prototype.remove = function (pos) { if (pos >= this._root.size) { return Nil; // There is no element to remove } var node = findNodeAtPos(this._root, pos); return this.removeNode(node); }; return SBTree; } (function (exports) { 'use strict'; /** * Node constructor of the Size-Balanced tree. * * @private * @constructor * @param {Object} value Value assigned to the node. * @param {Node} parent Parent node. * @param {Node} left Left node. * @param {Node} right Right node. * @param {Number} size Node's, means the Node count of this . */ var NodeConstructor = function (value, parent, left, right, size) { this.value = value; this.parent = parent; this.left = left; this.right = right; this.size = size; }; var createNil = function (Node, value) { var Nil = new Node(value, null, null, null, 0); Nil.parent = Nil; Nil.left = Nil; Nil.right = Nil; return Nil; }; /** * Update node's size. * * @private * @method */ var updateSize = function () { this.size = this.left.size + this.right.size + 1; }; // node, childNode must not be Nil, // if the childNode turn out to be the root, the parent should be Nil var updateChild = function (node, childNode) { var parent = node.parent; node.parent = childNode; childNode.parent = parent; node.updateSize(); childNode.updateSize(); if (parent.right === node) { parent.right = childNode; parent.updateSize(); } else if (parent.left === node) { parent.left = childNode; parent.updateSize(); } // otherwise parent is Nil }; var Node = function () { NodeConstructor.apply(this, arguments); }; Node.prototype.updateSize = updateSize; var Nil = createNil(Node, null); exports.NodeConstructor = NodeConstructor; exports.createNil = createNil; exports.updateSize = updateSize; exports.updateChild = updateChild; exports.CreateSBTreeClass = CreateSBTreeClass; exports.Node = Node; exports.Nil = Nil; exports.SBTree = CreateSBTreeClass(Node, Nil, updateChild); })(typeof module === 'undefined' ? window : module.exports); ================================================ FILE: src/data-structures/splay-tree.js ================================================ /** * Splay Tree. * * @example * var STree = require('path-to-algorithms/src/data-structures'+ * '/splay-tree'); * var sTree = new STree.SplayTree(); * sTree.insert(10); * sTree.insert(5); * sTree.insert(15); * sTree.insert(7); * sTree.insert(12); * sTree.search(10); * console.log(sTree._root); * sTree.remove(10); * console.log(sTree._root); * sTree.search(15); * console.log(sTree._root); * * @module data-structures/splay-tree */ (function (exports) { 'use strict'; /** * Node of the tree. * * @public * @constructor * @param {Number|String} value Value of the node. * @param {Node} left Left sibling. * @param {Node} right Right sibling. * @param {Node} parent Parent of the node. */ exports.Node = function (value, left, right, parent) { /** * @member {Number|String} */ this.value = value; this._left = left; this._right = right; this._parent = parent; }; /** * Splay tree. * {@link http://en.wikipedia.org/wiki/Splay_tree} * @public * @constructor */ exports.SplayTree = function () { this._root = null; }; /** * Splays a node to the root.

* * @private * @method * @param {Node} node Node to be splayed. * @returns {Node} The same node from the parameter, post splayed. */ exports.SplayTree.prototype._splay = function (node) { while (this._root !== node) { var hasParent = node._parent !== null; var hasGrandparent = (hasParent && node._parent._parent !== null); if (hasParent && hasGrandparent) { var isLeftChild = node._parent._left === node; var isParentLeftChild = node._parent._parent._left === node._parent; if ( (isLeftChild && isParentLeftChild) || (!isLeftChild && !isParentLeftChild) ) { node = this._zigZig(node); } else { node = this._zigZag(node); } } else { node = this._zig(node); } } return node; }; /** * Performs a zig-zig splay pattern

* * @private * @method * @param {Node} node Node to be zig-zig'd. * @returns {Node} The same node from the parameter, post splayed. */ exports.SplayTree.prototype._zigZig = function (node) { var parent = node._parent; var grandParent = node._parent._parent; var greatGrandParent = grandParent._parent !== undefined ? grandParent._parent : null; var orientation = (parent._right === node) ? '_right' : '_left'; var oppositeOrientation = (orientation === '_left') ? '_right' : '_left'; var grandParentOrientation = (greatGrandParent !== null && greatGrandParent._left === grandParent) ? '_left' : '_right'; // Fix grandparent & great if it exists/not root if (this._root === grandParent) { this._root = node; } else { greatGrandParent[grandParentOrientation] = node; } grandParent._parent = parent; // Fix grandparent subtree grandParent[orientation] = parent[oppositeOrientation]; if (grandParent[orientation] !== null) { grandParent[orientation]._parent = grandParent; } // Fix Parent parent[oppositeOrientation] = grandParent; parent[orientation] = node[oppositeOrientation]; if (parent[orientation] !== null) { parent[orientation]._parent = parent; } parent._parent = node; // Fix Curr Node node[oppositeOrientation] = parent; if (node === this._root) { node._parent = null; } else if (greatGrandParent !== null) { node._parent = greatGrandParent; } return node; }; /** * Performs a zig-zag splay pattern

* * @private * @method * @param {Node} node Node to be zig-zag'd. * @returns {Node} The same node from the parameter, post splayed. */ exports.SplayTree.prototype._zigZag = function (node) { var parent = node._parent; var grandParent = parent._parent; var greatGrandParent = grandParent._parent !== undefined ? grandParent._parent : null; var orientation = (parent._left === node) ? '_left' : '_right'; var oppositeOrientation = (orientation === '_right') ? '_left' : '_right'; var grandParentOrientation = (greatGrandParent !== null && greatGrandParent._left === grandParent) ? '_left' : '_right'; // Fix GrandParent if (this._root === grandParent) { this._root = node; } else { greatGrandParent[grandParentOrientation] = node; } grandParent._parent = node; // Fix GrandParent subtree grandParent[oppositeOrientation] = node[orientation]; if (grandParent[oppositeOrientation] !== null) { grandParent[oppositeOrientation]._parent = grandParent; } // Fix Parent parent[orientation] = node[oppositeOrientation]; if (parent[orientation] !== null) { parent[orientation]._parent = parent; } parent._parent = node; // Fix Curr Node node[orientation] = grandParent; node[oppositeOrientation] = parent; if (this._root === node) { node._parent = null; } else if (greatGrandParent !== null) { node._parent = greatGrandParent; } return node; }; /** * Performs a zig splay pattern

* * @private * @method * @param {Node} node Node to be zig'd. * @returns {Node} The same node from the parameter, post splayed. */ exports.SplayTree.prototype._zig = function (node) { var parent = node._parent; var orientation = (parent._right === node) ? '_right' : '_left'; var oppositeOrientation = (orientation === '_right') ? '_left' : '_right'; if (this._root === parent) { this._root = node; } // Fix Parent parent[orientation] = node[oppositeOrientation]; if (parent[orientation] !== null) { parent[orientation]._parent = parent; } parent._parent = node; // Fix Curr Node node[oppositeOrientation] = parent; node._parent = null; return node; }; /** * Inserts a node into the splay tree.

* Time complexity: O(log N) in the average case * and amortized O(log n) in the worst case. * * @public * @method * @param {Number|String} value Node value. * @param {Node} current Current node. */ exports.SplayTree.prototype.insert = function (value, current) { if (this._root === null) { this._root = new exports.Node(value, null, null, null); return; } var insertKey; current = current || this._root; if (current.value > value) { insertKey = '_left'; } else { insertKey = '_right'; } if (!current[insertKey]) { current[insertKey] = new exports.Node(value, null, null, current); this._splay(current[insertKey]); } else { this.insert(value, current[insertKey]); } }; /** * In-order traversal from the given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.SplayTree.prototype._inorder = function (current, callback) { if (!current) { return; } this._inorder(current._left, callback); if (typeof callback === 'function') { callback(current); } this._inorder(current._right, callback); }; /** * In-order traversal of the whole Splay Tree. * * @public * @method * @param {Function} callback Callback which will be * called for each traversed node. */ exports.SplayTree.prototype.inorder = function (callback) { return this._inorder(this._root, callback); }; /** * Post-order traversal from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which will * be called for each traversed node */ exports.SplayTree.prototype._postorder = function (current, callback) { if (!current) { return; } if (typeof callback === 'function') { callback(current); } this._postorder(current._left, callback); this._postorder(current._right, callback); }; /** * Post-order traversal of the whole tree. * * @public * @param {Function} callback Callback which * will be called for each traversed node. */ exports.SplayTree.prototype.postorder = function (callback) { return this._postorder(this._root, callback); }; /** * Pre-order traversal of the tree from given node. * * @private * @param {Node} current Node from which to start the traversal. * @param {Function} callback Callback which * will be called for each traversed node. */ exports.SplayTree.prototype._preorder = function (current, callback) { if (!current) { return; } if (typeof callback === 'function') { callback(current); } this._preorder(current._left, callback); this._preorder(current._right, callback); }; /** * Pre-order preorder traversal of the whole tree. * * @public * @param {Function} callback Callback which will * be called for each traversed node. */ exports.SplayTree.prototype.preorder = function (callback) { return this._preorder(this._root, callback); }; /** * Finds a node by it's value.

* Average time complexity: O(log N). * * @public * @param {Number|String} value of the node which should be found. */ exports.SplayTree.prototype.search = function (value) { var node = this._search(value, this._root); return this._splay(node); }; /** * Finds a node by it's value.

* Average time complexity: O(log N). * * @public * @param {Number|String} value of the node which should be found. */ exports.SplayTree.prototype._splaylessSearch = function (value) { return this._search(value, this._root); }; /** * Finds a node by it's value in a given sub-tree. * Average time complexity: O(log N). * * @private * @param {Number|String} value of the node which should be found. * @param {Node} current node to be checked. */ exports.SplayTree.prototype._search = function (value, current) { if (!current) { return null; } if (current.value === value) { return current; } if (current.value > value) { return this._search(value, current._left); } if (current.value < value) { return this._search(value, current._right); } }; /** * Replaces given child with new one, for given parent. * * @private * @param {Node} parent Parent node. * @param {Node} oldChild Child to be replaced. * @param {Node} newChild Child replacement. */ exports.SplayTree.prototype._replaceChild = function (parent, oldChild, newChild) { if (!parent) { this._root = newChild; this._root._parent = null; } else { if (parent._left === oldChild) { parent._left = newChild; } else { parent._right = newChild; } if (newChild) { newChild._parent = parent; } } }; /** * Removes node with given value from the tree.

* Average runtime complexity: O(log N). * * @public * @param {Number|String} value Value to be removed * @returns {Boolean} True/false depending * on whether the given node is removed. */ exports.SplayTree.prototype.remove = function (value) { var node = this._splaylessSearch(value); if (!node) { return false; } if (node._left && node._right) { var min = this._findMin(node._right); var temp = node.value; node.value = min.value; min.value = temp; return this.remove(min); } else { if (node._parent !== null) { if (node._left) { this._replaceChild(node._parent, node, node._left); } else if (node._right) { this._replaceChild(node._parent, node, node._right); } else { this._replaceChild(node._parent, node, null); } this._splay(node._parent); } else { this._root = null; } return true; } }; /** * Finds the node with minimum value in given sub-tree. * * @private * @param {Node} node Root of the sub-tree. * @param {Number|String} current Current minimum value of the sub-tree. * @returns {Node} Node with the minimum value in the sub-tree. */ exports.SplayTree.prototype._findMin = function (node, current) { current = current || { value: Infinity }; if (!node) { return current; } if (current.value > node.value) { current = node; } return this._findMin(node._left, current); }; exports.SplayTree.prototype._isBalanced = function (current) { if (!current) { return true; } return this._isBalanced(current._left) && this._isBalanced(current._right) && Math.abs(this._getHeight(current._left) - this._getHeight(current._right)) <= 1; }; /** * Returns whether the Splay Tree is balanced. * * @public * @returns {Boolean} Whether the tree is balanced or not. */ exports.SplayTree.prototype.isBalanced = function () { return this._isBalanced(this._root); }; /** * Finds the diameter of the Splay Tree. * * @public * @returns {Number} The longest path in the tree. */ exports.SplayTree.prototype.getDiameter = function () { var getDiameter = function (root) { if (!root) { return 0; } var leftHeight = this._getHeight(root._left); var rightHeight = this._getHeight(root._right); var path = leftHeight + rightHeight + 1; return Math.max(path, getDiameter(root._left), getDiameter(root._right)); }.bind(this); return getDiameter(this._root); }; /** * Returns the height of the tree. * * @public * @returns {Number} The height of the tree. */ exports.SplayTree.prototype.getHeight = function () { return this._getHeight(this._root); }; /** * Recursive worker function for getHeight() * * @public * @param {Node} node The node of the current recursive frame. * @returns {Number} The height of the tree. */ exports.SplayTree.prototype._getHeight = function (node) { if (!node) { return 0; } return 1 + Math.max(this._getHeight(node._left), this._getHeight(node._right)); }; /** * Finds the lowest common ancestor of two nodes. * * @public * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.SplayTree.prototype.lowestCommonAncestor = function (firstNode, secondNode) { return this._lowestCommonAncestor(firstNode, secondNode, this._root); }; /** * Obtains the lowest common ancestor for the given nodes. * * @private * @param {Node} firstNode First node to be considered when checking * for ancestor. * @param {Node} secondNode Second node to be considered when checking * for ancestor. * @param {Node} current Current node. * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.SplayTree.prototype._lowestCommonAncestor = function (firstNode, secondNode, current) { var firstNodeInLeft = this._existsInSubtree(firstNode, current._left); var secondNodeInLeft = this._existsInSubtree(secondNode, current._left); var firstNodeInRight = this._existsInSubtree(firstNode, current._right); var secondNodeInRight = this._existsInSubtree(secondNode, current._right); if ((firstNodeInLeft && secondNodeInRight) || (firstNodeInRight && secondNodeInLeft)) { return current; } if (secondNodeInLeft && firstNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._left); } if (secondNodeInRight && secondNodeInLeft) { return this._lowestCommonAncestor(firstNode, secondNode, current._right); } return null; }; /** * Checks if a given node exists in a subtree. * * @private * @param {Node} node Node to check for. * @param {Node} root Root node of a given subtree. * @returns {Node} The lowest common ancestor of the two nodes or null. */ exports.SplayTree.prototype._existsInSubtree = function (node, root) { if (!root) { return false; } if (node === root.value) { return true; } return this._existsInSubtree(node, root._left) || this._existsInSubtree(node, root._right); }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/data-structures/suffix-tree.js ================================================ (function (exports) { 'use strict'; function Node(val) { this.value = val; this.nodes = {}; } function SuffixTree() { this.root = new Node(); } SuffixTree.prototype.addNode = (function () { function maxPrefix(a, b) { var res = []; for (var i = 0; i < Math.min(a.length, b.length); i += 1) { if (a[i] === b[i]) { res.push(a[i]); } else { return ''; } } return res.join(''); } function addNode(suffix, current) { // Empty string already exists in the suffix tree if (!suffix) { return; } // The suffix is already inside the tree if (current.value === suffix) { return; } // Insert recursively if (current.nodes[suffix[0]]) { return addNode(suffix.substr(1, suffix.length), current.nodes[suffix[0]]); } // Find the maximum prefix and split the current node if prefix exists var prefix = maxPrefix(current.value, suffix); if (prefix.length) { var temp = current.value; var suffixSuffix = suffix.substr(prefix.length, suffix.length); var currentSuffix = temp.substr(prefix.length, temp.length); current.value = prefix; addNode(currentSuffix, current); addNode(suffixSuffix, current); // If prefix doesn't exists add new child node } else { current.nodes[suffix[0]] = new Node(suffix); } } return function (suffix) { addNode(suffix, this.root); }; }()); // O(n^2) or even O(n^3) because of maxPrefix SuffixTree.prototype.build = function (string) { this.root.value = string; for (var i = 1; i < string.length; i += 1) { this.addNode(string.substr(i, string.length)); } }; exports.Node = Node; exports.SuffixTree = SuffixTree; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/data-structures/vertex.js ================================================ (function (exports) { 'use strict'; /** * Graph vertex. * * @constructor * @public * @param {Number} id Id of the vertex. * @module data-structures/vertex */ exports.Vertex = function (id) { this.id = id; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/graphics/bezier.js ================================================ (function (exports) { 'use strict'; function linearBezier(p0, p1, t) { return p0 + t * (p1 - p0); } function quadraticBezier(p0, p1, p2, t) { return linearBezier(linearBezier(p0, p1, t), linearBezier(p1, p2, t), t); } function cubicBezier(p0, p1, p2, p3, t) { return linearBezier(quadraticBezier(p0, p1, p2, t), quadraticBezier(p1, p2, p3, t), t); } exports.linearBezier = linearBezier; exports.quadraticBezier = quadraticBezier; exports.cubicBezier = cubicBezier; })(typeof exports === 'undefined' ? window : exports); ================================================ FILE: src/graphics/bresenham-line-drawing.js ================================================ (function (exports) { 'use strict'; /** * Draws (prints) the given coordinates * @param {number} x The first coordinate of the point * @param {number} y The second coordinate of the point */ function drawPoint(x, y) { console.log(x, y); } /** * Bresenham's line drawing algorithm. * It has complexity O(n) * @param {number} x1 The first coordinate of the beginning of the line * @param {number} y1 The second coordinate of the beginning of the line * @param {number} x2 The first coordinate of the end of the line * @param {number} y2 The second coordinate of the end of the line * @param {function} draw Optional custom drawing function. */ function drawLine(x1, y1, x2, y2, draw) { var drawPointStrategy = draw || drawPoint; var dx = Math.abs(x2 - x1); var dy = Math.abs(y2 - y1); var cx = (x1 < x2) ? 1 : -1; var cy = (y1 < y2) ? 1 : -1; var error = dx - dy; var doubledError; while (x1 !== x2 || y1 !== y2) { drawPointStrategy(x1, y1); doubledError = error + error; if (doubledError > -dy) { error -= dy; x1 += cx; } if (doubledError < dx) { error += dx; y1 += cy; } } } exports.drawLine = drawLine; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/graphics/graham.js ================================================ (function(exports) { 'use strict'; const slope = (p, a) => (a.y - p.y) / (a.x - p.x); const dist = (a, b) => Math.sqrt((b.y - a.y) * (b.y - a.y) + (b.x - a.x) * (b.x - a.x)); const sort = (p, memo, a, b) => { const sa = slope(p, a); const sb = slope(p, b); [[sa, a], [sb, b]].forEach(e => { const el = memo.get(e[0]); if (!el || dist(p, el) < dist(p, e[1])) { memo.set(e[0], e[1]); } }); return sa - sb; }; const ccw = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); /** * Graham's algorithm for calculating the convex hull. * * @public * @module graphics/graham * @param {Array} all * @returns {Array} * * @example * const points = [ * { x: 0, y: 0 }, * { x: 1, y: 0 }, * { x: 0, y: 1 }, * { x: 0.15, y: 0.15 }, * { x: 0.5, y: 0.5 } * ]; * const list = convexHull(points); * // [{ x: 0, y: 0 }, * // { x: 1, y: 0 }, * // { x: 0.5, y: 0.5 }, * // { x: 0, y: 1 }] */ const convexHull = all => { if (!all.length) { return []; } const p = all.reduce((a, c) => { if (a.y < c.y) { return a; } if (a.y > c.y) { return c; } if (a.x < c.x) { return a; } return c; }); const memo = new Map(); const stack = []; all .sort(sort.bind(null, p, memo)) .filter(c => memo.get(slope(p, c)) === c) .forEach(p => { while (stack.length > 1 && ccw(stack[stack.length - 2], stack[stack.length - 1], p) < 0) { stack.pop(); } stack.push(p); }); return stack; }; exports.convexHull = convexHull; })(typeof exports === 'undefined' ? window : exports); ================================================ FILE: src/graphs/others/tarjan-connected-components.js ================================================ (function (exports) { 'use strict'; /** * Tarjan's algorithm for finding the connected components in a graph.

* Time complexity: O(|E| + |V|) where E is a number of edges and |V| * is the number of nodes. * * @public * @module graphs/others/tarjan-connected-components * @param {Array} graph Adjacency list, which represents the graph. * @returns {Array} Connected components. * * @example * var tarjanConnectedComponents = * require('path-to-algorithms/src/graphs/' + * 'others/tarjan-connected-components').tarjanConnectedComponents; * var graph = { * v1: ['v2', 'v5'], * v2: [], * v3: ['v1', 'v2', 'v4', 'v5'], * v4: [], * v5: [] * }; * var vertices = topsort(graph); // ['v3', 'v4', 'v1', 'v5', 'v2'] */ function tarjanConnectedComponents(graph) { graph = graph || {}; const indexes = {}; const lowIndexes = {}; const onStack = {}; const result = []; const stack = []; var index = 1; const connectedComponent = function (node) { stack.push(node); onStack[node] = true; indexes[node] = index; lowIndexes[node] = index; index += 1; graph[node].forEach(function (n) { if (indexes[n] === undefined) { connectedComponent(n); lowIndexes[node] = Math.min(lowIndexes[n], lowIndexes[node]); } else if (onStack[n]) { lowIndexes[node] = Math.min(lowIndexes[node], indexes[n]); } }); // This is a "root" node const cc = []; if (indexes[node] === lowIndexes[node]) { var current; do { current = stack.pop(); onStack[current] = false; cc.push(current); } while (stack.length > 0 && node !== current); result.push(cc); } }; Object.keys(graph) .forEach(function (n) { if (!indexes[n]) { connectedComponent(n); } }); return result; } exports.tarjanConnectedComponents = tarjanConnectedComponents; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/graphs/others/topological-sort.js ================================================ (function (exports) { 'use strict'; var topologicalSort = (function () { function topologicalSortHelper(node, visited, temp, graph, result) { temp[node] = true; var neighbors = graph[node]; for (var i = 0; i < neighbors.length; i += 1) { var n = neighbors[i]; if (temp[n]) { throw new Error('The graph is not a DAG'); } if (!visited[n]) { topologicalSortHelper(n, visited, temp, graph, result); } } temp[node] = false; visited[node] = true; result.push(node); } /** * Topological sort algorithm of a directed acyclic graph.

* Time complexity: O(|E| + |V|) where E is a number of edges * and |V| is the number of nodes. * * @public * @module graphs/others/topological-sort * @param {Array} graph Adjacency list, which represents the graph. * @returns {Array} Ordered vertices. * * @example * var topsort = * require('path-to-algorithms/src/graphs/' + * 'others/topological-sort').topologicalSort; * var graph = { * v1: ['v2', 'v5'], * v2: [], * v3: ['v1', 'v2', 'v4', 'v5'], * v4: [], * v5: [] * }; * var vertices = topsort(graph); // ['v3', 'v4', 'v1', 'v5', 'v2'] */ return function (graph) { var result = []; var visited = []; var temp = []; for (var node in graph) { if (!visited[node] && !temp[node]) { topologicalSortHelper(node, visited, temp, graph, result); } } return result.reverse(); }; }()); exports.topologicalSort = topologicalSort; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/graphs/searching/bfs.js ================================================ (function (exports) { 'use strict'; var bfs = (function () { function buildPath(parents, targetNode) { var result = [targetNode]; while (parents[targetNode] !== null) { targetNode = parents[targetNode]; result.push(targetNode); } return result.reverse(); } /** * Breath-First graph searching algorithm. * Returns the shortest path between startNode and targetNode.

* Time complexity: O(|V|^2). * * @public * @module graphs/searching/bfs * @param {Array} graph Adjacency matrix, which represents the graph. * @param {Number} startNode Start node. * @param {Number} targetNode Target, which should be reached. * @returns {Array} Shortest path from startNode to targetNode. * * @example * var bfs = require('path-to-algorithms/src/graphs/searching/bfs').bfs; * var graph = [[1, 1, 0, 0, 1, 0], * [1, 0, 1, 0, 1, 0], * [0, 1, 0, 1, 0, 0], * [0, 0, 1, 0, 1, 1], * [1, 1, 0, 1, 0, 0], * [0, 0, 0, 1, 0, 0]]; * var shortestPath = bfs(graph, 1, 5); // [1, 2, 3, 5] */ return function (graph, startNode, targetNode) { var parents = []; var queue = []; var visited = []; var current; queue.push(startNode); parents[startNode] = null; visited[startNode] = true; while (queue.length) { current = queue.shift(); if (current === targetNode) { return buildPath(parents, targetNode); } for (var i = 0; i < graph.length; i += 1) { if (i !== current && graph[current][i] && !visited[i]) { parents[i] = current; visited[i] = true; queue.push(i); } } } return null; }; }()); exports.bfs = bfs; }((typeof window === 'undefined') ? module.exports : window)); ================================================ FILE: src/graphs/searching/dfs.js ================================================ (function (exports) { 'use strict'; var dfs = (function () { function hasPath(graph, current, goal) { var stack = []; var visited = []; var node; stack.push(current); visited[current] = true; while (stack.length) { node = stack.pop(); if (node === goal) { return true; } for (var i = 0; i < graph[node].length; i += 1) { if (graph[node][i] && !visited[i]) { stack.push(i); visited[i] = true; } } } return false; } /** * Depth-First graph searching algorithm. * Returns whether there's a path between two nodes in a graph.

* Time complexity: O(|V|^2). * * @module graphs/searching/dfs * @public * @param {Array} graph Adjacency matrix, which represents the graph. * @param {Number} start Start node. * @param {Number} goal Target node. * @return {Boolean} Returns true if path between two nodes exists. * * @example * var dfs = require('../src/graphs/searching/dfs').dfs; * var graph = [[1, 1, 0, 0, 1, 0], * [1, 0, 1, 0, 1, 0], * [0, 1, 0, 1, 0, 0], * [0, 0, 1, 0, 1, 1], * [1, 1, 0, 1, 0, 0], * [0, 0, 0, 1, 0, 0]]; * var pathExists = dfs(graph, 1, 5); // true */ return function (graph, start, goal) { return hasPath(graph, start, goal); }; }()); exports.dfs = dfs; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/graphs/shortest-path/bellman-ford.js ================================================ /** * Bellman–Ford algorithm computes shortest paths from a single source * vertex to all of the other vertices in a weighted digraph * (negative weights allowed).

* Time complexity: O(|V||E|) where V and E are the number of * vertices and edges respectively. * * @example * * var BellmanFord = * require('path-to-algorithms/src/graphs/shortest-path/bellman-ford'); * var Edge = BellmanFord.Edge; * var bellmanFord = BellmanFord.bellmanFord; * var edges = []; * var vertexes = [ * new Vertex(0), * new Vertex(1), * new Vertex(2), * new Vertex(3), * new Vertex(4) * ]; * * edges.push(new Edge(0, 1, -1)); * edges.push(new Edge(0, 2, 4)); * edges.push(new Edge(1, 2, 3)); * edges.push(new Edge(1, 3, 2)); * edges.push(new Edge(3, 1, 1)); * edges.push(new Edge(4, 3, -3)); * edges.push(new Edge(1, 4, 2)); * edges.push(new Edge(3, 2, 5)); * * // { * // parents: { '0': null, '1': 0, '2': 1, '3': 4, '4': 1 }, * // distances: { '0': 0, '1': -1, '2': 2, '3': -2, '4': 1 } * // } * var pathInfo = bellmanFord(vertexes, edges, 0); * * @module graphs/shortest-path/bellman-ford */ (function (exports) { 'use strict'; exports.Vertex = require('../../data-structures/vertex').Vertex; exports.Edge = require('../../data-structures/edge').Edge; /** * Computes shortest paths from a single source * vertex to all of the other vertices. * * @public * @param {Array} vertexes Vertices of the graph. * @param {Array} edges Edges of the graph. * @param {Number} source Start vertex. * @returns {Object} Object with two arrays (parents and distances) * with shortest-path information or undefined if the graph * has a negative cycle. */ exports.bellmanFord = function (vertexes, edges, source) { var distances = {}; var parents = {}; var c; if (source) { for (var i = 0; i < vertexes.length; i += 1) { distances[vertexes[i].id] = Infinity; parents[vertexes[i].id] = null; } distances[source.id] = 0; for (i = 0; i < vertexes.length - 1; i += 1) { for (var j = 0; j < edges.length; j += 1) { c = edges[j]; if (distances[c.from.id] + c.distance < distances[c.to.id]) { distances[c.to.id] = distances[c.from.id] + c.distance; parents[c.to.id] = c.from.id; } } } for (i = 0; i < edges.length; i += 1) { c = edges[i]; if (distances[c.from.id] + c.distance < distances[c.to.id]) { return undefined; } } } return { parents: parents, distances: distances }; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/graphs/shortest-path/dijkstra.js ================================================ (function (exports) { 'use strict'; var dijkstra = (function () { var Heap = require('../../data-structures/heap.js').Heap; var current; var visited; var distance; var unvisited; /** * Creates a new node instance. * * @constructor * @private * @param {Number} id Id of the node. * @param {Number} distance Distance from the beginning. */ function Node(id, distance) { this.node = id; this.distance = distance; } /** * Compares the distances between two nodes. * * @private * @param {Node} a 1st node. * @param {Node} b 2nd node. * @returns {number} diff between node distances. */ function compareNodesDistance(a, b) { return b.distance - a.distance; } /** * Initialize all variables used for the algorithm. * * @private * @param {number} src Start node. * @param {Array} graph A distance matrix of the graph. */ function init(src, graph) { var currentTemp; current = {}; visited = []; distance = []; unvisited = new Heap(compareNodesDistance); for (var i = 0; i < graph.length; i += 1) { currentTemp = new Node(); if (src === i) { currentTemp.distance = 0; } else { currentTemp.distance = Infinity; } currentTemp.node = i; visited[i] = false; distance[i] = currentTemp; unvisited.add(currentTemp); } current.node = src; current.distance = 0; } /** * Dijkstra's shortest path algorithm. Finds the minimum * distance between two given nodes using a distance matrix.

* For the implementation is not used the most suitable data structure * (Fibonacci heap) but the Binary heap gives also good results.

* * Time complexity: O(|E|+|V|log(|V|)) where V and E are the number of * vertices and edges respectively. * * @public * @module graphs/shortest-path/dijkstra * @param {Number} src Source node. * @param {Number} dest Destination node. * @param {Array} graph A distance matrix of the graph. * @returns {Number} The shortest distance between two nodes. * * @example * var dijkstra = * require('path-to-algorithms/src/graphs/shortest-path/dijkstra').dijkstra; * var distMatrix = * [[Infinity, 7, 9, Infinity, Infinity, 16], * [7, Infinity, 10, 15, Infinity, Infinity], * [9, 10, Infinity, 11, Infinity, 2], * [Infinity, 15, 11, Infinity, 6, Infinity], * [Infinity, Infinity, Infinity, 6, Infinity, 9], * [16, Infinity, 2, Infinity, 9, Infinity]]; * var shortestDist = dijkstra(0, 2, distMatrix); // 9 */ return function (src, dest, graph) { var tempDistance = 0; init(src, graph); while (current.node !== dest && isFinite(current.distance)) { for (var i = 0; i < graph.length; i += 1) { if (current.node !== i && //if it's not the current node !visited[i] && //and if we haven't visited this node //and this node is sibling of the current... Number.isFinite(graph[i][current.node])) { tempDistance = current.distance + graph[i][current.node]; if (tempDistance < distance[i].distance) { distance[i].distance = tempDistance; unvisited.update(current); } } } visited[current.node] = true; current = unvisited.extract(); } if (distance[dest]) { return distance[dest].distance; } return Infinity; }; })(); exports.dijkstra = dijkstra; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/graphs/shortest-path/floyd-warshall.js ================================================ (function (exports) { 'use strict'; var floydWarshall = (function () { /** * Matrix used for the algorithm. */ var dist; /** * Initialize the distance matrix. * * @private * @param {Array} graph Distance matrix of the array. * @return {Array} Distance matrix used for the algorithm. */ function init(graph) { var dist = []; var size = graph.length; for (var i = 0; i < size; i += 1) { dist[i] = []; for (var j = 0; j < size; j += 1) { if (i === j) { dist[i][j] = 0; } else if (!isFinite(graph[i][j])) { dist[i][j] = Infinity; } else { dist[i][j] = graph[i][j]; } } } return dist; } /** * Floyd-Warshall algorithm. Finds the shortest path between * each two vertices.

* Complexity: O(|V|^3) where V is the number of vertices. * * @public * @module graphs/shortest-path/floyd-warshall * @param {Array} graph A distance matrix of the graph. * @return {Array} Array which contains the shortest * distance between each two vertices. * * @example * var floydWarshall = * require('path-to-algorithms/src/graphs/shortest-path/floyd-warshall').floydWarshall; * var distMatrix = * [[Infinity, 7, 9, Infinity, Infinity, 16], * [7, Infinity, 10, 15, Infinity, Infinity], * [9, 10, Infinity, 11, Infinity, 2], * [Infinity, 15, 11, Infinity, 6, Infinity], * [Infinity, Infinity, Infinity, 6, Infinity, 9], * [16, Infinity, 2, Infinity, 9, Infinity]]; * * // [ [ 0, 7, 9, 20, 20, 11 ], * // [ 7, 0, 10, 15, 21, 12 ], * // [ 9, 10, 0, 11, 11, 2 ], * // [ 20, 15, 11, 0, 6, 13 ], * // [ 20, 21, 11, 6, 0, 9 ], * // [ 11, 12, 2, 13, 9, 0 ] ] * var shortestDists = floydWarshall(distMatrix); */ return function (graph) { dist = init(graph); var size = graph.length; for (var k = 0; k < size; k += 1) { for (var i = 0; i < size; i += 1) { for (var j = 0; j < size; j += 1) { if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } return dist; }; }()); exports.floydWarshall = floydWarshall; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/graphs/spanning-trees/kruskal.js ================================================ // Kruskal's algorithm for minimal spanning tree implemented with the UnionFind datastructure. (function(exports) { 'use strict'; var QuickUnion = require('../../sets/quickunion').QuickUnion; var mergeSort = require('../../sorting/mergesort').mergeSort; exports.Vertex = require('../../data-structures/vertex').Vertex; exports.Edge = require('../../data-structures/edge').Edge; exports.Graph = function (edges) { this.edges = edges || []; } exports.Graph.prototype.kruskal = (function () { var qunion; var spanningTree; var indexes; /** * Used for sorting the edges * * @private * @param {Vertex} a First operand of the comparison. * @param {Vertex} b Second operand of the comparison. * @return {number} Number which which is equal, greater or * less then zero and indicates whether the first vertex is * "smaller" than the second. */ function compareEdges(a, b) { return a.distance - b.distance; } /** * Initialize the algorithm. * * @private */ function init() { var edge; var i = 0; mergeSort(this.edges, compareEdges); spanningTree = []; indexes = {}; // Create links from vertices to QuickUnion elements for (edge of this.edges) { if (!(edge.from.id in indexes)) { indexes[edge.from.id] = i; i += 1; } if (!(edge.to.id in indexes)) { indexes[edge.to.id] = i; i += 1; } } qunion = new QuickUnion(i); } return function () { init.call(this); var edge; for (edge of this.edges) { var from = indexes[edge.from.id]; var to = indexes[edge.to.id]; if (!qunion.connected(from, to)) { qunion.union(from, to); spanningTree.push(edge); } } return new exports.Graph(spanningTree); } })(); })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/graphs/spanning-trees/prim.js ================================================ /** * Prim's algorithm is a greedy algorithm that finds a minimum * spanning tree for a connected weighted undirected graph. * * @example * * var Prim = require('path-to-algorithms/src/graphs/spanning-trees/prim'); * var Graph = Prim.Graph; * var Edge = Prim.Edge; * var Vertex = Prim.Vertex; * * var graph, edges = []; * edges.push(new Edge(new Vertex(0), new Vertex(1), 4)); * edges.push(new Edge(new Vertex(0), new Vertex(7), 8)); * edges.push(new Edge(new Vertex(1), new Vertex(7), 11)); * edges.push(new Edge(new Vertex(1), new Vertex(2), 8)); * edges.push(new Edge(new Vertex(2), new Vertex(8), 2)); * edges.push(new Edge(new Vertex(2), new Vertex(3), 7)); * edges.push(new Edge(new Vertex(2), new Vertex(5), 4)); * edges.push(new Edge(new Vertex(2), new Vertex(3), 7)); * edges.push(new Edge(new Vertex(3), new Vertex(4), 9)); * edges.push(new Edge(new Vertex(3), new Vertex(5), 14)); * edges.push(new Edge(new Vertex(4), new Vertex(5), 10)); * edges.push(new Edge(new Vertex(5), new Vertex(6), 2)); * edges.push(new Edge(new Vertex(6), new Vertex(8), 6)); * edges.push(new Edge(new Vertex(8), new Vertex(7), 7)); * graph = new Graph(edges, edges.length); * * // { edges: * // [ { e: '1', v: 0, distance: 4 }, * // { e: '2', v: 8, distance: 2 }, * // { e: '3', v: 2, distance: 7 }, * // { e: '4', v: 3, distance: 9 }, * // { e: '5', v: 2, distance: 4 }, * // { e: '6', v: 5, distance: 2 }, * // { e: '7', v: 0, distance: 8 }, * // { e: '8', v: 7, distance: 7 } ], * // nodesCount: 0 } * var spanningTree = graph.prim(); * * @module graphs/spanning-trees/prim */ (function (exports) { 'use strict'; var Heap = require('../../data-structures/heap').Heap; exports.Vertex = require('../../data-structures/vertex').Vertex; exports.Edge = require('../../data-structures/edge').Edge; /** * Graph. * * @constructor * @public * @param {Array} edges Array with graph edges. * @param {Number} nodesCount Number of nodes in graph. */ exports.Graph = function (edges, nodesCount) { this.edges = edges || []; this.nodesCount = nodesCount || 0; }; /** * Executes Prim's algorithm and returns minimum spanning tree. * * @public * @method * @return {Graph} Graph which is the minimum spanning tree. */ exports.Graph.prototype.prim = (function () { var queue; /** * Used for comparitions in the heap * * @private * @param {Vertex} a First operand of the comparition. * @param {Vertex} b Second operand of the comparition. * @return {number} Number which which is equal, greater or * less then zero and indicates whether the first vertex is * "greater" than the second. */ function compareEdges(a, b) { return b.distance - a.distance; } /** * Initialize the algorithm. * * @private */ function init() { queue = new Heap(compareEdges); } return function () { init.call(this); var inTheTree = {}; var startVertex = this.edges[0].e.id; var spannigTree = []; var parents = {}; var distances = {}; var current; inTheTree[startVertex] = true; queue.add({ node: startVertex, distance: 0 }); const process = function (e) { if (inTheTree[e.v.id] && inTheTree[e.e.id]) { return; } var collection = queue.getCollection(); var node; if (e.e.id === current) { node = e.v.id; } else if (e.v.id === current) { node = e.e.id; } else { return; } for (var i = 0; i < collection.length; i += 1) { if (collection[i].node === node) { if (collection[i].distance > e.distance) { queue.changeKey(i, { node: node, distance: e.distance }); parents[node] = current; distances[node] = e.distance; } return; } } queue.add({ node: node, distance: e.distance }); parents[node] = current; distances[node] = e.distance; }; for (var i = 0; i < this.nodesCount - 1; i += 1) { current = queue.extract().node; inTheTree[current] = true; this.edges.forEach(process); } for (var node in parents) { spannigTree.push( new exports.Edge(node, parents[node], distances[node])); } return new exports.Graph(spannigTree); }; }()); })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/fibonacci.js ================================================ /** * Nth number of fibonacci's sequence * * Returns the nth number of fibonacci's sequence. * * @public * * @example * var fibonacci = require('path-to-algorithms/src/others/fibonacci').fibonacci; * var nth = fibonacci(20); * * console.log(nth); // 6765 * * @param {Number} n The nth position in fibonacci's sequence * * @module others/fibonacci */ (function (exports) { 'use strict'; function fibonacci(n) { if (n > 97) { throw 'Input too large, results in inaccurate fibonacci value.'; } var n1 = 0; var n2 = 1; var aux; while (n > 0) { aux = n1; n1 = n2; n2 += aux; n = n - 1; } return n1; } exports.fibonacci = fibonacci; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/fibonacciMemory.js ================================================ /** * Nth number of fibonacciMemory's sequence * * Returns the nth number of fibonacciMemory's sequence. * * @public * * @example * var fibonacciMemory = require('path-to-algorithms/src/others/fibonacciMemory').fibonacciMemory; * var nth = fibonacciMemory(20); * * console.log(nth); // 6765 * * @param {Number} n The nth position in fibonacciMemory's sequence * * @module others/fibonacciMemory */ (function (exports) { 'use strict'; function fibonacciMemory(n) { var i = 0; var aux = [0, 1]; while (n !== i) { aux[i + 2] = aux[i] + aux[i + 1]; i += 1; } return aux[i]; } exports.fibonacciMemory = fibonacciMemory; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/hanoi.js ================================================ (function (exports) { 'use strict'; /** * Returns all movements needed to solve Hanoi Tower problem. * * @public * @module others/hanoi * * @example * * var hanoi = require('path-to-algorithms/src/others/hanoi').hanoi; * var movements = hanoi(3, 'a', 'b', 'c'); * * // Move a to c * // Move a to b * // Move c to b * // Move a to c * // Move b to a * // Move b to c * // Move a to c * movements.forEach(function (move) { * console.log('Move', move[0], 'to', move[1]); * }); * * @param {Number} count Count of the plates/stones. * @param {String|Number} source Identifier of the 1st peg. * @param {String|Number} intermediate Identifier of the 2nd peg. * @param {String|Number} goal Identifier of the 3rd peg. * @return Array which contains all the moves required * in order to place all the plates onto the last peg. */ function hanoi(count, source, intermediate, goal, result) { result = result || []; if (count === 1) { result.push([source, goal]); } else { hanoi(count - 1, source, goal, intermediate, result); result.push([source, goal]); hanoi(count - 1, intermediate, source, goal, result); } return result; } exports.hanoi = hanoi; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/levenshtein-distance.js ================================================ (function (exports) { 'use strict'; var levenshteinDistance = (function () { function levenshteinDistance (s, ls, t, lt) { var memo = []; var currRowMemo; var i; var k; for (k = 0; k <= lt; k += 1) { memo[k] = k; } for (i = 1; i <= ls; i += 1) { currRowMemo = [i]; for (k = 1; k <= lt; k += 1) { currRowMemo[k] = Math.min( currRowMemo[k - 1] + 1, memo[k] + 1, memo[k - 1] + (s[i - 1] !== t[k - 1] ? 1 : 0) ); } memo = currRowMemo; } return memo[lt]; } /** * The Levenshtein distance between two strings is a minimum number * of edits needed to transform one string into the other, with the * allowable edit operations being insertion, deletion, * or substitution of a single character. * * @public * @module others/levenshtein-distance * * @example * * var dist = require('path-to-algorithms/src/others/' + * 'levenshtein-distance').levenshteinDistance; * console.log(dist('kitten', 'sitting')); // 3 * * @param {String} s Source string. * @param {String} t Target string. * @return {Number} Minimum number of edits needed * to transform source string into the target string. */ return function (s, t) { return levenshteinDistance(s, s.length, t, t.length); }; }()); exports.levenshteinDistance = levenshteinDistance; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/others/min-coins-change.js ================================================ (function (exports) { 'use strict'; /** * Returns the minimum number of coins from given set, * which sum equals to given change. This is famous * problem from the dynamic programming: * {@link https://en.wikipedia.org/wiki/Change-making_problem} * * @public * @module others/minCoinsChange * * @example * * var minCoinsChange = * require('path-to-algorithms/src/others/min-coins-change') * .minCoinsChange; * var coins = minCoinsChange([1, 2, 3], 5); // [ 2, 3 ] * * @param {Array} coins The sorted list of the coins used for the change. * @param {Number} change The change, which should be returned. * @return Array which contains the minimum coins from the given * list, required for the change. */ function minCoinsChange(coins, change) { var minChange = [[0]]; if (coins.indexOf(change) >= 0) { return [change]; } for (var i = 1; i <= change; i += 1) { for (var j = 0; j < coins.length && coins[j] <= change; j += 1) { for (var k = 0; k < minChange.length; k += 1) { if (k + coins[j] === i) { minChange[i] = minChange[k].concat([coins[j]]); } } } } var result = minChange[change]; if (!result) { return undefined; } return result.slice(1); } exports.minCoinsChange = minCoinsChange; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/minimax.js ================================================ (function (exports) { 'use strict'; /* eslint max-params: 0 */ /** * @param {Function} getPossibleNextStatesFn Function which returns all possible next moves with states . * @param {Function} isGameOverFn Function which returns if game is over. * @param {Function} getScoreFn Function which returns score. * @return {Function} minimax function */ function minimaxBuilder( getPossibleNextStatesFn, isGameOverFn, getScoreFn ) { /** * Minimax (sometimes MinMax, MM[1] or saddle point[2]) is a decision rule used in artificial intelligence, * decision theory, game theory, statistics, and philosophy for minimizing the possible loss for a worst case (maximum loss) scenario. * Optimized with alpha-beta pruning. * {@link https://en.wikipedia.org/wiki/Minimax} * {@link https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning} * * @public * @module others/minimax * * @example * * var miniMax = * require('path-to-algorithms/src/others/minimax').minimax; * var result = minimax( * [1, 2, 3], * true, * 5, * -Infinity, * Infinity, * state => ({ move: 0, state: [2, 3, 4] }), * state => state[1] < 3, * state => state[1] * ); * * @param {*} state Current game state * @param {Boolean} maximize Defines if the result should be maximized or minimized * @param {Number} depth Defines the maximum depth search * @param {Number} alpha Maximum score that the minimizing player is assured * @param {Number} beta Minimum score that the maximizing player is assured * @return {{score: Number, move: *}} which contains the minimum coins from the given * list, required for the change. */ const minimax = ( state, maximize, depth, alpha, beta ) => { if (depth === 0 || isGameOverFn(state)) { const score = getScoreFn(state); return {score, move: null}; } const possibleMoveResults = getPossibleNextStatesFn(state); if (maximize) { let maxResult = {score: -Infinity, move: null}; for (const next of possibleMoveResults) { const result = minimax( next.state, false, depth - 1, alpha, beta ); if (result.score > maxResult.score) { maxResult = {score: result.score, move: next.move}; } alpha = Math.max(alpha, result.score); if (alpha >= beta) { break; } } return maxResult; } else { let minResult = {score: Infinity, move: null}; for (const next of possibleMoveResults) { const result = minimax( next.state, true, depth - 1, alpha, beta ); if (result.score < minResult.score) { minResult = {score: result.score, move: next.move}; } beta = Math.min(beta, result.score); if (beta <= alpha) { break; } } return minResult; } } return minimax; } exports.minimaxBuilder = minimaxBuilder; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/others/minkowski-distance.js ================================================ (function (exports) { 'use strict'; var minkowskiDistance = (function () { function chebyshevDistance (x, y, lx, p, mathfn) { var ret = -p; var i; for (i = 0; i < lx; i += 1) { ret = mathfn(ret, Math.abs(x[i] - y[i])); } return ret; } function minkowskiDistance (x, lx, y, ly, p) { var d; var i; if (lx !== ly) { throw 'Both vectors should have same dimension'; } if (isNaN(p)) { throw 'The order "p" must be a number'; } if (p === Number.POSITIVE_INFINITY) { return chebyshevDistance(x, y, lx, p, Math.max); } else if (p === Number.NEGATIVE_INFINITY) { return chebyshevDistance(x, y, lx, p, Math.min); } else if (p < 1) { throw 'Order less than 1 will violate the triangle inequality'; } else { d = 0; for (i = 0; i < lx; i += 1) { d += Math.pow(Math.abs(x[i] - y[i]), p); } return isNaN(d) ? 0 : Math.pow(d, 1 / p); } } /** * The Minkowski distance between two points gets generalized * metric distance * when p === 1, this becomes same as Manhattan Distance * when p === 2, this becomes same as Euclidean Distance * when p === Positive or Negative Infinity, * this becomes chebyshev distance * * @public * @module others/minkowski-distance * * @example * var dist = require('path-to-algorithms/src/others/' + * 'minkowski-distance').minkowskiDistance; * console.log(dist([0, 1], [1, 1], 2)); // 1 * * @param {Array} x source point * @param {Array} y target point * @param {Number} p order of Minkowski distance * @returns {Number} distance between two points, if distance * is NaN, then this returns 0 */ return function (x, y, p) { return minkowskiDistance (x, x.length, y, y.length, p); }; }()); exports.minkowskiDistance = minkowskiDistance; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/primes/is-prime.js ================================================ (function (exports) { 'use strict'; /** * Advanced (optimised) method for checking if provided number is prime. * For example for number 104743 it should return true, for 104744 - false. * * @module primes/is-prime * @param {Number} number - Number that we check on prime. * @returns {Boolean} Will return true if provided number is prime. * * @example * var isPrime = require('path-to-algorithms/src/is-prime').isPrime; * * console.log(isPrime(7)); // true * console.log(isPrime(18)); // false */ exports.isPrime = function (number) { if (number < 2) { return false; } if (number % 2 === 0) { return (number === 2); } if (number % 3 === 0) { return (number === 3); } var horizon = Math.floor(Math.sqrt(number)); var factor = 5; while (factor <= horizon) { if (number % factor === 0) { return false; } if (number % (factor + 2) === 0) { return false; } factor += 6; } return true; }; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/primes/prime-factor-tree.js ================================================ (function (exports) { 'use strict'; /** * Method will return list of all primes for provided number. * For example for number 18 it should return following list of primes * [2, 3, 3]. * * @module primes/prime-factor-tree * @param {Number} number - Number for which method will find all primes. * @returns {Array} List of available primes for provided number. * * @example * var primeFactorTree = require('path-to-algorithms/src/prime-factor-tree') * .primeFactorTree; * * console.log(primeFactorTree(18)); // [2, 3, 3] * console.log(primeFactorTree(600851475143)); // [71, 839, 1471, 6857] */ exports.primeFactorTree = function (number) { var array = []; var s = 6; while (number > 1 && number % 2 === 0) { number /= 2; array.push(2); } while (number > 2 && number % 3 === 0) { number /= 3; array.push(3); } while (number > 4) { var p = s - 1; var q = s + 1; while (number > 4 && number % p === 0) { number /= p; array.push(p); } while (number > 4 && number % q === 0) { number /= q; array.push(q); } s += 6; } return array; }; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/primes/sieve-of-atkins.js ================================================ (function (exports) { 'use strict'; /** * Sieve of Atkins. * * Modern algorithm for finding all prime numbers up to a specified integer. * * Returns list of primes up to specified limit. * * For example, for limit 10 it should return following list of primes: * [2, 3, 5, 7]. * * @module primes/sieve-of-atkins * @param {Number} limit - Algorithm will returns list of primes up to * specified limit. * @returns {Array} Will return list with all prime numbers up to provided. * limit. * * @example * var sieveOfAtkins = * require('path-to-algorithms/src/sieve-of-atkins').sieveOfAtkins; * * console.log(sieveOfAtkins(12)); // [2, 3, 5, 7, 11] */ exports.sieveOfAtkins = function (limit) { if (limit <= 1) { return []; } const sieve = Array(limit + 1); const testingLimit = Math.ceil(Math.sqrt(limit)); var i; var j; var n; for (i = 1; i < testingLimit; i += 1) { var ii = i * i; for (j = 1; j < testingLimit; j += 1) { var jj = j * j; if (ii + jj >= limit) { break; } n = 4 * ii + jj; if (n <= limit && (n % 12 === 1 || n % 12 === 5)) { sieve[n] = !sieve[n]; } n = 3 * ii + jj; if (n <= limit && (n % 12 === 7)) { sieve[n] = !sieve[n]; } n = 3 * ii - jj; if (i > j && n <= limit && (n % 12 === 11)) { sieve[n] = !sieve[n]; } } } for (n = 5; n <= testingLimit; n += 1) { if (sieve[n]) { j = n * n; for (i = j; i <= limit; i += j) { sieve[i] = false; } } } const primes = [2]; if (limit > 2) { primes.push(3); } sieve.forEach(function (value, key) { if (value) { this.push(key); } }, primes); return primes; } }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/primes/sieve-of-eratosthenes.js ================================================ (function (exports) { 'use strict'; /** * Sieve of Eratosthenes. * * Simple, ancient algorithm for finding all prime numbers up to given limit. * * Returns list of primes up to specified limit. * * For example, for limit 10 it should return following list of primes: * [2, 3, 5, 7]. * * @module primes/sieve-of-eratosthenes * @param {Number} limit - Algorithm will returns list of primes up to * specified limit. * @returns {Array} Will return list with all prime numbers up to provided. * limit. * * @example * var sieveOfEratosthenes = * require('path-to-algorithms/src/sieve-of-eratosthenes').sieveOfEratosthenes; * * console.log(sieveOfEratosthenes(12)); // [2, 3, 5, 7, 11] */ exports.sieveOfEratosthenes = function (limit) { var sieve = []; var primes = []; var k; var l; sieve[1] = false; for (k = 2; k <= limit; k += 1) { sieve[k] = true; } for (k = 2; k * k <= limit; k += 1) { if (sieve[k] !== true) { continue; } for (l = k * k; l <= limit; l += k) { sieve[l] = false; } } sieve.forEach(function (value, key) { if (value) { this.push(key); } }, primes); return primes; }; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/searching/binarysearch.js ================================================ (function (exports) { 'use strict'; function id (val) { return val; } function get (key) { return function (val) { return val[key]; }; } /** * Searches for specific element in a given array using * the binary search algorithm.

* Time complexity: O(log N). * * @example * * var search = require('path-to-algorithms/src/searching/'+ * 'binarysearch').binarySearch; * console.log(search([1, 2, 3, 4, 5], 4)); // 3 * * @public * @module searching/binarysearch * @param {Array} array Input array. * @param {Number} value Value of the element which index should be found. * @returns {Number} Index of the element or -1 if not found. */ function binarySearch(array, value, key) { key = !key ? id : typeof key === 'string' ? get(key) : key; value = key(value); var middle = Math.floor(array.length / 2); var left = 0; var right = array.length; while (right >= left) { var middleValue = key(array[middle]); if (middleValue === value) { return middle; } else if (middleValue > value) { right = middle - 1; } else { left = middle + 1; } middle = Math.floor((left + right) / 2); } return -1; } exports.binarySearch = binarySearch; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/interpolation-search.js ================================================ (function(exports) { 'use strict'; /** * Searches for specific element in a given array using * the interpolation search algorithm.

* Time complexity: O(log log N) when elements are uniformly * distributed, and O(N) in the worst case * * @example * * var search = require('path-to-algorithms/src/searching/'+ * 'interpolation-search').interpolationSearch; * console.log(search([1, 2, 3, 4, 5], 4)); // 3 * * @public * @module searching/interpolation-search * @param {Array} sortedArray Input array. * @param {Number} seekIndex of the element which index should be found. * @returns {Number} Index of the element or -1 if not found. */ function interpolationSearch(sortedArray, seekIndex) { let leftIndex = 0; let rightIndex = sortedArray.length - 1; while (leftIndex <= rightIndex) { const rangeDiff = sortedArray[rightIndex] - sortedArray[leftIndex]; const indexDiff = rightIndex - leftIndex; const valueDiff = seekIndex - sortedArray[leftIndex]; if (valueDiff < 0) { return -1; } if (!rangeDiff) { return sortedArray[leftIndex] === seekIndex ? leftIndex : -1; } const middleIndex = leftIndex + Math.floor((valueDiff * indexDiff) / rangeDiff); if (sortedArray[middleIndex] === seekIndex) { return middleIndex; } if (sortedArray[middleIndex] < seekIndex) { leftIndex = middleIndex + 1; } else { rightIndex = middleIndex - 1; } } return -1; } exports.interpolationSearch = interpolationSearch; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/jump-search.js ================================================ (function(exports) { 'use strict'; /** * Searches for specific element in a given array using * the jump search algorithm.

* Time complexity: O(log N). * * @example * * var search = require('path-to-algorithms/src/searching/'+ * 'jump-search').jumpSearch; * console.log(search([1, 2, 3, 4, 5], 4)); // 3 * * @public * @module searching/jumpsearch * @param {Array} sortedArray Input array. * @param {Number} seekIndex of the element which index should be found. * @returns {Number} Index of the element or -1 if not found. */ function jumpSearch(sortedArray, seekIndex) { // exit if array empty const arrayLength = sortedArray.length; if (!arrayLength) { return -1; } // set jumpSize const jumpSize = Math.floor(Math.sqrt(arrayLength)); let blockStart = 0; let blockEnd = jumpSize; while (seekIndex > sortedArray[Math.min(blockEnd, arrayLength) - 1]) { blockStart = blockEnd; blockEnd += jumpSize; // if out of array bounds exit if (blockStart > arrayLength) { return -1; } } let currentIndex = blockStart; while (currentIndex < Math.min(blockEnd, arrayLength)) { if (sortedArray[currentIndex] === seekIndex) { return currentIndex; } currentIndex += 1; } return -1; } exports.jumpSearch = jumpSearch; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/knuth-morris-pratt.js ================================================ (function (exports) { 'use strict'; var kmp = (function () { function builtKMPTable(str) { var res = []; var len; var front; var end; var found; for (var i = 1; i <= str.length; i += 1) { front = Math.max(1, i - ((res[i - 2] || 0) + 1)); end = Math.min(i - 1, (res[i - 2] || 0) + 1); found = false; len = 0; while (end >= 1 && front <= i && !found) { if (str.substring(0, end) === str.substring(front, i)) { found = true; len = end; } else { end -= 1; front += 1; } } res[i - 1] = len; } return res; } /** * Knuth–Morris–Pratt algorithm. Searches for the position of * the first occurrence of a specified value in a string. * * @example * * var indexOf = require('path-to-algorithm/src/searching/'+ * 'knuth-morris-pratt').kmp; * console.log(indexOf('hello', 'll')); // 2 * * @public * @module searching/knuth-morris-pratt * @param {String} str String. * @param {String} substr Substring. * @return {Number} A Number, representing the position * where the specified substring occurs for the first * time, or -1 if it never occurs. */ function indexOf(str, substr) { if (str === substr) { return 0; } var table = builtKMPTable(substr); var i = 0; var j = 0; while (i < str.length) { if (str[i] === substr[j]) { i += 1; j += 1; } if (j === substr.length) { return i - j; } if (i < str.length && str[i] !== substr[j]) { if (j > 0 && table[j - 1] !== 0) { j = table[j - 1]; } else { i += 1; j = 0; } } } return -1; } return indexOf; }()); exports.kmp = kmp; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/linearSearch.js ================================================ (function (exports) { 'use strict'; /** * Searches for specific element in a given array * using the linear search algorithm * Time complexity: O(n) * * @param {Array} array Input array * @param {Number} key the number whose index is to be found * @returns {Number} the index of the first instance of number or else -1 if not found */ const linearSearch = (array, key) => { for (let i = 0; i < array.length; i += 1) { if (array[i] === key) { return i; } } return -1; }; exports.linearSearch = linearSearch; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/longest-common-subsequence.js ================================================ (function (exports) { 'use strict'; exports.longestCommonSubsequence = (function () { /** * Find the lengths of longest common sub-sequences * of two strings and their substrings. * * Complexity: O(MN). * * @private * @param {String} first string * @param {String} second string * @return {Array} two dimensional array with LCS * lengths of input strings and their substrings. * */ function getLcsLengths(str1, str2) { var result = []; for (var i = -1; i < str1.length; i = i + 1) { result[i] = []; for (var j = -1; j < str2.length; j = j + 1) { if (i === -1 || j === -1) { result[i][j] = 0; } else if (str1[i] === str2[j]) { result[i][j] = result[i - 1][j - 1] + 1; } else { result[i][j] = Math.max(result[i - 1][j], result[i][j - 1]); } } } return result; } /** * Find longest common sub-sequences of two strings. * * Complexity: O(M + N). * * @private * @param {String} first string * @param {String} second string * @return {Array} two dimensional array with LCS * lengths of input strings and their substrings * returned from 'getLcsLengths' function. * */ function getLcs(str1, str2, lcsLengthsMatrix) { var execute = function (i, j) { if (!lcsLengthsMatrix[i][j]) { return ''; } else if (str1[i] === str2[j]) { return execute(i - 1, j - 1) + str1[i]; } else if (lcsLengthsMatrix[i][j - 1] > lcsLengthsMatrix[i - 1][j]) { return execute(i, j - 1); } else { return execute(i - 1, j); } }; return execute(str1.length - 1, str2.length - 1); } /** * Algorithm from dynamic programming. It finds the longest * common sub-sequence of two strings. For example for strings 'abcd' * and 'axxcda' the longest common sub-sequence is 'acd'. * * @example * var subsequence = require('path-to-algorithms/src/searching/'+ * 'longest-common-subsequence').longestCommonSubsequence; * console.log(subsequence('abcd', 'axxcda'); // 'acd' * * @public * @module searching/longest-common-subsequence * @param {String} first input string. * @param {String} second input string. * @return {Array} Longest common subsequence. */ return function (str1, str2) { var lcsLengthsMatrix = getLcsLengths(str1, str2); return getLcs(str1, str2, lcsLengthsMatrix); }; })(); })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/longest-increasing-subsequence.js ================================================ (function (exports) { 'use strict'; exports.longestIncreasingSubsequence = (function () { /** * Find the index of the first largest element in array. * Complexity: O(N). * * @private * @param {Array} array The array in which the largest * element should be found. * @return {Number} index of the first largest element */ function max(array) { if (!array || !array.length) { return -1; } var maxIdx = 0; for (var i = 1; i < array.length; i += 1) { if (array[maxIdx].distance < array[i].distance) { maxIdx = i; } } return maxIdx; } /** * Default comparison method. * @private */ function asc(a, b) { return a - b; } /** * Creates directed graph from given array. * Each element's neighbours are the elements which can be * after the element in the resulting sequence.

* Complexity: O(N^2). * @private * @param {Array} array The input array. * @param {Function} cmp Comparator. * @return {Object} Graph represented with list of neighbours. */ function buildDag(array, cmp) { var result = []; for (var i = 0; i < array.length; i += 1) { result[i] = []; for (var j = i + 1; j < array.length; j += 1) { if (cmp(array[i], array[j]) < 0) { result[i].push(j); } } } return result; } /** * Finds the longest increasing sub-sequence for given node.

* Complexity: O(N^N). * @private * @param {Object} dag Graph represented with list of neighbours. * @param {number} node The current node. * @return {object} The longest increasing sub-sequence for given node. */ function find(dag, node) { node = node || 0; if (find.memo[node]) { return find.memo[node]; } var neighbours = dag[node]; var neighboursDistance = []; var maxDist; // var maxNode; var distance; var result; if (!neighbours.length) { return { distance: 1, neighbour: undefined, node: node }; } for (var i = 0; i < neighbours.length; i += 1) { neighboursDistance[i] = find(dag, neighbours[i]); } maxDist = max(neighboursDistance); // maxNode = neighbours[maxDist]; distance = 1 + neighboursDistance[maxDist].distance; find.memo[node] = result = { distance: distance, neighbour: neighboursDistance[maxDist], node: node }; return result; } /** * Algorithm from dynamic programming. It finds the longest * sub-sequence of increasing numbers. It is not required * the numbers to be neighboring. For example for 1, 5, 2 * sequence the longest sub-sequence is 1, 2. * * @example * var subsequence = require('path-to-algorithms/src/searching/'+ * 'longest-increasing-subsequence').longestIncreasingSubsequence; * console.log(subsequence([1, 0, 4, 3, 5])); // 1, 4, 5 * * @public * @module searching/longest-increasing-subsequence * @param {Array} array Input sequence. * @param {Function} cmp Comparator. * @return {Array} Longest increasing subsequence. */ return function (array, cmp) { cmp = cmp || asc; var results = []; var dag = buildDag(array, cmp); var maxPath; find.memo = []; for (var i = 0; i < array.length; i += 1) { results.push(find(dag, i)); } maxPath = results[max(results)]; results = []; while (maxPath) { results.push(array[maxPath.node]); maxPath = maxPath.neighbour; } return results; }; })(); })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/maximum-subarray-divide-and-conquer.js ================================================ (function (exports) { 'use strict'; /** * Accepts an array and range. Finds the maximum sum of elements * around the middle of the range. * @private * @param {Array} array Input array. * @param {Number} left Left interval of the range. * @param {Number} middle Middle of the range. * @param {Number} right Right side of the range. * @return {Number} The maximum sum including the middle element. */ function crossSubarray(array, left, middle, right) { var leftSum = -Infinity; var rightSum = -Infinity; var sum = 0; var i; for (i = middle; i >= left; i -= 1) { if (sum + array[i] >= leftSum) { leftSum = sum + array[i]; } sum += array[i]; } sum = 0; for (i = middle + 1; i < right; i += 1) { if (sum + array[i] >= rightSum) { rightSum = sum + array[i]; } sum += array[i]; } return leftSum + rightSum; } /** * @private * @param {Array} array Input array. * @param {Number} left Left side of the range. * @param {Number} right Right side of the range. * @return {Number} Maximum sum of the elements of * subarray whithin the given range. */ function maxSubarrayPartitioner(array, left, right) { if (right - left <= 1) { return array[left]; } var middle = Math.floor((left + right) / 2); var leftSum = maxSubarrayPartitioner(array, left, middle); var rightSum = maxSubarrayPartitioner(array, middle, right); var crossSum = crossSubarray(array, left, middle, right); return Math.max(crossSum, leftSum, rightSum); } /** * Finds the maximum sum of the elements of a subarray in a given array * using the divide and conquer algorithm by Bentley, Jon (1984). * For example, for the sequence of values -2, 1, -3, 4, -1, 2, 1, -5, 4 * the contiguous subarray with the largest sum is 4, -1, 2, 1, with sum 6. *

* Time complexity: O(N log N). * * @example * var max = require('path-to-algorithms/src/searching/'+ * 'maximum-subarray-divide-and-conquer').maxSubarray; * console.log(max([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 * * @public * @module searching/maximum-subarray-divide-and-conquer * @param {Array} array Input array. * @return {Number} Maximum sum of the elements of a subarray. */ function maxSubarray(array) { return maxSubarrayPartitioner(array, 0, array.length); } exports.maxSubarray = maxSubarray; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/maximum-subarray.js ================================================ (function (exports) { 'use strict'; /** * Finds the maximum sum of the elements of a subarray in a given array * using the Kadane's algorithm. * For example, for the sequence of values -2, 1, -3, 4, -1, 2, 1, -5, 4 * the contiguous subarray with the largest sum is 4, -1, 2, 1, with sum 6. *

* Time complexity: O(N). * * @example * var max = require('path-to-algorithms/src/searching/'+ * 'maximum-subarray').maxSubarray; * console.log(max([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 * * @public * @module searching/maximum-subarray * @param {Array} array Input array. * @return {Number} Maximum sum of the elements of a subarray. */ function maxSubarray(array) { var currentMax = array[0]; var max = array[0]; for (var i = 1; i < array.length; i += 1) { currentMax = Math.max(array[i], currentMax + array[i]); max = Math.max(max, currentMax); } return max; } exports.maxSubarray = maxSubarray; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/quickselect.js ================================================ (function (exports) { 'use strict'; /** * Returns the n-th smallest element of list within * lo..hi inclusive (i.e. lo <= n <= hi).

* Time complexity: O(N). * * @example * * var quickselect = require('path-to-algorithms/src/searching/'+ * 'quickselect').quickselect; * var result = quickselect([5, 1, 2, 2, 0, 3], 1, 0, 5); * console.log(result); // 1 * * @public * @module searching/quickselect * @param {Array} arr Input array. * @param {Number} n A number of an element. * @param {Number} lo Low index. * @param {Number} hi High index. * @return Returns n-th smallest element. */ function quickselect(arr, n, lo, hi) { function partition(arr, lo, hi, pivotIdx) { function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } var pivot = arr[pivotIdx]; swap(arr, pivotIdx, hi); for (var i = lo; i < hi; i += 1) { if (arr[i] < pivot) { swap(arr, i, lo); lo += 1; } } swap(arr, hi, lo); return lo; } if (arr.length <= n) { return undefined; } lo = lo || 0; hi = hi || arr.length - 1; if (lo === hi) { return arr[lo]; } while (hi >= lo) { var pivotIdx = partition(arr, lo, hi, lo + Math.floor(Math.random() * (hi - lo + 1))); if (n === pivotIdx) { return arr[pivotIdx]; } if (n < pivotIdx) { hi = pivotIdx - 1; } else { lo = pivotIdx + 1; } } return undefined; } exports.quickselect = quickselect; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/searching/recursive-binarysearch.js ================================================ (function (exports) { 'use strict'; var binarySearch = (function () { /** * @private * @param {Array} array Array where we should find the index of the element * @param {Number} value Value of the element which index should be found * @param {Number} left Left index * @param {Number} right Right index * @returns {Number} index The index of the element or -1 if not found */ function recursiveBinarySearch(array, value, left, right) { if (left > right) { return -1; } var middle = Math.floor((right + left) / 2); if (array[middle] === value) { return middle; } else if (array[middle] > value) { return recursiveBinarySearch(array, value, left, middle - 1); } else { return recursiveBinarySearch(array, value, middle + 1, right); } } /** * Recursive version of binary search. * Searches for specific element in a given array using * the binary search algorithm.

* Time complexity: O(log N). * * @example * * var search = require('path-to-algorithms/src/searching/'+ * 'recursive-binarysearch').binarySearch; * console.log(search([1, 2, 3, 4, 5], 4)); // 3 * * @public * @module searching/recursive-binarysearch * @param {Array} array Input array. * @param {Number} value Value of the element which index should be found. * @returns {Number} Index of the element or -1 if not found. */ return function (array, value) { return recursiveBinarySearch(array, value, 0, array.length); }; }()); exports.binarySearch = binarySearch; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sets/quickfind.js ================================================ /** * Keeps track of a set of elements partitioned into a * number of disjoint (nonoverlapping) subsets. * Allows to check whether the path between two nodes exists. * The algorithm is inspired by Robert Sedgewick's Java implementation. *
* The algorithm is inspired by Robert Sedgewick's Java implementation. * {@link http://algs4.cs.princeton.edu/home/} * * @example * * var QuickFind = require('path-to-algorithms/src/sets/quickfind').QuickFind; * * var qfind = new QuickFind(10); * qfind.union(0, 1); * qfind.union(2, 1); * qfind.union(3, 4); * qfind.union(8, 9); * qfind.union(4, 8); * * console.log(qfind.connected(0, 9)); // false * console.log(qfind.connected(3, 9)); // true * * @public * @module sets/quickfind */ (function (exports) { 'use strict'; /** * Initialization.

* Time complexity: O(N). * * @public * @constructor * @param {Numner} size Count of the nodes. */ exports.QuickFind = function (size) { this._ids = []; for (var i = 0; i < size; i += 1) { this._ids[i] = i; } }; /** * Connects two nodes - p and q.

* Time complexity: O(N). * * @public * @method * @param {Number} p The first node. * @param {Number} q The second node. */ exports.QuickFind.prototype.union = function (p, q) { var size = this._ids.length; var pval = this._ids[p]; var qval = this._ids[q]; for (var i = 0; i < size; i += 1) { if (this._ids[i] === qval) { this._ids[i] = pval; } } }; /** * Checks whether two nodes are connected.

* Time complexity: O(1). * * @public * @method * @param {Number} p The first node. * @param {Number} q The second node. * @return {Boolean} */ exports.QuickFind.prototype.connected = function (p, q) { return this._ids[p] === this._ids[q]; }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sets/quickunion.js ================================================ /** * Keeps track of a set of elements partitioned into a * number of disjoint (nonoverlapping) subsets. * Allows to check whether the path between two nodes exists. *
* The algorithm is inspired by Robert Sedgewick's Java implementation. * {@link http://algs4.cs.princeton.edu/home/} * * @example * * var QuickUnion = require('path-to-algorithms/' + * 'src/sets/quickunion').QuickUnion; * * var qunion = new QuickUnion(10); * qunion.union(0, 1); * qunion.union(2, 1); * qunion.union(3, 4); * qunion.union(8, 9); * qunion.union(4, 8); * * console.log(qunion.connected(0, 9)); // false * console.log(qunion.connected(3, 9)); // true * * @public * @module sets/quickunion */ (function (exports) { 'use strict'; /** * Initialization.

* Time complexity: O(N). * * @public * @constructor * @param {Numner} size Count of the nodes. */ exports.QuickUnion = function (n) { this._ids = []; for (var i = 0; i < n; i += 1) { this._ids[i] = i; } }; /** * Finds the root of given node.

* Time complexity: O(N). * @private * @param {Number} i The given node. * @return {Number} Root of the given node. */ exports.QuickUnion.prototype._root = function (i) { while (i !== this._ids[i]) { i = this._ids[i]; } return i; }; /** * Connects two nodes - p and q.

* Time complexity: O(N). * * @public * @method * @param {Number} p The first node. * @param {Number} q The second node. */ exports.QuickUnion.prototype.union = function (p, q) { var pRoot = this._root(p); var qRoot = this._root(q); this._ids[pRoot] = qRoot; }; /** * Checks whether two nodes are connected.

* Time complexity: O(N). * * @param {Number} p The first node. * @param {Number} q The second node. * @return {Boolean} True/false depending on whether the nodes are connected. */ exports.QuickUnion.prototype.connected = function (p, q) { return this._root(p) === this._root(q); }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sets/weightquickunion.js ================================================ /** * Keeps track of a set of elements partitioned into a * number of disjoint (nonoverlapping) subsets. * Allows to check whether the path between two nodes exists. *
* The algorithm is inspired by Robert Sedgewick's Java implementation. * {@link http://algs4.cs.princeton.edu/home/} * * @example * * var QuickUnion = require('path-to-algorithms/' + * 'src/sets/weightquickunion').QuickUnion; * * var qunion = new QuickUnion(10); * qunion.union(0, 1); * qunion.union(2, 1); * qunion.union(3, 4); * qunion.union(8, 9); * qunion.union(4, 8); * * console.log(qunion.connected(0, 9)); // false * console.log(qunion.connected(3, 9)); // true * * @public * @module sets/weightquickunion */ (function (exports) { 'use strict'; /** * Initialization.

* Time complexity: O(N). * * @public * @constructor * @param {Numner} size Count of the nodes. */ exports.QuickUnion = function (n) { this._ids = []; this._size = []; for (var i = 0; i < n; i += 1) { this._ids[i] = i; this._size[i] = 1; } }; /** * Finds the root of given node.

* Time complexity: O(log N). * @private * @param {Number} i The given node. * @return {Number} Root of the given node. */ exports.QuickUnion.prototype._root = function (i) { while (i !== this._ids[i]) { // this._ids[i] = this._ids[this._ids[i]]; //enables the path compression i = this._ids[i]; } return i; }; /** * Checks whether two nodes are connected.

* Time complexity: O(log N). * * @param {Number} p The first node. * @param {Number} q The second node. * @return {Boolean} True/false depending on whether the nodes are connected. */ exports.QuickUnion.prototype.connected = function (p, q) { return this._root(p) === this._root(q); }; /** * Connects two nodes - p and q.

* Time complexity: O(log N). * * @public * @method * @param {Number} p The first node. * @param {Number} q The second node. */ exports.QuickUnion.prototype.union = function (p, q) { var pf = this._root(p); var qf = this._root(q); if (pf === qf) { return; // already linked } var psz = this._size[qf]; var qsz = this._size[pf]; if (psz < qsz) { this._ids[pf] = qf; this._size[qf] += psz; } else { this._ids[qf] = pf; this._size[pf] += qsz; } }; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/shuffle/fisheryates.js ================================================ (function (exports) { 'use strict'; /** * The shuffling algorithm of * Fisher-Yates.

* Time complexity: O(N). * * @example * var shuffle = require('path-to-algorithms/src/' + * 'shuffle/fisheryates').shuffle; * console.log(shuffle([1, 2, 3, 4, 5])); // shuffled array * * @public * @module shuffle/fisheryates * @param {Array} array Array which should be shuffled. * @return {Array} Shuffled array. */ function shuffle(array) { var size = array.length; var rand; for (var i = 0; i < size; i += 1) { rand = Math.floor(i + Math.random() * (size - i)); [array[rand], array[i]] = [array[i], array[rand]]; } return array; } exports.shuffle = shuffle; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/shuffle/richarddurstenfeld.js ================================================ (function (exports) { 'use strict'; /** * Shuffle of an array elements. * This algorithm is modified version of Fisher-Yates shuffle * algorithm and is introduced by Richard Durstenfeld.

* Time complexity: O(N). * * @example * var shuffle = require('path-to-algorithms/src/shuffle' + * '/richarddurstenfeld').shuffle; * console.log(shuffle([1, 2, 3, 4, 5])); // random shuffled * * @public * @module shuffle/richarddurstenfeld * @param {Array} array An array which should be shuffled. * @return {Array} Shuffled array. */ function shuffle(array) { var arraySize = array.length - 1; var rand; var temp; for (var i = arraySize; i >= 0; i -= 1) { rand = Math.round(Math.random() * arraySize); temp = array[i]; array[i] = array[rand]; array[rand] = temp; } return array; } exports.shuffle = shuffle; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/sorting/3-way-string-quicksort.js ================================================ (function (exports) { 'use strict'; var quicksort = (function () { function charAt(str, i) { return (i < str.length) ? str.charCodeAt(i) : -1; } function swap(arr, i, j) { var temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; } function quicksort(arr, lo, hi, d) { if (lo >= hi) { return; } var lowPointer = lo; var highPointer = hi; var p = charAt(arr[lo], d); var i = lo + 1; var current; while (i <= highPointer) { current = charAt(arr[i], d); if (current < p) { swap(arr, i, lowPointer); lowPointer += 1; } else if (current > p) { swap(arr, i, highPointer); highPointer -= 1; i += 1; } else { i += 1; } } quicksort(arr, lo, lowPointer - 1, d); if (p >= 0) { quicksort(arr, lowPointer, highPointer, d + 1); } quicksort(arr, highPointer + 1, hi, d); } /** * Effective inplace string sorting algorithm. * Algorithm is NOT stable. * * @example * * var sort = require('path-to-algorithms/src/sorting'+ * '/3-way-string-quicksort').quicksort; * console.log(sort(['bb', 'aa', 'cc'])); // [ 'aa', 'bb', 'cc' ] * * @public * @module sorting/3-way-string-quicksort * @param arr {Array} array which should be sorted. * @return {Array} Sorted array. */ return function sort(arr) { quicksort(arr, 0, arr.length - 1, 0); return arr; }; }()); exports.quicksort = quicksort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/bubblesort.js ================================================ (function (exports) { 'use strict'; function comparator(a, b) { return a - b; } /** * Bubble sort algorithm.

* Complexity: O(N^2). * * @example * var sort = require('path-to-algorithms/src/' + * 'sorting/bubblesort').bubbleSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/bubblesort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ function bubbleSort(array, cmp) { cmp = cmp || comparator; var temp; for (var i = 0; i < array.length - 1 ; i += 1) { var swapCount = 0; for (var j = 0; j < array.length - 1 - i; j += 1) { if (cmp(array[j], array[j + 1 ]) > 0) { temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; swapCount += 1; } } if (swapCount === 0){ break; } } return array; } exports.bubbleSort = bubbleSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/bucketsort.js ================================================ (function (exports) { 'use strict'; var bucketSort = (function () { /** * Insertionsort. * * @private * @param {array} array Input array * @returns {array} array Sorted input array */ function insertionSort(array) { var current; var j; for (var i = 1; i < array.length; i += 1) { current = array[i]; j = i - 1; while (j >= 0 && current < array[j]) { array[j + 1] = array[j]; j -= 1; } array[j + 1] = current; } return array; } /** * Creates buckets for given array * * @private * @param {array} array Input array * @returns {array} buckets Array whith array for each bucket. * Each bucket contains an array with all elements * from the input which are with suitable size. */ function createBuckets(array) { var buckets = []; var currentBucket; var current; for (var i = 0; i < array.length; i += 1) { current = array[i]; currentBucket = Math.floor(current); buckets[currentBucket] = buckets[currentBucket] || []; buckets[currentBucket].push(current); } return buckets; } /** * Sorts the arrays from each bucket. * * @private * @param {array} buckets Given buckets * @returns {array} buckets Buckets with sorted arrays for each bucket */ function sortBuckets(buckets) { for (var i = 0; i < buckets.length; i += 1) { if (buckets[i] !== undefined) { insertionSort(buckets[i]); } } return buckets; } /** * Unions all buckets' arrays * * @private * @param {array} buckets Input buckets * @returns {array} result Sorted array which contains * all elements form each bucket */ function unionBuckets(buckets) { var result = []; var currentBucket; for (var i = 0; i < buckets.length; i += 1) { currentBucket = buckets[i]; if (currentBucket !== undefined) { result = result.concat(currentBucket); } } return result; } /** * Sorts given array with bucketsort.

* Time complexity: O(N) in case the * data is with uniform distribution. * * @example * * var sort = require('path-to-algorithms/src/'+ * 'sorting/bucketsort').bucketSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/bucketsort * @param {Array} array Input array which should be sorted. * @return {Array} Sorted array. */ return function (array) { var buckets = createBuckets(array); sortBuckets(buckets); return unionBuckets(buckets); }; }()); exports.bucketSort = bucketSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/countingsort.js ================================================ (function (exports) { 'use strict'; var countingSort = (function () { /** * Gets the count of the elements into the input array. * * @private * @param {Array} array The input array. * @return {Array} The count of each element from the input array. */ function getCount(array) { var count = []; var current; for (var i = 0; i < array.length; i += 1) { current = array[i]; count[current] = (count[current] || 0) + 1; } return count; } /** * Gets the count of the elements which are less than a given. * * @private * @param {Array} array The input array. * @return {Array} less The count of the elements which. * are less than each element from the input. */ function getLessCount(array) { var less = []; var last; less[0] = array[0] || 0; for (var i = 1; i < array.length; i += 1) { last = array[i - 1] || 0; less[i] = last + less[i - 1]; } return less; } /** * Sorts the input array. * * @private * @param {Array} array Input which should be sorted. * @param {Array} less Count of the less elements for each element. * @return {Array} The sorted input. */ function sort(array, less) { var result = []; var currentPositions = []; var current; var position; for (var i = 0; i < array.length; i += 1) { current = array[i]; position = less[current]; if (currentPositions[current] === undefined) { currentPositions[current] = position; } result[currentPositions[current]] = current; currentPositions[current] += 1; } return result; } /** * Counting sort algorithm. It's correct only * for array of integers.

* Time complexity: O(N). * * @example * var sort = require('path-to-algorithms/src/' + * 'sorting/countingsort').countingSort; * console.log(sort([2, 5, 1, 3, 4])); // [ 1, 2, 3, 4, 5 ] * * @public * @module sorting/countingsort * @param {Array} array Array which should be sorted. * @return {Array} Sorted array. */ return function (array) { var less = getLessCount(getCount(array)); return sort(array, less); }; }()); exports.countingSort = countingSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/heapsort.js ================================================ (function (exports) { 'use strict'; function comparator(a, b) { return a - b; } var heapSort = (function () { /** * Finds the correct place of given element in given max heap. * * @private * @param {Array} array Array. * @param {Number} index Index of the element which palce in * the max heap should be found. * @param {Number} heapSize Size of the heap. * @param {function} cmp Comparison function. */ function heapify(array, index, heapSize, cmp) { var left = 2 * index + 1; var right = 2 * index + 2; var largest = index; if (left < heapSize && cmp(array[left], array[index]) > 0) { largest = left; } if (right < heapSize && cmp(array[right], array[largest]) > 0) { largest = right; } if (largest !== index) { var temp = array[index]; array[index] = array[largest]; array[largest] = temp; heapify(array, largest, heapSize, cmp); } } /** * Builds max heap from given array. * * @private * @param {Array} array Array which should be turned into max heap. * @param {function} cmp Comparison function. * @return {Array} array Array turned into max heap. */ function buildMaxHeap(array, cmp) { for (var i = Math.floor(array.length / 2); i >= 0; i -= 1) { heapify(array, i, array.length, cmp); } return array; } /** * Heapsort. Turns the input array into max * heap and after that sorts it.

* Time complexity: O(N log N). * * @example * * var sort = require('path-to-algorithms/src' + * '/sorting/heapsort').heapSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/heapsort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ return function (array, cmp) { cmp = cmp || comparator; var size = array.length; var temp; buildMaxHeap(array, cmp); for (var i = array.length - 1; i > 0; i -= 1) { temp = array[0]; array[0] = array[i]; array[i] = temp; size -= 1; heapify(array, 0, size, cmp); } return array; }; }()); exports.heapSort = heapSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/insertion-binary-sort.js ================================================ (function (exports) { 'use strict'; function comparator(a, b) { return a - b; } /** * Modified version of insertion sort. It uses binary search for finding * where the current element should be inserted. It's correct because * the binary search looks just in the first part of the array * which is actually sorted.

* Time complexity: O(N^2). * * @example * * var sort = require('path-to-algorithms/src' + * '/sorting/insertion-binary-sort').insertionBinarySort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/insertion-binary-sort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ function insertionBinarySort(array, cmp) { cmp = cmp || comparator; var current; var middle; var left; var right; for (var i = 1; i < array.length; i += 1) { current = array[i]; left = 0; right = i; middle = Math.floor((left + right) / 2); while (left <= right) { if (cmp(array[middle], current) <= 0) { left = middle + 1; } else if (cmp(array[middle], current) > 0) { right = middle - 1; } middle = Math.floor((right + left) / 2); } for (var j = i; j > left; j -= 1) { array[j] = array[j - 1]; } array[j] = current; } return array; } exports.insertionBinarySort = insertionBinarySort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/insertionsort.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } /** * Insertionsort algorithm.

* Time complexity: O(N^2). * * @example * * var sort = require('path-to-algorithms/src' + * '/sorting/insertion-sort').insertionSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/insertionsort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ function insertionSort(array, cmp) { cmp = cmp || compare; var current; var j; for (var i = 1; i < array.length; i += 1) { current = array[i]; j = i - 1; while (j >= 0 && cmp(array[j], current) > 0) { array[j + 1] = array[j]; j -= 1; } array[j + 1] = current; } return array; } exports.insertionSort = insertionSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/lsd.js ================================================ (function (exports) { 'use strict'; /** * Sorts strings lexicographically.

* Time complexity: O(N*M) for N keys which have M or fewer digits. * * @example * * var sort = require('../src/sorting/lsd').lsd; * // [ 'aab', 'aaa', 'acc', 'bbb', 'bcc' ] * console.log(sort(['aab', 'bbb', 'aaa', 'acc', 'bcc'])); * * @public * @module sorting/lsd * @param {Array} arr Array which should be sorted. * @param {Number} letterIdx Optional. Index to start sorting from. * @return {Array} Sorted array. */ function lsd(arr, letterIdx) { var temp; var count; letterIdx = letterIdx || 1; for (var i = letterIdx - 1; i >= 0; i -= 1) { count = []; temp = []; for (var j = 0; j < arr.length; j += 1) { var charCode = arr[j].charCodeAt(i); var old = count[charCode + 1] || 0; count[charCode + 1] = old + 1; } for (var c = 0; c < count.length - 1; c += 1) { count[c] = count[c] || 0; count[c + 1] = count[c + 1] || 0; count[c + 1] += count[c]; } for (j = 0; j < arr.length; j += 1) { var code = arr[j].charCodeAt(i); temp[count[code]] = arr[j]; count[code] += 1; } for (j = 0; j < arr.length; j += 1) { arr[j] = temp[j]; } } return arr; } exports.lsd = lsd; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/mergesort.js ================================================ (function (exports) { /** * Mergesort module. */ 'use strict'; var ll = require('../data-structures/linked-list.js'); function compare(a, b) { return a - b; } /** * Mergesort method which is recursively called for sorting the input array. * * @public * @module sorting/mergesort * @param {Array} array The array which should be sorted. * @param {Function} cmp Compares two items in an array. * @param {Number} start Left side of the subarray. * @param {Number} end Right side of the subarray. * @returns {Array} Array with sorted subarray. * * @example * var array = [2, 4, 1, 5, 6, 7]; * var mergeSort = * require('path-to-algorithms/src/sorting/mergesort').mergeSort; * mergeSort(array); // [1, 2, 4, 5, 6, 7] */ function mergeSort(array, cmp, start, end) { cmp = cmp || compare; start = start || 0; end = end || array.length; if (Math.abs(end - start) <= 1) { return []; } var middle = Math.ceil((start + end) / 2); mergeSort(array, cmp, start, middle); mergeSort(array, cmp, middle, end); return mergeSort.merge(array, cmp, start, middle, end); } /** * Devides and sort merges two subarrays of given array * * @public * @module sorting/mergesort/merge * @param {Array} array The array which subarrays should be sorted. * @param {Number} start The start of the first subarray. * This subarray is with end middle - 1. * @param {Number} middle The start of the second array. * @param {Number} end end - 1 is the end of the second array. * @returns {Array} The array with sorted subarray. * * @example * var array = [1, 2, 3, 1, 4, 5, 6]; * var merge = * require('path-to-algorithms/src/sorting/mergesort').merge; * merge(array, function (a, b) { // [1, 1, 2, 3, 4, 5, 6] * return a - b; * }, 0, 4, 7); */ mergeSort.merge = function (array, cmp, start, middle, end) { var left = new ll.LinkedList(); var right = new ll.LinkedList(); var leftSize = middle - start; var rightSize = end - middle; var maxSize = Math.max(leftSize, rightSize); var size = end - start; var i; for (i = 0; i < maxSize; i += 1) { if (i < leftSize) { left.push(array[start + i]); } if (i < rightSize) { right.push(array[middle + i]); } } i = 0; while (i < size) { if (left.first && right.first) { if (cmp(left.first.data, right.first.data) > 0) { array[start + i] = right.shift().data; } else { array[start + i] = left.shift().data; } } else if (left.first) { array[start + i] = left.shift().data; } else { array[start + i] = right.shift().data; } i += 1; } return array; }; exports.mergeSort = mergeSort; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/sorting/msd.js ================================================ (function (exports) { 'use strict'; function charCodeAt(str, i) { return (i < str.length) ? str.charCodeAt(i) : -1; } function sort(arr, lo, hi, d) { var temp = []; var count = []; var j; var idx; // Use Insertion sort when the // array is smaller than given threshold for (j = lo; j <= hi; j += 1) { idx = charCodeAt(arr[j], d) + 2; count[idx] = count[idx] || 0; count[idx] += 1; } for (j = 0; j < count.length - 1; j += 1) { count[j] = count[j] || 0; count[j + 1] = count[j + 1] || 0; count[j + 1] += count[j]; } for (j = lo; j <= hi; j += 1) { idx = charCodeAt(arr[j], d) + 1; temp[count[idx]] = arr[j]; count[idx] += 1; } for (j = lo; j <= hi; j += 1) { arr[j] = temp[j - lo]; } for (j = 0; j < count.length - 2; j += 1) { sort(arr, lo + count[j], lo + count[j + 1] - 1, d + 1); } } /** * Sorts given array lexicographically. * Algorithms knows how to treat * differently length strings.

* Algorithm is stable. * Time complexity: O(N*M) for N keys which have M or fewer digits. * * @example * * var sort = require('../src/sorting/msd').msd; * // [ 'aab', 'aaa', 'acc', 'bbb', 'bcc' ] * console.log(sort(['aab', 'bbb', 'aaa', 'acc', 'bcc'])); * * @public * @module sorting/msd * @param {Array} arr Array which should be sorted. * @param {Number} d Optional. Digit from which sorting should start. * @return {Array} Sorted array. */ function msd(arr, d) { d = d || 0; sort(arr, 0, arr.length - 1, d); return arr; } exports.msd = msd; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/oddeven-sort.js ================================================ (function (exports) { 'use strict'; /** * Odd even sort algorithm.

* Complexity: O(N^2). * * @example * var sort = require('path-to-algorithms/src/' + * 'sorting/oddeven-sort').oddEvenSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/oddeven-sort * @param {Array} array Input array. * @return {Array} Sorted array. */ function oddEvenSort(arr) { function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } var sorted = false; while (!sorted) { sorted = true; for (var i = 1; i < arr.length - 1; i += 2) { if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); sorted = false; } } for (i = 0; i < arr.length - 1; i += 2) { if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); sorted = false; } } } return arr; } exports.oddEvenSort = oddEvenSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/quicksort-declarative.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } /** * Quicksort algorithm (declarative variant) * * @public * @param {array} array Array which should be sorted. * @return {array} Sorted array. */ var quickSort = (function () { /** * Recursively calls itself. * * @private * @param {array} array Array which should be processed */ function quicksort(array, cmp) { if (array.length < 1) { return array; } const [x, ...rest] = array; return [ ...quicksort(rest.filter(v => cmp(v, x) < 0), cmp), x, ...quicksort(rest.filter(v => cmp(v, x) >= 0), cmp) ]; } /** * Quicksort algorithm. In this version of quicksort used * declarative programming mechanisms.

* Time complexity: O(N log(N)). * * @example * * var sort = require('path-to-algorithms/src' + * '/sorting/quicksort-declarative').quickSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/quicksort-declarative * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ return function (array, cmp) { cmp = cmp || compare; array = quicksort(array, cmp); return array; }; }()); exports.quickSort = quickSort; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/sorting/quicksort-middle.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } /** * Quicksort algorithm * * @public * @param {array} array Array which should be sorted. * @return {array} Sorted array. */ var quickSort = (function () { /** * Partitions the array in two parts by the middle elements. * All elements which are less than the chosen one goes left from it * all which are greater goes right from it. * Uses Hoare's partitioning algorithm. * * @param {array} array Array which should be partitioned * @param {number} left Left part of the array * @param {number} right Right part of the array * @return {number} */ function partition(array, left, right, cmp) { var pivot = array[Math.floor((left + right) / 2)]; var temp; while (left <= right) { while (cmp(array[left], pivot) < 0) { left += 1; } while (cmp(array[right], pivot) > 0) { right -= 1; } if (left <= right) { temp = array[left]; array[left] = array[right]; array[right] = temp; left += 1; right -= 1; } } return left; } /** * Recursively calls itself with different values for * left/right part of the array which should be processed * * @private * @param {array} array Array which should be processed * @param {number} left Left part of the array which should be processed * @param {number} right Right part of the array which should be processed */ function quicksort(array, left, right, cmp) { var mid = partition(array, left, right, cmp); if (left < mid - 1) { quicksort(array, left, mid - 1, cmp); } if (right > mid) { quicksort(array, mid, right, cmp); } } /** * Quicksort algorithm. In this version of quicksort used * middle element of array for the pivot.

* Time complexity: O(N log(N)). * * @example * * var sort = require('path-to-algorithms/src' + * '/sorting/quicksort-middle').quickSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/quicksort-middle * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ return function (array, cmp) { cmp = cmp || compare; quicksort(array, 0, array.length - 1, cmp); return array; }; }()); exports.quickSort = quickSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/quicksort.js ================================================ (function (exports) { 'use strict'; /** * The quicksort algorithm. It's complexity is O(nlog n). * * @public */ var quickSort = (function () { function compare(a, b) { return a - b; } /** * Swap the places of two elements * * @private * @param {array} array The array which contains the elements * @param {number} i The index of the first element * @param {number} j The index of the second element * @returns {array} array The array with swapped elements */ function swap(array, i, j) { var temp = array[i]; array[i] = array[j]; array[j] = temp; return array; } /** * Partitions given subarray using Lomuto's partitioning algorithm. * * @private * @param {array} array Input array * @param {number} left The start of the subarray * @param {number} right The end of the subarray */ function partition(array, left, right, compare) { var cmp = array[right - 1]; var minEnd = left; var maxEnd; for (maxEnd = left; maxEnd < right - 1; maxEnd += 1) { if (compare(array[maxEnd], cmp) < 0) { swap(array, maxEnd, minEnd); minEnd += 1; } } swap(array, minEnd, right - 1); return minEnd; } /** * Sorts given array. * * @private * @param {array} array Array which should be sorted * @param {number} left The start of the subarray which should be handled * @param {number} right The end of the subarray which should be handled * @returns {array} array Sorted array */ function quickSort(array, left, right, cmp) { if (left < right) { var p = partition(array, left, right, cmp); quickSort(array, left, p, cmp); quickSort(array, p + 1, right, cmp); } return array; } /** * Calls the quicksort function with it's initial values. * * @public * @param {array} array The input array which should be sorted * @returns {array} array Sorted array */ return function (array, cmp) { cmp = cmp || compare; return quickSort(array, 0, array.length, cmp); }; }()); exports.quickSort = quickSort; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: src/sorting/radixsort.js ================================================ (function (exports) { 'use strict'; var radixSort = (function () { /** * Returns the digit of a number that is 'lsdOffset' * places from the least significant digit. * * @private * @param {Number} number Number * @param {Number} lsdOffset Offset of the digit to return, counting * from the position of the least significant digit (e.g. lsdOffset = 0 * will return the least significant digit itself) * @return {String} digit The specified number digit. Returns 'undefined' * if lsdOffset is bigger or equal to the number of digits of the 'number' * argument. */ var getDigit = function (number, lsdOffset) { var size = number.toString().length; var digit; if (lsdOffset >= 0 && lsdOffset < size) { digit = number.toString()[size - 1 - lsdOffset]; } return digit; }; /** * Least significant digit (LSD) Radix sort. A non-comparative, * stable integer sorting algorithm.

* Worst-case time complexity is O(N K) for N keys with K being * the average key length, measured in number of digits. * * @example * var sort = require('path-to-algorithms/src/' + * 'sorting/radixsort').radixSort; * console.log(sort([2, 5, 1, 3, 4])); // [ 1, 2, 3, 4, 5 ] * * @public * @module sorting/radixsort * @param {Array} array Input integer array * @return {Array} Sorted array */ return function (array) { var size = array.length; var R = 10; /* Alphabet size ([0-9] for integers) */ var count; var digit; var i; var j; /* Find maximum key size */ var maxKeySize = (array[0] || '').toString().length; for (i = 1; i < size; i += 1) { var numStr = array[i].toString(); if (numStr.length > maxKeySize) { maxKeySize = numStr.length; } } for (i = 0; i < maxKeySize; i += 1) { /* Initialize count */ count = []; for (j = 0; j < R; j += 1) { count[j] = 0; } /* Count frequency of each array element */ for (j = 0; j < size; j += 1) { digit = getDigit(array[j], i) || 0; count[digit] += 1; } /* Compute cumulates */ for (j = 1; j < R; j += 1) { count[j] += count[j - 1]; } /* Move elements to auxiliary array */ var aux = []; for (j = size - 1; j >= 0; j -= 1) { digit = getDigit(array[j], i) || 0; count[digit] -= 1; aux[count[digit]] = array[j]; } /* Copy elements back from auxilary array */ for (j = 0; j < size; j += 1) { array[j] = aux[j]; } } return array; }; })(); exports.radixSort = radixSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/readme.md ================================================ # Comparison of all sorting algorithms | Algorithm | Complexity | When to use? | |----------------------------|-----------------------------------------------------|--------------| | 3-way-string-quicksort.js | O(N^2) | | | bubblesort.js | O(N^2) | | | bucketsort.js | O(N) | | | countingsort.js | O(N) | | | heapsort.js | O(N log N) | | | insertion-binary-sort.js | O(N^2) | | | insertionsort.js | O(N^2) | | | lsd.js | O(N*M) for N keys which have M or fewer digits | | | mergesort.js | O(n log(n)) | | | msd.js | O(N*M) for N keys which have M or fewer digits | | | oddeven-sort.js | O(N^2) | | | quicksort-middle.js | O(N log(N)) | | | quicksort.js | O(nlog n) | | | radixsort.js | O(N K) for N keys with K being | | | recursive-insertionsort.js | O(N^2) | | | selectionsort.js | O(N^2) | | | shellsort.js | O((nlog(n))^2) | | ================================================ FILE: src/sorting/recursive-insertionsort.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } /** * Recursive version of insertion sort.

* Time complexity: O(N^2). * * @example * * var sort = require('path-to-algorithms/src/sorting/'+ * 'recursive-insertionsort').recursiveInsertionSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/recursive-insertionsort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @param {Number} max Optional. Index of the element which place * we should find in the current function call. * @return {Array} Sorted array. */ function recursiveInsertionSort(array, cmp, max) { cmp = cmp || compare; if (max === undefined) { max = array.length - 1; } if (max <= 0) { return array; } recursiveInsertionSort(array, cmp, max - 1); for (var i = max - 1, current = array[max]; i >= 0 && cmp(current, array[i]) < 0; i -= 1) { array[i + 1] = array[i]; } array[i + 1] = current; return array; } exports.recursiveInsertionSort = recursiveInsertionSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/selectionsort.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } /** * Selection sort.

* Time complexity: O(N^2). * * @example * * var sort = require('path-to-algorithms/src/sorting/'+ * 'selectionsort').selectionSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/selectionsort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ var selectionSort = function (array, cmp) { cmp = cmp || compare; var idx; var temp; for (var i = 0; i < array.length - 1; i += 1) { idx = i; for (var j = i + 1; j < array.length; j += 1) { if (cmp(array[idx], array[j]) > 0) { idx = j; } } temp = array[i]; array[i] = array[idx]; array[idx] = temp; } return array; }; exports.selectionSort = selectionSort; })(typeof window === 'undefined' ? module.exports : window); ================================================ FILE: src/sorting/shellsort.js ================================================ (function (exports) { 'use strict'; function compare(a, b) { return a - b; } var shellSort = (function () { var gaps = [701, 301, 132, 57, 23, 10, 4, 1]; /** * Shellsort which uses the gaps 701, 301, 132, 57, 23, 10, 4, 1 and * insertion sort to sort sub-arrays which match for the different gaps. * * @example * * var sort = require('path-to-algorithms/src/' + * 'sorting/shellsort').shellSort; * console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ] * * @public * @module sorting/shellsort * @param {Array} array Input array. * @param {Function} cmp Optional. A function that defines an * alternative sort order. The function should return a negative, * zero, or positive value, depending on the arguments. * @return {Array} Sorted array. */ return function (array, cmp) { cmp = cmp || compare; var gap; var current; for (var k = 0; k < gaps.length; k += 1) { gap = gaps[k]; for (var i = gap; i < array.length; i += gap) { current = array[i]; for (var j = i; j >= gap && cmp(array[j - gap], current) > 0; j -= gap) { array[j] = array[j - gap]; } array[j] = current; } } return array; }; }()); exports.shellSort = shellSort; }(typeof exports === 'undefined' ? window : exports)); ================================================ FILE: test/compression/burrows-wheeler/burrows-wheeler.spec.js ================================================ var bw = require('../../../src/compression/burrows-wheeler/burrows-wheeler').burrowsWheeler; describe('Burrows Wheeler', function () { 'use strict'; it('should return "annnnb$aaaaa" for the entry "ananabanana"', function () { expect(bw.encode('ananabanana')).toEqual('annnnb$aaaaa'); }); it('should return "ananabanana" for the entry "annnnb$aaaaa"', function () { expect(bw.decode('annnnb$aaaaa')).toEqual('ananabanana'); }); }); ================================================ FILE: test/data-structures/avl-tree.spec.js ================================================ var mod = require('../../src/data-structures/avl-tree.js'); var Node = mod.Node; var AVLTree = mod.AVLTree; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); }); describe('AVL Tree', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof AVLTree).toBe('function'); }); it('should start with null root', function () { expect(new AVLTree()._root).toBe(null); }); it('should insert and single rotate (leftRight) properly', function () { var avlTree = new AVLTree(); avlTree.insert(66); avlTree.insert(3); avlTree.insert(5); expect(avlTree._root.value).toBe(5); expect(avlTree._root._left.value).toBe(3); expect(avlTree._root._right.value).toBe(66); expect(avlTree._root._height).toBe(2); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right._height).toBe(1); }); it('should insert and single rotate (rightLeft) properly', function () { var avlTree = new AVLTree(); avlTree.insert(50); avlTree.insert(75); avlTree.insert(60); expect(avlTree._root.value).toBe(60); expect(avlTree._root._left.value).toBe(50); expect(avlTree._root._right.value).toBe(75); expect(avlTree._root._height).toBe(2); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right._height).toBe(1); }); it('should insert and double rotate (leftLeft) properly', function () { var avlTree = new AVLTree(); avlTree.insert(50); avlTree.insert(25); avlTree.insert(10); expect(avlTree._root.value).toBe(25); expect(avlTree._root._left.value).toBe(10); expect(avlTree._root._right.value).toBe(50); expect(avlTree._root._height).toBe(2); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right._height).toBe(1); }); it('should insert and double rotate (rightRight) properly', function () { var avlTree = new AVLTree(); avlTree.insert(50); avlTree.insert(75); avlTree.insert(100); expect(avlTree._root.value).toBe(75); expect(avlTree._root._left.value).toBe(50); expect(avlTree._root._right.value).toBe(100); expect(avlTree._root._height).toBe(2); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right._height).toBe(1); }); it('should insert multiple nodes and balance properly (1)', function () { var avlTree = new AVLTree(); avlTree.insert(30); avlTree.insert(15); avlTree.insert(60); avlTree.insert(90); avlTree.insert(100); expect(avlTree._root.value).toBe(30); expect(avlTree._root._left.value).toBe(15); expect(avlTree._root._right.value).toBe(90); expect(avlTree._root._right._left.value).toBe(60); expect(avlTree._root._right._right.value).toBe(100); expect(avlTree._root._height).toBe(3); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right._height).toBe(2); expect(avlTree._root._right._left._height).toBe(1); expect(avlTree._root._right._right._height).toBe(1); }); it('should insert multiple nodes and balance properly (2)', function () { var avlTree = new AVLTree(); avlTree.insert(24); avlTree.insert(67); avlTree.insert(33); avlTree.insert(52); avlTree.insert(11); avlTree.insert(15); avlTree.insert(26); avlTree.insert(27); // depth 1 expect(avlTree._root.value).toBe(33); expect(avlTree._root._height).toBe(4); // depth 2 expect(avlTree._root._left.value).toBe(15); expect(avlTree._root._left._height).toBe(3); expect(avlTree._root._right.value).toBe(67); expect(avlTree._root._right._height).toBe(2); // depth 3 expect(avlTree._root._left._left.value).toBe(11); expect(avlTree._root._left._left._height).toBe(1); expect(avlTree._root._left._right.value).toBe(26); expect(avlTree._root._left._right._height).toBe(2); expect(avlTree._root._right._left.value).toBe(52); expect(avlTree._root._right._left._height).toBe(1); // depth 4 expect(avlTree._root._left._right._left.value).toBe(24); expect(avlTree._root._left._right._left._height).toBe(1); expect(avlTree._root._left._right._right.value).toBe(27); expect(avlTree._root._left._right._right._height).toBe(1); }); it('should remove nodes and balance properly (1)', function () { var avlTree = new AVLTree(); avlTree.insert(30); avlTree.insert(15); avlTree.insert(60); avlTree.insert(90); avlTree.insert(100); avlTree.remove(15); // depth 1 expect(avlTree._root.value).toBe(90); expect(avlTree._root._height).toBe(3); // depth 2 expect(avlTree._root._left.value).toBe(30); expect(avlTree._root._left._height).toBe(2); expect(avlTree._root._right.value).toBe(100); expect(avlTree._root._right._height).toBe(1); // depth 3 expect(avlTree._root._left._right.value).toBe(60); expect(avlTree._root._left._right._height).toBe(1); }); it('should remove nodes and balance properly (2)', function () { var avlTree = new AVLTree(); avlTree.insert(55); avlTree.insert(25); avlTree.insert(11); avlTree.insert(1); avlTree.remove(55); // depth 1 expect(avlTree._root.value).toBe(11); expect(avlTree._root._height).toBe(2); // depth 2 expect(avlTree._root._left.value).toBe(1); expect(avlTree._root._left._height).toBe(1); expect(avlTree._root._right.value).toBe(25); expect(avlTree._root._right._height).toBe(1); }); it('should remove nodes and balance properly (3)', function () { var avlTree = new AVLTree(); avlTree.insert(55); avlTree.insert(25); avlTree.insert(11); avlTree.insert(1); avlTree.remove(55); avlTree.insert(32); avlTree.insert(37); avlTree.insert(41); avlTree.insert(8); avlTree.insert(44); avlTree.insert(6); avlTree.remove(32); avlTree.remove(11); avlTree.remove(25); // depth 1 expect(avlTree._root.value).toBe(37); expect(avlTree._root._height).toBe(4); // depth 2 expect(avlTree._root._left.value).toBe(6); expect(avlTree._root._left._height).toBe(3); expect(avlTree._root._right.value).toBe(41); expect(avlTree._root._right._height).toBe(2); }); }); ================================================ FILE: test/data-structures/binary-search-tree.spec.js ================================================ var mod = require('../../src/data-structures/binary-search-tree.js'); var Node = mod.Node; var BinaryTree = mod.BinaryTree; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); }); describe('Binary Tree', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof BinaryTree).toBe('function'); }); it('should start with null root', function () { expect(new BinaryTree()._root).toBe(null); }); it('should insert and remove single node properly', function () { var bTree = new BinaryTree(); bTree.insert(15); var node = bTree.find(15); bTree.remove(node); expect(bTree._root).toBe(null); }); it('should remove root and replace with valid child', function () { var bTree = new BinaryTree(); bTree.insert(15); bTree.insert(30); bTree.insert(45); var node = bTree.find(15); bTree.remove(node); expect(bTree._root.value).toBe(30); }); it('should insert multiple nodes properly', function () { var bTree = new BinaryTree(); bTree.insert(10); bTree.insert(5); bTree.insert(15); bTree.insert(4); bTree.insert(6); bTree.insert(14); bTree.insert(16); var leftRootChild = bTree._root._left; var rightRootChild = bTree._root._right; expect(bTree._root.value).toBe(10); expect(leftRootChild.value).toBe(5); expect(rightRootChild.value).toBe(15); expect(leftRootChild._left.value).toBe(4); expect(leftRootChild._right.value).toBe(6); expect(rightRootChild._left.value).toBe(14); expect(rightRootChild._right.value).toBe(16); }); it('should remove multiple nodes properly', function () { var bTree = new BinaryTree(); bTree.insert(10); bTree.insert(5); bTree.insert(15); bTree.insert(4); bTree.insert(6); bTree.insert(7); bTree.insert(14); bTree.insert(16); var leftRootChild = bTree._root._left; var rightRootChild = bTree._root._right; var sixteen = bTree.find(16); bTree.remove(sixteen); expect(bTree._root.value).toBe(10); expect(leftRootChild.value).toBe(5); expect(rightRootChild.value).toBe(15); expect(leftRootChild._left.value).toBe(4); expect(leftRootChild._right.value).toBe(6); expect(leftRootChild._right._right.value).toBe(7); expect(rightRootChild._left.value).toBe(14); expect(rightRootChild._right).toBe(null); var fourteen = bTree.find(14); bTree.remove(fourteen); expect(rightRootChild._left).toBe(null); var five = bTree.find(5); bTree.remove(five); expect(leftRootChild.value).toBe(6); expect(leftRootChild._left.value).toBe(4); expect(leftRootChild._right.value).toBe(7); }); }); ================================================ FILE: test/data-structures/bloomfilter.spec.js ================================================ var mod = require('../../src/data-structures/bloomfilter.js'); var Bitmap = mod.Bitmap; var Bloomfilter = mod.Bloomfilter; describe('Bitmap', function() { 'use strict'; it('should be able to get and set values', function() { var bitmap = new Bitmap(1024); expect(bitmap.exists(0)).toBe(false); bitmap.set(0, true); expect(bitmap.exists(0)).toBe(true); expect(bitmap.exists(1023)).toBe(false); bitmap.set(1023, 1); expect(bitmap.exists(1023)).toBe(true); }); it('should be able to change everthing back', function() { var bitmap = new Bitmap(2048); for (var i = 0; i < 2048; i = i + 1) { expect(bitmap.get(i)).toBe(0); bitmap.set(i, 1); expect(bitmap.get(i)).toBe(1); bitmap.set(i, 0); expect(bitmap.get(i)).toBe(0); } }); }); describe('Bloomfilter', function() { 'use strict'; it('should be able to identify duplicates', function() { var bloomfilter = new Bloomfilter(1024, 0.01); expect(bloomfilter.get('a')).toBe(false); expect(bloomfilter.get('b')).toBe(false); bloomfilter.set('a'); expect(bloomfilter.get('a')).toBe(true); expect(bloomfilter.get('b')).toBe(false); bloomfilter.set('b'); expect(bloomfilter.get('a')).toBe(true); expect(bloomfilter.get('b')).toBe(true); }); it('should handle large amount of data inside', function() { var bloomfilter = new Bloomfilter(4096, 0.001); // high precision var falsePositive = 0; for (var i = 0; i < 1024; i = i + 1) { if (bloomfilter.get(i)) { falsePositive = falsePositive + 1; } bloomfilter.set(i, true); expect(bloomfilter.get(i)).toBe(true); } expect(falsePositive).toBeLessThan(100); // set a high theshold }); }); ================================================ FILE: test/data-structures/hash-table.spec.js ================================================ var mod = require('../../src/data-structures/hash-table.js'); var Node = mod.Node; var Hashtable = mod.Hashtable; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); }); describe('Hash table', function () { 'use strict'; it('should be a constructor function.', function () { expect(typeof Hashtable).toBe('function'); }); it('should start with empty table.', function () { expect(new Hashtable().buckets.length).toBe(0); }); it('should put() K(int):V in table properly.', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value'); expect(hashTable.buckets[10].data).toBe('value'); }); it('should put() K(string):V in table properly.', function () { var hashTable = new Hashtable(); hashTable.put('key', 'value'); /* 'key' hashCode()'s to 106079. Then the hash is adjusted to fit the number of configurable buckets (array size). 106079 % 100 (100 is default maxBucketCount) result is 79. This is done to avoid using get() since it's untested at this point. */ expect(hashTable.buckets[79].data).toBe('value'); }); it('should put() multiple K(int):Vs with hash collisions in properly (1).', function () { var hashTable = new Hashtable(); // Same hash so going to same bucket, but different keys. Collision. hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); /* 'someHash' hashCode()'s to 1504481314. Then the hash is adjusted to fit the number of configurable buckets (array size). 1504481314 % 100 (100 is default maxBucketCount) result is 14. This is done to avoid using get() since it's untested at this point. */ expect(hashTable.buckets[14].data).toBe('value'); expect(hashTable.buckets[14].next.data).toBe('anotherValue'); }); it('should put() multiple K(int):Vs with hash collisions in properly (2).', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(77, 'lastValue', 'someHash'); expect(hashTable.buckets[14].data).toBe('value'); expect(hashTable.buckets[14].next.data).toBe('anotherValue'); expect(hashTable.buckets[14].next.next.data).toBe('lastValue'); }); it('should put() multiple K(string):Vs with hash collisions in properly (1).', function () { var hashTable = new Hashtable(); // Same hash so going to same bucket, but different keys. Collision. hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); /* 'someHash' hashCode()'s to 1504481314. Then the hash is adjusted to fit the number of configurable buckets (array size). 1504481314 % 100 (100 is default maxBucketCount) result is 14. This is done to avoid using get() since it's untested at this point. */ expect(hashTable.buckets[14].data).toBe('value'); expect(hashTable.buckets[14].next.data).toBe('anotherValue'); }); it('should put() multiple K(string):Vs with hash collisions in properly (2).', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.buckets[14].data).toBe('value'); expect(hashTable.buckets[14].next.data).toBe('anotherValue'); expect(hashTable.buckets[14].next.next.data).toBe('lastValue'); }); it('should get() a K(int):V from table properly.', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value'); expect(hashTable.get(10)).toBe('value'); }); it('should get() a K(string):V from table properly.', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value'); expect(hashTable.get('keyA')).toBe('value'); }); it('should get() a K(int):V with collisions from table properly (1).', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); expect(hashTable.get(35, 'someHash')).toBe('anotherValue'); }); it('should get() a K(int):V with collisions from table properly (2).', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(77, 'lastValue', 'someHash'); expect(hashTable.get(77, 'someHash')).toBe('lastValue'); }); it('should get() a K(int):V with collisions from table properly (3).', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(77, 'lastValue', 'someHash'); expect(hashTable.get(35, 'someHash')).toBe('anotherValue'); }); it('should get() a K(int):V with collisions from table properly (4).', function () { var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(77, 'lastValue', 'someHash'); expect(hashTable.get(10, 'someHash')).toBe('value'); }); it('should get() a K(string):V with collisions from table properly (1).', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); expect(hashTable.get('keyB', 'someHash')).toBe('anotherValue'); }); it('should get() a K(string):V with collisions from table properly (2).', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.get('keyC', 'someHash')).toBe('lastValue'); }); it('should get() a K(string):V with collisions from table properly (3).', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.get('keyB', 'someHash')).toBe('anotherValue'); }); it('should get() a K(string):V with collisions from table properly (4).', function () { var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.get('keyA', 'someHash')).toBe('value'); }); it('should remove() a K(int):V from table properly (1).', function () { // remove only node/link in bucket : (B) var hashTable = new Hashtable(); hashTable.put(10, 'value'); hashTable.remove(10); expect(hashTable.get(10)).toBe(undefined); }); it('should remove() a K(int):V with collisions from table properly (2).', function () { // remove start node/link in bucket : (B) - A var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); expect(hashTable.remove(10, 'someHash')).toBe('value'); expect(hashTable.get(35, 'someHash')).toBe('anotherValue'); expect(hashTable.get(10, 'someHash')).toBe(undefined); }); it('should remove() a K(int):V with collisions from table properly (3).', function () { // remove start node/link in bucket : (B) - A - C var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(66, 'lastValue', 'someHash'); expect(hashTable.remove(10, 'someHash')).toBe('value'); expect(hashTable.get(35, 'someHash')).toBe('anotherValue'); expect(hashTable.get(66, 'someHash')).toBe('lastValue'); }); it('should remove() a K(int):V with collisions from table properly (4).', function () { // remove middle node/link in bucket : A - (B) - C var hashTable = new Hashtable(); hashTable.put(10, 'value', 'someHash'); hashTable.put(35, 'anotherValue', 'someHash'); hashTable.put(66, 'lastValue', 'someHash'); expect(hashTable.remove(35, 'someHash')).toBe('anotherValue'); expect(hashTable.get(10, 'someHash')).toBe('value'); expect(hashTable.get(66, 'someHash')).toBe('lastValue'); }); it('should remove() a K(string):V from table properly (1).', function () { // remove only node/link in bucket : (B) var hashTable = new Hashtable(); hashTable.put('keyA', 'value'); hashTable.remove('keyA'); expect(hashTable.get('keyA')).toBe(undefined); }); it('should remove() a K(string):V with collisions from table properly (2).', function () { // remove start node/link in bucket : (B) - A var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); expect(hashTable.remove('keyA', 'someHash')).toBe('value'); expect(hashTable.get('keyB', 'someHash')).toBe('anotherValue'); expect(hashTable.get('keyA', 'someHash')).toBe(undefined); }); it('should remove() a K(string):V with collisions from table properly (3).', function () { // remove start node/link in bucket : (B) - A - C var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.remove('keyA', 'someHash')).toBe('value'); expect(hashTable.get('keyB', 'someHash')).toBe('anotherValue'); expect(hashTable.get('keyC', 'someHash')).toBe('lastValue'); }); it('should remove() a K(string):V with collisions from table properly (4).', function () { // remove middle node/link in bucket : A - (B) - C var hashTable = new Hashtable(); hashTable.put('keyA', 'value', 'someHash'); hashTable.put('keyB', 'anotherValue', 'someHash'); hashTable.put('keyC', 'lastValue', 'someHash'); expect(hashTable.remove('keyB', 'someHash')).toBe('anotherValue'); expect(hashTable.get('keyA', 'someHash')).toBe('value'); expect(hashTable.get('keyC', 'someHash')).toBe('lastValue'); }); }); ================================================ FILE: test/data-structures/heap.spec.js ================================================ var mod = require('../../src/data-structures/heap.js'); var Heap = mod.Heap; describe('Heap', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Heap).toBe('function'); }); it('should have default comparison function', function () { var heap = new Heap(); expect(typeof heap._cmp).toBe('function'); }); it('should add an object properly', function () { var heap = new Heap(); heap.add(1); expect(heap._heap[0]).toBe(1); }); it('should remove an object properly', function () { var heap = new Heap(); heap.add(1); var res = heap.extract(); expect(res).toBe(1); expect(heap._heap.length).toBe(0); }); it('should add multiple nodes properly', function () { var heap = new Heap(); heap.add(55); heap.add(11); heap.add(66); expect(heap._heap.indexOf(55)).toBeGreaterThan(-1); expect(heap._heap.indexOf(11)).toBeGreaterThan(-1); expect(heap._heap.indexOf(66)).toBeGreaterThan(-1); }); it('should remove multiple nodes properly (max heap)', function () { var heap = new Heap(); heap.add(55); heap.add(11); heap.add(66); var res = heap.extract(); expect(res).toBe(66); res = heap.extract(); expect(res).toBe(55); res = heap.extract(); expect(res).toBe(11); }); it('should remove multiple nodes properly (min heap)', function () { var heap = new Heap(function (a, b) { return b - a; }); heap.add(55); heap.add(11); heap.add(66); var res = heap.extract(); expect(res).toBe(11); res = heap.extract(); expect(res).toBe(55); res = heap.extract(); expect(res).toBe(66); }); it('should update top node properly', function () { var heap = new Heap(function (a, b) { return a.val - b.val; }); var objectToUpdate = { val: 66 }; heap.add(objectToUpdate); heap.add({ val: 11 }); heap.add({ val: 55 }); objectToUpdate.val = 0; heap.update(objectToUpdate); var res = heap.extract(); expect(res.val).toBe(55); res = heap.extract(); expect(res.val).toBe(11); res = heap.extract(); expect(res.val).toBe(0); }); it('should update bottom node properly', function () { var heap = new Heap(function (a, b) { return a.val - b.val; }); var objectToUpdate = { val: 0 }; heap.add(objectToUpdate); heap.add({ val: 11 }); heap.add({ val: 55 }); objectToUpdate.val = 66; heap.update(objectToUpdate); var res = heap.extract(); expect(res.val).toBe(66); res = heap.extract(); expect(res.val).toBe(55); res = heap.extract(); expect(res.val).toBe(11); }); }); ================================================ FILE: test/data-structures/interval-tree.spec.js ================================================ var mod = require('../../src/data-structures/interval-tree.js'); var IntervalTree = mod.IntervalTree; describe('IntervalTree', function () { 'use strict'; it('should correctly detect intersections', function () { var it = new IntervalTree(); it.add([10383734, 10594186]) it.add([10383734, 10594186]) it.add([8891125, 9095610]) it.add([9495571, 9677853]) it.add([10093457, 10257167]) it.add([9303743, 9404967]) it.intersects([9303743, 9303744]) expect(it.intersects([9303743, 9303744])).toBe(true) expect(it.intersects([10383734, 10383734])).toBe(true); it.add([9495571, 9677853]) it.add([9303743, 9404967]) expect(it.intersects([9303743, 9303744])).toBe(true) expect(it.intersects([9303742, 9303742])).toBe(false) expect(it.intersects([9404967,9404967])).toBe(true) expect(it.intersects([9404968,9404969])).toBe(false) it = new IntervalTree(); expect(it.intersects([1,2])).toBe(false); it.add([1,2]); expect(it.contains(0.4)).toBe(false); expect(it.contains(1.4)).toBe(true); expect(it.intersects([0,3])).toBe(true); expect(it.intersects([1.5,1.6])).toBe(true); expect(it.intersects([2.1,3.0])).toBe(false); it.add([1.4,2.1]); expect(it.intersects([0,3])).toBe(true); expect(it.intersects([1.5,1.6])).toBe(true); expect(it.intersects([2.1,3.0])).toBe(true); }); }); ================================================ FILE: test/data-structures/linked-list.spec.js ================================================ var mod = require('../../src/data-structures/linked-list.js'); var Node = mod.Node; var LinkedList = mod.LinkedList; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); it('should construct properly', function () { var node = new Node('data'); expect(node.data).toBe('data'); expect(node.next).toBe(null); expect(node.prev).toBe(null); }); }); describe('Linked List', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof LinkedList).toBe('function'); }); it('should push properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); expect(linkedList.first.data).toBe(1); expect(linkedList.first.next.data).toBe(2); expect(linkedList.first.next.next.data).toBe(3); expect(linkedList.first.next.next.next.data).toBe(4); expect(linkedList.first.next.next.next.next.data).toBe(5); expect(linkedList.last.data).toBe(5); }); it('should pop properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); expect(linkedList.pop().data).toBe(5); expect(linkedList.pop().data).toBe(4); expect(linkedList.pop().data).toBe(3); expect(linkedList.pop().data).toBe(2); expect(linkedList.pop().data).toBe(1); }); it('should shift properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); expect(linkedList.shift().data).toBe(1); expect(linkedList.shift().data).toBe(2); expect(linkedList.shift().data).toBe(3); expect(linkedList.shift().data).toBe(4); expect(linkedList.shift().data).toBe(5); }); it('should reverse properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); linkedList.reverse(); expect(linkedList.shift().data).toBe(5); expect(linkedList.shift().data).toBe(4); expect(linkedList.shift().data).toBe(3); expect(linkedList.shift().data).toBe(2); expect(linkedList.shift().data).toBe(1); }); it('should recursive reverse properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); linkedList.recursiveReverse(); expect(linkedList.shift().data).toBe(5); expect(linkedList.shift().data).toBe(4); expect(linkedList.shift().data).toBe(3); expect(linkedList.shift().data).toBe(2); expect(linkedList.shift().data).toBe(1); }); it('should unshift properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); linkedList.unshift(3); expect(linkedList.shift().data).toBe(3); expect(linkedList.shift().data).toBe(1); expect(linkedList.shift().data).toBe(2); expect(linkedList.shift().data).toBe(3); expect(linkedList.shift().data).toBe(4); expect(linkedList.shift().data).toBe(5); }); it('should properly check for existing cycle', function () { var linkedList = new LinkedList(); var last = new Node(2); var first = new Node(1); last.next = first; last.prev = first; first.next = last; first.prev = last; linkedList.first = first; linkedList.last = last; expect(linkedList.hasCycle()).toBe(true); }); it('should properly check for non existing cycle', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); expect(linkedList.hasCycle()).toBe(false); }); it('should inorder properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); var pushedValue = 1; function callback(node){ expect(node.data).toBe(pushedValue); pushedValue += 1; } linkedList.inorder(callback); }); it('should delete data properly', function () { var linkedList = new LinkedList(); linkedList.push(1); linkedList.push(2); linkedList.push(3); linkedList.push(4); linkedList.push(5); linkedList.remove(3); expect(linkedList.first.data).toBe(1); expect(linkedList.first.next.data).toBe(2); expect(linkedList.first.next.next.data).toBe(4); expect(linkedList.first.next.next.next.data).toBe(5); expect(linkedList.last.data).toBe(5); }); it('should delete complex data properly', function () { var linkedList = new LinkedList(); var item1 = {id: 1}; var item2 = {id: 2}; var item3 = {id: 3}; var item4 = {id: 4}; var item5 = {id: 5}; linkedList.push(item1); linkedList.push(item2); linkedList.push(item3); linkedList.push(item4); linkedList.push(item5); var equals = function(a, b) { return a.id === b.id }; linkedList.remove({id: 3}, equals); expect(linkedList.first.data).toBe(item1); expect(linkedList.first.next.data).toBe(item2); expect(linkedList.first.next.next.data).toBe(item4); expect(linkedList.first.next.next.next.data).toBe(item5); expect(linkedList.last.data).toBe(item5); }); }); ================================================ FILE: test/data-structures/red-black-tree.spec.js ================================================ var mod = require('../../src/data-structures/red-black-tree.js'); var Vertex = mod.Node; var RBTree = mod.RBTree; var Colors = mod.Colors; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Vertex).toBe('function'); }); it('should set all properties via the constructor', function () { var node = new Vertex('key', 'value', 1, 2, Colors.RED); expect(node.getKey()).toBe('key'); expect(node.getLeft()).toBe(1); expect(node.getRight()).toBe(2); expect(node.getValue()).toBe('value'); expect(node.isRed()).toBeTruthy(); }); describe('Node flipColor', function () { it('should has method flipColor', function () { var node = new Vertex(); expect(typeof node.flipColor).toBe('function'); }); it('should work properly', function () { var node = new Vertex(); expect(node.isRed()).toBe(false); node.flipColor(); expect(node.isRed()).toBe(true); node.flipColor(); expect(node.isRed()).toBe(false); }); }); }); describe('RBTree', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof RBTree).toBe('function'); }); it('should initialize root to null by default', function () { expect(new RBTree()._root).toBeNull(); }); describe('node insertion', function () { it('should be able to insert a node in empty tree', function () { var tree = new RBTree(); tree.put('foo', 'bar'); expect(tree._root.getKey()).toBe('foo'); expect(tree._root.getValue()).toBe('bar'); }); it('should be able to insert a node in 1 level tree', function () { var tree = new RBTree(); tree.put(1, 'bar'); tree.put(0, 'baz'); expect(tree._root.getLeft()).not.toBeNull(); expect(tree._root.getLeft().isRed()).toBeTruthy(); tree.put(2, 'baz'); expect(tree._root.getRight()).not.toBeNull(); expect(tree._root.getRight().isRed()).toBeFalsy(); tree = new RBTree(); tree.put(1, 'bar'); tree.put(2, 'foo'); tree.put(3, 'baz'); expect(tree._root.getRight()).not.toBeNull(); expect(tree._root.getLeft()).not.toBeNull(); expect(tree._root.isRed()).toBeFalsy(); expect(tree._root.getRight().isRed()).toBeFalsy(); expect(tree._root.getLeft().isRed()).toBeFalsy(); tree.put(4, 'foobar'); tree.put(5, 'foobar'); expect(tree._root.getRight().getRight()).not.toBeNull(); expect(tree._root.getRight().getRight().isRed()).toBeFalsy(); }); }); describe('get method', function () { it('should be able to find value by given key', function () { var tree = new RBTree(); expect(tree.get(1)).toBeUndefined(); tree.put(1, 'baz'); expect(tree.get(1)).toBe('baz'); tree.put(2, 'foo'); expect(tree.get(2)).toBe('foo'); tree.put(3, 'bar'); expect(tree.get(3)).toBe('bar'); expect(tree.get(4)).toBeUndefined(); tree.put(5, 'foobar'); expect(tree.get(5)).toBe('foobar'); tree.put(5, 'foobar1'); expect(tree.get(5)).toBe('foobar1'); }); }); describe('levelOrderTraversal method', function () { it('should be able to traverse tree in level order', function () { var tree = new RBTree(); expect(tree.levelOrderTraversal()).toBe('Level Order Traversal -: Tree is empty'); tree.put(10); tree.put(20); expect(tree.levelOrderTraversal()).toBe('Level Order Traversal -: 20 10'); tree.put(30); expect(tree.levelOrderTraversal()).toBe('Level Order Traversal -: 20 10 30'); tree.put(45); expect(tree.levelOrderTraversal()).toBe('Level Order Traversal -: 20 10 45 30'); tree.put(5); expect(tree.levelOrderTraversal()).toBe('Level Order Traversal -: 20 10 45 5 30'); }); }); }); ================================================ FILE: test/data-structures/segment-tree.spec.js ================================================ var SegmentTree = require('../../src/data-structures/segment-tree.js') .SegmentTree; var defaultAggregate = function (a, b) { 'use strict'; return Math.min(a, b); }; describe('Segment Tree', function () { 'use strict'; describe('indexing', function () { it('should be a constructor function', function () { expect(typeof SegmentTree).toBe('function'); }); it('should start with null original array', function () { expect(new SegmentTree()._original).toBe(null); }); it('should start with empty array as data', function () { expect(new SegmentTree()._data).not.toBe(null); expect(new SegmentTree()._data.length).toBe(0); }); it('should work with empty arrays', function () { var tree = SegmentTree.indexArray([], Infinity, defaultAggregate); expect(tree._data).toBeTruthy(); expect(tree._data.length).toBe(0); }); it('should index arrays with one element', function () { var tree = SegmentTree.indexArray([1], Infinity, defaultAggregate); expect(tree._data).toBeTruthy(); expect(tree._data.length).toBe(1); }); it('should index any array', function () { var tree = SegmentTree.indexArray([1, 2, 3], Infinity, defaultAggregate); expect(tree._data).toEqual([1, 1, 3, 1, 2]); tree = SegmentTree.indexArray([1, 2, 3, 6], Infinity, defaultAggregate); expect(tree._data).toEqual([1, 1, 3, 1, 2, 3, 6]); }); }); describe('should find the proper value at given interval', function () { it('should properly find the minimum when in range', function () { var tree = SegmentTree.indexArray([1], Infinity, defaultAggregate); expect(tree.query(0, 0)).toBe(1); tree = SegmentTree.indexArray([1, 2], Infinity, defaultAggregate); expect(tree.query(0, 0)).toBe(1); expect(tree.query(0, 1)).toBe(1); expect(tree.query(1, 1)).toBe(2); tree = SegmentTree.indexArray([1, -1, 2], Infinity, defaultAggregate); expect(tree.query(0, 2)).toBe(-1); expect(tree.query(0, 1)).toBe(-1); expect(tree.query(1, 1)).toBe(-1); expect(tree.query(1, 2)).toBe(-1); expect(tree.query(2, 2)).toBe(2); }); it('should properly find the minimum when outside range', function () { var tree = SegmentTree.indexArray([1], Infinity, defaultAggregate); expect(tree.query(0, 2)).toBe(1); tree = SegmentTree.indexArray([1, 2, 3], Infinity, defaultAggregate); expect(tree.query(0, 20)).toBe(1); expect(tree.query(2, 20)).toBe(3); expect(Number.isFinite(tree.query(20, 25))).toBe(false); }); it('should throw when the start index is bigger than end', function () { var tree = SegmentTree.indexArray([1], Infinity, defaultAggregate); expect(function () { tree.query(2, 1); }).toThrow(); expect(function () { tree.query(1, 1); }).not.toThrow(); }); }); }); ================================================ FILE: test/data-structures/size-balanced-tree.spec.js ================================================ var mod = require('../../src/data-structures/size-balanced-tree.js'); var Node = mod.Node; var Nil = mod.Nil; var SBTree = mod.SBTree; var updateChild = mod.updateChild; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); it('should be a construct properly', function () { var node = new Node(10, Nil, Nil, Nil, 1); expect(node.value).toBe(10); expect(node.left).toBe(Nil); expect(node.right).toBe(Nil); expect(node.parent).toBe(Nil); expect(node.size).toBe(1); }); it('should reference children/parent properly', function () { var root = new Node(10, Nil, Nil, Nil, 1); var left = new Node(5, root, Nil, Nil, 1); var right = new Node(15, root, Nil, Nil, 1); root.left = left; root.right = right; expect(root.value).toBe(10); expect(root.left).toBe(left); expect(root.right).toBe(right); expect(root.parent).toBe(Nil); expect(right.parent).toBe(root); expect(left.parent).toBe(root); expect(right.size).toBe(1); expect(left.size).toBe(1); expect(root.size).toBe(1); root.updateSize(); expect(root.size).toBe(3); }); }); describe('SBTree', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof SBTree).toBe('function'); }); it('should start with null root', function () { expect(new SBTree()._root).toBe(Nil); }); it('should insert and remove correctly', function () { var sTree = new SBTree(); expect(sTree.size).toBe(0); sTree.insert(0, 10); expect(sTree.size).toBe(1); sTree.remove(0); expect(sTree.size).toBe(0); expect(sTree._root).toBe(Nil); }); function checkNil() { expect(Nil.size).toBe(0); expect(Nil.left).toBe(Nil); expect(Nil.right).toBe(Nil); expect(Nil.parent).toBe(Nil); expect(Nil.value).toBe(null); } it('test updateChild', function () { checkNil(); var root = new Node(10, Nil, Nil, Nil, 1); var left = new Node(5, root, Nil, Nil, 1); var right = new Node(15, root, Nil, Nil, 1); var leftLeft = new Node(10, left, Nil, Nil, 1); left.left = leftLeft; left.updateSize(); root.left = left; root.right = right; root.updateSize(); expect(root.size).toBe(4); updateChild(left, leftLeft); expect(leftLeft.parent).toBe(root); expect(root.left).toBe(leftLeft); expect(root.left.size).toBe(1); checkNil(); }); // Returns a random integer between min (included) and max (excluded) // Using Math.round() will give you a non-uniform distribution! function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } // Returns a random integer between min (included) and max (included) // Using Math.round() will give you a non-uniform distribution! function getRandomIntInclusive(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } it('push and get 100000 elements, remove the array by always remove the first/last element', function () { var sTree = new SBTree(); for (var i = 0; i < 200000; i += 1) { sTree.push(i); } checkNil(); for (var i = 0; i < 200000; i += 1) { var node = sTree.get(i); expect(node.value).toBe(i); } for (var i = 0; i < 200000; i += 1) { expect(sTree.get(0).value).toBe(i); var node = sTree.remove(0); // Always remove the first element; expect(node.value).toBe(i); } checkNil(); expect(sTree._root).toBe(Nil); var count = 10000; for (var i = 0; i < count; i += 1) { sTree.insert(0, i); } for (var i = 0; i < count; i += 1) { var node = sTree.remove(count - i - 1); // Always remove the last element; expect(node.value).toBe(i); expect(sTree.size).toBe(count - i - 1); } checkNil(); var expectedArray = []; for (var i = 0; i < 100000; i += 1) { var newPos = getRandomIntInclusive(0, sTree.size); sTree.insert(newPos, i); expectedArray.splice(newPos, 0, i); } expect(sTree.size).toBe(expectedArray.length); for (var i = 0; i < sTree.size; i += 1) { var node = sTree.get(i); expect(node.value).toBe(expectedArray[i]); } for (var i = 0; i < 90000; i += 1) { var removedPos = getRandomInt(0, sTree.size); sTree.remove(removedPos); expectedArray.splice(removedPos, 1); } for (var i = 0; i < sTree.size; i += 1) { var node = sTree.get(i); expect(node.value).toBe(expectedArray[i]); } checkNil(); }); it('test getIndex', function () { var sTree = new SBTree(); for (var i = 0; i < 10000; i += 1) { var key = i.toString(); sTree.push(key); } for (var i = 0; i < 100; i += 1) { var item = sTree.get(i); expect(item.value).toBe(i.toString()); expect(sTree.getIndex(item)).toBe(i); } }); it('test binary search', function () { var sTree = new SBTree(); for (var i = 0; i < 10000; i += 1) { sTree.push(i); } var cmp = function (a, b) { return a - b; } expect(sTree.binarySearch(cmp, 10.5)).toBe(11) expect(sTree.binarySearch(cmp, 0)).toBe(1) expect(sTree.binarySearch(cmp, -1)).toBe(0) expect(sTree.binarySearch(cmp, 9999)).toBe(10000) expect(sTree.binarySearch(cmp, 10000)).toBe(10000) }); }); ================================================ FILE: test/data-structures/splay-tree.spec.js ================================================ var mod = require('../../src/data-structures/splay-tree.js'); var Node = mod.Node; var SplayTree = mod.SplayTree; describe('Node', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof Node).toBe('function'); }); it('should be a construct properly', function () { var node = new Node(10, null, null, null); expect(node.value).toBe(10); expect(node._left).toBe(null); expect(node._right).toBe(null); expect(node._parent).toBe(null); }); it('should reference children/parent properly', function () { var root = new Node(10, null, null, null); var left = new Node(5, null, null, root); var right = new Node(15, null, null, root); root._left = left; root._right = right; expect(root.value).toBe(10); expect(root._left).toBe(left); expect(root._right).toBe(right); expect(root._parent).toBe(null); }); }); describe('SplayTree', function () { 'use strict'; it('should be a constructor function', function () { expect(typeof SplayTree).toBe('function'); }); it('should start with null root', function () { expect(new SplayTree()._root).toBe(null); }); it('should insert and remove correctly', function () { var sTree = new SplayTree(); sTree.insert(10); sTree.remove(10); expect(sTree._root).toBe(null); }); it('should splay correctly upon inserts', function () { var sTree = new SplayTree(); sTree.insert(10); sTree.insert(5); sTree.insert(15); sTree.insert(7); sTree.insert(12); expect(sTree._root.value).toBe(12); expect(sTree._root._left.value).toBe(7); expect(sTree._root._right.value).toBe(15); }); it('should splay correctly upon search', function () { var sTree = new SplayTree(); sTree.insert(10); sTree.insert(5); sTree.insert(15); sTree.insert(7); sTree.insert(12); sTree.search(5); expect(sTree._root.value).toBe(5); expect(sTree._root._right.value).toBe(7); expect(sTree._root._right._right.value).toBe(12); }); it('should splay correctly upon remove', function () { var sTree = new SplayTree(); sTree.insert(10); sTree.insert(5); sTree.insert(15); sTree.insert(7); sTree.insert(12); sTree.remove(10); expect(sTree._root.value).toBe(7); expect(sTree._root._left.value).toBe(5); expect(sTree._root._right.value).toBe(12); expect(sTree._root._right._right.value).toBe(15); }); }); ================================================ FILE: test/graphics/bezier.spec.js ================================================ var bezier = require('../../src/graphics/bezier'); var linearBezier = bezier.linearBezier; var quadraticBezier = bezier.quadraticBezier; var cubicBezier = bezier.cubicBezier; // see https://www.geogebra.org/m/ek7RHvuc for graphical representation of test values describe('linearBezier', function () { 'use strict'; it('should return 0.5 for p0=0 p1=1 t=0.5', function () { expect(linearBezier(0, 1, 0.5)).toEqual(0.5); }); it('should return -2.8 for p0=-4.67 p1=-0.7 t=0.47', function () { expect(linearBezier(-4.67, -0.7, 0.47)).toBeCloseTo(-2.8, 1); }); it('should return 2.67 for p0=-0.6 p1=6.33 t=0.47', function () { expect(linearBezier(-0.6, 6.33, 0.47)).toBeCloseTo(2.67, 1); }); }); describe('quadraticBezier', function () { 'use strict'; it('should return 1 for p0=0 p1=1 p2=2 t=0.5', function () { expect(quadraticBezier(0, 1, 2, 0.5)).toEqual(1); }); it('should return 7.15 for p0=2.33 p1=8.23 p2=10.77 t=0.47', function () { expect(quadraticBezier(2.33, 8.23, 10.77, 0.47)).toBeCloseTo(7.15, 1); }); it('should return 6.84 for p0=4.67 p1=8.93 p2=4.9 t=0.47', function () { expect(quadraticBezier(4.67, 8.93, 4.9, 0.47)).toBeCloseTo(6.84, 1); }); }); describe('cubicBezier', function () { 'use strict'; it('should return 1.5 for p0=0 p1=1 p2=2 p3=3 t=0.5', function () { expect(cubicBezier(0, 1, 2, 3, 0.5)).toEqual(1.5); }); it('should return 9.78 for p0=2.4 p1=1.33 p2=19.87 p3=18.13 t=0.47', function () { expect(cubicBezier(2.4, 1.33, 19.87, 18.13, 0.47)).toBeCloseTo(9.78, 1); }); it('should return -4.87 for p0=-7.03 p1=-1.4 p2=-10.63 p3=4.5 t=0.47', function () { expect(cubicBezier(-7.03, -1.4, -10.63, 4.5, 0.47)).toBeCloseTo(-4.87, 1); }); }); ================================================ FILE: test/graphics/grapham.spec.js ================================================ var convexHull = require('../../src/graphics/graham').convexHull; const points = [ { x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: 0.15, y: 0.15 }, { x: 0.5, y: 0.5 } ]; describe('Graham\'s algorithm for convex hull', function() { 'use strict'; it('should not throw with empty list', () => { expect(() => convexHull([])).not.toThrow(); }); it('should calculate the convex hull', () => { expect(convexHull(points)).toEqual([ { x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0.5, y: 0.5 }, { x: 0, y: 1 } ]); }); }); ================================================ FILE: test/graphs/others/tarjan-connected-components.spec.js ================================================ var tj = require('../../../src/graphs/others/tarjan-connected-components').tarjanConnectedComponents; var nonConnected = { v1: [], v2: [], v3: [], v4: [], v5: [] }; var cyclicGraph = { v1: ['v2'], v2: ['v3'], v3: ['v4'], v4: ['v5'], v5: ['v1'] }; describe('Tarjan\'s algorithm for finding connected components', function () { 'use strict'; it('should be defined', function () { expect(typeof tj).toBe('function'); }); it('should return an array', function () { expect(tj() instanceof Array).toBeTruthy(); }); it('should work with non-connected graphs', function () { expect(tj(nonConnected)).toEqual([['v1'], ['v2'], ['v3'], ['v4'], ['v5']]); }); it('should workw ith cycles', function () { expect(tj(cyclicGraph)).toEqual([['v5', 'v4', 'v3', 'v2', 'v1']]); }); }); ================================================ FILE: test/graphs/others/topological-sort.spec.js ================================================ var ts = require('../../../src/graphs/others/topological-sort').topologicalSort; describe('Topological sort', function () { 'use strict'; it('should be defined', function () { expect(typeof ts).toBe('function'); }); it('should work with empty graphs', function () { expect(ts({})).toEqual([]); }); it('should give the proper topological order', function () { expect(ts({ v1: [] })).toEqual(['v1']); var graph = { v1: ['v2'], v2: ['v3'], v3: [] }; expect(ts(graph)).toEqual(['v1', 'v2', 'v3']); graph = { v1: ['v2', 'v5'], v2: [], v3: ['v1', 'v2', 'v4', 'v5'], v4: [], v5: [] }; expect(ts(graph)).toEqual(['v3', 'v4', 'v1', 'v5', 'v2']); }); it('should throw an error on cycle', function () { function runTs() { ts({ v1: ['v2'], v2: ['v1'] }); } expect(runTs).toThrow(); }); }); ================================================ FILE: test/graphs/searching/bfs.spec.js ================================================ /* jshint multistr: true */ var graph = [[0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 0, 0, 0], [1, 0, 1, 0, 0], [0, 1, 0, 1, 0]]; var bfs = require('../../../src/graphs/searching/bfs').bfs; describe('BFS', function () { 'use strict'; it('should work with empty graph', function () { expect(bfs([], 0, 0)).toEqual([0]); }); it('should return the correct output when used with\ source node equals target node', function () { expect(bfs(graph, 2, 2)).toEqual([2]); }); it('should return work with cycles', function () { expect(bfs(graph, 0, 2)).toEqual([0, 4, 3, 2]); }); it('should return falsy value when there\'s no path', function () { var graph = [[0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [0, 0, 0, 0, 0], [1, 0, 0, 0, 0], [0, 1, 0, 1, 0]]; expect(bfs(graph, 0, 2)).toBeFalsy(); }); /** * In this case the graph should not * update the parent of 2, in case it was called * with source 0 and target 2, after the first iteration. * * 0 ---> 1 * \ | * \ v * -> 2 */ it('should not update the parent node once set', function () { var graph = [[0, 1, 1], [0, 0, 1], [0, 0, 0]]; expect(bfs(graph, 0, 2)).toEqual([0, 2]); }); }); ================================================ FILE: test/graphs/searching/dfs.spec.js ================================================ var dfs = require('../../../src/graphs/searching/dfs').dfs; describe('dfs', function () { 'use strict'; it('should work with empty graph', function () { expect(dfs([[]])).toBeTruthy(); }); it('should always find a path between node and itself', function () { expect(dfs([[0]]), 0, 0).toBeTruthy(); }); it('should always find a path between two directly connected nodes', function () { expect(dfs([[0, 1], [1, 0]], 0, 1)).toBeTruthy(); expect(dfs([[0, 1], [1, 0]], 1, 0)).toBeTruthy(); }); it('should always find a path between two directly connected' + 'connected nodes in a directed graph', function () { expect(dfs([[0, 0], [1, 0]], 1, 0)).toBeTruthy(); }); it('should always find a path between two indirectly connected nodes', function () { expect(dfs([[0, 1, 0], [0, 0, 1], [0, 0, 0]], 0, 2)).toBeTruthy(); }); it('should not find a path between two nodes, which are not connected', function () { expect(dfs([[0, 0], [1, 0]], 0, 1)).toBeFalsy(); expect(dfs([[0, 0, 0], [0, 0, 1], [0, 0, 0]], 0, 2)).toBeFalsy(); }); }); ================================================ FILE: test/graphs/shortest-path/bellman-ford.spec.js ================================================ var exported = require('../../../src/graphs/shortest-path/bellman-ford'); var bellmanFord = exported.bellmanFord; var Vertex = exported.Vertex; var Edge = exported.Edge; describe('Bellman-Ford', function () { 'use strict'; it('should exports a method called bellmanFord', function () { expect(typeof bellmanFord).toBe('function'); }); it('should work for an empty graph', function () { var vs = []; var e = []; expect(bellmanFord(vs, e, undefined)) .toEqual({ parents: {}, distances: {} }); }); it('should work for a graph with a single vertex', function () { var vs = [new Vertex(1)]; var e = []; expect(bellmanFord(vs, e, vs[0])) .toEqual({ parents: { 1: null }, distances: { 1: 0 }}); }); it('should work in the general case', function () { var vs = [new Vertex(1), new Vertex(2), new Vertex(3)]; var e = [new Edge(vs[0], vs[1], 2), new Edge(vs[0], vs[2], 10), new Edge(vs[1], vs[2], 1) ]; var output = bellmanFord(vs, e, vs[0]); expect(output.distances['3']).toBe(3); }); }); ================================================ FILE: test/graphs/shortest-path/dijkstra.spec.js ================================================ var dijkstra = require('../../../src/graphs/shortest-path/dijkstra').dijkstra; describe('dijkstra', function () { 'use strict'; it('should define a function', function () { expect(dijkstra).toBeDefined(); expect(typeof dijkstra).toBe('function'); }); it('should work with empty graph', function () { expect(dijkstra(0, 0, [])).toBe(Infinity); }); it('should work when the src and dest are the same', function () { expect(dijkstra(0, 0, [[0]])).toBe(0); }); it('should work when there\'s no path', function () { expect(dijkstra(0, 1, [[0, Infinity], [Infinity, 0]])).toBe(Infinity); }); it('should find the shortest path', function () { expect(dijkstra(0, 2, [[0, 1, 4], [1, 0, 1], [4, 1, 0]])).toBe(2); }); }); ================================================ FILE: test/graphs/spanning-trees/kruskal.spec.js ================================================ var kruskal = require('../../../src/graphs/spanning-trees/kruskal'); describe('Kruskal', function() { 'use strict'; it('should define a function', function () { expect(kruskal).toBeDefined(); expect(typeof kruskal).toBe('object'); expect(typeof kruskal.Graph).toBe('function'); expect(typeof kruskal.Edge).toBe('function'); expect(typeof kruskal.Vertex).toBe('function'); }); it('should work with an empty graph', function() { var graph = new kruskal.Graph([], 0); var spanningTree = graph.kruskal(); expect(spanningTree.edges.length).toEqual(0); }); it('should correctly compute general example', function() { var nodes = []; var edges = []; var i; for (i = 0; i < 7; i += 1) { nodes[i] = new kruskal.Vertex(i); } edges.push(new kruskal.Edge(nodes[0], nodes[1], 7)); edges.push(new kruskal.Edge(nodes[1], nodes[2], 8)); edges.push(new kruskal.Edge(nodes[2], nodes[4], 5)); edges.push(new kruskal.Edge(nodes[4], nodes[6], 9)); edges.push(new kruskal.Edge(nodes[5], nodes[6], 11)); edges.push(new kruskal.Edge(nodes[3], nodes[5], 6)); edges.push(new kruskal.Edge(nodes[0], nodes[3], 5)); edges.push(new kruskal.Edge(nodes[1], nodes[4], 7)); edges.push(new kruskal.Edge(nodes[1], nodes[3], 9)); edges.push(new kruskal.Edge(nodes[3], nodes[4], 15)); edges.push(new kruskal.Edge(nodes[4], nodes[5], 8)); var graph = new kruskal.Graph(edges); var spanningTree = graph.kruskal(); expect(spanningTree.edges.length).toEqual(6); var sum = spanningTree.edges.reduce(function(acc, edge) { return acc += edge.distance; }, 0); expect(sum).toEqual(39); }) }); ================================================ FILE: test/others/fibonacci.spec.js ================================================ var mod = require('../../src/others/fibonacci.js'); var fibonacci = mod.fibonacci; describe('fibonacci algorithm', function () { 'use strict'; it('should return value 1 with input 1.', function () { expect(fibonacci(1)).toBe(1); }); it('should return value 1 with input 2.', function () { expect(fibonacci(2)).toBe(1); }); it('should return value 2 with input 3.', function () { expect(fibonacci(3)).toBe(2); }); it('should return value 3 with input 4.', function () { expect(fibonacci(4)).toBe(3); }); it('should return value 5 with input 5.', function () { expect(fibonacci(5)).toBe(5); }); it('should be 83621143489848422977 with input 97.', function () { expect(fibonacci(97)).toBe(83621143489848422977); }); it('should throw when input is too large.', function () { expect(function () {fibonacci(98)}).toThrow('Input too large, results in inaccurate fibonacci value.'); }); }); ================================================ FILE: test/others/fibonacciMemory.spec.js ================================================ var mod = require('../../src/others/fibonacciMemory.js'); var fibonacci = mod.fibonacciMemory; describe('fibonacci with Memory algorithm', function () { 'use strict'; it('should return value 1 with input 1.', function () { expect(fibonacci(1)).toBe(1); }); it('should return value 6 with input 8.', function () { expect(fibonacci(6)).toBe(8); }); it('should return value 7 with input 13.', function () { expect(fibonacci(7)).toBe(13); }); it('should return value 8 with input 21.', function () { expect(fibonacci(8)).toBe(21); }); it('should return value 9 with input 34.', function () { expect(fibonacci(9)).toBe(34); }); it('should return value 10 with input 55.', function () { expect(fibonacci(10)).toBe(55); }); it('should be 135301852344706760000 with input 98.', function () { expect(fibonacci(98)).toBe(135301852344706760000); }); }); ================================================ FILE: test/others/levenshtein-distance.spec.js ================================================ var mod = require('../../src/others/levenshtein-distance.js'); var levenshteinDistance = mod.levenshteinDistance; describe('Levenstein\'s minimum edit distance algorithm', function () { 'use strict'; it('should be defined', function () { expect(levenshteinDistance).toBeDefined(); }); it('"" -> "" should return 0.', function () { expect(levenshteinDistance('', '')).toBe(0); }); it('"T" -> "" should return 1.', function () { expect(levenshteinDistance('T', '')).toBe(1); }); it('"cake" -> "rake" should return 1.', function () { expect(levenshteinDistance('cake', 'rake')).toBe(1); }); it('"Sofia" -> "Sof" should return 2.', function () { expect(levenshteinDistance('Sofia', 'Sof')).toBe(2); }); it('"kitten" -> "sitting" should return 3', function () { expect(levenshteinDistance('kitten', 'sitting')).toBe(3); }); it('"google" -> "lookat" should return 4.', function () { expect(levenshteinDistance('google', 'lookat')).toBe(4); }); it('"emacs" -> "vim" should return 5.', function () { expect(levenshteinDistance('emacs', 'vim')).toBe(5); }); it('"coffee" -> "cocoa" should return 4.', function () { expect(levenshteinDistance('coffee', 'cocoa')).toBe(4); }); it('"Munich" -> "Muenchen" should return 4.', function () { expect(levenshteinDistance('Munich', 'Muenchen')).toBe(4); }); it('"rosebud" -> "budrose" should return 6.', function () { expect(levenshteinDistance('rosebud', 'budrose')).toBe(6); }); it('"decided" -> "decisive" should return 4.', function () { expect(levenshteinDistance('decided', 'decisive')).toBe(4); }); it('"similar" -> "simile" should return 2.', function () { expect(levenshteinDistance('similar', 'simile')).toBe(2); }); }); ================================================ FILE: test/others/min-coins-sum.spec.js ================================================ var minCoinsChange = require('../../src/others/min-coins-change.js').minCoinsChange; describe('Change making problem', function () { 'use strict'; it('should be defined', function () { expect(minCoinsChange).toBeDefined(); }); it('should work for 0 change', function () { expect(minCoinsChange([1, 2], 0)).toEqual([]); }); it('should work for change equals to array element', function () { expect(minCoinsChange([1, 2], 1)).toEqual([1]); }); it('should return the minimum amount of coins', function () { expect(minCoinsChange([1], 2)).toEqual([1, 1]); expect(minCoinsChange([1, 2], 3)).toEqual([1, 2]); // [2, 3, 2, 3] or [1, 3, 3, 3] expect(minCoinsChange([1, 2, 3], 10).length).toEqual(4); }); it('should return undefined for combination, which is not possible', function () { expect(minCoinsChange([1, 2, 3], 0.5)).not.toBeDefined(); }); }); ================================================ FILE: test/others/minimax.spec.js ================================================ const minimaxBuilder = require('../../src/others/minimax.js').minimaxBuilder; describe('Minimax', function () { 'use strict'; it('builder should be defined', function () { expect(minimaxBuilder).toBeDefined(); }); describe('with tic tac toe', function () { let game = ticTacToe(); function getAllNextStates(state) { const possibleMoves = game.emptyCells(state); return possibleMoves.map(move => ({ move, state: game.nextState(state, move), })); } const minimaxForX = minimaxBuilder( getAllNextStates, state => game.isGameOver(state), state => game.getScore(state).x - game.getScore(state).o ) const minimaxForO = minimaxBuilder( getAllNextStates, state => game.isGameOver(state), state => game.getScore(state).o - game.getScore(state).x ) it('should win versus dumb agent as first player', function () { let state = game.newState('x'); while (!game.isGameOver(state)) { if (state.turn === 'x') { state = game.nextState(state, minimaxForX(state, true, 5, -Infinity, Infinity).move); } else { const move = game.emptyCells(state)[0]; state = game.nextState(state, move); } } expect(game.isGameOver(state)).toBe(true); expect(game.getScore(state)).toEqual({x: 1, o: 0}); }); it('should win versus dumb agent as second player', function () { let state = game.newState('x'); while (!game.isGameOver(state)) { if (state.turn === 'o') { state = game.nextState(state, minimaxForO(state, true, 5, -Infinity, Infinity).move); } else { const move = game.emptyCells(state)[0]; state = game.nextState(state, move); } } expect(game.isGameOver(state)).toBe(true); expect(game.getScore(state)).toEqual({x: 0, o: 1}); }); it('should be a tie for two minimax agents', function () { let state = game.newState('x'); while (!game.isGameOver(state)) { if (state.turn === 'o') { state = game.nextState(state, minimaxForO(state, true, 5, -Infinity, Infinity).move); } else { state = game.nextState(state, minimaxForX(state, true, 5, -Infinity, Infinity).move); } } expect(game.isGameOver(state)).toBe(true); expect(game.getScore(state)).toEqual({x: 0, o: 0}); }); }); describe('with simple game', function () { let game = simpleGame(); const minimaxForA = minimaxBuilder( state => [true, false].map(move => ({ move, state: game.nextState(state, move)})), state => game.isGameOver(state), state => game.getScore(state).A - game.getScore(state).B ); const minimaxForB = minimaxBuilder( state => [true, false].map(move => ({ move, state: game.nextState(state, move)})), state => game.isGameOver(state), state => game.getScore(state).B - game.getScore(state).A ); it('should win versus dumb agent as a first player', function () { /* o / \ o o / \ / \ o o o o / \ / \ / \ / \ -1 1 1 1 1 -1 1 -1 */ const binaryTree = [0, 0, 0, 0, 0, 0, 0, -1, 1, 1, 1, 1, -1, 1, -1]; let state = game.newState(binaryTree); while (!game.isGameOver(state)) { if (state.turn === 'A') { state = game.nextState(state, minimaxForA(state, true, 5, -Infinity, Infinity).move); } else { state = game.nextState(state, false); } } expect(game.isGameOver(state)).toBe(true); expect(game.getScore(state)).toEqual({A: 1, B: -1}); }); it('should win versus dumb agent as a second player', function () { /* o / \ o o / \ / \ -1 -1 -1 1 */ const binaryTree = [0, 0, 0, -1, -1, -1, 1]; let state = game.newState(binaryTree); while (!game.isGameOver(state)) { if (state.turn === 'B') { state = game.nextState(state, minimaxForB(state, true, 5, -Infinity, Infinity).move); } else { state = game.nextState(state, false); } } expect(game.isGameOver(state)).toBe(true); expect(game.getScore(state)).toEqual({A: -1, B: 1}); }); }); }); function ticTacToe() { 'use strict'; function newState(turn) { return { board: [[0, 0, 0], [0, 0, 0], [0, 0, 0]], turn }; } function emptyCells(state) { const result = []; state.board.forEach((row, y) => { row.forEach((cell, x) => { if (cell === 0) { result.push({x, y}) } }); }); return result; } function getWinner(state) { const winVariants = [ [{x: 0, y: 0}, {x: 0, y: 1}, {x: 0, y: 2}], [{x: 1, y: 0}, {x: 1, y: 1}, {x: 1, y: 2}], [{x: 2, y: 0}, {x: 2, y: 1}, {x: 2, y: 2}], [{x: 0, y: 0}, {x: 1, y: 0}, {x: 2, y: 0}], [{x: 0, y: 1}, {x: 1, y: 1}, {x: 2, y: 1}], [{x: 0, y: 2}, {x: 1, y: 0}, {x: 2, y: 2}], [{x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2}], [{x: 2, y: 0}, {x: 1, y: 1}, {x: 2, y: 0}], ]; for (const variant of winVariants) { const combo = variant.map(cell => state.board[cell.y][cell.x]).join(''); if (combo === 'xxx') { return 'x'; } else if (combo === 'ooo') { return 'o'; } } return null; } function allFieldsMarked(state) { return state.board.every(row => row.every(cell => cell !== 0)); } function isGameOver(state) { return allFieldsMarked(state) || getWinner(state) !== null; } function getScore(state) { if (getWinner(state) === 'x') { return {x: 1, o: 0}; } else if (getWinner(state) === 'o') { return {x: 0, o: 1}; } return {x: 0, o: 0}; } function nextState(state, move) { const newBoard = state.board.map(row => row.slice()); newBoard[move.y][move.x] = state.turn; return { board: newBoard, turn: state.turn === 'x' ? 'o' : 'x', }; } return { newState, getScore, nextState, isGameOver, emptyCells, } } /* A simple game made for the purpose of minimax testing. The game has a binary tree with end values: 1 for player A win and -1 for player B win. Game starts from the root node and each player has a binary choose - "false" moves to the left child and "true" moves to the right child. The game ends when the very bottom leaf is reached. o / \ o o / \ / \ 1 -1 -1 -1 */ function simpleGame() { 'use strict'; function newState(binaryTree) { return { turn: 'A', tree: binaryTree, position: 0, }; } function nextState(state, move) { return { tree: state.tree, position: move ? state.position * 2 + 2 : state.position * 2 + 1, turn: state.turn === 'A' ? 'B' : 'A', }; } function isGameOver(state) { return state.tree[state.position] !== 0; } function getScore(state) { return { A: state.tree[state.position], B: state.tree[state.position] === 0 ? 0 : -state.tree[state.position], } } return { newState, nextState, isGameOver, getScore, } } ================================================ FILE: test/others/minkowski-distance.spec.js ================================================ var mod = require('../../src/others/minkowski-distance.js'); var minkowskiDistance = mod.minkowskiDistance; describe('Minkowski Distance', function () { 'use strict'; it('should return 1 with points (0, 1), (1, 1) in order 1.', function () { expect(minkowskiDistance([0, 1], [1, 1], 1)).toBe(1); }); it('should return 2 with points (0, 1), (1, 1) in order 2.', function () { expect(minkowskiDistance([0, 1], [1, 1], 2)).toBe(1); }); it('should return 2 with points (0, 1, 4), (1, 1, 6) in order Positive Infinity.', function () { expect(minkowskiDistance([0, 1, 4], [1, 1, 6], Number.POSITIVE_INFINITY)).toBe(2); }); it('should return 0 with points (0, 1, 4), (1, 1, 6) in order Negative Infinity.', function () { expect(minkowskiDistance([0, 1, 4], [1, 1, 6], Number.NEGATIVE_INFINITY)).toBe(0); }); it('should return 8.372966759705923 with points (0, 3, 4, 5), (7, 6, 3, -1) in order 3.', function () { expect(minkowskiDistance([0, 3, 4, 5], [7, 6, 3, -1], 3)).toBe(8.372966759705923); }); it('should throw when both vectors don\'t have same dimension', function () { expect(function () { minkowskiDistance([1, 2], [1], 1) }).toThrow('Both vectors should have same dimension'); }); it('should throw when p is not defined', function () { expect(function () { minkowskiDistance([1, 2], [1, 2]) }).toThrow('The order "p" must be a number'); }); it('should throw when p is not a number', function () { expect(function () { minkowskiDistance([1, 2], [1, 2], NaN) }).toThrow('The order "p" must be a number'); }); it('should throw when p is less than 1', function () { expect(function () { minkowskiDistance([1, 2], [1, 2], 0) }).toThrow('Order less than 1 will violate the triangle inequality'); }); }); ================================================ FILE: test/primes/is-prime.spec.js ================================================ var isPrime = require('../../src/primes/is-prime').isPrime; describe('Advanced (optimised) method that checks number on prime', function () { 'use strict'; it('should give true for number 104743', function () { expect(isPrime(104743)).toBe(true); }); it('should give false for number 104744', function () { expect(isPrime(104744)).toBe(false); }); it('the 10001st prime number should be 104743', function () { var count = 1; //we know that 2 is prime var value = 1; while (count < 10001) { value += 2; if (isPrime(value)) { count += 1; } } expect(value).toEqual(104743); }); }); ================================================ FILE: test/primes/prime-factor-tree.spec.js ================================================ var primeFactorTree = require('../../src/primes/prime-factor-tree').primeFactorTree; describe('Prime factor tree', function () { 'use strict'; it('for number 104743 should return [104743]', function () { expect(primeFactorTree(104743).toString()).toEqual([104743].toString()); }); it('for number 18 should return [2, 3, 3]', function () { expect(primeFactorTree(18).toString()).toEqual([2, 3, 3].toString()); }); it('should give the empty list for number less or equal 1', function () { expect(primeFactorTree(-12).toString()).toEqual([].toString()); expect(primeFactorTree(0).toString()).toEqual([].toString()); expect(primeFactorTree(1).toString()).toEqual([].toString()); }); it('sum of primes for given number 600851475143 should be 9238', function () { var primes = primeFactorTree(600851475143); var sumOfPrimes = primes.reduce(function (previousValue, currentValue) { return previousValue + currentValue; }); expect(sumOfPrimes).toEqual(9238); }); }); ================================================ FILE: test/primes/sieve-of-atkins.spec.js ================================================ var sieveOfAtkins = require('../../src/primes/sieve-of-atkins').sieveOfAtkins; describe('Sieve Of Atkins', function () { 'use strict'; it('should give the right sequence of primes for limit 12', function () { expect(sieveOfAtkins(12).toString()) .toEqual([2, 3, 5, 7, 11].toString()); }); it('should give the empty list for limit less or equal 1', function () { expect(sieveOfAtkins(-12).toString()).toEqual([].toString()); expect(sieveOfAtkins(0).toString()).toEqual([].toString()); expect(sieveOfAtkins(1).toString()).toEqual([].toString()); }); it('sum of prime numbers up to 2000000 limit should be 142913828922', function () { var sieve = sieveOfAtkins(2000000); var sumOfPrimes = sieve.reduce(function (previousValue, currentValue) { return previousValue + currentValue; }); expect(sumOfPrimes).toEqual(142913828922); }); }); ================================================ FILE: test/primes/sieve-of-eratosthenes.spec.js ================================================ var sieveOfEratosthenes = require('../../src/primes/sieve-of-eratosthenes').sieveOfEratosthenes; describe('Sieve Of Eratosthenes', function () { 'use strict'; it('should give the right sequence of primes for limit 12', function () { expect(sieveOfEratosthenes(12).toString()) .toEqual([2, 3, 5, 7, 11].toString()); }); it('should give the empty list for limit less or equal 1', function () { expect(sieveOfEratosthenes(-12).toString()).toEqual([].toString()); expect(sieveOfEratosthenes(0).toString()).toEqual([].toString()); expect(sieveOfEratosthenes(1).toString()).toEqual([].toString()); }); it('sum of prime numbers up to 2000000 limit should be 142913828922', function () { var sieve = sieveOfEratosthenes(2000000); var sumOfPrimes = sieve.reduce(function (previousValue, currentValue) { return previousValue + currentValue; }); expect(sumOfPrimes).toEqual(142913828922); }); }); ================================================ FILE: test/searching/binarysearch.spec.js ================================================ var binarySearch = require('../../src/searching/binarysearch').binarySearch; describe('Binary search', function () { 'use strict'; it('should find the element at position 0 ', function () { expect(binarySearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should find the element in position arr.length - 1', function () { var arr = [1, 2, 3, 4, 6, 8]; expect(binarySearch(arr, 8)).toBe(arr.length - 1); }); it('should work with arrays with 2 elements', function () { expect(binarySearch([1, 8], 1)).toBe(0); expect(binarySearch([1, 8], 8)).toBe(1); }); it('should return a negative number for missing elements', function () { expect(binarySearch([1, 2, 3], 4)).toBeLessThan(0); }); it('should work with empty arrays', function () { expect(binarySearch([], 4)).toBe(-1); }); it('should work with a key string', function () { expect(binarySearch([{ x: 1 }, { x: 2 }, { x: 3 }], { x: 2 }, 'x')).toBe(1); }); it('should work with a key function', function () { expect(binarySearch([{ x: 1 }, { x: 2 }, { x: 3 }], { x: 2 }, function (o) { return o.x; })).toBe(1); }); }); ================================================ FILE: test/searching/interpolation-search.spec.js ================================================ var interpolationSearch = require('../../src/searching/interpolation-search') .interpolationSearch; describe('Interpolation search', function() { 'use strict'; it('should find the element at position 0 ', function() { expect(interpolationSearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should find the element at position 4 ', function() { expect(interpolationSearch([1, 2, 3, 4, 6, 8], 6)).toBe(4); }); it('should return -1 if element is not found', function() { expect(interpolationSearch([1, 2, 3, 4, 6, 8], 17)).toBe(-1); }); it('should return -1 if array is empty', function() { expect(interpolationSearch([], 10)).toBe(-1); }); }); ================================================ FILE: test/searching/jump-search.spec.js ================================================ var jumpSearch = require('../../src/searching/jump-search').jumpSearch; describe('Jump search', function() { 'use strict'; it('should find the element at position 0 ', function() { expect(jumpSearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should find the element at position 4 ', function() { expect(jumpSearch([1, 2, 3, 4, 6, 8], 6)).toBe(4); }); it('should return -1 ', function() { expect(jumpSearch([1, 2, 3, 4, 6, 8], 10)).toBe(-1); }); it('should return -1 ', function() { expect(jumpSearch([], 10)).toBe(-1); }); }); ================================================ FILE: test/searching/knuth-morris-pratt.spec.js ================================================ var indexOf = require('../../src/searching/knuth-morris-pratt').kmp; describe('The string searching algorithm of Knuth-Morris-Pratt', function () { 'use strict'; it('should find the empty string in any string', function () { expect(indexOf('', '')).toBe(0); expect(indexOf('foo', '')).toBe(0); }); it('should return negative value for patterns, which are ' + 'not part of the string', function () { expect(indexOf('foo', 'bar') < 0).toBeTruthy(); expect(indexOf('f', 'foobar') < 0).toBeTruthy(); expect(indexOf('foobar', 'fobar') < 0).toBeTruthy(); }); it('should return the first index of the matching pattern', function () { expect(indexOf('foo', 'f')).toBe(0); expect(indexOf('foo', 'oo')).toBe(1); expect(indexOf('foo', 'o')).toBe(1); expect(indexOf('foobar', 'foo')).toBe(0); expect(indexOf('foobar', 'bar')).toBe(3); }); }); ================================================ FILE: test/searching/linearSearch.spec.js ================================================ var linearSearch = require('../../src/searching/linearSearch').linearSearch; describe('Linear Search', function () { 'use strict'; it('should find the element at position 0 ', function () { expect(linearSearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should find the element in position arr.length - 1', function () { var arr = [1, 2, 3, 4, 6, 8]; expect(linearSearch(arr, 8)).toBe(arr.length - 1); }); it('should work with arrays with 2 elements', function () { expect(linearSearch([1, 8], 1)).toBe(0); expect(linearSearch([1, 8], 8)).toBe(1); }); it('should return a negative number for missing elements', function () { expect(linearSearch([1, 2, 3], 4)).toBeLessThan(0); }); it('should work with empty arrays', function () { expect(linearSearch([], 4)).toBe(-1); }); }); ================================================ FILE: test/searching/longest-common-subsequence.spec.js ================================================ var longestCommonSubsequence = require('../../src/searching/' + 'longest-common-subsequence') .longestCommonSubsequence; describe('longest common subsequence', function () { 'use strict'; it('should work with empty strings', function () { expect(longestCommonSubsequence('', '')).toBe(''); }); it('should work with first string empty', function () { expect(longestCommonSubsequence('', 'abcd')).toBe(''); }); it('should work with second string empty', function () { expect(longestCommonSubsequence('abcd', '')).toBe(''); }); it('should work if there is no lcs', function () { expect(longestCommonSubsequence('qtwer', 'zvxcv')).toBe(''); }); it('should work if lcs is whole first string', function () { expect(longestCommonSubsequence('abc', 'abcdefghi')).toBe('abc'); }); it('should work if lcs is whole second string', function () { expect(longestCommonSubsequence('qwerty', 'rty')).toBe('rty'); }); it('should work with repeated letter', function () { expect(longestCommonSubsequence('AAATC', 'GGTAGGC')).toBe('AC'); }); it('should work with custom characters', function () { expect(longestCommonSubsequence(':-)', 'B-)')).toBe('-)'); }); it('should work with long strings', function () { expect(longestCommonSubsequence('this is the first string', 'that is second')).toBe('tht is sn'); }); it('should work with very long strings', function () { expect(longestCommonSubsequence('giiiiiiit1huuuuuu2bbb', 'zzxxcvasdfgmntplpliiggggu2b222')).toBe('giiu2b'); }); }); ================================================ FILE: test/searching/longest-increasing-subsequence.spec.js ================================================ var longestIncreasingSubsequence = require('../../src/searching/' + 'longest-increasing-subsequence') .longestIncreasingSubsequence; describe('longest increasing subsequence', function () { 'use strict'; var sequence; beforeEach(function () { sequence = [5, 2, 8, 6, 3, 6, 9, 7, 11]; }); it('should work with empty array', function () { expect(longestIncreasingSubsequence([]).length).toBe(0); }); it('should return the only element in a single element array', function () { var array = [1]; expect(longestIncreasingSubsequence(array)).toEqual([1]); }); it('should give the right length', function () { expect(longestIncreasingSubsequence(sequence).length).toBe(5); }); it('should work with empty arrays', function () { expect(longestIncreasingSubsequence([]).length).toBe(0); }); it('should return the correct path', function () { expect(longestIncreasingSubsequence(sequence).toString()) .toBe([2, 3, 6, 9, 11].toString()); }); it('should work with a custom comparator', function () { var cmp = function (a, b) { return b - a; }; var seq = [1, 2, -1]; var result = longestIncreasingSubsequence(seq, cmp); expect(result.length).toBe(2); expect(result).toEqual([1, -1]); }); }); ================================================ FILE: test/searching/maximum-subarray-divide-and-conquer.spec.js ================================================ var maxSubArray = require('../../src/searching/maximum-subarray-divide-and-conquer') .maxSubarray; describe('Maximum subarray implemented with divide and conquer', function () { 'use strict'; it('should work with empty arrays', function () { expect(isNaN(maxSubArray([]))).toBeTruthy(); }); it('should return the only element when an array with' + 'single element is passed', function () { expect(maxSubArray([42])).toBe(42); }); it('should return the only negative element when an array with' + 'single element is passed', function () { expect(maxSubArray([-42])).toBe(-42); }); it('should return the zero when an array with' + 'single element, which is zero is passed', function () { expect(maxSubArray([0])).toBe(0); }); it('should return the max sum of a subarray', function () { expect(maxSubArray([1, -1, 2, 3, -1])).toBe(5); }); it('should return the max nevative number when array' + 'with nevative numbers is provided', function () { expect(maxSubArray([-10, -1, -2, -3, -1])).toBe(-1); }); }); ================================================ FILE: test/searching/maximum-subarray.spec.js ================================================ var maxSubArray = require('../../src/searching/maximum-subarray').maxSubarray; describe('Maximum subarray', function() { 'use strict'; it('should work with empty arrays', function() { expect(maxSubArray([])).toBeUndefined(); }); it('should return the only element when an array with single element is passed', function() { expect(maxSubArray([42])).toBe(42); }); it('should return the only negative element when an array with single element is passed', function() { expect(maxSubArray([-42])).toBe(-42); }); it('should return the zero when an array with single element, which is zero is passed', function() { expect(maxSubArray([0])).toBe(0); }); it('should return the max sum of a subarray', function() { expect(maxSubArray([1, -1, 2, 3, -1])).toBe(5); }); it('should return the max negative number when array with negative numbers is provided', function() { expect(maxSubArray([-10, -1, -2, -3, -1])).toBe(-1); }); }); ================================================ FILE: test/searching/quickselect.spec.js ================================================ var quickselect = require('../../src/searching/quickselect').quickselect; describe('quickselect', function () { 'use strict'; it('should be defined as function', function () { expect(typeof quickselect).toBe('function'); }); it('should work with empty array', function () { expect(quickselect([], 1)).toBe(undefined); }); it('should find the only element in the list', function () { expect(quickselect([1], 0)).toBe(1); }); it('should return undefined if the list is smaller than the index', function () { expect(quickselect([2, 1], 3)).toBeUndefined(); }); it('should find the element if in sorted order', function () { expect(quickselect([1, 2], 0)).toBe(1); expect(quickselect([1, 2], 1)).toBe(2); }); it('should fine the element if not in sorted order', function () { expect(quickselect([2, 1, 9, 6], 3)).toBe(9); }); }); ================================================ FILE: test/searching/recursive-binarysearch.spec.js ================================================ var binarySearch = require('../../src/searching/recursive-binarysearch').binarySearch; describe('Binary search', function () { 'use strict'; it('should find the element at position 0 ', function () { expect(binarySearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should find the eleent in position arr.length', function () { expect(binarySearch([1, 2, 3, 4, 6, 8], 1)).toBe(0); }); it('should work with arrays with 2 elements', function () { expect(binarySearch([1, 8], 1)).toBe(0); expect(binarySearch([1, 8], 8)).toBe(1); }); it('should return a negative number for missing elements', function () { expect(binarySearch([1, 2, 3], 4)).toBeLessThan(0); }); it('should work with empty arrays', function () { expect(binarySearch([], 4)).toBe(-1); }); }); ================================================ FILE: test/sorting/3-way-string-quicksort.spec.js ================================================ var quicksort = require('../../src/sorting/3-way-string-quicksort.js').quicksort; describe('Most-Significant Digit', function () { 'use strict'; it('should work with empty arrays', function () { expect(quicksort([]).length).toBe(0); }); it('should work with arrays with a single element', function () { var arr = ['a']; quicksort(arr); expect(arr.length).toBe(1); expect(arr[0]).toBe('a'); }); it('should work with arrays with equally length strings', function () { var arr = ['bb', 'aa', 'cc']; quicksort(arr); expect(arr.length).toBe(3); expect(arr[0]).toBe('aa'); expect(arr[1]).toBe('bb'); expect(arr[2]).toBe('cc'); }); it('should work with arrays with differently length strings', function () { var arr = ['bb', 'aaa', 'a', 'aa']; quicksort(arr); expect(arr.length).toBe(4); expect(arr[0]).toBe('a'); expect(arr[1]).toBe('aa'); expect(arr[2]).toBe('aaa'); expect(arr[3]).toBe('bb'); }); }); ================================================ FILE: test/sorting/bubblesort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var bubbleSort = require('../../src/sorting/bubblesort.js').bubbleSort; sortTestCase(bubbleSort, 'Bubble sort'); ================================================ FILE: test/sorting/bucketsort.spec.js ================================================ var bs = require('../../src/sorting/bucketsort').bucketSort; describe('bucketsort', function () { 'use strict'; it('should sort the empty array', function () { expect(bs([])).toEqual([]); }); it('should return array with the same count of elements', function () { expect(bs([2, 3, 4]).length).toBe(3); }); it('should sort the given array in ascending order', function () { expect(bs([42, 3, 10])).toEqual([3, 10, 42]); }); }); ================================================ FILE: test/sorting/countingsort.spec.js ================================================ var cs = require('../../src/sorting/countingsort').countingSort; describe('countingsort', function () { 'use strict'; it('should sort the empty array', function () { expect(cs([])).toEqual([]); }); it('should return array with the same count of elements', function () { expect(cs([2, 3, 4]).length).toBe(3); }); it('should sort the given array in ascending order', function () { expect(cs([42, 3, 10])).toEqual([3, 10, 42]); }); }); ================================================ FILE: test/sorting/heapsort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var heapSort = require('../../src/sorting/heapsort.js').heapSort; sortTestCase(heapSort, 'Heap sort'); ================================================ FILE: test/sorting/insertionbinarysort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var insertionBinarySort = require('../../src/sorting/' + 'insertion-binary-sort.js').insertionBinarySort; sortTestCase(insertionBinarySort, 'Insertion binary sort'); ================================================ FILE: test/sorting/insertionsort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var insertionSort = require('../../src/sorting/' + 'insertionsort.js').insertionSort; sortTestCase(insertionSort, 'Insertion sort'); ================================================ FILE: test/sorting/lsd.spec.js ================================================ var lsd = require('../../src/sorting/lsd.js').lsd; describe('Least-Significant Digit', function () { 'use strict'; it('should work with empty arrays', function () { expect(lsd([]).length).toBe(0); }); it('should work with arrays with a single element', function () { var arr = ['a']; lsd(arr); expect(arr.length).toBe(1); expect(arr[0]).toBe('a'); }); it('should work with arrays with equally length strings', function () { var arr = ['bb', 'aa', 'cc']; lsd(arr); expect(arr.length).toBe(3); expect(arr[0]).toBe('aa'); expect(arr[1]).toBe('bb'); expect(arr[2]).toBe('cc'); }); it('should work with arrays with equally length strings', function () { var arr = ['bbb', 'aac', 'aaa']; lsd(arr, 3); expect(arr.length).toBe(3); expect(arr[0]).toBe('aaa'); expect(arr[1]).toBe('aac'); expect(arr[2]).toBe('bbb'); }); }); ================================================ FILE: test/sorting/mergesort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var mergeSort = require('../../src/sorting/mergesort.js').mergeSort; sortTestCase(mergeSort, 'Merge sort'); ================================================ FILE: test/sorting/msd.spec.js ================================================ var msd = require('../../src/sorting/msd.js').msd; describe('Most-Significant Digit', function () { 'use strict'; it('should work with empty arrays', function () { expect(msd([]).length).toBe(0); }); it('should work with arrays with a single element', function () { var arr = ['a']; msd(arr); expect(arr.length).toBe(1); expect(arr[0]).toBe('a'); }); it('should work with arrays with equally length strings', function () { var arr = ['bb', 'aa', 'cc']; msd(arr); expect(arr.length).toBe(3); expect(arr[0]).toBe('aa'); expect(arr[1]).toBe('bb'); expect(arr[2]).toBe('cc'); }); it('should work with arrays with differently length strings', function () { var arr = ['bb', 'aaa', 'a', 'aa']; msd(arr); expect(arr.length).toBe(4); expect(arr[0]).toBe('a'); expect(arr[1]).toBe('aa'); expect(arr[2]).toBe('aaa'); expect(arr[3]).toBe('bb'); }); }); ================================================ FILE: test/sorting/oddeven-sort.spec.js ================================================ var oes = require('../../src/sorting/oddeven-sort').oddEvenSort; describe('oddeven-sort', function () { 'use strict'; it('should sort the empty array', function () { expect(oes([])).toEqual([]); }); it('should return array with the same count of elements', function () { expect(oes([2, 3, 4]).length).toBe(3); }); it('should sort the given array in ascending order', function () { expect(oes([42, 3, 10])).toEqual([3, 10, 42]); }); }); ================================================ FILE: test/sorting/quicksort-declarative.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var quickSort = require('../../src/sorting/quicksort-declarative.js').quickSort; sortTestCase(quickSort, 'Quick sort'); ================================================ FILE: test/sorting/quicksort-middle.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var quickSort = require('../../src/sorting/quicksort-middle.js').quickSort; sortTestCase(quickSort, 'Quick sort'); ================================================ FILE: test/sorting/quicksort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var quickSort = require('../../src/sorting/quicksort.js').quickSort; sortTestCase(quickSort, 'Quick sort'); ================================================ FILE: test/sorting/radixsort.spec.js ================================================ var rx = require('../../src/sorting/radixsort.js').radixSort; describe('radixsort', function () { 'use strict'; it('should sort the empty array', function () { expect(rx([])).toEqual([]); }); it('should return array with the same count of elements', function () { expect(rx([2, 3, 4]).length).toBe(3); }); it('should sort the given array in ascending order', function () { expect(rx([42, 3, 10])).toEqual([3, 10, 42]); }); }); ================================================ FILE: test/sorting/recursiveinsertionsort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var recursiveInsertionSort = require('../../src/sorting/' + 'recursive-insertionsort.js').recursiveInsertionSort; sortTestCase(recursiveInsertionSort, 'Recursive insertion sort'); ================================================ FILE: test/sorting/selectionsort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var selectionSort = require('../../src/sorting/selectionsort.js') .selectionSort; sortTestCase(selectionSort, 'Selection sort'); ================================================ FILE: test/sorting/shellsort.spec.js ================================================ var sortTestCase = require('./sort.testcase.js'); var shellSort = require('../../src/sorting/shellsort.js') .shellSort; sortTestCase(shellSort, 'Shell sort'); ================================================ FILE: test/sorting/sort.testcase.js ================================================ module.exports = function (sort, algorithmName, options) { 'use strict'; options = options || { integers: false, reverse: true }; describe(algorithmName, function () { function createRandomArray(config) { config = config || {}; var size = config.size || 100; var precision = config.precision || 2; var multiplier = config.multiplier || 100; var result = []; for (var i = size; i > 0; i -= 1) { result.push(parseFloat((Math.random() * multiplier).toFixed(precision))); } return result; } it('should work with empty array', function () { expect(sort([])).toEqual([]); }); it('should work with sorted arrays', function () { expect(sort([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); }); it('should work with random non-sorted arrays', function () { var array; if (options.integers) { array = createRandomArray(); } else { array = createRandomArray({ precision: 0 }); } array = sort(array); for (var i = 0; i < array.length - 1; i += 1) { expect(array[i] <= array[i + 1]).toBeTruthy(); } }); if (options.reverse) { it('should sort the numbers in descending order ' + 'when such comparator is provided', function () { function comparator(a, b) { return b - a; } var array = createRandomArray(); array = sort(array, comparator); for (var i = 0; i < array.length - 1; i += 1) { expect(array[i] >= array[i + 1]).toBeTruthy(); } }); } }); };