Merge pull request 'refactor(Complex): Now a template type!' (#46) from template_complex into main
Reviewed-on: #46
This commit is contained in:
commit
40619b9a2e
@ -2,14 +2,18 @@ export * from './Types/bigint.mjs'
|
|||||||
import isqrt from 'bigint-isqrt'
|
import isqrt from 'bigint-isqrt'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
bigint: ({config, complex, 'self(Complex)': complexSqrt}) => {
|
bigint: ({
|
||||||
|
config,
|
||||||
|
'complex(bigint,bigint)': cplx,
|
||||||
|
'negate(bigint)': neg
|
||||||
|
}) => {
|
||||||
if (config.predictable) {
|
if (config.predictable) {
|
||||||
// Don't just return the constant isqrt here because the object
|
// Don't just return the constant isqrt here because the object
|
||||||
// gets decorated with info that might need to be different
|
// gets decorated with info that might need to be different
|
||||||
// for different PocomathInstancss
|
// for different PocomathInstancss
|
||||||
return b => isqrt(b)
|
return b => isqrt(b)
|
||||||
}
|
}
|
||||||
if (!complexSqrt) {
|
if (!cplx) {
|
||||||
return b => {
|
return b => {
|
||||||
if (b >= 0n) {
|
if (b >= 0n) {
|
||||||
const trial = isqrt(b)
|
const trial = isqrt(b)
|
||||||
@ -19,12 +23,16 @@ export const sqrt = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b => {
|
return b => {
|
||||||
if (b >= 0n) {
|
if (b === undefined) return undefined
|
||||||
|
let real = true
|
||||||
|
if (b < 0n) {
|
||||||
|
b = neg(b)
|
||||||
|
real = false
|
||||||
|
}
|
||||||
const trial = isqrt(b)
|
const trial = isqrt(b)
|
||||||
if (trial * trial === b) return trial
|
if (trial * trial !== b) return undefined
|
||||||
return undefined
|
if (real) return trial
|
||||||
}
|
return cplx(0n, trial)
|
||||||
return complexSqrt(complex(b))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,27 @@
|
|||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
|
||||||
/* Use a plain object with keys re and im for a complex; note the components
|
|
||||||
* can be any type (for this proof-of-concept; in reality we'd want to
|
|
||||||
* insist on some numeric or scalar supertype).
|
|
||||||
*/
|
|
||||||
function isComplex(z) {
|
|
||||||
return z && typeof z === 'object' && 're' in z && 'im' in z
|
|
||||||
}
|
|
||||||
|
|
||||||
const Complex = new PocomathInstance('Complex')
|
const Complex = new PocomathInstance('Complex')
|
||||||
|
// Base type that should generally not be used directly
|
||||||
Complex.installType('Complex', {
|
Complex.installType('Complex', {
|
||||||
test: isComplex,
|
test: z => z && typeof z === 'object' && 're' in z && 'im' in z
|
||||||
from: {
|
|
||||||
number: x => ({re: x, im: 0})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
Complex.installType('GaussianInteger', {
|
// Now the template type: Complex numbers are actually always homeogeneous
|
||||||
test: z => typeof z.re == 'bigint' && typeof z.im == 'bigint',
|
// in their component types.
|
||||||
refines: 'Complex',
|
Complex.installType('Complex<T>', {
|
||||||
|
infer: ({typeOf, joinTypes}) => z => joinTypes([typeOf(z.re), typeOf(z.im)]),
|
||||||
|
test: testT => z => testT(z.re) && testT(z.im),
|
||||||
from: {
|
from: {
|
||||||
bigint: x => ({re: x, im: 0n})
|
T: t => ({re: t, im: t-t}), // hack: maybe need a way to call zero(T)
|
||||||
|
U: convert => u => {
|
||||||
|
const t = convert(u)
|
||||||
|
return ({re: t, im: t-t})
|
||||||
|
},
|
||||||
|
'Complex<U>': convert => cu => ({re: convert(cu.re), im: convert(cu.im)})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Complex.promoteUnary = {
|
Complex.promoteUnary = {
|
||||||
Complex: ({self,complex}) => z => complex(self(z.re), self(z.im))
|
'Complex<T>': ({'self(T)': me, complex}) => z => complex(me(z.re), me(z.im))
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Complex}
|
export {Complex}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const abs = {
|
export const abs = {
|
||||||
Complex: ({sqrt, 'absquare(Complex)': absq}) => z => sqrt(absq(z))
|
'Complex<T>': ({
|
||||||
|
'sqrt(T)': sqt,
|
||||||
|
'absquare(Complex<T>)': absq
|
||||||
|
}) => z => sqt(absq(z))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
Complex: ({add, square}) => z => add(square(z.re), square(z.im))
|
'Complex<T>': ({
|
||||||
|
'add(T,T)': plus,
|
||||||
|
'square(T)': sqr
|
||||||
|
}) => z => plus(sqr(z.re), sqr(z.im))
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,8 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const add = {
|
export const add = {
|
||||||
/* Relying on conversions for both complex + number and complex + bigint
|
'Complex<T>,Complex<T>': ({
|
||||||
* leads to an infinite loop when adding a number and a bigint, since they
|
'self(T,T)': me,
|
||||||
* both convert to Complex.
|
'complex(T,T)': cplx
|
||||||
*/
|
}) => (w,z) => cplx(me(w.re, z.re), me(w.im, z.im))
|
||||||
'Complex,number': ({
|
|
||||||
'self(number,number)': addNum,
|
|
||||||
'complex(number,number)': cplx
|
|
||||||
}) => (z,x) => cplx(addNum(z.re, x), z.im),
|
|
||||||
|
|
||||||
'Complex,bigint': ({
|
|
||||||
'self(bigint,bigint)': addBigInt,
|
|
||||||
'complex(bigint,bigint)': cplx
|
|
||||||
}) => (z,x) => cplx(addBigInt(z.re, x), z.im),
|
|
||||||
|
|
||||||
'Complex,Complex': ({
|
|
||||||
self,
|
|
||||||
complex
|
|
||||||
}) => (w,z) => complex(self(w.re, z.re), self(w.im, z.im))
|
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,16 @@ export * from './Types/Complex.mjs'
|
|||||||
|
|
||||||
/* Returns true if w is z multiplied by a complex unit */
|
/* Returns true if w is z multiplied by a complex unit */
|
||||||
export const associate = {
|
export const associate = {
|
||||||
'Complex,Complex': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
'multiply(Complex,Complex)': times,
|
'multiply(Complex<T>,Complex<T>)': times,
|
||||||
'equalTT(Complex,Complex)': eq,
|
'equalTT(Complex<T>,Complex<T>)': eq,
|
||||||
zero,
|
'zero(T)': zr,
|
||||||
one,
|
'one(T)': uno,
|
||||||
complex,
|
'complex(T,T)': cplx,
|
||||||
'negate(Complex)': neg
|
'negate(Complex<T>)': neg
|
||||||
}) => (w,z) => {
|
}) => (w,z) => {
|
||||||
if (eq(w,z) || eq(w,neg(z))) return true
|
if (eq(w,z) || eq(w,neg(z))) return true
|
||||||
const ti = times(z, complex(zero(z.re), one(z.im)))
|
const ti = times(z, cplx(zr(z.re), uno(z.im)))
|
||||||
return eq(w,ti) || eq(w,neg(ti))
|
return eq(w,ti) || eq(w,neg(ti))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,9 @@ export const complex = {
|
|||||||
'undefined,undefined': () => (u, v) => u,
|
'undefined,undefined': () => (u, v) => u,
|
||||||
'T,T': () => (x, y) => ({re: x, im: y}),
|
'T,T': () => (x, y) => ({re: x, im: y}),
|
||||||
/* Take advantage of conversions in typed-function */
|
/* Take advantage of conversions in typed-function */
|
||||||
Complex: () => z => z
|
// 'Complex<T>': () => z => z
|
||||||
|
/* But help out because without templates built in to typed-function,
|
||||||
|
* type inference turns out to be too hard
|
||||||
|
*/
|
||||||
|
'T': ({'zero(T)': zr}) => x => ({re: x, im: zr(x)})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const conjugate = {
|
export const conjugate = {
|
||||||
Complex: ({negate, complex}) => z => complex(z.re, negate(z.im))
|
'Complex<T>': ({
|
||||||
|
'negate(T)': neg,
|
||||||
|
'complex(T,T)': cplx
|
||||||
|
}) => z => cplx(z.re, neg(z.im))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'Complex,number': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
'isZero(number)': isZ,
|
'self(T,T)': me
|
||||||
'self(number,number)': eqNum
|
}) => (w,z) => me(w.re, z.re) && me(w.im, z.im),
|
||||||
}) => (z, x) => eqNum(z.re, x) && isZ(z.im),
|
// NOTE: Although I do not understand exactly why, with typed-function@3.0's
|
||||||
|
// matching algorithm, the above template must come first to ensure the
|
||||||
|
// most specific match to a template call. I.e, if one of the below
|
||||||
|
// comes first, a call with two complex numbers can match via conversions
|
||||||
|
// with (Complex<Complex<number>>, Complex<number>) (!, hopefully in some
|
||||||
|
// future iteration typed-function will be smart enough to prefer
|
||||||
|
// Complex<T>, Complex<T>. Possibly the problem is in Pocomath's bolted-on
|
||||||
|
// type resolution and the difficulty will go away when features are moved into
|
||||||
|
// typed-function.
|
||||||
|
'Complex<T>,T': ({
|
||||||
|
'isZero(T)': isZ,
|
||||||
|
'self(T,T)': eqReal
|
||||||
|
}) => (z, x) => eqReal(z.re, x) && isZ(z.im),
|
||||||
|
|
||||||
'Complex,bigint': ({
|
'T,Complex<T>': ({
|
||||||
'isZero(bigint)': isZ,
|
'isZero(T)': isZ,
|
||||||
'self(bigint,bigint)': eqBigInt
|
'self(T,T)': eqReal
|
||||||
}) => (z, b) => eqBigInt(z.re, b) && isZ(z.im),
|
}) => (b, z) => eqReal(z.re, b) && isZ(z.im),
|
||||||
|
|
||||||
'Complex,Complex': ({self}) => (w,z) => self(w.re, z.re) && self(w.im, z.im),
|
|
||||||
|
|
||||||
'GaussianInteger,GaussianInteger': ({
|
|
||||||
'self(bigint,bigint)': eq
|
|
||||||
}) => (a,b) => eq(a.re, b.re) && eq(a.im, b.im)
|
|
||||||
}
|
}
|
||||||
|
@ -15,4 +15,17 @@ export default async function extendToComplex(pmath) {
|
|||||||
// Guess it wasn't a method available in complex; no worries
|
// Guess it wasn't a method available in complex; no worries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Since extension to complex was specifically requested, instantiate
|
||||||
|
// all of the templates so that the associated type conversions will
|
||||||
|
// be available to make function calls work immediately:
|
||||||
|
for (const baseType in pmath.Types) {
|
||||||
|
if (baseType in pmath.Templates || baseType.includes('<')) {
|
||||||
|
continue // don't mess with templates
|
||||||
|
}
|
||||||
|
const ignore = new Set(['undefined', 'any', 'T', 'ground'])
|
||||||
|
if (ignore.has(baseType)) continue
|
||||||
|
// (What we really want is a check for "numeric" types but we don't
|
||||||
|
// have that concept (yet?)). If we did, we'd instantiate just for those...
|
||||||
|
pmath.instantiateTemplate('Complex', baseType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,20 @@ import PocomathInstance from '../core/PocomathInstance.mjs'
|
|||||||
import * as Complex from './Types/Complex.mjs'
|
import * as Complex from './Types/Complex.mjs'
|
||||||
import gcdType from '../generic/gcdType.mjs'
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
|
|
||||||
|
const gcdComplexRaw = {}
|
||||||
|
Object.assign(gcdComplexRaw, gcdType('Complex<bigint>'))
|
||||||
|
Object.assign(gcdComplexRaw, gcdType('Complex<NumInt>'))
|
||||||
const imps = {
|
const imps = {
|
||||||
gcdGIRaw: gcdType('GaussianInteger'),
|
gcdComplexRaw,
|
||||||
gcd: { // Only return gcds with positive real part
|
gcd: { // Only return gcds with positive real part
|
||||||
'GaussianInteger,GaussianInteger': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
'gcdGIRaw(GaussianInteger,GaussianInteger)': gcdRaw,
|
'gcdComplexRaw(Complex<T>,Complex<T>)': gcdRaw,
|
||||||
'sign(bigint)': sgn,
|
'sign(T)': sgn,
|
||||||
'negate(GaussianInteger)': neg
|
'one(T)': uno,
|
||||||
|
'negate(Complex<T>)': neg
|
||||||
}) => (z,m) => {
|
}) => (z,m) => {
|
||||||
const raw = gcdRaw(z, m)
|
const raw = gcdRaw(z, m)
|
||||||
if (sgn(raw.re) === 1n) return raw
|
if (sgn(raw.re) === uno(raw.re)) return raw
|
||||||
return neg(raw)
|
return neg(raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const invert = {
|
export const invert = {
|
||||||
Complex: ({conjugate, absquare, complex, divide}) => z => {
|
'Complex<T>': ({
|
||||||
const c = conjugate(z)
|
'conjugate(Complex<T>)': conj,
|
||||||
const d = absquare(z)
|
'absquare(Complex<T>)': asq,
|
||||||
return complex(divide(c.re, d), divide(c.im, d))
|
'complex(T,T)': cplx,
|
||||||
|
'divide(T,T)': div
|
||||||
|
}) => z => {
|
||||||
|
const c = conj(z)
|
||||||
|
const d = asq(z)
|
||||||
|
return cplx(div(c.re, d), div(c.im, d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
Complex: ({self}) => z => self(z.re) && self(z.im)
|
'Complex<T>': ({'self(T)': me}) => z => me(z.re) && me(z.im)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const multiply = {
|
export const multiply = {
|
||||||
'Complex,Complex': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
'complex(any,any)': cplx,
|
'complex(T,T)': cplx,
|
||||||
add,
|
'add(T,T)': plus,
|
||||||
subtract,
|
'subtract(T,T)': sub,
|
||||||
self
|
'self(T,T)': me,
|
||||||
|
'conjugate(T)': conj // makes quaternion multiplication work
|
||||||
}) => (w,z) => {
|
}) => (w,z) => {
|
||||||
return cplx(
|
return cplx(
|
||||||
subtract(self(w.re, z.re), self(w.im, z.im)),
|
sub(me(w.re, z.re), me(conj(w.im), z.im)),
|
||||||
add(self(w.re, z.im), self(w.im, z.re)))
|
plus(me(conj(w.re), z.im), me(w.im, z.re)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
export * from './roundquotient.mjs'
|
export * from './roundquotient.mjs'
|
||||||
|
|
||||||
export const quotient = {
|
export const quotient = {
|
||||||
'Complex,Complex': ({roundquotient}) => (w,z) => roundquotient(w,z)
|
'Complex<T>,Complex<T>': ({
|
||||||
|
'roundquotient(Complex<T>,Complex<T>)': rq
|
||||||
|
}) => (w,z) => rq(w,z)
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'Complex,Complex': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
'isZero(Complex)': isZ,
|
'isZero(Complex<T>)': isZ,
|
||||||
conjugate,
|
'conjugate(Complex<T>)': conj,
|
||||||
'multiply(Complex,Complex)': mult,
|
'multiply(Complex<T>,Complex<T>)': mult,
|
||||||
absquare,
|
'absquare(Complex<T>)': asq,
|
||||||
self,
|
'self(T,T)': me,
|
||||||
complex
|
'complex(T,T)': cplx
|
||||||
}) => (n,d) => {
|
}) => (n,d) => {
|
||||||
if (isZ(d)) return d
|
if (isZ(d)) return d
|
||||||
const cnum = mult(n, conjugate(d))
|
const cnum = mult(n, conj(d))
|
||||||
const dreal = absquare(d)
|
const dreal = asq(d)
|
||||||
return complex(self(cnum.re, dreal), self(cnum.im, dreal))
|
return cplx(me(cnum.re, dreal), me(cnum.im, dreal))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,39 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
Complex: ({
|
'Complex<T>': ({
|
||||||
config,
|
config,
|
||||||
isZero,
|
'isZero(T)': isZ,
|
||||||
sign,
|
'sign(T)': sgn,
|
||||||
one,
|
'one(T)': uno,
|
||||||
add,
|
'add(T,T)': plus,
|
||||||
complex,
|
'complex(T)': cplxU,
|
||||||
multiply,
|
'complex(T,T)': cplxB,
|
||||||
self,
|
'multiply(T,T)': mult,
|
||||||
divide,
|
'self(T)': me,
|
||||||
'abs(Complex)': abs,
|
'divide(T,T)': div,
|
||||||
subtract
|
'abs(Complex<T>)': absC,
|
||||||
|
'subtract(T,T)': sub
|
||||||
}) => {
|
}) => {
|
||||||
if (config.predictable) {
|
if (config.predictable) {
|
||||||
return z => {
|
return z => {
|
||||||
const reOne = one(z.re)
|
const reOne = uno(z.re)
|
||||||
if (isZero(z.im) && sign(z.re) === reOne) return complex(self(z.re))
|
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(me(z.re))
|
||||||
const reTwo = add(reOne, reOne)
|
const reTwo = plus(reOne, reOne)
|
||||||
return complex(
|
return cplxB(
|
||||||
multiply(sign(z.im), self(divide(add(abs(z),z.re), reTwo))),
|
mult(sgn(z.im), me(div(plus(absC(z),z.re), reTwo))),
|
||||||
self(divide(subtract(abs(z),z.re), reTwo))
|
me(div(sub(absC(z),z.re), reTwo))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return z => {
|
return z => {
|
||||||
const reOne = one(z.re)
|
const reOne = uno(z.re)
|
||||||
if (isZero(z.im) && sign(z.re) === reOne) return self(z.re)
|
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
||||||
const reTwo = add(reOne, reOne)
|
const reTwo = plus(reOne, reOne)
|
||||||
return complex(
|
const reSqrt = me(div(plus(absC(z),z.re), reTwo))
|
||||||
multiply(sign(z.im), self(divide(add(abs(z),z.re), reTwo))),
|
const imSqrt = me(div(sub(absC(z),z.re), reTwo))
|
||||||
self(divide(subtract(abs(z),z.re), reTwo))
|
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
||||||
)
|
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ export default class PocomathInstance {
|
|||||||
'importDependencies',
|
'importDependencies',
|
||||||
'install',
|
'install',
|
||||||
'installType',
|
'installType',
|
||||||
|
'instantiateTemplate',
|
||||||
'joinTypes',
|
'joinTypes',
|
||||||
'name',
|
'name',
|
||||||
'self',
|
'self',
|
||||||
@ -436,7 +437,12 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Nothing actually happens until we match a template parameter
|
// update the typeOf function
|
||||||
|
const imp = {}
|
||||||
|
imp[type] = {uses: new Set(['T']), does: ({T}) => () => `${base}<${T}>`}
|
||||||
|
this._installFunctions({typeOf: imp})
|
||||||
|
|
||||||
|
// Nothing else actually happens until we match a template parameter
|
||||||
this.Templates[base] = {type, spec}
|
this.Templates[base] = {type, spec}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,8 +773,9 @@ export default class PocomathInstance {
|
|||||||
const subsig = substituteInSig(
|
const subsig = substituteInSig(
|
||||||
needsig, theTemplateParam, instantiateFor)
|
needsig, theTemplateParam, instantiateFor)
|
||||||
let resname = simplifiedDep
|
let resname = simplifiedDep
|
||||||
if (resname === 'self') resname = name
|
if (resname == 'self') resname = name
|
||||||
innerRefs[dep] = self._pocoresolve(resname, subsig)
|
innerRefs[dep] = self._pocoresolve(
|
||||||
|
resname, subsig, refs[simplifiedDep])
|
||||||
} else {
|
} else {
|
||||||
innerRefs[dep] = refs[simplifiedDep]
|
innerRefs[dep] = refs[simplifiedDep]
|
||||||
}
|
}
|
||||||
@ -782,7 +789,7 @@ export default class PocomathInstance {
|
|||||||
this._addTFimplementation(
|
this._addTFimplementation(
|
||||||
tf_imps, signature, {uses: outerUses, does: patch})
|
tf_imps, signature, {uses: outerUses, does: patch})
|
||||||
}
|
}
|
||||||
this._correctPartialSelfRefs(tf_imps)
|
this._correctPartialSelfRefs(name, tf_imps)
|
||||||
const tf = this._typed(name, tf_imps)
|
const tf = this._typed(name, tf_imps)
|
||||||
Object.defineProperty(this, name, {configurable: true, value: tf})
|
Object.defineProperty(this, name, {configurable: true, value: tf})
|
||||||
return tf
|
return tf
|
||||||
@ -845,7 +852,7 @@ export default class PocomathInstance {
|
|||||||
} else {
|
} else {
|
||||||
// can bundle up func, and grab its signature if need be
|
// can bundle up func, and grab its signature if need be
|
||||||
let destination = this[func]
|
let destination = this[func]
|
||||||
if (needsig) {
|
if (destination &&needsig) {
|
||||||
destination = this._pocoresolve(func, needsig)
|
destination = this._pocoresolve(func, needsig)
|
||||||
}
|
}
|
||||||
refs[dep] = destination
|
refs[dep] = destination
|
||||||
@ -878,7 +885,7 @@ export default class PocomathInstance {
|
|||||||
imps[signature] = does(refs)
|
imps[signature] = does(refs)
|
||||||
}
|
}
|
||||||
|
|
||||||
_correctPartialSelfRefs(imps) {
|
_correctPartialSelfRefs(name, imps) {
|
||||||
for (const aSignature in imps) {
|
for (const aSignature in imps) {
|
||||||
if (!(imps[aSignature].deferred)) continue
|
if (!(imps[aSignature].deferred)) continue
|
||||||
const part_self_references = imps[aSignature].psr
|
const part_self_references = imps[aSignature].psr
|
||||||
@ -894,7 +901,7 @@ export default class PocomathInstance {
|
|||||||
// No exact match, try to get one that matches with
|
// No exact match, try to get one that matches with
|
||||||
// subtypes since the whole conversion thing in typed-function
|
// subtypes since the whole conversion thing in typed-function
|
||||||
// is too complicated to reproduce
|
// is too complicated to reproduce
|
||||||
const foundSig = this._findSubtypeImpl(imps, neededSig)
|
const foundSig = this._findSubtypeImpl(name, imps, neededSig)
|
||||||
if (foundSig) {
|
if (foundSig) {
|
||||||
corrected_self_references.push(foundSig)
|
corrected_self_references.push(foundSig)
|
||||||
} else {
|
} else {
|
||||||
@ -936,7 +943,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
const resultingTypes = new Set()
|
const resultingTypes = new Set()
|
||||||
for (const iType of instantiations) {
|
for (const iType of instantiations) {
|
||||||
const resultType = this._maybeAddTemplateType(base, iType)
|
const resultType = this.instantiateTemplate(base, iType)
|
||||||
if (resultType) resultingTypes.add(resultType)
|
if (resultType) resultingTypes.add(resultType)
|
||||||
}
|
}
|
||||||
return resultingTypes
|
return resultingTypes
|
||||||
@ -946,7 +953,7 @@ export default class PocomathInstance {
|
|||||||
* instantiator to the Types of this instance, if it hasn't happened already.
|
* instantiator to the Types of this instance, if it hasn't happened already.
|
||||||
* Returns the name of the type if added, false otherwise.
|
* Returns the name of the type if added, false otherwise.
|
||||||
*/
|
*/
|
||||||
_maybeAddTemplateType(base, instantiator) {
|
instantiateTemplate(base, instantiator) {
|
||||||
const wantsType = `${base}<${instantiator}>`
|
const wantsType = `${base}<${instantiator}>`
|
||||||
if (wantsType in this.Types) return false
|
if (wantsType in this.Types) return false
|
||||||
// OK, need to generate the type from the template
|
// OK, need to generate the type from the template
|
||||||
@ -956,7 +963,7 @@ export default class PocomathInstance {
|
|||||||
const template = this.Templates[base].spec
|
const template = this.Templates[base].spec
|
||||||
if (!template) {
|
if (!template) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Implementor error in _maybeAddTemplateType ${base} ${instantiator}`)
|
`Implementor error in instantiateTemplate(${base}, ${instantiator})`)
|
||||||
}
|
}
|
||||||
const instantiatorSpec = this.Types[instantiator]
|
const instantiatorSpec = this.Types[instantiator]
|
||||||
let beforeTypes = []
|
let beforeTypes = []
|
||||||
@ -1010,7 +1017,7 @@ export default class PocomathInstance {
|
|||||||
return wantsType
|
return wantsType
|
||||||
}
|
}
|
||||||
|
|
||||||
_findSubtypeImpl(imps, neededSig) {
|
_findSubtypeImpl(name, imps, neededSig) {
|
||||||
if (neededSig in imps) return neededSig
|
if (neededSig in imps) return neededSig
|
||||||
let foundSig = false
|
let foundSig = false
|
||||||
const typeList = typeListOfSignature(neededSig)
|
const typeList = typeListOfSignature(neededSig)
|
||||||
@ -1047,6 +1054,13 @@ export default class PocomathInstance {
|
|||||||
|| this._subtypes[otherType].has(myType)) {
|
|| this._subtypes[otherType].has(myType)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (otherType in this.Templates) {
|
||||||
|
if (this.instantiateTemplate(otherType, myType)) {
|
||||||
|
let dummy
|
||||||
|
dummy = this[name] // for side effects
|
||||||
|
return this._findSubtypeImpl(name, this._imps[name], neededSig)
|
||||||
|
}
|
||||||
|
}
|
||||||
allMatch = false
|
allMatch = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1058,17 +1072,28 @@ export default class PocomathInstance {
|
|||||||
return foundSig
|
return foundSig
|
||||||
}
|
}
|
||||||
|
|
||||||
_pocoresolve(name, sig) {
|
_pocoresolve(name, sig, typedFunction) {
|
||||||
const typedfunc = this[name]
|
if (!this._typed.isTypedFunction(typedFunction)) {
|
||||||
|
typedFunction = this[name]
|
||||||
|
}
|
||||||
let result = undefined
|
let result = undefined
|
||||||
try {
|
try {
|
||||||
result = this._typed.find(typedfunc, sig, {exact: true})
|
result = this._typed.find(typedFunction, sig, {exact: true})
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
if (result) return result
|
if (result) return result
|
||||||
const foundsig = this._findSubtypeImpl(this._imps[name], sig)
|
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig)
|
||||||
if (foundsig) return this._typed.find(typedfunc, foundsig)
|
if (foundsig) return this._typed.find(typedFunction, foundsig)
|
||||||
return this._typed.find(typedfunc, sig)
|
// Make sure bundle is up-to-date:
|
||||||
|
typedFunction = this[name]
|
||||||
|
try {
|
||||||
|
result = this._typed.find(typedFunction, sig)
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
if (result) return result
|
||||||
|
// total punt, revert to typed-function resolution on every call;
|
||||||
|
// hopefully this happens rarely:
|
||||||
|
return typedFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ export const reducingOperation = {
|
|||||||
'undefined': () => u => u,
|
'undefined': () => u => u,
|
||||||
'undefined,...any': () => (u, rest) => u,
|
'undefined,...any': () => (u, rest) => u,
|
||||||
'any,undefined': () => (x, u) => u,
|
'any,undefined': () => (x, u) => u,
|
||||||
|
'undefined,undefined': () => (u,v) => u,
|
||||||
any: () => x => x,
|
any: () => x => x,
|
||||||
'any,any,...any': ({
|
'any,any,...any': ({
|
||||||
self
|
self
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
number: ({config, complex, 'self(Complex)': complexSqrt}) => {
|
number: ({
|
||||||
if (config.predictable || !complexSqrt) {
|
config,
|
||||||
|
'complex(number,number)': cplx,
|
||||||
|
'negate(number)': neg}) => {
|
||||||
|
if (config.predictable || !cplx) {
|
||||||
return n => isNaN(n) ? NaN : Math.sqrt(n)
|
return n => isNaN(n) ? NaN : Math.sqrt(n)
|
||||||
}
|
}
|
||||||
return n => {
|
return n => {
|
||||||
if (isNaN(n)) return NaN
|
if (isNaN(n)) return NaN
|
||||||
if (n >= 0) return Math.sqrt(n)
|
if (n >= 0) return Math.sqrt(n)
|
||||||
return complexSqrt(complex(n))
|
return cplx(0, Math.sqrt(neg(n)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import {Complex} from '../complex/Types/Complex.mjs'
|
|||||||
export const floor = {
|
export const floor = {
|
||||||
bigint: () => x => x,
|
bigint: () => x => x,
|
||||||
NumInt: () => x => x, // Because Pocomath isn't part of typed-function, or
|
NumInt: () => x => x, // Because Pocomath isn't part of typed-function, or
|
||||||
GaussianInteger: () => x => x, // at least have access to the real
|
'Complex<bigint>': () => x => x, // at least have access to the real
|
||||||
// typed-function parse, we unfortunately can't coalesce these into one
|
// typed-function parse, we unfortunately can't coalesce these into one
|
||||||
// entry with type `bigint|NumInt|GaussianInteger` because they couldn't
|
// entry with type `bigint|NumInt|GaussianInteger` because they couldn't
|
||||||
// be separately activated then
|
// be separately activated then
|
||||||
@ -17,7 +17,7 @@ export const floor = {
|
|||||||
return Math.floor(n)
|
return Math.floor(n)
|
||||||
},
|
},
|
||||||
|
|
||||||
Complex: Complex.promoteUnary.Complex,
|
'Complex<T>': Complex.promoteUnary['Complex<T>'],
|
||||||
|
|
||||||
// OK to include a type totally not in Pocomath yet, it'll never be
|
// OK to include a type totally not in Pocomath yet, it'll never be
|
||||||
// activated.
|
// activated.
|
||||||
|
@ -32,7 +32,12 @@ Tuple.installType('Tuple<T>', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Tuple.promoteUnary = {
|
Tuple.promoteUnary = {
|
||||||
'Tuple<T>': ({'self(T)': me, tuple}) => t => tuple(...(t.elts.map(me)))
|
'Tuple<T>': ({
|
||||||
|
'self(T)': me,
|
||||||
|
tuple
|
||||||
|
}) => t => tuple(...(t.elts.map(x => me(x)))) // NOTE: this must use
|
||||||
|
// the inner arrow function to drop additional arguments that Array.map
|
||||||
|
// supplies, as otherwise the wrong signature of `me` might be used.
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinaryUnary = {
|
Tuple.promoteBinaryUnary = {
|
||||||
|
@ -16,8 +16,8 @@ describe('The default full pocomath instance "math"', () => {
|
|||||||
assert.strictEqual(math.typeOf(-1.5), 'number')
|
assert.strictEqual(math.typeOf(-1.5), 'number')
|
||||||
assert.strictEqual(math.typeOf(-42n), 'bigint')
|
assert.strictEqual(math.typeOf(-42n), 'bigint')
|
||||||
assert.strictEqual(math.typeOf(undefined), 'undefined')
|
assert.strictEqual(math.typeOf(undefined), 'undefined')
|
||||||
assert.strictEqual(math.typeOf({re: 15n, im: -2n}), 'GaussianInteger')
|
assert.strictEqual(math.typeOf({re: 15n, im: -2n}), 'Complex<bigint>')
|
||||||
assert.strictEqual(math.typeOf({re: 6.28, im: 2.72}), 'Complex')
|
assert.strictEqual(math.typeOf({re: 6.28, im: 2.72}), 'Complex<number>')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can subtract numbers', () => {
|
it('can subtract numbers', () => {
|
||||||
@ -105,11 +105,9 @@ describe('The default full pocomath instance "math"', () => {
|
|||||||
|
|
||||||
it('calculates multi-way gcds and lcms', () => {
|
it('calculates multi-way gcds and lcms', () => {
|
||||||
assert.strictEqual(math.gcd(30,105,42), 3)
|
assert.strictEqual(math.gcd(30,105,42), 3)
|
||||||
assert.ok(
|
const gaussianLCM = math.lcm(
|
||||||
math.associate(
|
math.complex(2n,1n), math.complex(1n,1n), math.complex(0n,1n))
|
||||||
math.lcm(
|
assert.strictEqual(math.associate(gaussianLCM, math.complex(1n,3n)), true)
|
||||||
math.complex(2n,1n), math.complex(1n,1n), math.complex(0n,1n)),
|
|
||||||
math.complex(1n,3n)))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -49,6 +49,15 @@ describe('complex', () => {
|
|||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)),
|
math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)),
|
||||||
math.complex(4n, 5n))
|
math.complex(4n, 5n))
|
||||||
|
// And now works for NumInt, too!
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.gcd(math.complex(53,56), math.complex(47, -13)),
|
||||||
|
math.complex(4, 5))
|
||||||
|
// But properly fails for general complex
|
||||||
|
assert.throws(
|
||||||
|
() => math.gcd(math.complex(5.3,5.6), math.complex(4.7, -1.3)),
|
||||||
|
TypeError
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('computes floor', () => {
|
it('computes floor', () => {
|
||||||
|
@ -3,12 +3,14 @@ import math from '../src/pocomath.mjs'
|
|||||||
import PocomathInstance from '../src/core/PocomathInstance.mjs'
|
import PocomathInstance from '../src/core/PocomathInstance.mjs'
|
||||||
import * as numbers from '../src/number/all.mjs'
|
import * as numbers from '../src/number/all.mjs'
|
||||||
import * as numberAdd from '../src/number/add.mjs'
|
import * as numberAdd from '../src/number/add.mjs'
|
||||||
|
import * as numberZero from '../src/number/zero.mjs'
|
||||||
import {add as genericAdd} from '../src/generic/arithmetic.mjs'
|
import {add as genericAdd} from '../src/generic/arithmetic.mjs'
|
||||||
import * as complex from '../src/complex/all.mjs'
|
import * as complex from '../src/complex/all.mjs'
|
||||||
import * as complexAdd from '../src/complex/add.mjs'
|
import * as complexAdd from '../src/complex/add.mjs'
|
||||||
import * as complexNegate from '../src/complex/negate.mjs'
|
import * as complexNegate from '../src/complex/negate.mjs'
|
||||||
import * as complexComplex from '../src/complex/complex.mjs'
|
import * as complexComplex from '../src/complex/complex.mjs'
|
||||||
import * as bigintAdd from '../src/bigint/add.mjs'
|
import * as bigintAdd from '../src/bigint/add.mjs'
|
||||||
|
import * as bigintZero from '../src/bigint/zero.mjs'
|
||||||
import * as concreteSubtract from '../src/generic/subtract.concrete.mjs'
|
import * as concreteSubtract from '../src/generic/subtract.concrete.mjs'
|
||||||
import * as genericSubtract from '../src/generic/subtract.mjs'
|
import * as genericSubtract from '../src/generic/subtract.mjs'
|
||||||
import extendToComplex from '../src/complex/extendToComplex.mjs'
|
import extendToComplex from '../src/complex/extendToComplex.mjs'
|
||||||
@ -54,9 +56,9 @@ describe('A custom instance', () => {
|
|||||||
math.complex(-5, -1))
|
math.complex(-5, -1))
|
||||||
// And now floor has been activated for Complex as well, since the type
|
// And now floor has been activated for Complex as well, since the type
|
||||||
// is present
|
// is present
|
||||||
assert.deepStrictEqual(
|
const fracComplex = math.complex(1.9, 0)
|
||||||
pm.floor(math.complex(1.9, 0)),
|
const intComplex = math.complex(1)
|
||||||
math.complex(1))
|
assert.deepStrictEqual(pm.floor(fracComplex), intComplex)
|
||||||
// And the chain functions refresh themselves:
|
// And the chain functions refresh themselves:
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
pm.chain(5).add(pm.chain(0).complex(7).value).value, math.complex(5,7))
|
pm.chain(5).add(pm.chain(0).complex(7).value).value, math.complex(5,7))
|
||||||
@ -65,10 +67,11 @@ describe('A custom instance', () => {
|
|||||||
it("can defer definition of (even used) types", () => {
|
it("can defer definition of (even used) types", () => {
|
||||||
const dt = new PocomathInstance('Deferred Types')
|
const dt = new PocomathInstance('Deferred Types')
|
||||||
dt.install(numberAdd)
|
dt.install(numberAdd)
|
||||||
|
dt.install(numberZero) // for promoting numbers to complex, to fill in im
|
||||||
dt.install({times: {
|
dt.install({times: {
|
||||||
'number,number': () => (m,n) => m*n,
|
'number,number': () => (m,n) => m*n,
|
||||||
'Complex,Complex': ({complex}) => (w,z) => {
|
'Complex<T>,Complex<T>': ({'complex(T,T)': cplx}) => (w,z) => {
|
||||||
return complex(w.re*z.re - w.im*z.im, w.re*z.im + w.im*z.re)
|
return cplx(w.re*z.re - w.im*z.im, w.re*z.im + w.im*z.re)
|
||||||
}
|
}
|
||||||
}})
|
}})
|
||||||
// complex type not present but should still be able to add numbers:
|
// complex type not present but should still be able to add numbers:
|
||||||
@ -82,6 +85,7 @@ describe('A custom instance', () => {
|
|||||||
it("can selectively import in cute ways", async function () {
|
it("can selectively import in cute ways", async function () {
|
||||||
const cherry = new PocomathInstance('cherry')
|
const cherry = new PocomathInstance('cherry')
|
||||||
cherry.install(numberAdd)
|
cherry.install(numberAdd)
|
||||||
|
cherry.install(numberZero) // for complex promotion
|
||||||
await extendToComplex(cherry)
|
await extendToComplex(cherry)
|
||||||
cherry.install({add: genericAdd})
|
cherry.install({add: genericAdd})
|
||||||
/* Now we have an instance that supports addition for number and complex
|
/* Now we have an instance that supports addition for number and complex
|
||||||
@ -124,12 +128,13 @@ describe('A custom instance', () => {
|
|||||||
inst.install(complexAdd)
|
inst.install(complexAdd)
|
||||||
inst.install(complexComplex)
|
inst.install(complexComplex)
|
||||||
inst.install(bigintAdd)
|
inst.install(bigintAdd)
|
||||||
|
inst.install(bigintZero) // for complex promotion
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
inst.typeMerge(6n, inst.complex(3n, 2n)),
|
inst.typeMerge(6n, inst.complex(3n, 2n)),
|
||||||
'Merge to GaussianInteger')
|
'Merge to Complex<bigint>')
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
inst.typeMerge(3, inst.complex(4.5,2.1)),
|
inst.typeMerge(3, inst.complex(4.5,2.1)),
|
||||||
'Merge to Complex')
|
'Merge to Complex<number>')
|
||||||
// The following is the current behavior, since 3 converts to 3+0i
|
// The following is the current behavior, since 3 converts to 3+0i
|
||||||
// which is technically the same Complex type as 3n+0ni.
|
// which is technically the same Complex type as 3n+0ni.
|
||||||
// This should clear up when Complex is templatized
|
// This should clear up when Complex is templatized
|
||||||
|
Loading…
Reference in New Issue
Block a user