diff --git a/src/bigint/all.mjs b/src/bigint/all.mjs index f9da9f0..6f1412d 100644 --- a/src/bigint/all.mjs +++ b/src/bigint/all.mjs @@ -1,6 +1,6 @@ import PocomathInstance from '../core/PocomathInstance.mjs' import * as bigints from './native.mjs' import * as generic from '../generic/all.mjs' -import * as floor from '../ops/floor.mjs' +import * as ops from '../ops/all.mjs' -export default PocomathInstance.merge('bigint', bigints, generic, floor) +export default PocomathInstance.merge('bigint', bigints, generic, ops) diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index c1cee4a..6fab6c9 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -66,6 +66,7 @@ export default class PocomathInstance { return true // successful } }) + this._plainFunctions = new Set() // the names of the plain functions this._chainRepository = {} // place to store chainified functions } @@ -136,6 +137,8 @@ export default class PocomathInstance { for (const [item, spec] of Object.entries(ops)) { if (spec instanceof PocomathInstance) { this._installInstance(spec) + } else if (typeof spec === 'function') { + stdFunctions[item] = spec } else { if (item.charAt(0) === '_') { throw new SyntaxError( @@ -183,6 +186,9 @@ export default class PocomathInstance { migrateImps[operator] = other._imps[operator] } } + for (const plain of other._plainFunctions) { + migrateImps[plain] = other[plain] + } this._installFunctions(migrateImps) } @@ -352,7 +358,21 @@ export default class PocomathInstance { /* Used internally by install, see the documentation there */ _installFunctions(functions) { for (const [name, spec] of Object.entries(functions)) { - // new implementations, so set the op up to lazily recreate itself + if (typeof spec === 'function') { + if (name in this) { + if (spec === this[name]) continue + throw new SyntaxError(`Attempt to redefine function ${name}`) + } + this._plainFunctions.add(name) + this[name] = spec + continue + } + // new implementations, first check the name isn't taken + if (this._plainFunctions.has(name)) { + throw new SyntaxError( + `Can't add implementations to function ${name}`) + } + // All clear, so set the op up to lazily recreate itself this._invalidate(name) const opImps = this._imps[name] for (const [signature, behavior] of Object.entries(spec)) { diff --git a/src/number/all.mjs b/src/number/all.mjs index bedaa24..622484a 100644 --- a/src/number/all.mjs +++ b/src/number/all.mjs @@ -1,7 +1,7 @@ import PocomathInstance from '../core/PocomathInstance.mjs' import * as numbers from './native.mjs' import * as generic from '../generic/all.mjs' -import * as floor from '../ops/floor.mjs' +import * as ops from '../ops/all.mjs' -export default PocomathInstance.merge('number', numbers, generic, floor) +export default PocomathInstance.merge('number', numbers, generic, ops) diff --git a/src/ops/all.mjs b/src/ops/all.mjs new file mode 100644 index 0000000..3d1646b --- /dev/null +++ b/src/ops/all.mjs @@ -0,0 +1,4 @@ +export * from './choose.mjs' +export * from './factorial.mjs' +export * from './floor.mjs' + diff --git a/src/ops/choose.mjs b/src/ops/choose.mjs new file mode 100644 index 0000000..c285dc7 --- /dev/null +++ b/src/ops/choose.mjs @@ -0,0 +1,11 @@ +/* Note this is not a good algorithm for computing binomial coefficients, + * it's just for demonstration purposes + */ +export const choose = { + 'NumInt,NumInt': ({factorial}) => (n,k) => Number( + factorial(n) / (factorial(k)*factorial(n-k))), + 'bigint,bigint': ({ + factorial + }) => (n,k) => factorial(n) / (factorial(k)*factorial(n-k)) +} + diff --git a/src/ops/factorial.mjs b/src/ops/factorial.mjs new file mode 100644 index 0000000..bb07047 --- /dev/null +++ b/src/ops/factorial.mjs @@ -0,0 +1,8 @@ +export function factorial(n) { + n = BigInt(n) + let prod = 1n + for (let i = n; i > 1n; --i) { + prod *= i + } + return prod +} diff --git a/src/pocomath.mjs b/src/pocomath.mjs index cb9bf94..dee980e 100644 --- a/src/pocomath.mjs +++ b/src/pocomath.mjs @@ -4,9 +4,9 @@ import * as numbers from './number/native.mjs' import * as bigints from './bigint/native.mjs' import * as complex from './complex/native.mjs' import * as generic from './generic/all.mjs' -import * as floor from './ops/floor.mjs' +import * as ops from './ops/all.mjs' const math = PocomathInstance.merge( - 'math', numbers, bigints, complex, generic, floor) + 'math', numbers, bigints, complex, generic, ops) export default math diff --git a/test/_pocomath.mjs b/test/_pocomath.mjs index 222fbd6..18643b0 100644 --- a/test/_pocomath.mjs +++ b/test/_pocomath.mjs @@ -93,4 +93,14 @@ describe('The default full pocomath instance "math"', () => { assert.throws(() => math.chain(3).foo(), /Unknown operation/) }) + it('calls plain factorial function', () => { + assert.strictEqual(math.factorial(4), 24n) + assert.strictEqual(math.factorial(7n), 5040n) + }) + + it('calculates binomial coefficients', () => { + assert.strictEqual(math.choose(6, 3), 20) + assert.strictEqual(math.choose(21n, 2n), 210n) + }) + })