├── 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 | [](https://tc39.es/ecma262/2020/)
2 | [](LICENSE)
3 | [](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 |
--------------------------------------------------------------------------------