Merge pull request 'feat(install): Allow plain functions' (#44) from install_plain into main

Reviewed-on: #44
This commit is contained in:
Glen Whitney 2022-08-02 07:50:14 +00:00
commit fd32ee1f10
8 changed files with 60 additions and 7 deletions

View File

@ -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)

View File

@ -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)) {

View File

@ -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)

4
src/ops/all.mjs Normal file
View File

@ -0,0 +1,4 @@
export * from './choose.mjs'
export * from './factorial.mjs'
export * from './floor.mjs'

11
src/ops/choose.mjs Normal file
View File

@ -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))
}

8
src/ops/factorial.mjs Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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)
})
})