diff --git a/R/.Rproj.user/CBF2E10B/sources/prop/INDEX b/R/.Rproj.user/CBF2E10B/sources/prop/INDEX index 1f13eb1..62cc263 100644 --- a/R/.Rproj.user/CBF2E10B/sources/prop/INDEX +++ b/R/.Rproj.user/CBF2E10B/sources/prop/INDEX @@ -2,3 +2,4 @@ ~%2FDevelopment%2F2020-code-advent%2FR%2F01.py="E3582989" ~%2FDevelopment%2F2020-code-advent%2FREADME.md="87542FC2" ~%2FDevelopment%2F2020-code-advent%2Finput%2F01-01.txt="22EEE1B0" +~%2FDevelopment%2F2020-code-advent%2Fjs%2F01.js="BCBA9F87" diff --git a/R/.Rproj.user/shared/notebooks/paths b/R/.Rproj.user/shared/notebooks/paths index 2a9472a..e457a99 100644 --- a/R/.Rproj.user/shared/notebooks/paths +++ b/R/.Rproj.user/shared/notebooks/paths @@ -2,3 +2,4 @@ /Users/hrbrmstr/Development/2020-code-advent/R/01.py="1AD9A8A4" /Users/hrbrmstr/Development/2020-code-advent/README.md="74DC8DCF" /Users/hrbrmstr/Development/2020-code-advent/input/01-01.txt="53BE9636" +/Users/hrbrmstr/Development/2020-code-advent/js/01.js="77D3BDB6" diff --git a/README.md b/README.md index 6af519f..facb2d2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # 2020 Advent of Code -Solutions in R, Swift, Python (in the `R` dir since I'm using RStudio's Python REPL) and mebbe others +Solutions in R, Swift, javascript, Python (in the `R` dir since I'm using RStudio's Python REPL) and mebbe others diff --git a/js/01.js b/js/01.js new file mode 100644 index 0000000..11d7378 --- /dev/null +++ b/js/01.js @@ -0,0 +1,28 @@ +// See R or Python code for the problems + +// 01-01 + +var fs = require("fs") +var combn = require('generatorics') // npm install generatorics + +input = fs.readFileSync("../input/01-01.txt", "utf-8") + .split("\n") + .map(Number) + +for (var pair of combn.combination(input, 2)) { + res = pair.reduce((a, b) => a+b) + if (res == 2020) { + console.log(pair.reduce((a, b) => a*b)) + break + } +} + +// 01-02 + +for (var pair of combn.combination(input, 3)) { + res = pair.reduce((a, b) => a+b) + if (res == 2020) { + console.log(pair.reduce((a, b) => a*b)) + break + } +} diff --git a/js/node_modules/.package-lock.json b/js/node_modules/.package-lock.json new file mode 100644 index 0000000..43664bb --- /dev/null +++ b/js/node_modules/.package-lock.json @@ -0,0 +1,15 @@ +{ + "name": "js", + "lockfileVersion": 2, + "requires": true, + "packages": { + "node_modules/generatorics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", + "integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=", + "engines": { + "node": ">=6.0.0" + } + } + } +} diff --git a/js/node_modules/generatorics/.eslintrc.json b/js/node_modules/generatorics/.eslintrc.json new file mode 100644 index 0000000..2a270bf --- /dev/null +++ b/js/node_modules/generatorics/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "never" + ] + } +} diff --git a/js/node_modules/generatorics/README.md b/js/node_modules/generatorics/README.md new file mode 100644 index 0000000..5b7c6ad --- /dev/null +++ b/js/node_modules/generatorics/README.md @@ -0,0 +1,388 @@ +# Generatorics + +### An efficient combinatorics library for JavaScript utilizing ES2015 generators. Generate combinations, permutations, and power sets of arrays or strings. + +- Node +``` +npm install generatorics +``` +```javascript +var G = require('generatorics'); +``` + +- Browser +``` +bower install generatorics +``` +```html + +``` + +**Note:** This module is not transpiled for compatibility, as it degrades the performance. Check your browser/node version. + +## Usage + +### power set +```javascript +for (var subset of G.powerSet(['a', 'b', 'c'])) { + console.log(subset); +} +// [ ] +// [ 'a' ] +// [ 'a', 'b' ] +// [ 'a', 'b', 'c' ] +// [ 'a', 'c' ] +// [ 'b' ] +// [ 'b', 'c' ] +// [ 'c' ] +``` + +### permutation +```javascript +for (var perm of G.permutation(['a', 'b', 'c'], 2)) { + console.log(perm); +} +// [ 'a', 'b' ] +// [ 'a', 'c' ] +// [ 'b', 'a' ] +// [ 'b', 'c' ] +// [ 'c', 'a' ] +// [ 'c', 'b' ] + +for (var perm of G.permutation(['a', 'b', 'c'])) { // assumes full length of array + console.log(perm); +} +// [ 'a', 'b', 'c' ] +// [ 'a', 'c', 'b' ] +// [ 'b', 'a', 'c' ] +// [ 'b', 'c', 'a' ] +// [ 'c', 'b', 'a' ] +// [ 'c', 'a', 'b' ] +``` + +### combination +```javascript +for (var comb of G.combination(['a', 'b', 'c'], 2)) { + console.log(comb); +} +// [ 'a', 'b' ] +// [ 'a', 'c' ] +// [ 'b', 'c' ] +``` + +For efficiency, each array being yielded is the same one being mutated on each iteration. **DO NOT** mutate the array. +```javascript +var combs = []; +for (var comb of G.combination(['a', 'b', 'c'], 2)) { + combs.push(comb); +} +console.log(combs); +// [ [ 'b', 'c' ], [ 'b', 'c' ], [ 'b', 'c' ] ] +``` +You can clone if necessary, or use the [clone submodule](#clone-submodule) + +### permutation of combination +```javascript +for (var perm of G.permutationCombination(['a', 'b', 'c'])) { + console.log(perm); +} +// [ ] +// [ 'a' ] +// [ 'a', 'b' ] +// [ 'a', 'b', 'c' ] +// [ 'a', 'c' ] +// [ 'a', 'c', 'b' ] +// [ 'b' ] +// [ 'b', 'a' ] +// [ 'b', 'a', 'c' ] +// [ 'b', 'c' ] +// [ 'b', 'c', 'a' ] +// [ 'c' ] +// [ 'c', 'a' ] +// [ 'c', 'a', 'b' ] +// [ 'c', 'b' ] +// [ 'c', 'b', 'a' ] +``` + +### cartesian product +```javascript +for (var prod of G.cartesian([0, 1, 2], [0, 10, 20], [0, 100, 200])) { + console.log(prod); +} +// [ 0, 0, 0 ], [ 0, 0, 100 ], [ 0, 0, 200 ] +// [ 0, 10, 0 ], [ 0, 10, 100 ], [ 0, 10, 200 ] +// [ 0, 20, 0 ], [ 0, 20, 100 ], [ 0, 20, 200 ] +// [ 1, 0, 0 ], [ 1, 0, 100 ], [ 1, 0, 200 ] +// [ 1, 10, 0 ], [ 1, 10, 100 ], [ 1, 10, 200 ] +// [ 1, 20, 0 ], [ 1, 20, 100 ], [ 1, 20, 200 ] +// [ 2, 0, 0 ], [ 2, 0, 100 ], [ 2, 0, 200 ] +// [ 2, 10, 0 ], [ 2, 10, 100 ], [ 2, 10, 200 ] +// [ 2, 20, 0 ], [ 2, 20, 100 ], [ 2, 20, 200 ] +``` + +### base N +```javascript +for (var num of G.baseN(['a', 'b', 'c'])) { + console.log(num); +} +// [ 'a', 'a', 'a' ], [ 'a', 'a', 'b' ], [ 'a', 'a', 'c' ] +// [ 'a', 'b', 'a' ], [ 'a', 'b', 'b' ], [ 'a', 'b', 'c' ] +// [ 'a', 'c', 'a' ], [ 'a', 'c', 'b' ], [ 'a', 'c', 'c' ] +// [ 'b', 'a', 'a' ], [ 'b', 'a', 'b' ], [ 'b', 'a', 'c' ] +// [ 'b', 'b', 'a' ], [ 'b', 'b', 'b' ], [ 'b', 'b', 'c' ] +// [ 'b', 'c', 'a' ], [ 'b', 'c', 'b' ], [ 'b', 'c', 'c' ] +// [ 'c', 'a', 'a' ], [ 'c', 'a', 'b' ], [ 'c', 'a', 'c' ] +// [ 'c', 'b', 'a' ], [ 'c', 'b', 'b' ], [ 'c', 'b', 'c' ] +// [ 'c', 'c', 'a' ], [ 'c', 'c', 'b' ], [ 'c', 'c', 'c' ] +``` + +## Clone Submodule +Each array yielded from the generator is actually the same array in memory, just mutated to have different elements. This is to avoid the unnecessary creation of a bunch of arrays, which consume memory. As a result, you get a strange result when trying to generate an array. +```javascript +var combs = G.combination(['a', 'b', 'c'], 2); +console.log([...combs]); +// [ [ 'b', 'c' ], [ 'b', 'c' ], [ 'b', 'c' ] ] +``` +Instead, you can use the clone submodule. +```javascript +var combs = G.clone.combination(['a', 'b', 'c'], 2); +console.log([...combs]); +// [ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ] +``` + +### G.clone +This submodule produces generators that yield a different array on each iteration in case you need to mutate it. The [combination](#module_G.combination), [permutation](#module_G.permutation), [powerSet](#module_G.powerSet), [permutationCombination](#module_G.permutationCombination), [baseN](#module_G.baseN), [baseNAll](#module_G.baseNAll), and [cartesian](#module_G.cartesian) methods are provided on this submodule. + +## Cool things to do with ES2015 generators +```javascript +var combs = G.clone.combination([1, 2, 3], 2); + +// "for-of" loop +for (let comb of combs) { + console.log(comb); +} + +// generate arrays +Array.from(combs); +[...combs]; + +// generate sets +new Set(combs); + +// spreading in function calls +console.log(...combs); +``` + +#### Writing a code generator? Need to produce an infinite stream of minified variable names? + +No problem! Just pass in a collection of all your valid characters and start generating. + +```javascript +var mininym = G.baseNAll('abcdefghijklmnopqrstuvwxyz$#') +var name = mininym.next().value.join('') +global[name] = 'some value' +``` + +#### Card games anyone? +```javascript +var cards = [...G.clone.cartesian('♠♥♣♦', 'A23456789JQK')]; +console.log(G.shuffle(cards)); +// [ [ '♦', '6' ], [ '♠', '6' ], [ '♣', '7' ], [ '♥', 'K' ], +// [ '♣', 'J' ], [ '♥', '4' ], [ '♦', '2' ], [ '♥', '9' ], +// [ '♦', 'Q' ], [ '♠', 'Q' ], [ '♠', '4' ], [ '♠', 'K' ], +// [ '♥', '3' ], [ '♥', '7' ], [ '♠', '5' ], [ '♦', '7' ], +// [ '♥', '5' ], [ '♣', 'Q' ], [ '♣', '9' ], [ '♠', 'A' ], +// [ '♣', '4' ], [ '♣', '3' ], [ '♥', 'A' ], [ '♥', '8' ], +// [ '♣', '8' ], [ '♦', '8' ], [ '♠', '8' ], [ '♣', '5' ], +// [ '♥', '2' ], [ '♥', 'Q' ], [ '♦', 'A' ], [ '♥', '6' ], +// [ '♠', '2' ], [ '♣', '6' ], [ '♠', '3' ], [ '♦', 'K' ], +// [ '♦', 'J' ], [ '♠', '7' ], [ '♥', 'J' ], [ '♦', '5' ], +// [ '♦', '9' ], [ '♦', '3' ], [ '♠', '9' ], [ '♣', '2' ], +// [ '♣', 'A' ], [ '♣', 'K' ], [ '♦', '4' ], [ '♠', 'J' ] ] +``` + +## Documentation + + +## G + +* [G](#module_G) + * [.factorial(n)](#module_G.factorial) ⇒ Number + * [.factoradic(n)](#module_G.factoradic) ⇒ Array + * [.P(n, k)](#module_G.P) ⇒ Number + * [.C(n, k)](#module_G.C) ⇒ Number + * [.choices(n, k, [options])](#module_G.choices) ⇒ Number + * [.combination(arr, [size])](#module_G.combination) ⇒ Generator + * [.permutation(arr, [size])](#module_G.permutation) ⇒ Generator + * [.powerSet(arr)](#module_G.powerSet) ⇒ Generator + * [.permutationCombination(arr)](#module_G.permutationCombination) ⇒ Generator + * [.baseN(arr, [size])](#module_G.baseN) ⇒ Generator + * [.baseNAll(arr)](#module_G.baseNAll) ⇒ Generator + * [.cartesian(...sets)](#module_G.cartesian) ⇒ Generator + * [.shuffle(arr)](#module_G.shuffle) ⇒ Array + + + +### G.factorial(n) ⇒ Number +Calculates a factorial + +**Kind**: static method of [G](#module_G) +**Returns**: Number - n! + +| Param | Type | Description | +| --- | --- | --- | +| n | Number | The number to operate the factorial on. | + + + +### G.factoradic(n) ⇒ Array +Converts a number to the factorial number system. Digits are in least significant order. + +**Kind**: static method of [G](#module_G) +**Returns**: Array - digits of n in factoradic in least significant order + +| Param | Type | Description | +| --- | --- | --- | +| n | Number | Integer in base 10 | + + + +### G.P(n, k) ⇒ Number +Calculates the number of possible permutations of "k" elements in a set of size "n". + +**Kind**: static method of [G](#module_G) +**Returns**: Number - n P k + +| Param | Type | Description | +| --- | --- | --- | +| n | Number | Number of elements in the set. | +| k | Number | Number of elements to choose from the set. | + + + +### G.C(n, k) ⇒ Number +Calculates the number of possible combinations of "k" elements in a set of size "n". + +**Kind**: static method of [G](#module_G) +**Returns**: Number - n C k + +| Param | Type | Description | +| --- | --- | --- | +| n | Number | Number of elements in the set. | +| k | Number | Number of elements to choose from the set. | + + + +### G.choices(n, k, [options]) ⇒ Number +Higher level method for counting number of possible combinations of "k" elements from a set of size "n". + +**Kind**: static method of [G](#module_G) +**Returns**: Number - Number of possible combinations. + +| Param | Type | Description | +| --- | --- | --- | +| n | Number | Number of elements in the set. | +| k | Number | Number of elements to choose from the set. | +| [options] | Object | | +| options.replace | Boolean | Is replacement allowed after each choice? | +| options.ordered | Boolean | Does the order of the choices matter? | + + + +### G.combination(arr, [size]) ⇒ Generator +Generates all combinations of a set. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields each combination as an array + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| arr | Array | String | | The set of elements. | +| [size] | Number | arr.length | Number of elements to choose from the set. | + + + +### G.permutation(arr, [size]) ⇒ Generator +Generates all permutations of a set. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields each permutation as an array + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| arr | Array | String | | The set of elements. | +| [size] | Number | arr.length | Number of elements to choose from the set. | + + + +### G.powerSet(arr) ⇒ Generator +Generates all possible subsets of a set (a.k.a. power set). + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields each subset as an array + +| Param | Type | Description | +| --- | --- | --- | +| arr | Array | String | The set of elements. | + + + +### G.permutationCombination(arr) ⇒ Generator +Generates the permutation of the combinations of a set. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields each permutation as an array + +| Param | Type | Description | +| --- | --- | --- | +| arr | Array | String | The set of elements. | + + + +### G.baseN(arr, [size]) ⇒ Generator +Generates all possible "numbers" from the digits of a set. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields all digits as an array + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| arr | Array | String | | The set of digits. | +| [size] | Number | arr.length | How many digits will be in the numbers. | + + + +### G.baseNAll(arr) ⇒ Generator +Infinite generator for all possible "numbers" from a set of digits. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields all digits as an array + +| Param | Type | Description | +| --- | --- | --- | +| arr | Array | String | The set of digits | + + + +### G.cartesian(...sets) ⇒ Generator +Generates the cartesian product of the sets. + +**Kind**: static method of [G](#module_G) +**Returns**: Generator - yields each product as an array + +| Param | Type | Description | +| --- | --- | --- | +| ...sets | Array | String | variable number of sets of n elements. | + + + +### G.shuffle(arr) ⇒ Array +Shuffles an array in place using the Fisher–Yates shuffle. + +**Kind**: static method of [G](#module_G) +**Returns**: Array - a random, unbiased perutation of arr + +| Param | Type | Description | +| --- | --- | --- | +| arr | Array | A set of elements. | + diff --git a/js/node_modules/generatorics/bower.json b/js/node_modules/generatorics/bower.json new file mode 100644 index 0000000..9a25ae9 --- /dev/null +++ b/js/node_modules/generatorics/bower.json @@ -0,0 +1,32 @@ +{ + "name": "generatorics", + "version": "1.0.5", + "description": "Combinatorics library for JavaScript using ES2015 generator functions. Generate power set, combination, and permutation.", + "main": "generatorics.js", + "authors": [ + "Andrew Carlson " + ], + "license": "MIT", + "keywords": [ + "combinatorics", + "combination", + "permutation", + "powerset", + "factoradic", + "shuffle" + ], + "homepage": "https://github.com/acarl005/generatorics", + "moduleType": [ + "amd", + "es6", + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/js/node_modules/generatorics/generatorics.js b/js/node_modules/generatorics/generatorics.js new file mode 100644 index 0000000..94b8aea --- /dev/null +++ b/js/node_modules/generatorics/generatorics.js @@ -0,0 +1,328 @@ +/* + * Licensed under the MIT license. + * http://www.opensource.org/licenses/mit-license.php + * + * References: + * http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination + * http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation + */ + +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory) + } else if (typeof exports === 'object') { + module.exports = factory() + } else { + root.G = factory() + } +}(this, function() { + +'use strict' + +/** @exports G */ +const G = { + + clones: false, + + /** + * Calculates a factorial + * @param {Number} n - The number to operate the factorial on. + * @returns {Number} n! + */ + factorial: function factorial(n) { + for (var ans = 1; n; ans *= n--); + return ans + }, + + /** + * Converts a number to the factorial number system. Digits are in least significant order. + * @param {Number} n - Integer in base 10 + * @returns {Array} digits of n in factoradic in least significant order + */ + factoradic: function factoradic(n) { + let radix = 1 + for (var digit = 1; radix < n; radix *= ++digit); + if (radix > n) radix /= digit-- + let result = [0] + for (; digit; radix /= digit--) { + result[digit] = Math.floor(n / radix) + n %= radix + } + return result + }, + + /** + * Calculates the number of possible permutations of "k" elements in a set of size "n". + * @param {Number} n - Number of elements in the set. + * @param {Number} k - Number of elements to choose from the set. + * @returns {Number} n P k + */ + P: function P(n, k) { + return this.factorial(n) / this.factorial(n - k) + }, + + /** + * Calculates the number of possible combinations of "k" elements in a set of size "n". + * @param {Number} n - Number of elements in the set. + * @param {Number} k - Number of elements to choose from the set. + * @returns {Number} n C k + */ + C: function C(n, k) { + return this.P(n, k) / this.factorial(k) + }, + + /** + * Higher level method for counting number of possible combinations of "k" elements from a set of size "n". + * @param {Number} n - Number of elements in the set. + * @param {Number} k - Number of elements to choose from the set. + * @param {Object} [options] + * @param {Boolean} options.replace - Is replacement allowed after each choice? + * @param {Boolean} options.ordered - Does the order of the choices matter? + * @returns {Number} Number of possible combinations. + */ + choices: function choices(n, k, options = {}) { + if (options.replace) { + if (options.ordered) { + return Math.pow(n, k) + } else { + return this.C(n + k - 1, k) + } + } else { + if (options.ordered) { + return this.P(n, k) + } else { + return this.C(n, k) + } + } + }, + + /** + * Generates all combinations of a set. + * @param {Array|String} arr - The set of elements. + * @param {Number} [size=arr.length] - Number of elements to choose from the set. + * @returns {Generator} yields each combination as an array + */ + combination: function* combination(arr, size = arr.length) { + let that = this + let end = arr.length - 1 + let data = [] + yield* combinationUtil(0, 0) + function* combinationUtil(start, index) { + if (index === size) { // Current combination is ready to be processed, yield it + return yield that.clones ? data.slice() : data // .slice() is a JS idiom for shallow cloning an array + } + // replace index with all possible elements. The condition + // "end - i + 1 >= size - index" makes sure that including one element + // at index will make a combination with remaining elements + // at remaining positions + for (let i = start; i <= end && end - i + 1 >= size - index; i++) { + data[index] = arr[i] + yield* combinationUtil(i + 1, index + 1) + } + } + }, + + /** + * Generates all permutations of a set. + * @param {Array|String} arr - The set of elements. + * @param {Number} [size=arr.length] - Number of elements to choose from the set. + * @returns {Generator} yields each permutation as an array + */ + permutation: function* permutation(arr, size = arr.length) { + let that = this + let len = arr.length + if (size === len) { // switch to Heap's algorithm. it's more efficient + return yield* heapsAlg(arr, that.clones) + } + let data = [] + let indecesUsed = [] // permutations do not repeat elements. keep track of the indeces of the elements already used + yield* permutationUtil(0) + function* permutationUtil(index) { + if (index === size) { + return yield that.clones ? data.slice() : data + } + for (let i = 0; i < len; i++) { + if (!indecesUsed[i]) { + indecesUsed[i] = true + data[index] = arr[i] + yield *permutationUtil(index + 1) + indecesUsed[i] = false + } + } + } + }, + + /** + * Generates all possible subsets of a set (a.k.a. power set). + * @param {Array|String} arr - The set of elements. + * @returns {Generator} yields each subset as an array + */ + powerSet: function* powerSet(arr) { + let that = this + let len = arr.length + let data = [] + yield* powerUtil(0, 0) + function* powerUtil(start, index) { + data.length = index + yield that.clones ? data.slice() : data + if (index === len) { + return + } + for (let i = start; i < len; i++) { + data[index] = arr[i] + yield* powerUtil(i + 1, index + 1) + } + } + }, + + /** + * Generates the permutation of the combinations of a set. + * @param {Array|String} arr - The set of elements. + * @returns {Generator} yields each permutation as an array + */ + permutationCombination: function* permutationCombination(arr) { + let that = this + let len = arr.length + let data = [] + let indecesUsed = [] + yield* permutationUtil(0) + function* permutationUtil(index) { + data.length = index + yield that.clones ? data.slice() : data + if (index === len) { + return + } + for (let i = 0; i < len; i++) { + if (!indecesUsed[i]) { + indecesUsed[i] = true + data[index] = arr[i] + yield *permutationUtil(index + 1) + indecesUsed[i] = false + } + } + } + }, + + /** + * Generates all possible "numbers" from the digits of a set. + * @param {Array|String} arr - The set of digits. + * @param {Number} [size=arr.length] - How many digits will be in the numbers. + * @returns {Generator} yields all digits as an array + */ + baseN: function* baseN(arr, size = arr.length) { + let that = this + let len = arr.length + let data = [] + yield* baseNUtil(0) + function* baseNUtil(index) { + if (index === size) { + return yield that.clones ? data.slice() : data + } + for (let i = 0; i < len; i++) { + data[index] = arr[i] + yield* baseNUtil(index + 1) + } + } + }, + + /** + * Infinite generator for all possible "numbers" from a set of digits. + * @param {Array|String} arr - The set of digits + * @returns {Generator} yields all digits as an array + */ + baseNAll: function* permutationAll(arr) { + for (let len = 1; true; len++) { + yield* this.baseN(arr, len) + } + }, + + /** + * Generates the cartesian product of the sets. + * @param {...(Array|String)} sets - variable number of sets of n elements. + * @returns {Generator} yields each product as an array + */ + cartesian: function* cartesian(...sets) { + let that = this + let data = [] + yield* cartesianUtil(0) + function* cartesianUtil(index) { + if (index === sets.length) { + return yield that.clones ? data.slice() : data + } + for (let i = 0; i < sets[index].length; i++) { + data[index] = sets[index][i] + yield* cartesianUtil(index + 1) + } + } + }, + + /** + * Shuffles an array in place using the Fisher–Yates shuffle. + * @param {Array} arr - A set of elements. + * @returns {Array} a random, unbiased perutation of arr + */ + shuffle: function shuffle(arr) { + for (let i = arr.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)) + swap(arr, i, j) + } + return arr + } + +} + + +let clone = { clones: true } +clone.combination = G.combination +clone.permutation = G.permutation +clone.powerSet = G.powerSet +clone.permutationCombination = G.permutationCombination +clone.baseN = G.baseN +clone.baseNAll = G.baseNAll +clone.cartesian = G.cartesian + +G.clone = clone + + + +/* + * More efficient alorithm for permutations of All elements in an array. Doesn't + * work for "sub-permutations", e.g. permutations of 3 elements from [1, 2, 3, 4, 5] + */ +function* heapsAlg(arr, clone) { + let size = arr.length + if (typeof arr === 'string') { + arr = arr.split('') + } + yield* heapsUtil(0) + function* heapsUtil(index) { + if (index === size) { + return yield clone ? arr.slice() : arr + } + + for (let j = index; j < size; j++) { + swap(arr, index, j) + yield* heapsUtil(index + 1) + swap(arr, index, j) + } + } +} + +/* + * Swaps two array elements. + */ +function swap(arr, i, j) { + let len = arr.length + if (i >= len || j >= len) { + console.warn('Swapping an array\'s elements past its length.') + } + let temp = arr[j] + arr[j] = arr[i] + arr[i] = temp + return arr +} + + +return G + +})) diff --git a/js/node_modules/generatorics/package.json b/js/node_modules/generatorics/package.json new file mode 100644 index 0000000..a4ae899 --- /dev/null +++ b/js/node_modules/generatorics/package.json @@ -0,0 +1,39 @@ +{ + "name": "generatorics", + "version": "1.1.0", + "description": "Efficient Combinatorics library for JavaScript using ES2015 generator functions. Generate power set, combination, and permutation.", + "main": "generatorics.js", + "engines": { + "node": ">=6.0.0" + }, + "directories": { + "test": "test" + }, + "dependencies": {}, + "devDependencies": { + "chai": "^3.5.0", + "eslint": "^3.14.0", + "jsdoc-to-markdown": "^3.0.0", + "mocha": "^2.4.5" + }, + "scripts": { + "lint": "eslint generatorics.js", + "test": "mocha", + "docs": "sed -n '/Documentation/q;p' README.md > README.bak.md; echo '## Documentation' >> README.bak.md; jsdoc2md generatorics.js >> README.bak.md && mv -f README.bak.md README.md" + }, + "author": "Andrew Carlson ", + "repository": { + "type": "git", + "url": "git+https://github.com/acarl005/generatorics.git" + }, + "homepage": "https://github.com/acarl005/generatorics#readme", + "license": "MIT", + "keywords": [ + "combinatorics", + "combination", + "permutation", + "powerset", + "factoradic", + "shuffle" + ] +} diff --git a/js/node_modules/generatorics/test/index.js b/js/node_modules/generatorics/test/index.js new file mode 100644 index 0000000..e65e2be --- /dev/null +++ b/js/node_modules/generatorics/test/index.js @@ -0,0 +1,529 @@ +var expect = require('chai').expect +var G = require('../generatorics') + +describe('Arithmetic functions', () => { + it('factorial works', () => { + expect(G.factorial(0)).to.equal(1) + expect(G.factorial(1)).to.equal(1) + expect(G.factorial(2)).to.equal(2) + expect(G.factorial(3)).to.equal(6) + expect(G.factorial(4)).to.equal(24) + expect(G.factorial(5)).to.equal(120) + }) + + it('factoradic works', () => { + expect(G.factoradic(0)).to.eql([ 0 ]) + expect(G.factoradic(1)).to.eql([ 0, 1 ]) + expect(G.factoradic(2)).to.eql([ 0, 0, 1 ]) + expect(G.factoradic(3)).to.eql([ 0, 1, 1 ]) + expect(G.factoradic(100)).to.eql([ 0, 0, 2, 0, 4 ]) + expect(G.factoradic(1337)).to.eql([ 0, 1, 2, 2, 0, 5, 1 ]) + expect(G.factoradic(9001)).to.eql([ 0, 1, 0, 0, 0, 3, 5, 1 ]) + expect(G.factoradic(3958174309503149571029856012)).to.eql([ 0, 1, 0, 2, 2, 3, 5, 5, 2, 2, 3, 7, 6, 1, 12, 14, 11, 12, 18, 0, 10, 2, 21, 12, 4, 21, 9 ]) + }) + + it('P(n,r) works', () => { + expect(G.P(10, 3)).to.equal(720) + expect(G.P(10, 1)).to.equal(10) + expect(G.P(10, 0)).to.equal(1) + expect(G.P(10, 10)).to.equal(3628800) + }) + + it('C(n,r) works', () => { + expect(G.C(10, 3)).to.equal(120) + expect(G.C(10, 1)).to.equal(10) + expect(G.C(10, 0)).to.equal(1) + expect(G.C(10, 10)).to.equal(1) + }) + + it('higher level method for counting choices', () => { + expect(G.choices(10, 3, { replace: true, ordered: true })).to.equal(1000) + expect(G.choices(10, 3, { replace: true, ordered: false })).to.equal(220) + expect(G.choices(10, 3, { replace: false, ordered: true })).to.equal(720) + expect(G.choices(10, 3, { replace: false, ordered: false })).to.equal(120) + }) +}) + +describe('Combinations', () => { + it('should get combinations of two from set of 3', () => { + var members = [ + [ 1, 2 ], + [ 1, 3 ], + [ 2, 3 ] + ] + var answers = [] + for (var comb of G.combination([1, 2, 3], 2)) { + answers.push(comb.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 1', () => { + var members = [ + [ 1 ], + [ 2 ], + [ 3 ] + ] + var answers = [] + for (var comb of G.combination([1, 2, 3], 1)) { + answers.push(comb.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 0', () => { + for (var comb of G.combination([1, 2, 3], 0)) { + expect(comb).to.eql([]) + } + }) + + it('should yield nothing if size is greater than array length', () => { + for (var comb of G.combination([1, 2, 3], 4)) { + throw new Error('Made a combination when it should not have') + } + }) + + it('combinations should default to arr.length without size specified', () => { + for (var comb of G.combination([1, 2, 3])) { + expect(comb).to.eql([1, 2, 3]) + } + }) + + it('should work with strings', () => { + var members = [ + [ 'a', 'b' ], + [ 'a', 'c' ], + [ 'a', 'd' ], + [ 'b', 'c' ], + [ 'b', 'd' ], + [ 'c', 'd' ], + ] + var answers = [] + for (var comb of G.combination('abcd', 2)) { + answers.push(comb.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('yields the same array each time', () => { + var arr = [...G.combination([1, 2, 3], 2)] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + +describe('Permutations', () => { + + it('should get permutations of 2 from a set of 3', () => { + var members = [ + [ 1, 2 ], + [ 1, 3 ], + [ 2, 1 ], + [ 2, 3 ], + [ 3, 1 ], + [ 3, 2 ] + ] + var answers = [] + for (var perm of G.permutation([1, 2, 3], 2)) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 1', () => { + var members = [ + [ 1 ], + [ 2 ], + [ 3 ] + ] + var answers = [] + for (var perm of G.permutation([1, 2, 3], 1)) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 0', () => { + for (var perm of G.permutation([1, 2, 3], 0)) { + expect(perm).to.eql([]) + } + }) + + it('should yield nothing if size is greater than array length', () => { + for (var perm of G.permutation([1, 2, 3], 4)) { + throw new Error('Made a permutation when it should not have') + } + }) + + it('permutations should default to arr.length without size specified', () => { + var members = [ + [ 1, 2, 3 ], + [ 1, 3, 2 ], + [ 2, 1, 3 ], + [ 2, 3, 1 ], + [ 3, 2, 1 ], + [ 3, 1, 2 ], + ] + var answers = [] + for (var perm of G.permutation([1, 2, 3])) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with strings', () => { + var members = [ + [ 'a', 'b' ], + [ 'a', 'c' ], + [ 'a', 'd' ], + [ 'b', 'a' ], + [ 'b', 'c' ], + [ 'b', 'd' ], + [ 'c', 'a' ], + [ 'c', 'b' ], + [ 'c', 'd' ], + [ 'd', 'a' ], + [ 'd', 'b' ], + [ 'd', 'c' ], + ] + var answers = [] + for (var perm of G.permutation('abcd', 2)) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with string without specifying length', () => { + var members = [ + [ 'a', 'b', 'c' ], + [ 'a', 'c', 'b' ], + [ 'b', 'a', 'c' ], + [ 'b', 'c', 'a' ], + [ 'c', 'b', 'a' ], + [ 'c', 'a', 'b' ] + ] + var answers = [] + for (var perm of G.permutation('abc')) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('yields the same array each time', () => { + var arr = [...G.permutation([1, 2, 3], 2)] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + +describe('Base N', () => { + + it('should get number of 2 digits a set of 3', () => { + var members = [ + [ 1, 1 ], + [ 1, 2 ], + [ 1, 3 ], + [ 2, 1 ], + [ 2, 2 ], + [ 2, 3 ], + [ 3, 1 ], + [ 3, 2 ], + [ 3, 3 ], + ] + var answers = [] + for (var perm of G.baseN([1, 2, 3], 2)) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 1', () => { + var members = [ + [ 1 ], + [ 2 ], + [ 3 ] + ] + var answers = [] + for (var perm of G.baseN([1, 2, 3], 1)) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with size 0', () => { + for (var perm of G.baseN([1, 2, 3], 0)) { + expect(perm).to.eql([]) + } + }) + + it('baseN should default to arr.length without size specified', () => { + var members = [ + [ 1, 1, 1 ], + [ 1, 1, 2 ], + [ 1, 1, 3 ], + [ 1, 2, 1 ], + [ 1, 2, 2 ], + [ 1, 2, 3 ], + [ 1, 3, 1 ], + [ 1, 3, 2 ], + [ 1, 3, 3 ], + [ 2, 1, 1 ], + [ 2, 1, 2 ], + [ 2, 1, 3 ], + [ 2, 2, 1 ], + [ 2, 2, 2 ], + [ 2, 2, 3 ], + [ 2, 3, 1 ], + [ 2, 3, 2 ], + [ 2, 3, 3 ], + [ 3, 1, 1 ], + [ 3, 1, 2 ], + [ 3, 1, 3 ], + [ 3, 2, 1 ], + [ 3, 2, 2 ], + [ 3, 2, 3 ], + [ 3, 3, 1 ], + [ 3, 3, 2 ], + [ 3, 3, 3 ], + ] + var answers = [] + for (var perm of G.baseN([1, 2, 3])) { + answers.push(perm.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('yields the same array each time', () => { + var arr = [...G.baseN([1, 2, 3])] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + + +describe('Power Set', () => { + + it('should calculate power set', () => { + var members = [ + [ ], + [ 1 ], + [ 2 ], + [ 1, 2 ], + [ 3 ], + [ 1, 3 ], + [ 2, 3 ], + [ 1, 2, 3 ] + ] + var answers = [] + for (var sett of G.powerSet([1, 2, 3])) { + answers.push(sett.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('yields the same array each time', () => { + var arr = [...G.powerSet([1, 2, 3])] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + +describe('Permutation Combination', () => { + + it('should get the permutation of combinations', () => { + var members = [ + [ ], + [ 'a' ], + [ 'b' ], + [ 'c' ], + [ 'a', 'b' ], + [ 'b', 'a' ], + [ 'a', 'c' ], + [ 'c', 'a' ], + [ 'b', 'c' ], + [ 'c', 'b' ], + [ 'a', 'b', 'c' ], + [ 'a', 'c', 'b' ], + [ 'b', 'a', 'c' ], + [ 'b', 'c', 'a' ], + [ 'c', 'a', 'b' ], + [ 'c', 'b', 'a' ] + ] + var answers = [] + for (var comb of G.permutationCombination(['a', 'b', 'c'])) { + answers.push(comb.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should yield the same array each time', () => { + var arr = [...G.permutationCombination([1, 2, 3])] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + +describe('Cartesian Product', () => { + + it('should find the cartesian product of a bunch of arrays', () => { + var members = [ + [0, 0, 0], [1, 0, 0], [2, 0, 0], + [0, 10, 0], [1, 10, 0], [2, 10, 0], + [0, 20, 0], [1, 20, 0], [2, 20, 0], + [0, 0, 100], [1, 0, 100], [2, 0, 100], + [0, 10, 100],[1, 10, 100],[2, 10, 100], + [0, 20, 100],[1, 20, 100],[2, 20, 100], + [0, 0, 200], [1, 0, 200], [2, 0, 200], + [0, 10, 200],[1, 10, 200],[2, 10, 200], + [0, 20, 200],[1, 20, 200],[2, 20, 200] + ] + var answers = [] + for (var sett of G.cartesian([0, 1, 2], [0, 10, 20], [0, 100, 200])) { + answers.push(sett.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with apply', () => { + var members = [ + [0, 0, 0], [1, 0, 0], [2, 0, 0], + [0, 10, 0], [1, 10, 0], [2, 10, 0], + [0, 20, 0], [1, 20, 0], [2, 20, 0], + [0, 0, 100], [1, 0, 100], [2, 0, 100], + [0, 10, 100],[1, 10, 100],[2, 10, 100], + [0, 20, 100],[1, 20, 100],[2, 20, 100], + [0, 0, 200], [1, 0, 200], [2, 0, 200], + [0, 10, 200],[1, 10, 200],[2, 10, 200], + [0, 20, 200],[1, 20, 200],[2, 20, 200] + ] + var answers = [] + for (var sett of G.cartesian.apply(G, [[0, 1, 2], [0, 10, 20], [0, 100, 200]])) { + answers.push(sett.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should work with strings', () => { + var members = [ + [ 'a', 'c', 'e' ], + [ 'a', 'c', 'f' ], + [ 'a', 'd', 'e' ], + [ 'a', 'd', 'f' ], + [ 'b', 'c', 'e' ], + [ 'b', 'c', 'f' ], + [ 'b', 'd', 'e' ], + [ 'b', 'd', 'f' ] + ] + var answers = [] + for (var sett of G.cartesian('ab', 'cd', 'ef')) { + answers.push(sett.slice()) + } + expect(answers).to.have.length(members.length) + expect(answers).to.deep.have.members(members) + }) + + it('should yield same array each time', () => { + var arr = [...G.cartesian('12', '34', '56')] + expect(arr).to.eql(Array(arr.length).fill(arr[0])) + }) + +}) + + +describe('Infinite Base N', () => { + + it('generates all permutations of an array', () => { + var members = [ + [ 'a' ], [ 'b' ], [ 'c' ], [ 'a', 'a' ], [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'a' ], [ 'b', 'b' ], [ 'b', 'c' ], [ 'c', 'a' ], [ 'c', 'b' ], [ 'c', 'c' ], + [ 'a', 'a', 'a' ], [ 'a', 'a', 'b' ], [ 'a', 'a', 'c' ], [ 'a', 'b', 'a' ], [ 'a', 'b', 'b' ], [ 'a', 'b', 'c' ], [ 'a', 'c', 'a' ], [ 'a', 'c', 'b' ], + [ 'a', 'c', 'c' ], [ 'b', 'a', 'a' ], [ 'b', 'a', 'b' ], [ 'b', 'a', 'c' ], [ 'b', 'b', 'a' ], [ 'b', 'b', 'b' ], [ 'b', 'b', 'c' ], [ 'b', 'c', 'a' ], + [ 'b', 'c', 'b' ], [ 'b', 'c', 'c' ], [ 'c', 'a', 'a' ], [ 'c', 'a', 'b' ], [ 'c', 'a', 'c' ], [ 'c', 'b', 'a' ], [ 'c', 'b', 'b' ], [ 'c', 'b', 'c' ], + [ 'c', 'c', 'a' ], [ 'c', 'c', 'b' ], [ 'c', 'c', 'c' ], [ 'a', 'a', 'a', 'a' ], [ 'a', 'a', 'a', 'b' ], [ 'a', 'a', 'a', 'c' ], [ 'a', 'a', 'b', 'a' ], + [ 'a', 'a', 'b', 'b' ], [ 'a', 'a', 'b', 'c' ], [ 'a', 'a', 'c', 'a' ], [ 'a', 'a', 'c', 'b' ], [ 'a', 'a', 'c', 'c' ], [ 'a', 'b', 'a', 'a' ], [ 'a', 'b', 'a', 'b' ], + [ 'a', 'b', 'a', 'c' ], [ 'a', 'b', 'b', 'a' ], [ 'a', 'b', 'b', 'b' ], [ 'a', 'b', 'b', 'c' ], [ 'a', 'b', 'c', 'a' ], [ 'a', 'b', 'c', 'b' ], [ 'a', 'b', 'c', 'c' ], + [ 'a', 'c', 'a', 'a' ], [ 'a', 'c', 'a', 'b' ], [ 'a', 'c', 'a', 'c' ], [ 'a', 'c', 'b', 'a' ], [ 'a', 'c', 'b', 'b' ], [ 'a', 'c', 'b', 'c' ], [ 'a', 'c', 'c', 'a' ], + ] + var nums = [ 'a', 'b', 'c' ] + var perms = [] + var gen = G.baseNAll(nums) + for (var i = 0; i < 64; i++) { + perms.push(gen.next().value.slice()) + } + expect(perms).to.eql(members) + }) + +}) + + +describe('Clone submodule', () => { + + it('clones combinations', () => { + var answers = [] + for (var comb of G.combination([1, 2, 3], 2)) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.combination([1, 2, 3], 2)]) + }) + + it('clones permutations', () => { + var answers = [] + for (var comb of G.permutation([1, 2, 3], 2)) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.permutation([1, 2, 3], 2)]) + + answers = [] + for (var comb of G.permutation([1, 2, 3])) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.permutation([1, 2, 3])]) + }) + + it('clones power sets', () => { + var answers = [] + for (var comb of G.powerSet([1, 2, 3])) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.powerSet([1, 2, 3])]) + }) + + it('clones permutations of combinations', () => { + var answers = [] + for (var comb of G.permutationCombination([1, 2, 3])) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.permutationCombination([1, 2, 3])]) + }) + + it('clones base N', () => { + var answers = [] + for (var comb of G.baseN([1, 2, 3])) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.baseN([1, 2, 3])]) + }) + + it('clones cartesian products', () => { + var answers = [] + for (var comb of G.cartesian([1, 2, 3], [5, 6], [1, 8])) { + answers.push(comb.slice()) + } + expect(answers).to.eql([...G.clone.cartesian([1, 2, 3], [5, 6], [1, 8])]) + }) + +}) diff --git a/js/package-lock.json b/js/package-lock.json new file mode 100644 index 0000000..ca02eab --- /dev/null +++ b/js/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "js", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "generatorics": "^1.1.0" + } + }, + "node_modules/generatorics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", + "integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=", + "engines": { + "node": ">=6.0.0" + } + } + }, + "dependencies": { + "generatorics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", + "integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=" + } + } +} diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..2e5b365 --- /dev/null +++ b/js/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "generatorics": "^1.1.0" + } +}