Skip to content

Commit b0014e2

Browse files
🚧 progress: First draft.
1 parent 6edef27 commit b0014e2

12 files changed

+9764
-8
lines changed

doc/manual/example.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
# Examples
22

3+
```js
4+
import { factorial } from '@combinatorics/factorial' ;
5+
factorial(18) ; // 6402373705728000
6+
```
7+
38
> More examples in [the test files](https://github.com/computational-combinatorics/factorial/tree/main/test/src).

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,15 @@
5959
"release": "np --message ':hatching_chick: release: Bumping to v%s.'",
6060
"test": "ava"
6161
},
62-
"dependencies": {},
62+
"dependencies": {
63+
"@failure-abstraction/error": "^6.0.1"
64+
},
6365
"devDependencies": {
6466
"@babel/core": "7.14.3",
6567
"@babel/preset-env": "7.14.2",
6668
"@babel/register": "7.13.16",
6769
"@commitlint/cli": "12.1.4",
70+
"@iterable-iterator/zip": "^1.0.1",
6871
"@js-library/commitlint-config": "0.0.4",
6972
"ava": "3.15.0",
7073
"babel-plugin-transform-remove-console": "6.9.4",

src/_factorialn.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import assert from 'assert';
2+
3+
/**
4+
* Returns the factorial of the input bigint _n!_ = <code>1 * 2 * ... * n</code>.
5+
*
6+
* @param {bigint} n The input bigint.
7+
* @returns {bigint} The factorial of the input bigint.
8+
*/
9+
const _factorialn = (n) => {
10+
assert(typeof n === 'bigint');
11+
assert(n >= 0n);
12+
let f = 1n;
13+
14+
while (n !== 0n) f *= n--;
15+
16+
return f;
17+
};
18+
19+
export default _factorialn;

src/factorial.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {TypeError, ValueError} from '@failure-abstraction/error';
2+
import lookup from './lookup.js';
3+
4+
const N = lookup.length;
5+
6+
/**
7+
* Returns the factorial of the input number _n!_ = <code>1 * 2 * ... * n</code>.
8+
*
9+
* @experimental Uses doubles, will return incorrect values for large input.
10+
*
11+
* @param {number} n The input number.
12+
* @returns {number} The factorial of the input number.
13+
*/
14+
const factorial = (n) => {
15+
// eslint-disable-next-line unicorn/error-message
16+
if (typeof n !== 'number') throw new TypeError();
17+
if (!Number.isInteger(n)) throw new ValueError();
18+
if (n < 0) throw new ValueError();
19+
if (n >= N) throw new ValueError();
20+
21+
return lookup[n];
22+
};
23+
24+
export default factorial;

src/factorialn.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {TypeError, ValueError} from '@failure-abstraction/error';
2+
import _factorialn from './_factorialn.js';
3+
4+
/**
5+
* Factorialn.
6+
*
7+
* @param {bigint} n
8+
*/
9+
const factorialn = (n) => {
10+
// eslint-disable-next-line unicorn/error-message
11+
if (typeof n !== 'bigint') throw new TypeError();
12+
if (n < 0n) throw new ValueError();
13+
14+
return _factorialn(n);
15+
};
16+
17+
export default factorialn;

src/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
const answer = 42;
2-
export default answer;
1+
export {default as _factorialn} from './_factorialn.js';
2+
export {default as factorial} from './factorial.js';
3+
export {default as factorialn} from './factorialn.js';
4+
export {default as lookup} from './lookup.js';

src/lookup.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const lookup = Float64Array.from([
2+
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600,
3+
6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000,
4+
6402373705728000, 121645100408832000, 2432902008176640000,
5+
51090942171709440000, 1124000727777607680000,
6+
]);
7+
8+
export default lookup;

test/src/_fixtures.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const sequence = [
2+
1n,
3+
1n,
4+
2n,
5+
6n,
6+
24n,
7+
120n,
8+
720n,
9+
5040n,
10+
40320n,
11+
362880n,
12+
3628800n,
13+
39916800n,
14+
479001600n,
15+
6227020800n,
16+
87178291200n,
17+
1307674368000n,
18+
20922789888000n,
19+
355687428096000n,
20+
6402373705728000n,
21+
121645100408832000n,
22+
2432902008176640000n,
23+
51090942171709440000n,
24+
1124000727777607680000n,
25+
];

test/src/api.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/src/factorial.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import test from 'ava';
2+
import {TypeError, ValueError} from '@failure-abstraction/error';
3+
import {enumerate} from '@iterable-iterator/zip';
4+
5+
import {sequence} from './_fixtures.js';
6+
7+
import {factorial, lookup} from '../../src.js';
8+
9+
// eslint-disable-next-line eqeqeq
10+
const macro = (t, n, f) => t.true(factorial(n) == f);
11+
12+
macro.title = (title, n, f) => title ?? `${n}! = ${f}`;
13+
14+
for (const [n, f] of enumerate(sequence)) test(macro, n, f);
15+
16+
const throws = (t, n, instanceOf) => t.throws(() => factorial(n), {instanceOf});
17+
18+
throws.title = (title, n, instanceOf) =>
19+
title ?? `${n}! throws ${instanceOf.name}`;
20+
21+
test(throws, '', TypeError);
22+
test(throws, 7n, TypeError);
23+
test(throws, 2.3, ValueError);
24+
test(throws, -1, ValueError);
25+
test(throws, lookup.length, ValueError);

test/src/factorialn.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import test from 'ava';
2+
import {TypeError, ValueError} from '@failure-abstraction/error';
3+
import {enumerate} from '@iterable-iterator/zip';
4+
5+
import {sequence} from './_fixtures.js';
6+
7+
import {factorialn as factorial} from '../../src.js';
8+
9+
const macro = (t, n, f) => t.is(factorial(n), f);
10+
11+
macro.title = (title, n, f) => title ?? `${n}! = ${f}`;
12+
13+
for (const [n, f] of enumerate(sequence)) test(macro, BigInt(n), f);
14+
15+
test(
16+
macro,
17+
1000n,
18+
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n,
19+
);
20+
21+
const throws = (t, n, instanceOf) => t.throws(() => factorial(n), {instanceOf});
22+
23+
throws.title = (title, n, instanceOf) =>
24+
title ?? `${n}! throws ${instanceOf.name}`;
25+
26+
test(throws, '', TypeError);
27+
test(throws, 7, TypeError);
28+
test(throws, 2.3, TypeError);
29+
test(throws, -1n, ValueError);

0 commit comments

Comments
 (0)