├── commonjs ├── package.json └── combinatorics.js ├── .npmignore ├── test ├── 00-node.js ├── 01-import.js ├── 03-permcomb.js ├── 03-basen.js ├── 03-powerset.js ├── 03-permutation.js ├── 03-cartesianproduct.js ├── index.html ├── 03-combination.js └── 02-arithmetics.js ├── Makefile ├── LICENSE ├── .github └── workflows │ └── node.js.yml ├── package.json ├── .gitignore ├── combinatorics.d.ts ├── combinatorics.js ├── combinatorics.ts ├── umd └── combinatorics.js └── README.md /commonjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintrc 3 | .travis.yml 4 | *,v 5 | *.bak 6 | -------------------------------------------------------------------------------- /test/00-node.js: -------------------------------------------------------------------------------- 1 | import * as _chai from 'chai'; 2 | global.chai = _chai; 3 | // global.chai = require('chai'); 4 | global.isLegacySafari = false; 5 | -------------------------------------------------------------------------------- /test/01-import.js: -------------------------------------------------------------------------------- 1 | import * as Combinatorics from '../combinatorics.js'; 2 | describe('import', () => { 3 | for (const k in Combinatorics) { 4 | const tn = k === 'version' ? 'string' : 'function'; 5 | it(`${k} is a ${tn}`, ()=>chai.expect(typeof Combinatorics[k]).to.equal(tn)); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PJ=package.json 2 | TS=combinatorics.ts 3 | JS=combinatorics.js 4 | MJS=combinatorics.mjs 5 | DTS=combinatorics.d.ts 6 | COMMONJS_DIR=commonjs 7 | COMMONJS=$(COMMONJS_DIR)/$(JS) 8 | UMD_DIR=umd 9 | UMD=$(UMD_DIR)/$(JS) 10 | 11 | all: $(PJ) $(JS) $(COMMONJS) $(UMD) 12 | 13 | $(JS): $(PJ) $(TS) 14 | tsc -d --module nodenext $(TS) 15 | 16 | $(COMMONJS): $(PJ) $(TS) 17 | tsc --module commonjs --outDir $(COMMONJS_DIR) --target es2020 $(TS) 18 | 19 | $(UMD): $(PJ) $(TS) 20 | -tsc --module umd --outDir $(UMD_DIR) --target es2020 $(TS) > /dev/null 21 | 22 | test: all 23 | mocha 24 | 25 | clean: 26 | -rm $(DTS) $(MJS) $(JS) $(COMMONJS) $(UMD) 27 | 28 | -------------------------------------------------------------------------------- /test/03-permcomb.js: -------------------------------------------------------------------------------- 1 | import { Permutation, Combination } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | class PermComb { 6 | constructor(seed) { 7 | this.seed = [...seed]; 8 | } 9 | [Symbol.iterator]() { 10 | return function*(it){ 11 | for (let i = 1, l = it.length; i <= l; i++) { 12 | yield* new Permutation(it, i); 13 | } 14 | }(this.seed); 15 | } 16 | } 17 | 18 | describe('Permutation of Combination (not in the module)', () => { 19 | let seed = []; 20 | for (let len = 0; len <= 4; seed.push(len++)) { 21 | let c = new PermComb(seed); 22 | let s = new Set(c); 23 | it(`new PermComb([${seed}])`, ()=>$$([...c]).to.deep.equal([...s])); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Dan Kogai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # cf. 2 | # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs#starting-with-the-nodejs-workflow-template 3 | # https://dev.classmethod.jp/articles/caching-dependencies-in-workflow-execution-on-github-actions/ 4 | name: CI via GitHub Actions 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | node-version: [22, 20, 18] 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - uses: actions/cache@v3 25 | id: node_modules_cache_id 26 | env: 27 | cache-name: cache-node-modules 28 | with: 29 | path: '**/node_modules' 30 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 31 | - run: echo '${{ toJSON(steps.node_modules_cache_id.outputs) }}' 32 | - if: ${{ steps.node_modules_cache_id.outputs.cache-hit != 'true' }} 33 | run: npm install 34 | - run: npm test 35 | -------------------------------------------------------------------------------- /test/03-basen.js: -------------------------------------------------------------------------------- 1 | import { BaseN } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | describe('class BaseN', () => { 6 | let seed = '0123'; 7 | for (let i = 1; i <= 8; i++) { 8 | let c = new BaseN(seed, i); 9 | let s = new Set(c); 10 | it(`new BaseN('${seed}', ${i})`, () => $$([...c]).to.deep.equal([...s])); 11 | } 12 | seed = '0123456789abcdef'; 13 | let c = new BaseN(seed, 8); 14 | it(`.nth(-1) === .nth(.length-1)`, () => 15 | $$(c.nth(-1)).to.deep.equal(c.nth(c.length - 1n))); 16 | 17 | if (typeof BigInt === 'function') { 18 | const sbn = '18446744073709551616'; 19 | let c = new BaseN(seed, 16); 20 | it(`new BaseN('${seed}', 16).length === ${sbn}n`, () => { 21 | $$(c.length).to.equal(BigInt(sbn)); 22 | }); 23 | it(`.nth(${sbn}n-1n)`, () => { 24 | $$(c.nth(BigInt(sbn) - BigInt(1))).to.deep.equal([...'ffffffffffffffff']) 25 | }); 26 | it('.nth(-1n) === .nth(.length-1n)', () => 27 | $$(c.nth(-BigInt(1))).to.deep.equal(c.nth(c.length - BigInt(1)))); 28 | } else { 29 | it.skip(`SKIP new BaseN('${seed}',16): BigInt unsupported`, x => x); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-combinatorics", 3 | "version": "2.1.2", 4 | "description": "Simple combinatorics like power set, combination, and permutation in JavaScript", 5 | "main": "combinatorics.js", 6 | "module": "combinatorics.js", 7 | "type": "module", 8 | "exports": { 9 | "import": "./combinatorics.js", 10 | "require": "./commonjs/combinatorics.js" 11 | }, 12 | "types": "combinatorics.d.ts", 13 | "files": [ 14 | "combinatorics.d.ts", 15 | "combinatorics.js", 16 | "umd", 17 | "commonjs" 18 | ], 19 | "runkitExample": "require=require(\"esm\")(module);\nvar Combinatorics=require(\"js-combinatorics\");\n", 20 | "devDependencies": { 21 | "typescript": "^5.0.0", 22 | "@types/node": "^20.9.0", 23 | "mocha": "^10.0.0", 24 | "chai": "^4.2.0" 25 | }, 26 | "scripts": { 27 | "test": "make clean && make test" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/dankogai/js-combinatorics.git" 32 | }, 33 | "keywords": [ 34 | "Combinatorics", 35 | "combination", 36 | "permutation" 37 | ], 38 | "author": "Dan Kogai", 39 | "license": "MIT", 40 | "readmeFilename": "README.md", 41 | "gitHead": "e3684eb54f7c6fbb24bb7a4d08d88671ce12e9eb" 42 | } 43 | -------------------------------------------------------------------------------- /test/03-powerset.js: -------------------------------------------------------------------------------- 1 | import { PowerSet } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | describe('class PowerSet', () => { 6 | let seed = []; 7 | for (let len = 0; len <= 8; seed.push(len++)) { 8 | let c = new PowerSet(seed); 9 | let s = new Set(c); 10 | it(`new PowerSet([${seed}])`, () => $$([...c]).to.deep.equal([...s])); 11 | } 12 | let c = new PowerSet(seed); 13 | it(`.nth(-1) === .nth(.length-1)`, () => 14 | $$(c.nth(-1)).to.deep.equal(c.nth(c.length - 1n))); 15 | seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 16 | if (typeof BigInt === 'function') { 17 | const sbn = '18446744073709551616'; 18 | let c = new PowerSet(seed); 19 | it(`new PowerSet('${seed}').length === ${sbn}n`, () => { 20 | $$(c.length).to.equal(BigInt(sbn)); 21 | }); 22 | it(`.nth(${sbn}n-1n)`, () => { 23 | $$(c.nth(BigInt(sbn) - BigInt(1))).to.deep.equal([...seed]) 24 | }); 25 | it('.nth(-1n) === .nth(.length-1n)', () => 26 | $$(c.nth(-BigInt(1))).to.deep.equal(c.nth(c.length - BigInt(1)))); 27 | } else { 28 | it.skip(`SKIP new PowerSet('${seed}'): BigInt unsupported`, x => x); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /test/03-permutation.js: -------------------------------------------------------------------------------- 1 | import { Permutation } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | describe('class Permutation', () => { 6 | let seed = 'abcd'; 7 | for (let len = 0; len <= seed.length; len++) { 8 | let c = [... new Permutation(seed, len)]; 9 | let s = [... new Set(c.map(v => [...new Set(v)]))]; 10 | it(`new Permutation('${seed}', ${len})`, 11 | () => $$(c).to.deep.equal(s)); 12 | } 13 | let c = new Permutation(seed); 14 | it(`.nth(-1) === .nth(.length-1)`, () => 15 | $$(c.nth(-1)).to.deep.equal(c.nth(c.length - 1n))); 16 | seed = 'abcdefghijklmnopqrstuvwxyz0123456789'; 17 | if (typeof BigInt === 'function') { 18 | const sbn = '371993326789901217467999448150835200000000'; 19 | let c = new Permutation(seed); 20 | it(`new Permutation('${seed}').length === ${sbn}n`, () => { 21 | $$(c.length).to.equal(BigInt(sbn)); 22 | }); 23 | it(`.nth(${sbn}n-1n)`, () => { 24 | $$(c.nth(BigInt(sbn) - BigInt(1))).to.deep.equal([...seed].reverse()) 25 | }); 26 | it('.nth(-1n) === .nth(.length-1n)', () => 27 | $$(c.nth(-BigInt(1))).to.deep.equal(c.nth(c.length - BigInt(1)))); 28 | } else { 29 | it.skip(`SKIP new Permutation('${seed}'): BigInt unsupported`, x => x); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /test/03-cartesianproduct.js: -------------------------------------------------------------------------------- 1 | import { CartesianProduct } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | describe('class CartesianProduct', () => { 6 | let seed = []; 7 | for (let len = 0; len <= 4; seed.push('0123'), len++) { 8 | let c = CartesianProduct.from(seed); 9 | let s = new Set(c); 10 | let seedstr = JSON.stringify(seed) 11 | it(`new CartesianProduct(${seedstr})`, () => $$([...c]).to.deep.equal([...s])); 12 | } 13 | let c = CartesianProduct.from(seed); 14 | it(`.nth(-1) === .nth(.length-1)`, () => 15 | $$(c.nth(-1)).to.deep.equal(c.nth(c.length - 1n))); 16 | seed = Array(16).fill('0123456789abcdef'); 17 | if (typeof BigInt === 'function') { 18 | const sbn = '18446744073709551616'; 19 | let c = CartesianProduct.from(seed); 20 | it(`new CartesianProduct(...'0123456789abcdef').length === ${sbn}n`, () => { 21 | $$(c.length).to.equal(BigInt(sbn)); 22 | }); 23 | it(`.nth(${sbn}n-1n)`, () => { 24 | $$(c.nth(BigInt(sbn) - BigInt(1))).to.deep.equal([...'ffffffffffffffff']); 25 | }); 26 | it('.nth(-1n) === .nth(.length-1n)', () => 27 | $$(c.nth(-BigInt(1))).to.deep.equal(c.nth(c.length - BigInt(1)))); 28 | } else { 29 | it.skip(`SKIP new CartesianProduct('${seed}'): BigInt unsupported`, x => x); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mocha Tests of js-combinatorics 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/03-combination.js: -------------------------------------------------------------------------------- 1 | import { Combination } from '../combinatorics.js'; 2 | 3 | const $$ = chai.expect.bind(chai); 4 | 5 | describe('class Combination', () => { 6 | let seed = 'abcdefgh'; 7 | for (let len = 0; len <= seed.length; len++) { 8 | let c = [...new Combination(seed, len)]; 9 | let s = [...new Set(c.slice(0).map(v => v.sort()))]; 10 | // all unique, each in lex order 11 | it(`new Combination('${seed}', ${len})`, () => $$(c).to.deep.equal(s)); 12 | } 13 | let c = new Combination(seed, 4); 14 | it(`.nth(-1) === .nth(.length-1)`, () => 15 | $$(c.nth(-1)).to.deep.equal(c.nth(c.length - 1n))); 16 | seed = Array(100).fill(0).map((v, i) => i); 17 | if (typeof BigInt === 'function') { 18 | const sbn = '100891344545564193334812497256'; 19 | let c = new Combination(seed, 50); 20 | it(`new Combination([0,1...99],50).length === ${sbn}n`, () => { 21 | $$(c.length).to.equal(BigInt(sbn)); 22 | }); 23 | it(`.nth(${sbn}n-1n)`, () => { 24 | $$(c.nth(BigInt(sbn) - BigInt(1))).to.deep.equal( 25 | Array(50).fill(50).map((v, i) => v + i) 26 | ) 27 | }); 28 | it('.nth(-1n) === .nth(.length-1n)', () => 29 | $$(c.nth(-BigInt(1))).to.deep.equal(c.nth(c.length - BigInt(1)))); 30 | } else { 31 | it.skip(`SKIP new Combination([0,1...99]): BigInt unsupported`, x => x); 32 | } 33 | // https://github.com/dankogai/js-combinatorics/issues/82 34 | seed = [[1, 2], [3, 4], [5, 6]]; 35 | it(`new Combination(${JSON.stringify(seed)}, 2)`, () => $$([...new Combination(seed, 2)]).to.deep.equal([ 36 | [[1, 2], [3, 4]], 37 | [[1, 2], [5, 6]], 38 | [[3, 4], [5, 6]] 39 | ])); 40 | }); 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # ignore all . folders but include . files 107 | .*/ 108 | -------------------------------------------------------------------------------- /test/02-arithmetics.js: -------------------------------------------------------------------------------- 1 | import { 2 | permutation, combination, factorial,factoradic 3 | } from '../combinatorics.js'; 4 | 5 | const $$ = chai.expect.bind(chai); 6 | 7 | describe('permutation', () => { 8 | it('permutation(3,2) === 6', ()=>$$(permutation(3, 2)).to.equal(6n)); 9 | it('permutation(1,1) === 1', ()=>$$(permutation(1, 1)).to.equal(1n)); 10 | it('permutation(1,0) === 1', ()=>$$(permutation(1, 0)).to.equal(1n)); 11 | it('permutation(2,5) === 0', ()=>$$(permutation(2, 5)).to.equal(0n)); 12 | if (!isLegacySafari) { 13 | it('permutation(5,1.5) throws RangeError', () => { 14 | chai.assert.throws(() => permutation(5, 1.5), RangeError) 15 | }); 16 | it('permutation(5, -1) throws RangeError', () => { 17 | chai.assert.throws(() => permutation(5, -1), RangeError) 18 | }); 19 | } else { 20 | it.skip('SKIP permutation(5,1.5): assert.throws may not work', x=>x); 21 | it.skip('SKIP permutation(5, -1): assert.throws may not work', x=>x); 22 | 23 | } 24 | if (typeof BigInt === 'function') { 25 | const sbn = '10333147966386144929666651337523200000000'; 26 | it(`permutation(35,35) === ${sbn}n`, () => { 27 | $$(permutation(35,35)).to.equal(BigInt(sbn)); 28 | }); 29 | } else { 30 | it.skip('SKIP permutation(35,35): BigInt unsupported', x=>x); 31 | } 32 | }); 33 | 34 | describe('combination', () => { 35 | it('combination(3,2) === 3', ()=>$$(combination(3, 2)).to.equal(3n)); 36 | it('combination(1,1) === 1', ()=>$$(combination(1, 1)).to.equal(1n)); 37 | it('combination(1,0) === 1', ()=>$$(combination(1, 0)).to.equal(1n)); 38 | it('combination(2,5) === 0', ()=>$$(combination(2, 5)).to.equal(0n)); 39 | if (!isLegacySafari) { 40 | it('combination(5,1.5) throws RangeError', () => { 41 | chai.assert.throws(() => combination(5, 1.5), RangeError) 42 | }); 43 | it('combination(5, -1) throws RangeError', () => { 44 | chai.assert.throws(() => combination(5, -1), RangeError) 45 | }); 46 | } else { 47 | it.skip('SKIP combination(5,1.5): assert.throws may not work', x=>x); 48 | it.skip('SKIP combination(5, -1): assert.throws may not work', x=>x); 49 | } 50 | if (typeof BigInt === 'function') { 51 | const sbn = '23951146041928082866135587776380551750'; 52 | it(`combination(128,64) === ${sbn}n`, () => { 53 | $$(combination(128,64)).to.equal(BigInt(sbn)); 54 | }); 55 | } else { 56 | it.skip('SKIP combination(128,64): BigInt unsupported', x=>x); 57 | } 58 | }); 59 | 60 | describe('factoradic', () => { 61 | const count = function*(n) { 62 | let i = 0; while (i < n) yield i++; 63 | }; 64 | for (let i of count(32)) { 65 | let n = factorial(i); 66 | if (Number.MAX_SAFE_INTEGER <= --n) { 67 | if (typeof BigInt !== 'function') { 68 | it.skip(`SKIP factoradic(${i}! - 1): BigInt unsupported`, x=>x); 69 | break; 70 | } 71 | } 72 | const a = i == 0 ? [0] : [...count(i)]; 73 | it(`factoradic(${i}! - 1)`, () => 74 | $$(factoradic(n)).to.deep.equal(a) 75 | ); 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /combinatorics.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * combinatorics.js 3 | * 4 | * Licensed under the MIT license. 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Dan Kogai 8 | * 9 | * References: 10 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination 11 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation 12 | * @link: http://en.wikipedia.org/wiki/Factorial_number_system 13 | * @link: https://en.wikipedia.org/wiki/Combinatorial_number_system 14 | */ 15 | export declare const version = "2.1.2"; 16 | /** 17 | * BigInt Workaround 18 | * 19 | * https://github.com/streamich/memfs/issues/275 20 | */ 21 | type anyint = number | bigint; 22 | /** 23 | * Optional will not be official so 24 | * @link: https://github.com/microsoft/TypeScript/issues/19944 25 | */ 26 | type Optional = T | undefined; 27 | /** 28 | * calculates `P(n, k)`. 29 | * 30 | * @link https://en.wikipedia.org/wiki/Permutation 31 | */ 32 | export declare function permutation(n: anyint, k: anyint): bigint; 33 | /** 34 | * calculates `C(n, k)`. 35 | * 36 | * @link https://en.wikipedia.org/wiki/Combination 37 | */ 38 | export declare function combination(n: anyint, k: anyint): bigint; 39 | /** 40 | * calculates `n!` === `P(n, n)`. 41 | * 42 | * @link https://en.wikipedia.org/wiki/Factorial 43 | */ 44 | export declare function factorial(n: anyint): bigint; 45 | /** 46 | * returns the factoradic representation of `n`, least significant order. 47 | * 48 | * @link https://en.wikipedia.org/wiki/Factorial_number_system 49 | * @param {number} l the number of digits 50 | */ 51 | export declare function factoradic(n: anyint, l?: number): number[]; 52 | /** 53 | * `combinadic(n, k)` returns a function 54 | * that takes `m` as an argument and 55 | * returns the combinadics representation of `m` for `n C k`. 56 | * 57 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system 58 | */ 59 | export declare function combinadic(n: anyint, k: anyint): (m: anyint) => number[]; 60 | /** 61 | * returns random integer `n` where `min` <= `n` < `max`: 62 | * 63 | * if the argument is `BigInt` the result is also `BigInt`. 64 | * 65 | * @param {anyint} min 66 | * @param {anyint} max 67 | */ 68 | export declare function randomInteger(min?: anyint, max?: anyint): anyint; 69 | /** 70 | * Base Class of `js-combinatorics` 71 | */ 72 | declare class _CBase { 73 | /** 74 | * does `new` 75 | * @param args 76 | */ 77 | static of(...args: any[]): any; 78 | /** 79 | * Same as `of` but takes a single array `arg` 80 | * 81 | * cf. https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 82 | */ 83 | static from(arg: any): any; 84 | /** 85 | * Common iterator 86 | */ 87 | [Symbol.iterator](): Generator; 88 | /** 89 | * returns `[...this]`. 90 | */ 91 | toArray(): U[][]; 92 | /** 93 | * @deprecated 94 | * tells wether you need `BigInt` to access all elements. 95 | */ 96 | get isBig(): boolean; 97 | /** 98 | * @deprecated 99 | * tells wether it is safe to work on this instance. 100 | * 101 | * * always `true` unless your platform does not support `BigInt`. 102 | * * if not, `true` iff `.isBig` is `false`. 103 | */ 104 | get isSafe(): boolean; 105 | /** 106 | * check n for nth 107 | */ 108 | _check(n: anyint): anyint; 109 | /** 110 | * get the `n`th element of the iterator. 111 | * negative `n` goes backwards 112 | * like `Array.prototype.at()` 113 | * @link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at 114 | */ 115 | at(n: anyint): Optional; 116 | /** 117 | * an alias of `at` 118 | */ 119 | nth(n: anyint): Optional; 120 | /** 121 | * the seed iterable 122 | */ 123 | seed: T[]; 124 | /** 125 | * the size (# of elements) of each element. 126 | */ 127 | size: number; 128 | /** 129 | * the number of elements 130 | */ 131 | length: bigint; 132 | /** 133 | * pick random element 134 | */ 135 | sample(): Optional; 136 | /** 137 | * an infinite steam of random elements 138 | */ 139 | samples(): Generator; 140 | } 141 | /** 142 | * Permutation 143 | */ 144 | export declare class Permutation extends _CBase { 145 | constructor(seed: Iterable, size?: number); 146 | at(n: anyint): Optional; 147 | } 148 | /** 149 | * Combination 150 | */ 151 | export declare class Combination extends _CBase { 152 | comb: (anyint: any) => number[]; 153 | constructor(seed: Iterable, size?: number); 154 | /** 155 | * returns an iterator which is more efficient 156 | * than the default iterator that uses .nth 157 | * 158 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system#Applications 159 | */ 160 | bitwiseIterator(): Generator; 161 | at(n: anyint): Optional; 162 | } 163 | /** 164 | * Base N 165 | */ 166 | export declare class BaseN extends _CBase { 167 | base: number; 168 | constructor(seed: Iterable, size?: number); 169 | at(n: anyint): Optional; 170 | } 171 | /** 172 | * Power Set 173 | */ 174 | export declare class PowerSet extends _CBase { 175 | constructor(seed: Iterable); 176 | at(n: anyint): Optional; 177 | } 178 | /** 179 | * Cartesian Product 180 | */ 181 | export declare class CartesianProduct extends _CBase { 182 | constructor(...args: Iterable[]); 183 | at(n: anyint): Optional; 184 | } 185 | export {}; 186 | -------------------------------------------------------------------------------- /combinatorics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * combinatorics.js 3 | * 4 | * Licensed under the MIT license. 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Dan Kogai 8 | * 9 | * References: 10 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination 11 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation 12 | * @link: http://en.wikipedia.org/wiki/Factorial_number_system 13 | * @link: https://en.wikipedia.org/wiki/Combinatorial_number_system 14 | */ 15 | export const version = '2.1.2'; 16 | /** 17 | * calculates `P(n, k)`. 18 | * 19 | * @link https://en.wikipedia.org/wiki/Permutation 20 | */ 21 | export function permutation(n, k) { 22 | if (n < 0) 23 | throw new RangeError(`${n} is out of range`); 24 | if (k < 0) 25 | throw new RangeError(`${k} is out of range`); 26 | if (0 == k) 27 | return 1n; 28 | if (n < k) 29 | return 0n; 30 | let [bn, bk, bp] = [BigInt(n), BigInt(k), 1n]; 31 | while (bk--) 32 | bp *= bn--; 33 | return bp; 34 | } 35 | /** 36 | * calculates `C(n, k)`. 37 | * 38 | * @link https://en.wikipedia.org/wiki/Combination 39 | */ 40 | export function combination(n, k) { 41 | if (0 == k) 42 | return 1n; 43 | if (n == k) 44 | return 1n; 45 | if (n < k) 46 | return 0n; 47 | return permutation(n, k) / permutation(k, k); 48 | } 49 | /** 50 | * calculates `n!` === `P(n, n)`. 51 | * 52 | * @link https://en.wikipedia.org/wiki/Factorial 53 | */ 54 | export function factorial(n) { 55 | return permutation(n, n); 56 | } 57 | /** 58 | * returns the factoradic representation of `n`, least significant order. 59 | * 60 | * @link https://en.wikipedia.org/wiki/Factorial_number_system 61 | * @param {number} l the number of digits 62 | */ 63 | export function factoradic(n, l = 0) { 64 | if (n < 0) 65 | throw new RangeError(`${n} is out of range`); 66 | let [bn, bf] = [BigInt(n), 1n]; 67 | if (!l) { 68 | for (l = 1; bf < bn; bf *= BigInt(++l)) 69 | ; 70 | if (bn < bf) 71 | bf /= BigInt(l--); 72 | } 73 | else { 74 | bf = BigInt(factorial(l)); 75 | } 76 | let digits = [0]; 77 | for (; l; bf /= BigInt(l--)) { 78 | digits[l] = Number(bn / bf); 79 | bn %= bf; 80 | } 81 | return digits; 82 | } 83 | /** 84 | * `combinadic(n, k)` returns a function 85 | * that takes `m` as an argument and 86 | * returns the combinadics representation of `m` for `n C k`. 87 | * 88 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system 89 | */ 90 | export function combinadic(n, k) { 91 | const count = combination(n, k); 92 | const [bn, bk] = [BigInt(n), BigInt(k)]; 93 | return (m) => { 94 | if (m < 0 || count <= m) 95 | throw new RangeError(`${m} is out of range`); 96 | let digits = []; 97 | let [ba, bb] = [bn, bk]; 98 | let x = BigInt(count) - 1n - BigInt(m); 99 | for (let i = 0; i < k; i++) { 100 | ba--; 101 | while (x < combination(ba, bb)) 102 | ba--; 103 | digits.push(Number(bn - 1n - ba)); 104 | x -= combination(ba, bb); 105 | bb--; 106 | } 107 | return digits; 108 | }; 109 | } 110 | /** 111 | * 112 | */ 113 | const _crypto = typeof crypto !== 'undefined' ? crypto : {}; 114 | const _randomBytes = typeof _crypto['randomBytes'] === 'function' 115 | ? (len) => Uint8Array.from(_crypto['randomBytes'](len)) 116 | : typeof _crypto['getRandomValues'] === 'function' 117 | ? (len) => _crypto['getRandomValues'](new Uint8Array(len)) 118 | : (len) => Uint8Array.from(Array(len), () => Math.random() * 256); 119 | /** 120 | * returns random integer `n` where `min` <= `n` < `max`: 121 | * 122 | * if the argument is `BigInt` the result is also `BigInt`. 123 | * 124 | * @param {anyint} min 125 | * @param {anyint} max 126 | */ 127 | export function randomInteger(min = 0, max = Math.pow(2, 53)) { 128 | let ctor = min.constructor; 129 | if (arguments.length === 0) { 130 | return Math.floor(Math.random() * ctor(max)); 131 | } 132 | if (arguments.length == 1) { 133 | [min, max] = [ctor(0), min]; 134 | } 135 | if (typeof min == 'number') { // number 136 | [min, max] = [Math.ceil(Number(min)), Math.ceil(Number(max))]; 137 | return Math.floor(Math.random() * (max - min)) + min; 138 | } 139 | const mag = ctor(max) - ctor(min); 140 | const len = mag.toString(16).length; 141 | const u8s = _randomBytes(len); 142 | const rnd = u8s.reduce((a, v) => ((a << ctor(8)) + ctor(v)), ctor(0)); 143 | return ((ctor(rnd) * mag) >> ctor(len * 8)) + ctor(min); 144 | } 145 | ; 146 | /** 147 | * Base Class of `js-combinatorics` 148 | */ 149 | class _CBase { 150 | /** 151 | * does `new` 152 | * @param args 153 | */ 154 | static of(...args) { 155 | return new (Function.prototype.bind.apply(this, [null].concat(args))); 156 | } 157 | /** 158 | * Same as `of` but takes a single array `arg` 159 | * 160 | * cf. https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 161 | */ 162 | static from(arg) { 163 | return new (Function.prototype.bind.apply(this, [null].concat(arg))); 164 | } 165 | /** 166 | * Common iterator 167 | */ 168 | [Symbol.iterator]() { 169 | return function* (it, len) { 170 | for (let i = 0n; i < len; i++) 171 | yield it.at(i); 172 | }(this, this.length); 173 | } 174 | /** 175 | * returns `[...this]`. 176 | */ 177 | toArray() { 178 | return [...this]; 179 | } 180 | /** 181 | * @deprecated 182 | * tells wether you need `BigInt` to access all elements. 183 | */ 184 | get isBig() { 185 | return Number.MAX_SAFE_INTEGER < this.length; 186 | } 187 | /** 188 | * @deprecated 189 | * tells wether it is safe to work on this instance. 190 | * 191 | * * always `true` unless your platform does not support `BigInt`. 192 | * * if not, `true` iff `.isBig` is `false`. 193 | */ 194 | get isSafe() { 195 | return typeof BigInt !== 'undefined' || !this.isBig; 196 | } 197 | /** 198 | * check n for nth 199 | */ 200 | _check(n) { 201 | if (n < 0) { 202 | if (this.length < -n) 203 | throw new RangeError(`${n} is out of range`); 204 | return BigInt(this.length) + BigInt(n); 205 | } 206 | if (this.length <= n) 207 | throw new RangeError(`${n} is out of range`); 208 | return n; 209 | } 210 | /** 211 | * get the `n`th element of the iterator. 212 | * negative `n` goes backwards 213 | * like `Array.prototype.at()` 214 | * @link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at 215 | */ 216 | at(n) { return undefined; } 217 | /** 218 | * an alias of `at` 219 | */ 220 | nth(n) { return this.at(n); } 221 | /** 222 | * the seed iterable 223 | */ 224 | seed; 225 | /** 226 | * the size (# of elements) of each element. 227 | */ 228 | size; 229 | /** 230 | * the number of elements 231 | */ 232 | length; 233 | /** 234 | * pick random element 235 | */ 236 | sample() { 237 | return this.at(randomInteger(this.length)); 238 | } 239 | /** 240 | * an infinite steam of random elements 241 | */ 242 | samples() { 243 | return function* (it) { 244 | while (true) 245 | yield it.sample(); 246 | }(this); 247 | } 248 | } 249 | /** 250 | * Permutation 251 | */ 252 | export class Permutation extends _CBase { 253 | constructor(seed, size = 0) { 254 | super(); 255 | this.seed = [...seed]; 256 | this.size = 0 < size ? size : this.seed.length; 257 | this.length = permutation(this.seed.length, this.size); 258 | Object.freeze(this); 259 | } 260 | at(n) { 261 | n = this._check(n); 262 | if (n === undefined) 263 | return undefined; 264 | const offset = this.seed.length - this.size; 265 | const skip = factorial(offset); 266 | let digits = factoradic(BigInt(n) * BigInt(skip), this.seed.length); 267 | let source = this.seed.slice(); 268 | let result = []; 269 | for (let i = this.seed.length - 1; offset <= i; i--) { 270 | result.push(source.splice(digits[i], 1)[0]); 271 | } 272 | return result; 273 | } 274 | } 275 | /** 276 | * Combination 277 | */ 278 | export class Combination extends _CBase { 279 | comb; 280 | constructor(seed, size = 0) { 281 | super(); 282 | this.seed = [...seed]; 283 | this.size = 0 < size ? size : this.seed.length; 284 | this.size = size; 285 | this.length = combination(this.seed.length, this.size); 286 | this.comb = combinadic(this.seed.length, this.size); 287 | Object.freeze(this); 288 | } 289 | /** 290 | * returns an iterator which is more efficient 291 | * than the default iterator that uses .nth 292 | * 293 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system#Applications 294 | */ 295 | bitwiseIterator() { 296 | // [Symbol.iterator]() { 297 | // console.log('overriding _CBase'); 298 | const inc = (x) => { 299 | if (x <= 0n) 300 | return 0n; 301 | const u = x & -x; 302 | const v = u + x; 303 | return v + (((v ^ x) / u) >> 2n); 304 | }; 305 | let x = (1n << BigInt(this.size)) - 1n; // 0b11...1 306 | return function* (it, len) { 307 | for (let i = 0n; i < BigInt(len); i++, x = inc(x)) { 308 | let result = []; 309 | for (let y = x, j = 0; 0n < y; y >>= 1n, j++) { 310 | if (y & 1n) 311 | result.push(it.seed[j]); 312 | } 313 | // console.log(`x = ${x}`); 314 | yield result; 315 | } 316 | }(this, this.length); 317 | } 318 | at(n) { 319 | n = this._check(n); 320 | if (n === undefined) 321 | return undefined; 322 | let result = []; 323 | for (let i of this.comb(n)) { 324 | result.push(this.seed[i]); 325 | } 326 | return result; 327 | } 328 | } 329 | /** 330 | * Base N 331 | */ 332 | export class BaseN extends _CBase { 333 | base; 334 | constructor(seed, size = 1) { 335 | if (size < 1) 336 | throw new RangeError(`${size} is out of range`); 337 | super(); 338 | this.seed = [...seed]; 339 | this.size = size; 340 | let base = this.seed.length; 341 | this.base = base; 342 | this.length = BigInt(base) ** BigInt(size); 343 | Object.freeze(this); 344 | } 345 | at(n) { 346 | n = this._check(n); 347 | if (n === undefined) 348 | return undefined; 349 | let bn = BigInt(n); 350 | const bb = BigInt(this.base); 351 | let result = []; 352 | for (let i = 0; i < this.size; i++) { 353 | let bd = bn % bb; 354 | result.push(this.seed[Number(bd)]); 355 | bn -= bd; 356 | bn /= bb; 357 | } 358 | return result; 359 | } 360 | } 361 | /** 362 | * Power Set 363 | */ 364 | export class PowerSet extends _CBase { 365 | constructor(seed) { 366 | super(); 367 | this.seed = [...seed]; 368 | const length = 1n << BigInt(this.seed.length); 369 | this.length = length; 370 | Object.freeze(this); 371 | } 372 | at(n) { 373 | n = this._check(n); 374 | if (n === undefined) 375 | return undefined; 376 | let bn = BigInt(n); 377 | let result = []; 378 | for (let bi = 0n; bn; bn >>= 1n, bi++) 379 | if (bn & 1n) 380 | result.push(this.seed[Number(bi)]); 381 | return result; 382 | } 383 | } 384 | /** 385 | * Cartesian Product 386 | */ 387 | export class CartesianProduct extends _CBase { 388 | constructor(...args) { 389 | super(); 390 | this.seed = args.map(v => [...v]); 391 | this.size = this.seed.length; 392 | const length = this.seed.reduce((a, v) => a * BigInt(v.length), 1n); 393 | this.length = length; 394 | Object.freeze(this); 395 | } 396 | at(n) { 397 | n = this._check(n); 398 | if (n === undefined) 399 | return undefined; 400 | let bn = BigInt(n); 401 | let result = []; 402 | for (let i = 0; i < this.size; i++) { 403 | const base = this.seed[i].length; 404 | const bb = BigInt(base); 405 | const bd = bn % bb; 406 | result.push(this.seed[i][Number(bd)]); 407 | bn -= bd; 408 | bn /= bb; 409 | } 410 | return result; 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /commonjs/combinatorics.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.CartesianProduct = exports.PowerSet = exports.BaseN = exports.Combination = exports.Permutation = exports.randomInteger = exports.combinadic = exports.factoradic = exports.factorial = exports.combination = exports.permutation = exports.version = void 0; 4 | /** 5 | * combinatorics.js 6 | * 7 | * Licensed under the MIT license. 8 | * http://www.opensource.org/licenses/mit-license.php 9 | * 10 | * @author: Dan Kogai 11 | * 12 | * References: 13 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination 14 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation 15 | * @link: http://en.wikipedia.org/wiki/Factorial_number_system 16 | * @link: https://en.wikipedia.org/wiki/Combinatorial_number_system 17 | */ 18 | exports.version = '2.1.2'; 19 | /** 20 | * calculates `P(n, k)`. 21 | * 22 | * @link https://en.wikipedia.org/wiki/Permutation 23 | */ 24 | function permutation(n, k) { 25 | if (n < 0) 26 | throw new RangeError(`${n} is out of range`); 27 | if (k < 0) 28 | throw new RangeError(`${k} is out of range`); 29 | if (0 == k) 30 | return 1n; 31 | if (n < k) 32 | return 0n; 33 | let [bn, bk, bp] = [BigInt(n), BigInt(k), 1n]; 34 | while (bk--) 35 | bp *= bn--; 36 | return bp; 37 | } 38 | exports.permutation = permutation; 39 | /** 40 | * calculates `C(n, k)`. 41 | * 42 | * @link https://en.wikipedia.org/wiki/Combination 43 | */ 44 | function combination(n, k) { 45 | if (0 == k) 46 | return 1n; 47 | if (n == k) 48 | return 1n; 49 | if (n < k) 50 | return 0n; 51 | return permutation(n, k) / permutation(k, k); 52 | } 53 | exports.combination = combination; 54 | /** 55 | * calculates `n!` === `P(n, n)`. 56 | * 57 | * @link https://en.wikipedia.org/wiki/Factorial 58 | */ 59 | function factorial(n) { 60 | return permutation(n, n); 61 | } 62 | exports.factorial = factorial; 63 | /** 64 | * returns the factoradic representation of `n`, least significant order. 65 | * 66 | * @link https://en.wikipedia.org/wiki/Factorial_number_system 67 | * @param {number} l the number of digits 68 | */ 69 | function factoradic(n, l = 0) { 70 | if (n < 0) 71 | throw new RangeError(`${n} is out of range`); 72 | let [bn, bf] = [BigInt(n), 1n]; 73 | if (!l) { 74 | for (l = 1; bf < bn; bf *= BigInt(++l)) 75 | ; 76 | if (bn < bf) 77 | bf /= BigInt(l--); 78 | } 79 | else { 80 | bf = BigInt(factorial(l)); 81 | } 82 | let digits = [0]; 83 | for (; l; bf /= BigInt(l--)) { 84 | digits[l] = Number(bn / bf); 85 | bn %= bf; 86 | } 87 | return digits; 88 | } 89 | exports.factoradic = factoradic; 90 | /** 91 | * `combinadic(n, k)` returns a function 92 | * that takes `m` as an argument and 93 | * returns the combinadics representation of `m` for `n C k`. 94 | * 95 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system 96 | */ 97 | function combinadic(n, k) { 98 | const count = combination(n, k); 99 | const [bn, bk] = [BigInt(n), BigInt(k)]; 100 | return (m) => { 101 | if (m < 0 || count <= m) 102 | throw new RangeError(`${m} is out of range`); 103 | let digits = []; 104 | let [ba, bb] = [bn, bk]; 105 | let x = BigInt(count) - 1n - BigInt(m); 106 | for (let i = 0; i < k; i++) { 107 | ba--; 108 | while (x < combination(ba, bb)) 109 | ba--; 110 | digits.push(Number(bn - 1n - ba)); 111 | x -= combination(ba, bb); 112 | bb--; 113 | } 114 | return digits; 115 | }; 116 | } 117 | exports.combinadic = combinadic; 118 | /** 119 | * 120 | */ 121 | const _crypto = typeof crypto !== 'undefined' ? crypto : {}; 122 | const _randomBytes = typeof _crypto['randomBytes'] === 'function' 123 | ? (len) => Uint8Array.from(_crypto['randomBytes'](len)) 124 | : typeof _crypto['getRandomValues'] === 'function' 125 | ? (len) => _crypto['getRandomValues'](new Uint8Array(len)) 126 | : (len) => Uint8Array.from(Array(len), () => Math.random() * 256); 127 | /** 128 | * returns random integer `n` where `min` <= `n` < `max`: 129 | * 130 | * if the argument is `BigInt` the result is also `BigInt`. 131 | * 132 | * @param {anyint} min 133 | * @param {anyint} max 134 | */ 135 | function randomInteger(min = 0, max = Math.pow(2, 53)) { 136 | let ctor = min.constructor; 137 | if (arguments.length === 0) { 138 | return Math.floor(Math.random() * ctor(max)); 139 | } 140 | if (arguments.length == 1) { 141 | [min, max] = [ctor(0), min]; 142 | } 143 | if (typeof min == 'number') { // number 144 | [min, max] = [Math.ceil(Number(min)), Math.ceil(Number(max))]; 145 | return Math.floor(Math.random() * (max - min)) + min; 146 | } 147 | const mag = ctor(max) - ctor(min); 148 | const len = mag.toString(16).length; 149 | const u8s = _randomBytes(len); 150 | const rnd = u8s.reduce((a, v) => ((a << ctor(8)) + ctor(v)), ctor(0)); 151 | return ((ctor(rnd) * mag) >> ctor(len * 8)) + ctor(min); 152 | } 153 | exports.randomInteger = randomInteger; 154 | ; 155 | /** 156 | * Base Class of `js-combinatorics` 157 | */ 158 | class _CBase { 159 | /** 160 | * does `new` 161 | * @param args 162 | */ 163 | static of(...args) { 164 | return new (Function.prototype.bind.apply(this, [null].concat(args))); 165 | } 166 | /** 167 | * Same as `of` but takes a single array `arg` 168 | * 169 | * cf. https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 170 | */ 171 | static from(arg) { 172 | return new (Function.prototype.bind.apply(this, [null].concat(arg))); 173 | } 174 | /** 175 | * Common iterator 176 | */ 177 | [Symbol.iterator]() { 178 | return function* (it, len) { 179 | for (let i = 0n; i < len; i++) 180 | yield it.at(i); 181 | }(this, this.length); 182 | } 183 | /** 184 | * returns `[...this]`. 185 | */ 186 | toArray() { 187 | return [...this]; 188 | } 189 | /** 190 | * @deprecated 191 | * tells wether you need `BigInt` to access all elements. 192 | */ 193 | get isBig() { 194 | return Number.MAX_SAFE_INTEGER < this.length; 195 | } 196 | /** 197 | * @deprecated 198 | * tells wether it is safe to work on this instance. 199 | * 200 | * * always `true` unless your platform does not support `BigInt`. 201 | * * if not, `true` iff `.isBig` is `false`. 202 | */ 203 | get isSafe() { 204 | return typeof BigInt !== 'undefined' || !this.isBig; 205 | } 206 | /** 207 | * check n for nth 208 | */ 209 | _check(n) { 210 | if (n < 0) { 211 | if (this.length < -n) 212 | throw new RangeError(`${n} is out of range`); 213 | return BigInt(this.length) + BigInt(n); 214 | } 215 | if (this.length <= n) 216 | throw new RangeError(`${n} is out of range`); 217 | return n; 218 | } 219 | /** 220 | * get the `n`th element of the iterator. 221 | * negative `n` goes backwards 222 | * like `Array.prototype.at()` 223 | * @link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at 224 | */ 225 | at(n) { return undefined; } 226 | /** 227 | * an alias of `at` 228 | */ 229 | nth(n) { return this.at(n); } 230 | /** 231 | * pick random element 232 | */ 233 | sample() { 234 | return this.at(randomInteger(this.length)); 235 | } 236 | /** 237 | * an infinite steam of random elements 238 | */ 239 | samples() { 240 | return function* (it) { 241 | while (true) 242 | yield it.sample(); 243 | }(this); 244 | } 245 | } 246 | /** 247 | * Permutation 248 | */ 249 | class Permutation extends _CBase { 250 | constructor(seed, size = 0) { 251 | super(); 252 | this.seed = [...seed]; 253 | this.size = 0 < size ? size : this.seed.length; 254 | this.length = permutation(this.seed.length, this.size); 255 | Object.freeze(this); 256 | } 257 | at(n) { 258 | n = this._check(n); 259 | if (n === undefined) 260 | return undefined; 261 | const offset = this.seed.length - this.size; 262 | const skip = factorial(offset); 263 | let digits = factoradic(BigInt(n) * BigInt(skip), this.seed.length); 264 | let source = this.seed.slice(); 265 | let result = []; 266 | for (let i = this.seed.length - 1; offset <= i; i--) { 267 | result.push(source.splice(digits[i], 1)[0]); 268 | } 269 | return result; 270 | } 271 | } 272 | exports.Permutation = Permutation; 273 | /** 274 | * Combination 275 | */ 276 | class Combination extends _CBase { 277 | constructor(seed, size = 0) { 278 | super(); 279 | this.seed = [...seed]; 280 | this.size = 0 < size ? size : this.seed.length; 281 | this.size = size; 282 | this.length = combination(this.seed.length, this.size); 283 | this.comb = combinadic(this.seed.length, this.size); 284 | Object.freeze(this); 285 | } 286 | /** 287 | * returns an iterator which is more efficient 288 | * than the default iterator that uses .nth 289 | * 290 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system#Applications 291 | */ 292 | bitwiseIterator() { 293 | // [Symbol.iterator]() { 294 | // console.log('overriding _CBase'); 295 | const inc = (x) => { 296 | if (x <= 0n) 297 | return 0n; 298 | const u = x & -x; 299 | const v = u + x; 300 | return v + (((v ^ x) / u) >> 2n); 301 | }; 302 | let x = (1n << BigInt(this.size)) - 1n; // 0b11...1 303 | return function* (it, len) { 304 | for (let i = 0n; i < BigInt(len); i++, x = inc(x)) { 305 | let result = []; 306 | for (let y = x, j = 0; 0n < y; y >>= 1n, j++) { 307 | if (y & 1n) 308 | result.push(it.seed[j]); 309 | } 310 | // console.log(`x = ${x}`); 311 | yield result; 312 | } 313 | }(this, this.length); 314 | } 315 | at(n) { 316 | n = this._check(n); 317 | if (n === undefined) 318 | return undefined; 319 | let result = []; 320 | for (let i of this.comb(n)) { 321 | result.push(this.seed[i]); 322 | } 323 | return result; 324 | } 325 | } 326 | exports.Combination = Combination; 327 | /** 328 | * Base N 329 | */ 330 | class BaseN extends _CBase { 331 | constructor(seed, size = 1) { 332 | if (size < 1) 333 | throw new RangeError(`${size} is out of range`); 334 | super(); 335 | this.seed = [...seed]; 336 | this.size = size; 337 | let base = this.seed.length; 338 | this.base = base; 339 | this.length = BigInt(base) ** BigInt(size); 340 | Object.freeze(this); 341 | } 342 | at(n) { 343 | n = this._check(n); 344 | if (n === undefined) 345 | return undefined; 346 | let bn = BigInt(n); 347 | const bb = BigInt(this.base); 348 | let result = []; 349 | for (let i = 0; i < this.size; i++) { 350 | let bd = bn % bb; 351 | result.push(this.seed[Number(bd)]); 352 | bn -= bd; 353 | bn /= bb; 354 | } 355 | return result; 356 | } 357 | } 358 | exports.BaseN = BaseN; 359 | /** 360 | * Power Set 361 | */ 362 | class PowerSet extends _CBase { 363 | constructor(seed) { 364 | super(); 365 | this.seed = [...seed]; 366 | const length = 1n << BigInt(this.seed.length); 367 | this.length = length; 368 | Object.freeze(this); 369 | } 370 | at(n) { 371 | n = this._check(n); 372 | if (n === undefined) 373 | return undefined; 374 | let bn = BigInt(n); 375 | let result = []; 376 | for (let bi = 0n; bn; bn >>= 1n, bi++) 377 | if (bn & 1n) 378 | result.push(this.seed[Number(bi)]); 379 | return result; 380 | } 381 | } 382 | exports.PowerSet = PowerSet; 383 | /** 384 | * Cartesian Product 385 | */ 386 | class CartesianProduct extends _CBase { 387 | constructor(...args) { 388 | super(); 389 | this.seed = args.map(v => [...v]); 390 | this.size = this.seed.length; 391 | const length = this.seed.reduce((a, v) => a * BigInt(v.length), 1n); 392 | this.length = length; 393 | Object.freeze(this); 394 | } 395 | at(n) { 396 | n = this._check(n); 397 | if (n === undefined) 398 | return undefined; 399 | let bn = BigInt(n); 400 | let result = []; 401 | for (let i = 0; i < this.size; i++) { 402 | const base = this.seed[i].length; 403 | const bb = BigInt(base); 404 | const bd = bn % bb; 405 | result.push(this.seed[i][Number(bd)]); 406 | bn -= bd; 407 | bn /= bb; 408 | } 409 | return result; 410 | } 411 | } 412 | exports.CartesianProduct = CartesianProduct; 413 | -------------------------------------------------------------------------------- /combinatorics.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * combinatorics.js 3 | * 4 | * Licensed under the MIT license. 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Dan Kogai 8 | * 9 | * References: 10 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination 11 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation 12 | * @link: http://en.wikipedia.org/wiki/Factorial_number_system 13 | * @link: https://en.wikipedia.org/wiki/Combinatorial_number_system 14 | */ 15 | export const version = '2.1.2'; 16 | /** 17 | * BigInt Workaround 18 | * 19 | * https://github.com/streamich/memfs/issues/275 20 | */ 21 | type anyint = number | bigint; 22 | /** 23 | * Optional will not be official so 24 | * @link: https://github.com/microsoft/TypeScript/issues/19944 25 | */ 26 | type Optional = T | undefined; 27 | /** 28 | * calculates `P(n, k)`. 29 | * 30 | * @link https://en.wikipedia.org/wiki/Permutation 31 | */ 32 | export function permutation(n: anyint, k: anyint): bigint { 33 | if (n < 0) throw new RangeError(`${n} is out of range`); 34 | if (k < 0) throw new RangeError(`${k} is out of range`); 35 | if (0 == k) return 1n; 36 | if (n < k) return 0n; 37 | let [bn, bk, bp] = [BigInt(n), BigInt(k), 1n]; 38 | while (bk--) 39 | bp *= bn--; 40 | return bp; 41 | } 42 | /** 43 | * calculates `C(n, k)`. 44 | * 45 | * @link https://en.wikipedia.org/wiki/Combination 46 | */ 47 | export function combination(n: anyint, k: anyint): bigint { 48 | if (0 == k) return 1n; 49 | if (n == k) return 1n; 50 | if (n < k) return 0n; 51 | return permutation(n, k) / permutation(k, k); 52 | } 53 | /** 54 | * calculates `n!` === `P(n, n)`. 55 | * 56 | * @link https://en.wikipedia.org/wiki/Factorial 57 | */ 58 | export function factorial(n: anyint): bigint { 59 | return permutation(n, n); 60 | } 61 | /** 62 | * returns the factoradic representation of `n`, least significant order. 63 | * 64 | * @link https://en.wikipedia.org/wiki/Factorial_number_system 65 | * @param {number} l the number of digits 66 | */ 67 | export function factoradic(n: anyint, l = 0): number[] { 68 | if (n < 0) throw new RangeError(`${n} is out of range`); 69 | let [bn, bf] = [BigInt(n), 1n]; 70 | if (!l) { 71 | for (l = 1; bf < bn; bf *= BigInt(++l)) 72 | ; 73 | if (bn < bf) 74 | bf /= BigInt(l--); 75 | } 76 | else { 77 | bf = BigInt(factorial(l)); 78 | } 79 | let digits = [0]; 80 | for (; l; bf /= BigInt(l--)) { 81 | digits[l] = Number(bn / bf); 82 | bn %= bf; 83 | } 84 | return digits; 85 | } 86 | /** 87 | * `combinadic(n, k)` returns a function 88 | * that takes `m` as an argument and 89 | * returns the combinadics representation of `m` for `n C k`. 90 | * 91 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system 92 | */ 93 | export function combinadic(n: anyint, k: anyint): (m: anyint) => number[] { 94 | const count = combination(n, k); 95 | const [bn, bk] = [BigInt(n), BigInt(k)]; 96 | return (m) => { 97 | if (m < 0 || count <= m) throw new RangeError(`${m} is out of range`); 98 | let digits: number[] = []; 99 | let [ba, bb] = [bn, bk]; 100 | let x = BigInt(count) - 1n - BigInt(m); 101 | for (let i = 0; i < k; i++) { 102 | ba--; 103 | while (x < combination(ba, bb)) ba--; 104 | digits.push(Number(bn - 1n - ba)); 105 | x -= combination(ba, bb); 106 | bb--; 107 | } 108 | return digits; 109 | } 110 | } 111 | /** 112 | * 113 | */ 114 | const _crypto = typeof crypto !== 'undefined' ? crypto : {}; 115 | const _randomBytes: (len: number) => Uint8Array 116 | = typeof _crypto['randomBytes'] === 'function' 117 | ? (len: number) => Uint8Array.from(_crypto['randomBytes'](len)) 118 | : typeof _crypto['getRandomValues'] === 'function' 119 | ? (len: number) => _crypto['getRandomValues'](new Uint8Array(len)) 120 | : (len: number) => Uint8Array.from(Array(len), () => Math.random() * 256); 121 | /** 122 | * returns random integer `n` where `min` <= `n` < `max`: 123 | * 124 | * if the argument is `BigInt` the result is also `BigInt`. 125 | * 126 | * @param {anyint} min 127 | * @param {anyint} max 128 | */ 129 | export function randomInteger(min: anyint = 0, max: anyint = Math.pow(2, 53)): anyint { 130 | let ctor = min.constructor; 131 | if (arguments.length === 0) { 132 | return Math.floor(Math.random() * ctor(max)); 133 | } 134 | if (arguments.length == 1) { 135 | [min, max] = [ctor(0), min]; 136 | } 137 | if (typeof min == 'number') { // number 138 | [min, max] = [Math.ceil(Number(min)), Math.ceil(Number(max))]; 139 | return Math.floor(Math.random() * (max - min)) + min; 140 | } 141 | const mag = ctor(max) - ctor(min); 142 | const len = mag.toString(16).length; 143 | const u8s = _randomBytes(len); 144 | const rnd = u8s.reduce((a, v) => ((a << ctor(8)) + ctor(v)), ctor(0)); 145 | return ((ctor(rnd) * mag) >> ctor(len * 8)) + ctor(min); 146 | }; 147 | /** 148 | * Base Class of `js-combinatorics` 149 | */ 150 | class _CBase { 151 | /** 152 | * does `new` 153 | * @param args 154 | */ 155 | static of(...args) { 156 | return new (Function.prototype.bind.apply(this, [null].concat(args))); 157 | } 158 | /** 159 | * Same as `of` but takes a single array `arg` 160 | * 161 | * cf. https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 162 | */ 163 | static from(arg) { 164 | return new (Function.prototype.bind.apply(this, [null].concat(arg))); 165 | } 166 | /** 167 | * Common iterator 168 | */ 169 | [Symbol.iterator]() { 170 | return function* (it, len) { 171 | for (let i = 0n; i < len; i++) yield it.at(i); 172 | }(this, this.length); 173 | } 174 | /** 175 | * returns `[...this]`. 176 | */ 177 | toArray() { 178 | return [...this]; 179 | } 180 | /** 181 | * @deprecated 182 | * tells wether you need `BigInt` to access all elements. 183 | */ 184 | get isBig() { 185 | return Number.MAX_SAFE_INTEGER < this.length; 186 | } 187 | /** 188 | * @deprecated 189 | * tells wether it is safe to work on this instance. 190 | * 191 | * * always `true` unless your platform does not support `BigInt`. 192 | * * if not, `true` iff `.isBig` is `false`. 193 | */ 194 | get isSafe() { 195 | return typeof BigInt !== 'undefined' || !this.isBig; 196 | } 197 | /** 198 | * check n for nth 199 | */ 200 | _check(n: anyint): anyint { 201 | if (n < 0) { 202 | if (this.length < -n) throw new RangeError(`${n} is out of range`); 203 | return BigInt(this.length) + BigInt(n); 204 | } 205 | if (this.length <= n) throw new RangeError(`${n} is out of range`); 206 | return n; 207 | } 208 | /** 209 | * get the `n`th element of the iterator. 210 | * negative `n` goes backwards 211 | * like `Array.prototype.at()` 212 | * @link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at 213 | */ 214 | at(n: anyint): Optional { return undefined; } 215 | /** 216 | * an alias of `at` 217 | */ 218 | nth(n: anyint): Optional { return this.at(n); } 219 | /** 220 | * the seed iterable 221 | */ 222 | seed: T[]; 223 | /** 224 | * the size (# of elements) of each element. 225 | */ 226 | size: number; 227 | /** 228 | * the number of elements 229 | */ 230 | length: bigint; 231 | /** 232 | * pick random element 233 | */ 234 | sample(): Optional { 235 | return this.at(randomInteger(this.length)); 236 | } 237 | /** 238 | * an infinite steam of random elements 239 | */ 240 | samples() { 241 | return function* (it) { 242 | while (true) yield it.sample(); 243 | }(this); 244 | } 245 | } 246 | /** 247 | * Permutation 248 | */ 249 | export class Permutation extends _CBase { 250 | constructor(seed: Iterable, size = 0) { 251 | super(); 252 | this.seed = [...seed]; 253 | this.size = 0 < size ? size : this.seed.length; 254 | this.length = permutation(this.seed.length, this.size); 255 | Object.freeze(this); 256 | } 257 | at(n: anyint): Optional { 258 | n = this._check(n); 259 | if (n === undefined) return undefined; 260 | const offset = this.seed.length - this.size; 261 | const skip = factorial(offset); 262 | let digits = factoradic(BigInt(n) * BigInt(skip), this.seed.length); 263 | let source = this.seed.slice(); 264 | let result: T[] = []; 265 | for (let i = this.seed.length - 1; offset <= i; i--) { 266 | result.push(source.splice(digits[i], 1)[0]); 267 | } 268 | return result; 269 | } 270 | } 271 | /** 272 | * Combination 273 | */ 274 | export class Combination extends _CBase { 275 | comb: (anyint) => number[]; 276 | constructor(seed: Iterable, size = 0) { 277 | super(); 278 | this.seed = [...seed]; 279 | this.size = 0 < size ? size : this.seed.length; 280 | this.size = size; 281 | this.length = combination(this.seed.length, this.size); 282 | this.comb = combinadic(this.seed.length, this.size); 283 | Object.freeze(this); 284 | } 285 | /** 286 | * returns an iterator which is more efficient 287 | * than the default iterator that uses .nth 288 | * 289 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system#Applications 290 | */ 291 | bitwiseIterator() { 292 | // [Symbol.iterator]() { 293 | // console.log('overriding _CBase'); 294 | const inc = (x: bigint): bigint => { 295 | if (x <= 0n) return 0n; 296 | const u = x & -x; 297 | const v = u + x; 298 | return v + (((v ^ x) / u) >> 2n); 299 | } 300 | let x = (1n << BigInt(this.size)) - 1n; // 0b11...1 301 | return function* (it, len) { 302 | for (let i = 0n; i < BigInt(len); i++, x = inc(x)) { 303 | let result: T[] = []; 304 | for (let y = x, j = 0; 0n < y; y >>= 1n, j++) { 305 | if (y & 1n) result.push(it.seed[j]); 306 | } 307 | // console.log(`x = ${x}`); 308 | yield result; 309 | } 310 | }(this, this.length); 311 | } 312 | at(n: anyint): Optional { 313 | n = this._check(n); 314 | if (n === undefined) return undefined; 315 | let result: T[] = []; 316 | for (let i of this.comb(n)) { 317 | result.push(this.seed[i]); 318 | } 319 | return result; 320 | } 321 | } 322 | /** 323 | * Base N 324 | */ 325 | export class BaseN extends _CBase { 326 | base: number; 327 | constructor(seed: Iterable, size = 1) { 328 | if (size < 1) throw new RangeError(`${size} is out of range`); 329 | super(); 330 | this.seed = [...seed]; 331 | this.size = size; 332 | let base = this.seed.length; 333 | this.base = base; 334 | this.length = BigInt(base) ** BigInt(size); 335 | Object.freeze(this); 336 | } 337 | at(n: anyint): Optional { 338 | n = this._check(n); 339 | if (n === undefined) return undefined; 340 | let bn = BigInt(n); 341 | const bb = BigInt(this.base); 342 | let result: T[] = []; 343 | for (let i = 0; i < this.size; i++) { 344 | let bd = bn % bb; 345 | result.push(this.seed[Number(bd)]); 346 | bn -= bd; 347 | bn /= bb; 348 | } 349 | return result; 350 | } 351 | } 352 | /** 353 | * Power Set 354 | */ 355 | export class PowerSet extends _CBase { 356 | constructor(seed: Iterable) { 357 | super(); 358 | this.seed = [...seed]; 359 | const length = 1n << BigInt(this.seed.length); 360 | this.length = length; 361 | Object.freeze(this); 362 | } 363 | at(n: anyint): Optional { 364 | n = this._check(n); 365 | if (n === undefined) return undefined; 366 | let bn = BigInt(n); 367 | let result: T[] = []; 368 | for (let bi = 0n; bn; bn >>= 1n, bi++) 369 | if (bn & 1n) 370 | result.push(this.seed[Number(bi)]); 371 | return result; 372 | } 373 | } 374 | /** 375 | * Cartesian Product 376 | */ 377 | export class CartesianProduct extends _CBase { 378 | constructor(...args: Iterable[]) { 379 | super(); 380 | this.seed = args.map(v => [...v]); 381 | this.size = this.seed.length; 382 | const length = this.seed.reduce((a, v) => a * BigInt(v.length), 1n); 383 | this.length = length; 384 | Object.freeze(this); 385 | } 386 | at(n: anyint): Optional { 387 | n = this._check(n); 388 | if (n === undefined) return undefined; 389 | let bn = BigInt(n); 390 | let result: T[] = []; 391 | for (let i = 0; i < this.size; i++) { 392 | const base = this.seed[i].length; 393 | const bb = BigInt(base); 394 | const bd = bn % bb; 395 | result.push(this.seed[i][Number(bd)]); 396 | bn -= bd; 397 | bn /= bb; 398 | } 399 | return result; 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /umd/combinatorics.js: -------------------------------------------------------------------------------- 1 | (function (factory) { 2 | if (typeof module === "object" && typeof module.exports === "object") { 3 | var v = factory(require, exports); 4 | if (v !== undefined) module.exports = v; 5 | } 6 | else if (typeof define === "function" && define.amd) { 7 | define(["require", "exports"], factory); 8 | } 9 | })(function (require, exports) { 10 | "use strict"; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.CartesianProduct = exports.PowerSet = exports.BaseN = exports.Combination = exports.Permutation = exports.randomInteger = exports.combinadic = exports.factoradic = exports.factorial = exports.combination = exports.permutation = exports.version = void 0; 13 | /** 14 | * combinatorics.js 15 | * 16 | * Licensed under the MIT license. 17 | * http://www.opensource.org/licenses/mit-license.php 18 | * 19 | * @author: Dan Kogai 20 | * 21 | * References: 22 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination 23 | * @link: http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation 24 | * @link: http://en.wikipedia.org/wiki/Factorial_number_system 25 | * @link: https://en.wikipedia.org/wiki/Combinatorial_number_system 26 | */ 27 | exports.version = '2.1.2'; 28 | /** 29 | * calculates `P(n, k)`. 30 | * 31 | * @link https://en.wikipedia.org/wiki/Permutation 32 | */ 33 | function permutation(n, k) { 34 | if (n < 0) 35 | throw new RangeError(`${n} is out of range`); 36 | if (k < 0) 37 | throw new RangeError(`${k} is out of range`); 38 | if (0 == k) 39 | return 1n; 40 | if (n < k) 41 | return 0n; 42 | let [bn, bk, bp] = [BigInt(n), BigInt(k), 1n]; 43 | while (bk--) 44 | bp *= bn--; 45 | return bp; 46 | } 47 | exports.permutation = permutation; 48 | /** 49 | * calculates `C(n, k)`. 50 | * 51 | * @link https://en.wikipedia.org/wiki/Combination 52 | */ 53 | function combination(n, k) { 54 | if (0 == k) 55 | return 1n; 56 | if (n == k) 57 | return 1n; 58 | if (n < k) 59 | return 0n; 60 | return permutation(n, k) / permutation(k, k); 61 | } 62 | exports.combination = combination; 63 | /** 64 | * calculates `n!` === `P(n, n)`. 65 | * 66 | * @link https://en.wikipedia.org/wiki/Factorial 67 | */ 68 | function factorial(n) { 69 | return permutation(n, n); 70 | } 71 | exports.factorial = factorial; 72 | /** 73 | * returns the factoradic representation of `n`, least significant order. 74 | * 75 | * @link https://en.wikipedia.org/wiki/Factorial_number_system 76 | * @param {number} l the number of digits 77 | */ 78 | function factoradic(n, l = 0) { 79 | if (n < 0) 80 | throw new RangeError(`${n} is out of range`); 81 | let [bn, bf] = [BigInt(n), 1n]; 82 | if (!l) { 83 | for (l = 1; bf < bn; bf *= BigInt(++l)) 84 | ; 85 | if (bn < bf) 86 | bf /= BigInt(l--); 87 | } 88 | else { 89 | bf = BigInt(factorial(l)); 90 | } 91 | let digits = [0]; 92 | for (; l; bf /= BigInt(l--)) { 93 | digits[l] = Number(bn / bf); 94 | bn %= bf; 95 | } 96 | return digits; 97 | } 98 | exports.factoradic = factoradic; 99 | /** 100 | * `combinadic(n, k)` returns a function 101 | * that takes `m` as an argument and 102 | * returns the combinadics representation of `m` for `n C k`. 103 | * 104 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system 105 | */ 106 | function combinadic(n, k) { 107 | const count = combination(n, k); 108 | const [bn, bk] = [BigInt(n), BigInt(k)]; 109 | return (m) => { 110 | if (m < 0 || count <= m) 111 | throw new RangeError(`${m} is out of range`); 112 | let digits = []; 113 | let [ba, bb] = [bn, bk]; 114 | let x = BigInt(count) - 1n - BigInt(m); 115 | for (let i = 0; i < k; i++) { 116 | ba--; 117 | while (x < combination(ba, bb)) 118 | ba--; 119 | digits.push(Number(bn - 1n - ba)); 120 | x -= combination(ba, bb); 121 | bb--; 122 | } 123 | return digits; 124 | }; 125 | } 126 | exports.combinadic = combinadic; 127 | /** 128 | * 129 | */ 130 | const _crypto = typeof crypto !== 'undefined' ? crypto : {}; 131 | const _randomBytes = typeof _crypto['randomBytes'] === 'function' 132 | ? (len) => Uint8Array.from(_crypto['randomBytes'](len)) 133 | : typeof _crypto['getRandomValues'] === 'function' 134 | ? (len) => _crypto['getRandomValues'](new Uint8Array(len)) 135 | : (len) => Uint8Array.from(Array(len), () => Math.random() * 256); 136 | /** 137 | * returns random integer `n` where `min` <= `n` < `max`: 138 | * 139 | * if the argument is `BigInt` the result is also `BigInt`. 140 | * 141 | * @param {anyint} min 142 | * @param {anyint} max 143 | */ 144 | function randomInteger(min = 0, max = Math.pow(2, 53)) { 145 | let ctor = min.constructor; 146 | if (arguments.length === 0) { 147 | return Math.floor(Math.random() * ctor(max)); 148 | } 149 | if (arguments.length == 1) { 150 | [min, max] = [ctor(0), min]; 151 | } 152 | if (typeof min == 'number') { // number 153 | [min, max] = [Math.ceil(Number(min)), Math.ceil(Number(max))]; 154 | return Math.floor(Math.random() * (max - min)) + min; 155 | } 156 | const mag = ctor(max) - ctor(min); 157 | const len = mag.toString(16).length; 158 | const u8s = _randomBytes(len); 159 | const rnd = u8s.reduce((a, v) => ((a << ctor(8)) + ctor(v)), ctor(0)); 160 | return ((ctor(rnd) * mag) >> ctor(len * 8)) + ctor(min); 161 | } 162 | exports.randomInteger = randomInteger; 163 | ; 164 | /** 165 | * Base Class of `js-combinatorics` 166 | */ 167 | class _CBase { 168 | /** 169 | * does `new` 170 | * @param args 171 | */ 172 | static of(...args) { 173 | return new (Function.prototype.bind.apply(this, [null].concat(args))); 174 | } 175 | /** 176 | * Same as `of` but takes a single array `arg` 177 | * 178 | * cf. https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible 179 | */ 180 | static from(arg) { 181 | return new (Function.prototype.bind.apply(this, [null].concat(arg))); 182 | } 183 | /** 184 | * Common iterator 185 | */ 186 | [Symbol.iterator]() { 187 | return function* (it, len) { 188 | for (let i = 0n; i < len; i++) 189 | yield it.at(i); 190 | }(this, this.length); 191 | } 192 | /** 193 | * returns `[...this]`. 194 | */ 195 | toArray() { 196 | return [...this]; 197 | } 198 | /** 199 | * @deprecated 200 | * tells wether you need `BigInt` to access all elements. 201 | */ 202 | get isBig() { 203 | return Number.MAX_SAFE_INTEGER < this.length; 204 | } 205 | /** 206 | * @deprecated 207 | * tells wether it is safe to work on this instance. 208 | * 209 | * * always `true` unless your platform does not support `BigInt`. 210 | * * if not, `true` iff `.isBig` is `false`. 211 | */ 212 | get isSafe() { 213 | return typeof BigInt !== 'undefined' || !this.isBig; 214 | } 215 | /** 216 | * check n for nth 217 | */ 218 | _check(n) { 219 | if (n < 0) { 220 | if (this.length < -n) 221 | throw new RangeError(`${n} is out of range`); 222 | return BigInt(this.length) + BigInt(n); 223 | } 224 | if (this.length <= n) 225 | throw new RangeError(`${n} is out of range`); 226 | return n; 227 | } 228 | /** 229 | * get the `n`th element of the iterator. 230 | * negative `n` goes backwards 231 | * like `Array.prototype.at()` 232 | * @link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at 233 | */ 234 | at(n) { return undefined; } 235 | /** 236 | * an alias of `at` 237 | */ 238 | nth(n) { return this.at(n); } 239 | /** 240 | * pick random element 241 | */ 242 | sample() { 243 | return this.at(randomInteger(this.length)); 244 | } 245 | /** 246 | * an infinite steam of random elements 247 | */ 248 | samples() { 249 | return function* (it) { 250 | while (true) 251 | yield it.sample(); 252 | }(this); 253 | } 254 | } 255 | /** 256 | * Permutation 257 | */ 258 | class Permutation extends _CBase { 259 | constructor(seed, size = 0) { 260 | super(); 261 | this.seed = [...seed]; 262 | this.size = 0 < size ? size : this.seed.length; 263 | this.length = permutation(this.seed.length, this.size); 264 | Object.freeze(this); 265 | } 266 | at(n) { 267 | n = this._check(n); 268 | if (n === undefined) 269 | return undefined; 270 | const offset = this.seed.length - this.size; 271 | const skip = factorial(offset); 272 | let digits = factoradic(BigInt(n) * BigInt(skip), this.seed.length); 273 | let source = this.seed.slice(); 274 | let result = []; 275 | for (let i = this.seed.length - 1; offset <= i; i--) { 276 | result.push(source.splice(digits[i], 1)[0]); 277 | } 278 | return result; 279 | } 280 | } 281 | exports.Permutation = Permutation; 282 | /** 283 | * Combination 284 | */ 285 | class Combination extends _CBase { 286 | constructor(seed, size = 0) { 287 | super(); 288 | this.seed = [...seed]; 289 | this.size = 0 < size ? size : this.seed.length; 290 | this.size = size; 291 | this.length = combination(this.seed.length, this.size); 292 | this.comb = combinadic(this.seed.length, this.size); 293 | Object.freeze(this); 294 | } 295 | /** 296 | * returns an iterator which is more efficient 297 | * than the default iterator that uses .nth 298 | * 299 | * @link https://en.wikipedia.org/wiki/Combinatorial_number_system#Applications 300 | */ 301 | bitwiseIterator() { 302 | // [Symbol.iterator]() { 303 | // console.log('overriding _CBase'); 304 | const inc = (x) => { 305 | if (x <= 0n) 306 | return 0n; 307 | const u = x & -x; 308 | const v = u + x; 309 | return v + (((v ^ x) / u) >> 2n); 310 | }; 311 | let x = (1n << BigInt(this.size)) - 1n; // 0b11...1 312 | return function* (it, len) { 313 | for (let i = 0n; i < BigInt(len); i++, x = inc(x)) { 314 | let result = []; 315 | for (let y = x, j = 0; 0n < y; y >>= 1n, j++) { 316 | if (y & 1n) 317 | result.push(it.seed[j]); 318 | } 319 | // console.log(`x = ${x}`); 320 | yield result; 321 | } 322 | }(this, this.length); 323 | } 324 | at(n) { 325 | n = this._check(n); 326 | if (n === undefined) 327 | return undefined; 328 | let result = []; 329 | for (let i of this.comb(n)) { 330 | result.push(this.seed[i]); 331 | } 332 | return result; 333 | } 334 | } 335 | exports.Combination = Combination; 336 | /** 337 | * Base N 338 | */ 339 | class BaseN extends _CBase { 340 | constructor(seed, size = 1) { 341 | if (size < 1) 342 | throw new RangeError(`${size} is out of range`); 343 | super(); 344 | this.seed = [...seed]; 345 | this.size = size; 346 | let base = this.seed.length; 347 | this.base = base; 348 | this.length = BigInt(base) ** BigInt(size); 349 | Object.freeze(this); 350 | } 351 | at(n) { 352 | n = this._check(n); 353 | if (n === undefined) 354 | return undefined; 355 | let bn = BigInt(n); 356 | const bb = BigInt(this.base); 357 | let result = []; 358 | for (let i = 0; i < this.size; i++) { 359 | let bd = bn % bb; 360 | result.push(this.seed[Number(bd)]); 361 | bn -= bd; 362 | bn /= bb; 363 | } 364 | return result; 365 | } 366 | } 367 | exports.BaseN = BaseN; 368 | /** 369 | * Power Set 370 | */ 371 | class PowerSet extends _CBase { 372 | constructor(seed) { 373 | super(); 374 | this.seed = [...seed]; 375 | const length = 1n << BigInt(this.seed.length); 376 | this.length = length; 377 | Object.freeze(this); 378 | } 379 | at(n) { 380 | n = this._check(n); 381 | if (n === undefined) 382 | return undefined; 383 | let bn = BigInt(n); 384 | let result = []; 385 | for (let bi = 0n; bn; bn >>= 1n, bi++) 386 | if (bn & 1n) 387 | result.push(this.seed[Number(bi)]); 388 | return result; 389 | } 390 | } 391 | exports.PowerSet = PowerSet; 392 | /** 393 | * Cartesian Product 394 | */ 395 | class CartesianProduct extends _CBase { 396 | constructor(...args) { 397 | super(); 398 | this.seed = args.map(v => [...v]); 399 | this.size = this.seed.length; 400 | const length = this.seed.reduce((a, v) => a * BigInt(v.length), 1n); 401 | this.length = length; 402 | Object.freeze(this); 403 | } 404 | at(n) { 405 | n = this._check(n); 406 | if (n === undefined) 407 | return undefined; 408 | let bn = BigInt(n); 409 | let result = []; 410 | for (let i = 0; i < this.size; i++) { 411 | const base = this.seed[i].length; 412 | const bb = BigInt(base); 413 | const bd = bn % bb; 414 | result.push(this.seed[i][Number(bd)]); 415 | bn -= bd; 416 | bn /= bb; 417 | } 418 | return result; 419 | } 420 | } 421 | exports.CartesianProduct = CartesianProduct; 422 | }); 423 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![ES2020](https://img.shields.io/badge/JavaScript-ES2020-blue.svg)](https://tc39.es/ecma262/2020/) 2 | [![MIT LiCENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 3 | [![CI via GitHub Actions](https://github.com/dankogai/js-combinatorics/actions/workflows/node.js.yml/badge.svg)](https://github.com/dankogai/js-combinatorics/actions/workflows/node.js.yml) 4 | 5 | js-combinatorics 6 | ================ 7 | 8 | Simple combinatorics in JavaScript 9 | 10 | ## HEADS UP: Version 2 and BigInt 11 | 12 | Now that [Internet Explorer has officially retired], It is safe to assume `BigInt` is available in every JavaScript environment. From version 2.0 this module goes fully BigInt. While integer arguments can still be either `number` or `bigint`, all integer values that can be `bigint` are always `bigint`, whereas previous versions may return `number` when the value <= `Number.MAX_SAFE_INTEGER`. It is not only more combinatorically natural, but also makes debugging easier especially on TypeScript. 13 | 14 | [Internet Explorer has officially retired]: https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/ 15 | [in every JavaScript environment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt 16 | 17 | ### For Swift programmers 18 | 19 | Check [swift-combinatorics]. More naturally implemented with generics and protocol. 20 | 21 | [swift-combinatorics]: https://github.com/dankogai/swift-combinatorics 22 | 23 | ## SYNOPSIS 24 | 25 | ```javascript 26 | import * as $C from './combinatorics.js'; 27 | let it = new $C.Combination('abcdefgh', 4); 28 | for (const elem of it) { 29 | console.log(elem) // ['a', 'b', 'c', 'd'] ... ['e', 'f', 'g', 'h'] 30 | } 31 | ``` 32 | 33 | ## Usage 34 | 35 | load everything… 36 | 37 | ```javascript 38 | import * as Combinatorics from './combinatorics.js'; 39 | ``` 40 | 41 | or just objects you want. 42 | 43 | ```javascript 44 | import { Combination, Permutation } from './combinatorics.js'; 45 | ``` 46 | 47 | You don't even have to install if you `import` from CDNs. 48 | 49 | ```javascript 50 | import * as $C from 'https://cdn.jsdelivr.net/npm/js-combinatorics@2.1.2/combinatorics.min.js'; 51 | ``` 52 | 53 | Since this is an ES6 module, `type="module"` is required the ` 60 | 64 | ``` 65 | 66 | ### node.js REPL 67 | 68 | ```shell 69 | % node 70 | Welcome to Node.js v16.15.0. 71 | Type ".help" for more information. 72 | > const $C = await import('js-combinatorics') 73 | undefined 74 | > $C 75 | [Module: null prototype] { 76 | BaseN: [class BaseN extends _CBase], 77 | CartesianProduct: [class CartesianProduct extends _CBase], 78 | Combination: [class Combination extends _CBase], 79 | Permutation: [class Permutation extends _CBase], 80 | PowerSet: [class PowerSet extends _CBase], 81 | combinadic: [Function: combinadic], 82 | combination: [Function: combination], 83 | factoradic: [Function: factoradic], 84 | factorial: [Function: factorial], 85 | permutation: [Function: permutation], 86 | randomInteger: [Function: randomInteger], 87 | version: '2.1.2' 88 | } 89 | > [...new $C.Permutation('abcd')] 90 | [ 91 | [ 'a', 'b', 'c', 'd' ], [ 'a', 'b', 'd', 'c' ], 92 | [ 'a', 'c', 'b', 'd' ], [ 'a', 'c', 'd', 'b' ], 93 | [ 'a', 'd', 'b', 'c' ], [ 'a', 'd', 'c', 'b' ], 94 | [ 'b', 'a', 'c', 'd' ], [ 'b', 'a', 'd', 'c' ], 95 | [ 'b', 'c', 'a', 'd' ], [ 'b', 'c', 'd', 'a' ], 96 | [ 'b', 'd', 'a', 'c' ], [ 'b', 'd', 'c', 'a' ], 97 | [ 'c', 'a', 'b', 'd' ], [ 'c', 'a', 'd', 'b' ], 98 | [ 'c', 'b', 'a', 'd' ], [ 'c', 'b', 'd', 'a' ], 99 | [ 'c', 'd', 'a', 'b' ], [ 'c', 'd', 'b', 'a' ], 100 | [ 'd', 'a', 'b', 'c' ], [ 'd', 'a', 'c', 'b' ], 101 | [ 'd', 'b', 'a', 'c' ], [ 'd', 'b', 'c', 'a' ], 102 | [ 'd', 'c', 'a', 'b' ], [ 'd', 'c', 'b', 'a' ] 103 | ] 104 | > 105 | ``` 106 | 107 | ### commonjs (node.js) 108 | 109 | `./combinatorics.js` is an ECMAScript module but if you still need a UMD or commonjs version, they are available as `./umd/combinatorics.js` and `./commonjs/combinatorics.js` respectively. 110 | 111 | ## Description 112 | 113 | ### Arithmetic Functions 114 | 115 | Self-explanatory, are they not? 116 | 117 | ```javascript 118 | import { permutation, combination, factorial, randomInteger } from './combinatorics.js'; 119 | 120 | permutation(24, 12); // 1295295050649600n 121 | permutation(26, 13); // 64764752532480000n 122 | 123 | combination(56, 28); // 7648690600760440n 124 | combination(58, 29); // 30067266499541040n 125 | 126 | factorial(18); // 6402373705728000n 127 | factorial(19); // 121645100408832000n 128 | 129 | randomInteger(6402373705727999); // random n [0,6402373705728000) 130 | randomInteger(121645100408832000n); // ramdom n [0n, 121645100408832000n) 131 | ``` 132 | 133 | The arithmetic functions above accept both `Number` and `BigInt` (if supported). Return answers always in `BigInt`. 134 | 135 | #### `factoradic()` and `combinadic()` 136 | 137 | They need a little more explanation. 138 | 139 | ```javascript 140 | import { factoradic, combinadic } from './combinatorics.js'; 141 | 142 | factoradic(6402373705727999); // [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] 143 | factoradic(121645100408831999n); // [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18] 144 | 145 | const c16_8 = combinadic(16, 8); 146 | c16_8(0); // [ 0, 1, 2, 3, 4, 5, 6, 7] 147 | c16_8(12870); // [ 8, 9, 10, 11, 12, 13, 14, 15] 148 | const c58_29 = combinadic(58, 29); 149 | c58_29(0); /* [ 150 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 151 | 9, 10, 11, 12, 13, 14, 15, 16, 17, 152 | 18, 19, 20, 21, 22, 23, 24, 25, 26, 153 | 27, 28 154 | ] */ 155 | c58_29(30067266499541039n); /* [ 156 | 29, 30, 31, 32, 33, 34, 35, 36, 37, 157 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 158 | 47, 48, 49, 50, 51, 52, 53, 54, 55, 159 | 56, 57 160 | ] */ 161 | ``` 162 | 163 | `factoradic(n)` returns the [factoradic] representation of `n`. For an array `ary` with `n` elements, you can get its `n`th permutation by picking `ary[i]` for each `i` in the factoradic. 164 | 165 | [factoradic]: https://en.wikipedia.org/wiki/Factorial_number_system 166 | 167 | Unlike other arithmetic functions, `combinadic()` returns a function which returns `m`th [combinadic] digit of `n C k`. For an array `ary` with `n` elements, you can get its `m`th combination by picking `ary[i]` for each `i` in the combinadic. 168 | 169 | [combinadic]: https://en.wikipedia.org/wiki/Combinatorial_number_system 170 | 171 | ### classes 172 | 173 | The module comes with `Permutation`, `Combination`, `PowerSet`, `BaseN`, and `CartesianProduct`. You can individually `import` them or all of them via `import *` 174 | 175 | ```javascript 176 | import * as $C from 'combinatorics.js'; 177 | ``` 178 | 179 | You construct an iterable object by giving a seed iterable and options. in the example below, `'abcdefgh'` is the seed and `4` is the size of the element. 180 | 181 | ```javascript 182 | let it = new $C.Combination('abcdefgh', 4); 183 | ``` 184 | 185 | if you hate `new`, you can use `Klass.of` where `Klass` is one of the classes this module offers. 186 | 187 | ```javascript 188 | let it = $C.Combination.of('abcdefgh', 4); 189 | ``` 190 | 191 | Once constructed, you can iterate via `for … of` statement or turn it into an array via `[...]` construct. 192 | 193 | ```javascript 194 | [...it]; /* [ 195 | [ 'a', 'b', 'c', 'd' ], [ 'a', 'b', 'c', 'e' ], [ 'a', 'b', 'c', 'f' ], 196 | [ 'a', 'b', 'c', 'g' ], [ 'a', 'b', 'c', 'h' ], [ 'a', 'b', 'd', 'e' ], 197 | [ 'a', 'b', 'd', 'f' ], [ 'a', 'b', 'd', 'g' ], [ 'a', 'b', 'd', 'h' ], 198 | [ 'a', 'b', 'e', 'f' ], [ 'a', 'b', 'e', 'g' ], [ 'a', 'b', 'e', 'h' ], 199 | [ 'a', 'b', 'f', 'g' ], [ 'a', 'b', 'f', 'h' ], [ 'a', 'b', 'g', 'h' ], 200 | [ 'a', 'c', 'd', 'e' ], [ 'a', 'c', 'd', 'f' ], [ 'a', 'c', 'd', 'g' ], 201 | [ 'a', 'c', 'd', 'h' ], [ 'a', 'c', 'e', 'f' ], [ 'a', 'c', 'e', 'g' ], 202 | [ 'a', 'c', 'e', 'h' ], [ 'a', 'c', 'f', 'g' ], [ 'a', 'c', 'f', 'h' ], 203 | [ 'a', 'c', 'g', 'h' ], [ 'a', 'd', 'e', 'f' ], [ 'a', 'd', 'e', 'g' ], 204 | [ 'a', 'd', 'e', 'h' ], [ 'a', 'd', 'f', 'g' ], [ 'a', 'd', 'f', 'h' ], 205 | [ 'a', 'd', 'g', 'h' ], [ 'a', 'e', 'f', 'g' ], [ 'a', 'e', 'f', 'h' ], 206 | [ 'a', 'e', 'g', 'h' ], [ 'a', 'f', 'g', 'h' ], [ 'b', 'c', 'd', 'e' ], 207 | [ 'b', 'c', 'd', 'f' ], [ 'b', 'c', 'd', 'g' ], [ 'b', 'c', 'd', 'h' ], 208 | [ 'b', 'c', 'e', 'f' ], [ 'b', 'c', 'e', 'g' ], [ 'b', 'c', 'e', 'h' ], 209 | [ 'b', 'c', 'f', 'g' ], [ 'b', 'c', 'f', 'h' ], [ 'b', 'c', 'g', 'h' ], 210 | [ 'b', 'd', 'e', 'f' ], [ 'b', 'd', 'e', 'g' ], [ 'b', 'd', 'e', 'h' ], 211 | [ 'b', 'd', 'f', 'g' ], [ 'b', 'd', 'f', 'h' ], [ 'b', 'd', 'g', 'h' ], 212 | [ 'b', 'e', 'f', 'g' ], [ 'b', 'e', 'f', 'h' ], [ 'b', 'e', 'g', 'h' ], 213 | [ 'b', 'f', 'g', 'h' ], [ 'c', 'd', 'e', 'f' ], [ 'c', 'd', 'e', 'g' ], 214 | [ 'c', 'd', 'e', 'h' ], [ 'c', 'd', 'f', 'g' ], [ 'c', 'd', 'f', 'h' ], 215 | [ 'c', 'd', 'g', 'h' ], [ 'c', 'e', 'f', 'g' ], [ 'c', 'e', 'f', 'h' ], 216 | [ 'c', 'e', 'g', 'h' ], [ 'c', 'f', 'g', 'h' ], [ 'd', 'e', 'f', 'g' ], 217 | [ 'd', 'e', 'f', 'h' ], [ 'd', 'e', 'g', 'h' ], [ 'd', 'f', 'g', 'h' ], 218 | [ 'e', 'f', 'g', 'h' ] 219 | ] */ 220 | ``` 221 | 222 | #### `.length` 223 | 224 | The object has `.length` so you don't have to iterate to count the elements. Note the value is in `bigint` so you may need to convert to `number`. 225 | 226 | ```javascript 227 | it.length // 70n 228 | [...it].length // 70 229 | it.length == [...it].length // true because comparisons work between number and bigint 230 | it.length === [...it].length // false because types are different 231 | ``` 232 | 233 | #### `.at()` (or `.nth()`) 234 | 235 | And the object has `.at(n)` method so you can random-access each element. This is the equivalent of subscript in `Array`. It was previously named `.nth()` but it was renamed to `.at()` ala `Array.prototype.at()` in ES2020. `.nth()` still available for backward compatibility. 236 | 237 | ```javascript 238 | it.at(0); // [ 'a', 'b', 'c', 'd' ]; 239 | it.at(69); // [ 'a', 'd', 'c', 'h' ]; 240 | ``` 241 | 242 | `at()` accepts both `Number` and `BigInt`. 243 | 244 | ```javascript 245 | it.at(69n); // [ 'a', 'd', 'c', 'h' ]; 246 | ``` 247 | 248 | `at()` also accepts negative indexes. In which case `n` is `(-n)th` element from `.length`. 249 | 250 | ```javascript 251 | it.at(-1); // [ 'a', 'd', 'c', 'h' ] 252 | it.at(-70); // [ 'a', 'b', 'c', 'd' ] 253 | ``` 254 | 255 | #### `.sample()` 256 | 257 | And `.sample()` picks random element, which is defined as `.at(randomInteger(.length))`. 258 | 259 | ```javascript 260 | it.sample() // one of ['a', 'b', 'c', 'd'] ... ['a', 'd', 'e', 'f'] 261 | ``` 262 | 263 | ### Beyond `Number.MAX_SAFE_INTEGER` 264 | 265 | Occasionally you need `BigInt` to access elements beyond `Number.MAX_SAFE_INTEGER`. 266 | 267 | ```javascript 268 | it = new $C.Permutation('abcdefghijklmnopqrstuvwxyz'); 269 | it.length; // 403291461126605635584000000n 270 | ``` 271 | 272 | You can still access elements before `Number.MAX_SAFE_INTEGER` in `Number`. 273 | 274 | ```javascript 275 | it.at(0); /* [ 276 | 'a', 'b', 'c', 'd', 'e', 'f', 277 | 'g', 'h', 'i', 'j', 'k', 'l', 278 | 'm', 'n', 'o', 'p', 'q', 'r', 279 | 's', 't', 'u', 'v', 'w', 'x', 280 | 'y', 'z' 281 | ] */ 282 | it.at(9007199254740990); /* [ 283 | 'a', 'b', 'c', 'd', 'e', 'f', 284 | 'g', 'i', 'p', 'n', 'r', 'z', 285 | 'm', 'h', 'y', 'x', 'u', 't', 286 | 'l', 'j', 'k', 'q', 's', 'o', 287 | 'v', 'w' 288 | ] */ 289 | ``` 290 | 291 | But how are you goint to acccess elements beyond that? Just use `BigInt`. 292 | 293 | ```javascript 294 | it.at(9007199254740991n); /* [ 295 | 'a', 'b', 'c', 'd', 'e', 'f', 296 | 'g', 'i', 'p', 'n', 'r', 'z', 297 | 'm', 'h', 'y', 'x', 'u', 't', 298 | 'l', 'j', 'k', 'q', 's', 'o', 299 | 'w', 'v' 300 | ] */ 301 | it.at(it.length - 1n); /* [ 302 | 'z', 'y', 'x', 'w', 'v', 'u', 303 | 't', 's', 'r', 'q', 'p', 'o', 304 | 'n', 'm', 'l', 'k', 'j', 'i', 305 | 'h', 'g', 'f', 'e', 'd', 'c', 306 | 'b', 'a' 307 | ] */ 308 | ``` 309 | 310 | You can tell if you need `BigInt` via `.isBig`. Note `.length` is always `bigint` from version 2.0 so you may not need this method any more. So it is now deprecated. 311 | 312 | ```javascript 313 | new $C.Permutation('0123456789').isBig; // false 314 | new $C.Permutation('abcdefghijklmnopqrstuvwxyz').isBig; // true 315 | ``` 316 | 317 | You can also check if it is safe on your platform via `.isSafe`. It is now deprecated for the same reason as `.isBig`. 318 | 319 | ```javascript 320 | new $C.Permutation('abcdefghijklmnopqrstuvwxyz').isSafe; // always true 321 | ``` 322 | 323 | ### class `Permutation` 324 | 325 | An iterable which permutes a given iterable. 326 | 327 | `new Permutation(seed, size)` 328 | 329 | * `seed`: the seed iterable. `[...seed]` becomes the seed array. 330 | * `size`: the number of elements in the iterated element. defaults to `seed.length` 331 | 332 | ````javascript 333 | import {Permutation} from './combinatorics.js'; 334 | 335 | let it = new Permutation('abcd'); // size 4 is assumed 336 | it.length; // 24n 337 | [...it]; /* [ 338 | [ 'a', 'b', 'c', 'd' ], [ 'a', 'b', 'd', 'c' ], 339 | [ 'a', 'c', 'b', 'd' ], [ 'a', 'c', 'd', 'b' ], 340 | [ 'a', 'd', 'b', 'c' ], [ 'a', 'd', 'c', 'b' ], 341 | [ 'b', 'a', 'c', 'd' ], [ 'b', 'a', 'd', 'c' ], 342 | [ 'b', 'c', 'a', 'd' ], [ 'b', 'c', 'd', 'a' ], 343 | [ 'b', 'd', 'a', 'c' ], [ 'b', 'd', 'c', 'a' ], 344 | [ 'c', 'a', 'b', 'd' ], [ 'c', 'a', 'd', 'b' ], 345 | [ 'c', 'b', 'a', 'd' ], [ 'c', 'b', 'd', 'a' ], 346 | [ 'c', 'd', 'a', 'b' ], [ 'c', 'd', 'b', 'a' ], 347 | [ 'd', 'a', 'b', 'c' ], [ 'd', 'a', 'c', 'b' ], 348 | [ 'd', 'b', 'a', 'c' ], [ 'd', 'b', 'c', 'a' ], 349 | [ 'd', 'c', 'a', 'b' ], [ 'd', 'c', 'b', 'a' ] 350 | ] */ 351 | 352 | it = new Permutation('abcdefghijklmnopqrstuvwxyz0123456789'); 353 | it.length; // 371993326789901217467999448150835200000000n 354 | it.at(371993326789901217467999448150835199999999n); /* [ 355 | '9', '8', '7', '6', '5', '4', '3', 356 | '2', '1', '0', 'z', 'y', 'x', 'w', 357 | 'v', 'u', 't', 's', 'r', 'q', 'p', 358 | 'o', 'n', 'm', 'l', 'k', 'j', 'i', 359 | 'h', 'g', 'f', 'e', 'd', 'c', 'b', 360 | 'a' 361 | ] */ 362 | ```` 363 | 364 | Making a permutation of the iterable then taking its sample is functionally the same as [Fisher–Yates shuffle] of the iterable. Instead of shuffling the deck, it make all possible cases available and let you pick one. 365 | 366 | ```javascript 367 | it.sample(); // something between ['a','b', ... '9'] and ['9','8',....'a'] 368 | ``` 369 | 370 | It is in fact a little better because `.sample()` only needs one random number (between 0 and `.length - 1`) while Fisher–Yates needs `n` random numbers. 371 | 372 | [Fisher–Yates shuffle]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle 373 | 374 | ### class `Combination` 375 | 376 | An iterable which emits a combination of a given iterable. 377 | 378 | `new Combination(seed, size)` 379 | 380 | * `seed`: the seed iterable. 381 | * `size`: the number of elements in the iterated element. 382 | 383 | ````javascript 384 | import {Combination} from './combinatorics.js'; 385 | 386 | let it = new Combination('abcd', 2); 387 | it.length; // 6n 388 | [...it]; /* [ 389 | [ 'a', 'b' ], 390 | [ 'a', 'c' ], 391 | [ 'a', 'd' ], 392 | [ 'b', 'c' ], 393 | [ 'b', 'd' ], 394 | [ 'c', 'd' ] 395 | ] */ 396 | 397 | let a100 = Array(100).fill(0).map((v,i)=>i); // [0, 1, ...99] 398 | it = new Combination(a100, 50); 399 | it.length; // 100891344545564193334812497256n 400 | it.at(100891344545564193334812497255n); /* [ 401 | 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 402 | 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 403 | 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 404 | 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 405 | 94, 95, 96, 97, 98, 99 406 | ] */ 407 | ```` 408 | 409 | ### class `PowerSet` 410 | 411 | An iterable which emits each element of its power set. 412 | 413 | `new PowerSet(seed)` 414 | 415 | * `seed`: the seed iterable. 416 | 417 | ````javascript 418 | import {PowerSet} from './combinatorics.js'; 419 | 420 | let it = new PowerSet('abc'); 421 | it.length; // 8n 422 | [...it]; /* [ 423 | [], 424 | [ 'a' ], 425 | [ 'b' ], 426 | [ 'a', 'b' ], 427 | [ 'c' ], 428 | [ 'a', 'c' ], 429 | [ 'b', 'c' ], 430 | [ 'a', 'b', 'c' ] 431 | ] */ 432 | 433 | it = new PowerSet( 434 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 435 | ); 436 | it.length; // 18446744073709551616n 437 | it.at(18446744073709551615n); /* [ 438 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 439 | 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 440 | 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 441 | 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 442 | 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 443 | 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', 444 | '2', '3', '4', '5', '6', '7', '8', '9', '+', 445 | '/' 446 | ] */ 447 | ```` 448 | 449 | ### class `BaseN` 450 | 451 | An iterable which emits all numbers in the given system. 452 | 453 | `new BaseN(seed, size)` 454 | 455 | * `seed`: the seed iterable whose elements represent digits. 456 | * `size`: the number of digits 457 | 458 | ```javascript 459 | import {BaseN} from './combinatorics.js'; 460 | 461 | let it = new BaseN('abc', 3); 462 | it.length; // 27n 463 | [...it]; /* [ 464 | [ 'a', 'a', 'a' ], [ 'b', 'a', 'a' ], 465 | [ 'c', 'a', 'a' ], [ 'a', 'b', 'a' ], 466 | [ 'b', 'b', 'a' ], [ 'c', 'b', 'a' ], 467 | [ 'a', 'c', 'a' ], [ 'b', 'c', 'a' ], 468 | [ 'c', 'c', 'a' ], [ 'a', 'a', 'b' ], 469 | [ 'b', 'a', 'b' ], [ 'c', 'a', 'b' ], 470 | [ 'a', 'b', 'b' ], [ 'b', 'b', 'b' ], 471 | [ 'c', 'b', 'b' ], [ 'a', 'c', 'b' ], 472 | [ 'b', 'c', 'b' ], [ 'c', 'c', 'b' ], 473 | [ 'a', 'a', 'c' ], [ 'b', 'a', 'c' ], 474 | [ 'c', 'a', 'c' ], [ 'a', 'b', 'c' ], 475 | [ 'b', 'b', 'c' ], [ 'c', 'b', 'c' ], 476 | [ 'a', 'c', 'c' ], [ 'b', 'c', 'c' ], 477 | [ 'c', 'c', 'c' ] 478 | ] */ 479 | 480 | it = BaseN('0123456789abcdef', 16); 481 | it.length; // 18446744073709551616n 482 | it.at(18446744073709551615n); /* [ 483 | 'f', 'f', 'f', 'f', 484 | 'f', 'f', 'f', 'f', 485 | 'f', 'f', 'f', 'f', 486 | 'f', 'f', 'f', 'f' 487 | ] */ 488 | ``` 489 | 490 | ### class `CartesianProduct` 491 | 492 | A [cartesian product] of given sets. 493 | 494 | [cartesian Product]: https://en.wikipedia.org/wiki/Cartesian_product 495 | 496 | `new CartesianProduct(...args)` 497 | 498 | * `args`: iterables that represent sets 499 | 500 | ```javascript 501 | import {CartesianProduct} from './combinatorics.js'; 502 | 503 | let it = new CartesianProduct('012','abc','xyz'); 504 | it.length; // 27n 505 | [...it]; /* [ 506 | [ '0', 'a', 'x' ], [ '1', 'a', 'x' ], 507 | [ '2', 'a', 'x' ], [ '0', 'b', 'x' ], 508 | [ '1', 'b', 'x' ], [ '2', 'b', 'x' ], 509 | [ '0', 'c', 'x' ], [ '1', 'c', 'x' ], 510 | [ '2', 'c', 'x' ], [ '0', 'a', 'y' ], 511 | [ '1', 'a', 'y' ], [ '2', 'a', 'y' ], 512 | [ '0', 'b', 'y' ], [ '1', 'b', 'y' ], 513 | [ '2', 'b', 'y' ], [ '0', 'c', 'y' ], 514 | [ '1', 'c', 'y' ], [ '2', 'c', 'y' ], 515 | [ '0', 'a', 'z' ], [ '1', 'a', 'z' ], 516 | [ '2', 'a', 'z' ], [ '0', 'b', 'z' ], 517 | [ '1', 'b', 'z' ], [ '2', 'b', 'z' ], 518 | [ '0', 'c', 'z' ], [ '1', 'c', 'z' ], 519 | [ '2', 'c', 'z' ] 520 | ] */ 521 | ``` 522 | 523 | Since the number of arguments to `CartesianProduct` is variable, it is sometimes helpful to give a single array with all arguments. But you cannot `new ctor.apply(null, args)` this case. To mitigate that, you can use `.from()`. 524 | 525 | ```javascript 526 | let a16 = Array(16).fill('0123456789abcdef'); 527 | it = CartesianProduct.from(a16); 528 | it.length; // 18446744073709551616n 529 | it.at(18446744073709551615n); /* [ 530 | 'f', 'f', 'f', 'f', 531 | 'f', 'f', 'f', 'f', 532 | 'f', 'f', 'f', 'f', 533 | 'f', 'f', 'f', 'f' 534 | ] */ 535 | ```` 536 | 537 | ## What's new from version 0.x? 538 | 539 | `js-combinatorics` has gone ES2015 since version 1. 540 | 541 | * native iterator instead of custom 542 | * module. `import` instead of `require`. 543 | * `BigInt` where possible 544 | 545 | And from version 1.2 it is written in TypeScript. `combinatorics.js` and `combinatorics.d.ts` are compiled from `combinatorics.ts`. 546 | 547 | APIs will change accordingly. Old versions are available in the `version0` branch. 548 | 549 | ### What's gone from version 0.x? 550 | 551 | * `bigCombination` is gone because all classes now can handle big -- combinatorially big! -- cases thanks to [BigInt] support getting standard. Safari 13 and below is a major exception but BigInt is coming to Safari 14 and up. 552 | 553 | [BigInt]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt 554 | 555 | * `permutationCombination` is gone because the name is misleading and it is now trivially easy to reconstruct as follow: 556 | 557 | ```javascript 558 | class permutationCombination { 559 | constructor(seed) { 560 | this.seed = [...seed]; 561 | } 562 | [Symbol.iterator]() { 563 | return function*(it){ 564 | for (let i = 1, l = it.length; i <= l; i++) { 565 | yield* new Permutation(it, i); 566 | } 567 | }(this.seed); 568 | } 569 | } 570 | ``` 571 | 572 | * `js-combinatorics` is now natively iterable. Meaning its custom iterators are gone -- with its methods like `.map` and `.filter`. JS iterators are very minimalistic with only `[...]` and `for ... of`. But don't worry. There are several ways to make those functional methods back again. 573 | 574 | For instance, You can use [js-xiterable] like so: 575 | 576 | [js-xiterable]: https://github.com/dankogai/js-xiterable 577 | 578 | ```javascript 579 | import {xiterable as $X} from 580 | 'https://cdn.jsdelivr.net/npm/js-xiterable@0.0.3/xiterable.min.js'; 581 | import {Permutation} from 'combinatorics.js'; 582 | let it = new Permutation('abcd'); 583 | let words = $X(it).map(v=>v.join('')) 584 | for (const word of words)) console.log(word) 585 | /* 586 | abcd 587 | abdc 588 | ... 589 | dcab 590 | dcba 591 | */ 592 | ``` 593 | --------------------------------------------------------------------------------