feat: Return type annotations #53
@ -1,6 +1,7 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
/* Absolute value squared */
|
/* Absolute value squared */
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
bigint: ({'square(bigint)': sqb}) => b => sqb(b)
|
bigint: ({'square(bigint)': sqb}) => Returns('bigint', b => sqb(b))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const add = {'bigint,bigint': () => (a,b) => a+b}
|
export const add = {'bigint,bigint': () => Returns('bigint', (a,b) => a+b)}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const compare = {
|
export const compare = {
|
||||||
'bigint,bigint': () => (a,b) => a === b ? 0n : (a > b ? 1n : -1n)
|
'bigint,bigint': () => Returns(
|
||||||
|
'boolean', (a,b) => a === b ? 0n : (a > b ? 1n : -1n))
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const divide = {
|
export const divide = {
|
||||||
'bigint,bigint': ({config, 'quotient(bigint,bigint)': quot}) => {
|
'bigint,bigint': ({config, 'quotient(bigint,bigint)': quot}) => {
|
||||||
if (config.predictable) return quot
|
if (config.predictable) return Returns('bigint', (n,d) => quot(n,d))
|
||||||
return (n, d) => {
|
return Returns('bigint|undefined', (n, d) => {
|
||||||
const q = n/d
|
const q = n/d
|
||||||
if (q * d == n) return q
|
if (q * d == n) return q
|
||||||
return undefined
|
return undefined
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const isZero = {bigint: () => b => b === 0n}
|
export const isZero = {bigint: () => Returns('boolean', b => b === 0n)}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const multiply = {'bigint,bigint': () => (a,b) => a*b}
|
export const multiply = {'bigint,bigint': () => Returns('bigint', (a,b) => a*b)}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import gcdType from '../generic/gcdType.mjs'
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
import {identity} from '../generic/identity.mjs'
|
import {identityType} from '../generic/identity.mjs'
|
||||||
|
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export {absquare} from './absquare.mjs'
|
export {absquare} from './absquare.mjs'
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
export {compare} from './compare.mjs'
|
export {compare} from './compare.mjs'
|
||||||
export const conjugate = {bigint: () => identity}
|
export const conjugate = {bigint: identityType('bigint')}
|
||||||
export {divide} from './divide.mjs'
|
export {divide} from './divide.mjs'
|
||||||
export const gcd = gcdType('bigint')
|
export const gcd = gcdType('bigint')
|
||||||
export {isZero} from './isZero.mjs'
|
export {isZero} from './isZero.mjs'
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const negate = {bigint: () => b => -b}
|
export const negate = {bigint: () => Returns('bigint', b => -b)}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const one = {bigint: () => () => 1n}
|
export const one = {bigint: () => Returns('bigint', () => 1n)}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
/* Returns the best integer approximation to n/d */
|
/* Returns the floor integer approximation to n/d */
|
||||||
export const quotient = {
|
export const quotient = {
|
||||||
'bigint,bigint': ({'sign(bigint)': sgn}) => (n, d) => {
|
'bigint,bigint': ({'sign(bigint)': sgn}) => Returns('bigint', (n, d) => {
|
||||||
const dSgn = sgn(d)
|
const dSgn = sgn(d)
|
||||||
if (dSgn === 0n) return 0n
|
if (dSgn === 0n) return 0n
|
||||||
if (sgn(n) === dSgn) return n/d
|
if (sgn(n) === dSgn) return n/d
|
||||||
const quot = n/d
|
const quot = n/d
|
||||||
if (quot * d == n) return quot
|
if (quot * d == n) return quot
|
||||||
return quot - 1n
|
return quot - 1n
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
/* Returns the closest integer approximation to n/d */
|
/* Returns the closest integer approximation to n/d */
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'bigint,bigint': ({'sign(bigint)': sgn}) => (n, d) => {
|
'bigint,bigint': ({'sign(bigint)': sgn}) => Returns('bigint', (n, d) => {
|
||||||
const dSgn = sgn(d)
|
const dSgn = sgn(d)
|
||||||
if (dSgn === 0n) return 0n
|
if (dSgn === 0n) return 0n
|
||||||
const candidate = n/d
|
const candidate = n/d
|
||||||
@ -11,5 +12,5 @@ export const roundquotient = {
|
|||||||
if (2n * rem > absd) return candidate + dSgn
|
if (2n * rem > absd) return candidate + dSgn
|
||||||
if (-2n * rem >= absd) return candidate - dSgn
|
if (-2n * rem >= absd) return candidate - dSgn
|
||||||
return candidate
|
return candidate
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const sign = {
|
export const sign = {
|
||||||
bigint: () => b => {
|
bigint: () => Returns('bigint', b => {
|
||||||
if (b === 0n) return 0n
|
if (b === 0n) return 0n
|
||||||
if (b > 0n) return 1n
|
if (b > 0n) return 1n
|
||||||
return -1n
|
return -1n
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export * from './Types/bigint.mjs'
|
import Returns from '../core/Returns.mjs'
|
||||||
import isqrt from 'bigint-isqrt'
|
import isqrt from 'bigint-isqrt'
|
||||||
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
bigint: ({
|
bigint: ({
|
||||||
@ -11,18 +12,18 @@ export const sqrt = {
|
|||||||
// 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 Returns('bigint', b => isqrt(b))
|
||||||
}
|
}
|
||||||
if (!cplx) {
|
if (!cplx) {
|
||||||
return b => {
|
return Returns('bigint|undefined', b => {
|
||||||
if (b >= 0n) {
|
if (b >= 0n) {
|
||||||
const trial = isqrt(b)
|
const trial = isqrt(b)
|
||||||
if (trial * trial === b) return trial
|
if (trial * trial === b) return trial
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
return Returns('bigint|Complex<bigint>|undefined', b => {
|
||||||
return b => {
|
|
||||||
if (b === undefined) return undefined
|
if (b === undefined) return undefined
|
||||||
let real = true
|
let real = true
|
||||||
if (b < 0n) {
|
if (b < 0n) {
|
||||||
@ -33,6 +34,6 @@ export const sqrt = {
|
|||||||
if (trial * trial !== b) return undefined
|
if (trial * trial !== b) return undefined
|
||||||
if (real) return trial
|
if (real) return trial
|
||||||
return cplx(0n, trial)
|
return cplx(0n, trial)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const zero = {bigint: () => () => 0n}
|
export const zero = {bigint: () => Returns('bigint', () => 0n)}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../../core/Returns.mjs'
|
||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
|
||||||
const Complex = new PocomathInstance('Complex')
|
const Complex = new PocomathInstance('Complex')
|
||||||
// Base type that should generally not be used directly
|
// Now the template type: Complex numbers are actually always homogeneous
|
||||||
Complex.installType('Complex', {
|
// in their component types. For an explanation of the meanings of the
|
||||||
test: z => z && typeof z === 'object' && 're' in z && 'im' in z
|
// properties, see ../../tuple/Types/Tuple.mjs
|
||||||
})
|
|
||||||
// Now the template type: Complex numbers are actually always homeogeneous
|
|
||||||
// in their component types.
|
|
||||||
Complex.installType('Complex<T>', {
|
Complex.installType('Complex<T>', {
|
||||||
infer: ({typeOf, joinTypes}) => z => joinTypes([typeOf(z.re), typeOf(z.im)]),
|
base: z => z && typeof z === 'object' && 're' in z && 'im' in z,
|
||||||
test: testT => z => testT(z.re) && testT(z.im),
|
test: testT => z => testT(z.re) && testT(z.im),
|
||||||
|
infer: ({typeOf, joinTypes}) => z => joinTypes([typeOf(z.re), typeOf(z.im)]),
|
||||||
from: {
|
from: {
|
||||||
T: t => ({re: t, im: t-t}), // hack: maybe need a way to call zero(T)
|
T: t => ({re: t, im: t-t}), // hack: maybe need a way to call zero(T)
|
||||||
U: convert => u => {
|
U: convert => u => {
|
||||||
@ -21,7 +20,12 @@ Complex.installType('Complex<T>', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Complex.promoteUnary = {
|
Complex.promoteUnary = {
|
||||||
'Complex<T>': ({'self(T)': me, complex}) => z => complex(me(z.re), me(z.im))
|
'Complex<T>': ({
|
||||||
|
T,
|
||||||
|
'self(T)': me,
|
||||||
|
complex
|
||||||
|
}) => Returns(
|
||||||
|
`Complex<${returnTypeOf(me)}>`, z => complex(me(z.re), me(z.im)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Complex}
|
export {Complex}
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const abs = {
|
export const abs = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
sqrt, // Calculation of the type needed in the square root (the
|
T,
|
||||||
// underlying numeric type of T, whatever T is, is beyond Pocomath's
|
sqrt, // Unfortunately no notation yet for the needed signature
|
||||||
// (current) template abilities, so punt and just do full resolution
|
'absquare(T)': baseabsq,
|
||||||
'absquare(Complex<T>)': absq
|
'absquare(Complex<T>)': absq
|
||||||
}) => z => sqrt(absq(z))
|
}) => {
|
||||||
|
const midType = returnTypeOf(baseabsq)
|
||||||
|
const sqrtImp = sqrt.fromInstance.resolve('sqrt', midType, sqrt)
|
||||||
|
let retType = returnTypeOf(sqrtImp)
|
||||||
|
if (retType.includes('|')) {
|
||||||
|
// This is a bit of a hack, as it relies on all implementations of
|
||||||
|
// sqrt returning the "typical" return type as the first option
|
||||||
|
retType = retType.split('|',1)[0]
|
||||||
|
}
|
||||||
|
return Returns(retType, z => sqrtImp(absq(z)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,27 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
add, // Calculation of exact type needed in add (underlying numeric of T)
|
add, // no easy way to write the needed signature; if T is number
|
||||||
// is (currently) too involved for Pocomath
|
// it is number,number; but if T is Complex<bigint>, it is just
|
||||||
|
// bigint,bigint. So unfortunately we depend on all of add, and
|
||||||
|
// we extract the needed implementation below.
|
||||||
'self(T)': absq
|
'self(T)': absq
|
||||||
}) => z => add(absq(z.re), absq(z.im))
|
}) => {
|
||||||
|
const midType = returnTypeOf(absq)
|
||||||
|
const addImp = add.fromInstance.resolve(
|
||||||
|
'add', `${midType},${midType}`, add)
|
||||||
|
return Returns(
|
||||||
|
returnTypeOf(addImp), z => addImp(absq(z.re), absq(z.im)))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We could imagine notations that Pocomath could support that would simplify
|
||||||
|
* the above, maybe something like
|
||||||
|
* 'Complex<T>': ({
|
||||||
|
* 'self(T): U': absq,
|
||||||
|
* 'add(U,U):V': plus,
|
||||||
|
* V
|
||||||
|
* }) => Returns(V, z => plus(absq(z.re), absq(z.im)))
|
||||||
|
*/
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const add = {
|
export const add = {
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'self(T,T)': me,
|
'self(T,T)': me,
|
||||||
'complex(T,T)': cplx
|
'complex(T,T)': cplx
|
||||||
}) => (w,z) => cplx(me(w.re, z.re), me(w.im, z.im))
|
}) => Returns(`Complex<${T}>`, (w,z) => cplx(me(w.re, z.re), me(w.im, z.im)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
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 */
|
||||||
@ -9,9 +10,9 @@ export const associate = {
|
|||||||
'one(T)': uno,
|
'one(T)': uno,
|
||||||
'complex(T,T)': cplx,
|
'complex(T,T)': cplx,
|
||||||
'negate(Complex<T>)': neg
|
'negate(Complex<T>)': neg
|
||||||
}) => (w,z) => {
|
}) => Returns('boolean', (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, cplx(zr(z.re), uno(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))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
export * from '../generic/Types/generic.mjs'
|
export * from '../generic/Types/generic.mjs'
|
||||||
|
|
||||||
@ -6,15 +7,16 @@ export const complex = {
|
|||||||
* have a numeric/scalar type, e.g. by implementing subtypes in
|
* have a numeric/scalar type, e.g. by implementing subtypes in
|
||||||
* typed-function
|
* typed-function
|
||||||
*/
|
*/
|
||||||
'undefined': () => u => u,
|
'undefined': () => Returns('undefined', u => u),
|
||||||
'undefined,any': () => (u, y) => u,
|
'undefined,any': () => Returns('undefined', (u, y) => u),
|
||||||
'any,undefined': () => (x, u) => u,
|
'any,undefined': () => Returns('undefined', (x, u) => u),
|
||||||
'undefined,undefined': () => (u, v) => u,
|
'undefined,undefined': () => Returns('undefined', (u, v) => u),
|
||||||
'T,T': () => (x, y) => ({re: x, im: y}),
|
'T,T': ({T}) => Returns(`Complex<${T}>`, (x, y) => ({re: x, im: y})),
|
||||||
/* Take advantage of conversions in typed-function */
|
/* Take advantage of conversions in typed-function */
|
||||||
// 'Complex<T>': () => z => z
|
// 'Complex<T>': () => z => z
|
||||||
/* But help out because without templates built in to typed-function,
|
/* But help out because without templates built in to typed-function,
|
||||||
* type inference turns out to be too hard
|
* type inference turns out to be too hard
|
||||||
*/
|
*/
|
||||||
'T': ({'zero(T)': zr}) => x => ({re: x, im: zr(x)})
|
'T': ({T, 'zero(T)': zr}) => Returns(
|
||||||
|
`Complex<${T}>`, x => ({re: x, im: zr(x)}))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const conjugate = {
|
export const conjugate = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
|
T,
|
||||||
'negate(T)': neg,
|
'negate(T)': neg,
|
||||||
'complex(T,T)': cplx
|
'complex(T,T)': cplx
|
||||||
}) => z => cplx(z.re, neg(z.im))
|
}) => Returns(`Complex<${T}>`, z => cplx(z.re, neg(z.im)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'self(T,T)': me
|
'self(T,T)': me
|
||||||
}) => (w,z) => me(w.re, z.re) && me(w.im, z.im),
|
}) => Returns('boolean', (w, z) => me(w.re, z.re) && me(w.im, z.im)),
|
||||||
// NOTE: Although I do not understand exactly why, with typed-function@3.0's
|
// 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
|
// 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
|
// most specific match to a template call. I.e, if one of the below
|
||||||
@ -11,16 +13,16 @@ export const equalTT = {
|
|||||||
// with (Complex<Complex<number>>, Complex<number>) (!, hopefully in some
|
// with (Complex<Complex<number>>, Complex<number>) (!, hopefully in some
|
||||||
// future iteration typed-function will be smart enough to prefer
|
// future iteration typed-function will be smart enough to prefer
|
||||||
// Complex<T>, Complex<T>. Possibly the problem is in Pocomath's bolted-on
|
// 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
|
// type resolution and the difficulty will go away when features are moved
|
||||||
// typed-function.
|
// into typed-function.
|
||||||
'Complex<T>,T': ({
|
'Complex<T>,T': ({
|
||||||
'isZero(T)': isZ,
|
'isZero(T)': isZ,
|
||||||
'self(T,T)': eqReal
|
'self(T,T)': eqReal
|
||||||
}) => (z, x) => eqReal(z.re, x) && isZ(z.im),
|
}) => Returns('boolean', (z, x) => eqReal(z.re, x) && isZ(z.im)),
|
||||||
|
|
||||||
'T,Complex<T>': ({
|
'T,Complex<T>': ({
|
||||||
'isZero(T)': isZ,
|
'isZero(T)': isZ,
|
||||||
'self(T,T)': eqReal
|
'self(T,T)': eqReal
|
||||||
}) => (b, z) => eqReal(z.re, b) && isZ(z.im),
|
}) => Returns('boolean', (b, z) => eqReal(z.re, b) && isZ(z.im)),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import PocomathInstance from '../core/PocomathInstance.mjs'
|
import PocomathInstance from '../core/PocomathInstance.mjs'
|
||||||
|
import Returns from '../core/Returns.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'
|
||||||
|
|
||||||
@ -9,15 +10,16 @@ const imps = {
|
|||||||
gcdComplexRaw,
|
gcdComplexRaw,
|
||||||
gcd: { // Only return gcds with positive real part
|
gcd: { // Only return gcds with positive real part
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'gcdComplexRaw(Complex<T>,Complex<T>)': gcdRaw,
|
'gcdComplexRaw(Complex<T>,Complex<T>)': gcdRaw,
|
||||||
'sign(T)': sgn,
|
'sign(T)': sgn,
|
||||||
'one(T)': uno,
|
'one(T)': uno,
|
||||||
'negate(Complex<T>)': neg
|
'negate(Complex<T>)': neg
|
||||||
}) => (z,m) => {
|
}) => Returns(`Complex<${T}>`, (z,m) => {
|
||||||
const raw = gcdRaw(z, m)
|
const raw = gcdRaw(z, m)
|
||||||
if (sgn(raw.re) === uno(raw.re)) return raw
|
if (sgn(raw.re) === uno(raw.re)) return raw
|
||||||
return neg(raw)
|
return neg(raw)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const invert = {
|
export const invert = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
|
T,
|
||||||
'conjugate(Complex<T>)': conj,
|
'conjugate(Complex<T>)': conj,
|
||||||
'absquare(Complex<T>)': asq,
|
'absquare(Complex<T>)': asq,
|
||||||
'complex(T,T)': cplx,
|
'complex(T,T)': cplx,
|
||||||
'divide(T,T)': div
|
'divide(T,T)': div
|
||||||
}) => z => {
|
}) => Returns(`Complex<${T}>`, z => {
|
||||||
const c = conj(z)
|
const c = conj(z)
|
||||||
const d = asq(z)
|
const d = asq(z)
|
||||||
return cplx(div(c.re, d), div(c.im, d))
|
return cplx(div(c.re, d), div(c.im, d))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
'Complex<T>': ({'self(T)': me}) => z => me(z.re) && me(z.im)
|
'Complex<T>': ({'self(T)': me}) => Returns(
|
||||||
|
'boolean', z => me(z.re) && me(z.im))
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const multiply = {
|
export const multiply = {
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'complex(T,T)': cplx,
|
'complex(T,T)': cplx,
|
||||||
'add(T,T)': plus,
|
'add(T,T)': plus,
|
||||||
'subtract(T,T)': sub,
|
'subtract(T,T)': subt,
|
||||||
'self(T,T)': me,
|
'self(T,T)': me,
|
||||||
'conjugate(T)': conj // makes quaternion multiplication work
|
'conjugate(T)': conj // makes quaternion multiplication work
|
||||||
}) => (w,z) => {
|
}) => Returns(
|
||||||
return cplx(
|
`Complex<${T}>`,
|
||||||
sub(me(w.re, z.re), me(conj(w.im), z.im)),
|
(w,z) => {
|
||||||
plus(me(conj(w.re), z.im), me(w.im, z.re)))
|
const realpart = subt(me( w.re, z.re), me(conj(w.im), z.im))
|
||||||
|
const imagpart = plus(me(conj(w.re), z.im), me( w.im, z.re))
|
||||||
|
return cplx(realpart, imagpart)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
// Might be nice to have type aliases!
|
||||||
export const quaternion = {
|
export const quaternion = {
|
||||||
'T,T,T,T': ({complex}) => (r,i,j,k) => complex(complex(r,j), complex(i,k))
|
'T,T,T,T': ({
|
||||||
|
T,
|
||||||
|
'complex(T,T)': cplxT,
|
||||||
|
'complex(Complex<T>,Complex<T>)': quat
|
||||||
|
}) => Returns(
|
||||||
|
`Complex<Complex<${T}>>`,
|
||||||
|
(r,i,j,k) => quat(cplxT(r,j), cplxT(i,k))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './roundquotient.mjs'
|
export * from './roundquotient.mjs'
|
||||||
|
|
||||||
export const quotient = {
|
export const quotient = {
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'roundquotient(Complex<T>,Complex<T>)': rq
|
'roundquotient(Complex<T>,Complex<T>)': rq
|
||||||
}) => (w,z) => rq(w,z)
|
}) => Returns(`Complex<${T}>`, (w,z) => rq(w,z))
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'Complex<T>,Complex<T>': ({
|
'Complex<T>,Complex<T>': ({
|
||||||
|
T,
|
||||||
'isZero(Complex<T>)': isZ,
|
'isZero(Complex<T>)': isZ,
|
||||||
'conjugate(Complex<T>)': conj,
|
'conjugate(Complex<T>)': conj,
|
||||||
'multiply(Complex<T>,Complex<T>)': mult,
|
'multiply(Complex<T>,Complex<T>)': mult,
|
||||||
'absquare(Complex<T>)': asq,
|
'absquare(Complex<T>)': asq,
|
||||||
'self(T,T)': me,
|
'self(T,T)': me,
|
||||||
'complex(T,T)': cplx
|
'complex(T,T)': cplx
|
||||||
}) => (n,d) => {
|
}) => Returns(`Complex<${T}>`, (n,d) => {
|
||||||
if (isZ(d)) return d
|
if (isZ(d)) return d
|
||||||
const cnum = mult(n, conj(d))
|
const cnum = mult(n, conj(d))
|
||||||
const dreal = asq(d)
|
const dreal = asq(d)
|
||||||
return cplx(me(cnum.re, dreal), me(cnum.im, dreal))
|
return cplx(me(cnum.re, dreal), me(cnum.im, dreal))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
@ -12,29 +13,41 @@ export const sqrt = {
|
|||||||
'multiply(T,T)': mult,
|
'multiply(T,T)': mult,
|
||||||
'self(T)': me,
|
'self(T)': me,
|
||||||
'divide(T,T)': div,
|
'divide(T,T)': div,
|
||||||
'abs(Complex<T>)': absC,
|
'absquare(Complex<T>)': absqC,
|
||||||
'subtract(T,T)': sub
|
'subtract(T,T)': sub
|
||||||
}) => {
|
}) => {
|
||||||
|
let baseReturns = returnTypeOf(me)
|
||||||
|
if (baseReturns.includes('|')) {
|
||||||
|
// Bit of a hack, because it is relying on other implementations
|
||||||
|
// to list the "typical" value of sqrt first
|
||||||
|
baseReturns = baseReturns.split('|', 1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
if (config.predictable) {
|
if (config.predictable) {
|
||||||
return z => {
|
return Returns(`Complex<${baseReturns}>`, z => {
|
||||||
const reOne = uno(z.re)
|
const reOne = uno(z.re)
|
||||||
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(me(z.re))
|
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(me(z.re))
|
||||||
const reTwo = plus(reOne, reOne)
|
const reTwo = plus(reOne, reOne)
|
||||||
|
const myabs = me(absqC(z))
|
||||||
return cplxB(
|
return cplxB(
|
||||||
mult(sgn(z.im), me(div(plus(absC(z),z.re), reTwo))),
|
mult(sgn(z.im), me(div(plus(myabs, z.re), reTwo))),
|
||||||
me(div(sub(absC(z),z.re), reTwo))
|
me(div(sub(myabs, z.re), reTwo))
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return z => {
|
return Returns(
|
||||||
|
`Complex<${baseReturns}>|${baseReturns}|undefined`,
|
||||||
|
z => {
|
||||||
const reOne = uno(z.re)
|
const reOne = uno(z.re)
|
||||||
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
||||||
const reTwo = plus(reOne, reOne)
|
const reTwo = plus(reOne, reOne)
|
||||||
const reSqrt = me(div(plus(absC(z),z.re), reTwo))
|
const myabs = me(absqC(z))
|
||||||
const imSqrt = me(div(sub(absC(z),z.re), reTwo))
|
const reSqrt = me(div(plus(myabs, z.re), reTwo))
|
||||||
|
const imSqrt = me(div(sub(myabs, z.re), reTwo))
|
||||||
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
||||||
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
34
src/core/Returns.mjs
Normal file
34
src/core/Returns.mjs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* Annotate a function with its return type */
|
||||||
|
|
||||||
|
/* Unfortunately JavaScript is missing a way to cleanly clone a function
|
||||||
|
* object, see https://stackoverflow.com/questions/1833588
|
||||||
|
*/
|
||||||
|
|
||||||
|
const clonedFrom = Symbol('the original function this one was cloned from')
|
||||||
|
function cloneFunction(fn) {
|
||||||
|
const behavior = fn[clonedFrom] || fn // don't nest clones
|
||||||
|
const theClone = function () { return behavior.apply(this, arguments) }
|
||||||
|
Object.assign(theClone, fn)
|
||||||
|
theClone[clonedFrom] = body
|
||||||
|
Object.defineProperty(
|
||||||
|
theClone, 'name', {value: fn.name, configurable: true })
|
||||||
|
return theClone
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Returns(type, fn) {
|
||||||
|
if ('returns' in fn) fn = cloneFunction(fn)
|
||||||
|
fn.returns = type
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
export function returnTypeOf(fn, signature, pmInstance) {
|
||||||
|
const typeOfReturns = typeof fn.returns
|
||||||
|
if (typeOfReturns === 'undefined') return 'any'
|
||||||
|
if (typeOfReturns === 'string') return fn.returns
|
||||||
|
// not sure if we will need a function to determine the return type,
|
||||||
|
// but allow it for now:
|
||||||
|
return fn.returns(signature, pmInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Returns
|
||||||
|
|
@ -8,6 +8,8 @@ export function subsetOfKeys(set, obj) {
|
|||||||
|
|
||||||
/* Returns a list of the types mentioned in a typed-function signature */
|
/* Returns a list of the types mentioned in a typed-function signature */
|
||||||
export function typeListOfSignature(signature) {
|
export function typeListOfSignature(signature) {
|
||||||
|
signature = signature.trim()
|
||||||
|
if (!signature) return []
|
||||||
return signature.split(',').map(s => s.trim())
|
return signature.split(',').map(s => s.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
import Returns from '../../core/Returns.mjs'
|
||||||
|
|
||||||
/* creates a PocomathInstance incorporating a new numeric type encapsulated
|
/* creates a PocomathInstance incorporating a new numeric type encapsulated
|
||||||
* as a class. (This instance can the be `install()ed` in another to add the
|
* as a class. (This instance can the be `install()ed` in another to add the
|
||||||
* type so created.)
|
* type so created.)
|
||||||
@ -22,15 +24,15 @@ export default function adapted(name, Thing, overrides) {
|
|||||||
// first a creator function, with name depending on the name of the thing:
|
// first a creator function, with name depending on the name of the thing:
|
||||||
const creatorName = overrides.creatorName || name.toLowerCase()
|
const creatorName = overrides.creatorName || name.toLowerCase()
|
||||||
const creator = overrides[creatorName]
|
const creator = overrides[creatorName]
|
||||||
? overrides[creatorName]('')
|
? overrides[creatorName]['']
|
||||||
: Thing[creatorName]
|
: Thing[creatorName]
|
||||||
? (Thing[creatorName])
|
? (Thing[creatorName])
|
||||||
: ((...args) => new Thing(...args))
|
: ((...args) => new Thing(...args))
|
||||||
const defaultCreatorImps = {
|
const defaultCreatorImps = {
|
||||||
'': () => () => creator(),
|
'': () => Returns(name, () => creator()),
|
||||||
'...any': () => args => creator(...args)
|
'...any': () => Returns(name, args => creator(...args))
|
||||||
}
|
}
|
||||||
defaultCreatorImps[name] = () => x => x // x.clone(x)?
|
defaultCreatorImps[name] = () => Returns(name, x => x) // x.clone(x)?
|
||||||
operations[creatorName] = overrides[creatorName] || defaultCreatorImps
|
operations[creatorName] = overrides[creatorName] || defaultCreatorImps
|
||||||
|
|
||||||
// We make the default instance, just as a place to check for methods
|
// We make the default instance, just as a place to check for methods
|
||||||
@ -47,34 +49,35 @@ export default function adapted(name, Thing, overrides) {
|
|||||||
negate: 'neg'
|
negate: 'neg'
|
||||||
}
|
}
|
||||||
const binaryOps = {
|
const binaryOps = {
|
||||||
add: 'add',
|
add: ['add', name],
|
||||||
compare: 'compare',
|
compare: ['compare', name],
|
||||||
divide: 'div',
|
divide: ['div', name],
|
||||||
equalTT: 'equals',
|
equalTT: ['equals', 'boolean'],
|
||||||
gcd: 'gcd',
|
gcd: ['gcd', name],
|
||||||
lcm: 'lcm',
|
lcm: ['lcm', name],
|
||||||
mod: 'mod',
|
mod: ['mod', name],
|
||||||
multiply: 'mul',
|
multiply: ['mul', name],
|
||||||
subtract: 'sub'
|
subtract: ['sub', name]
|
||||||
}
|
}
|
||||||
for (const [mathname, standardname] of Object.entries(unaryOps)) {
|
for (const [mathname, standardname] of Object.entries(unaryOps)) {
|
||||||
if (standardname in instance) {
|
if (standardname in instance) {
|
||||||
operations[mathname] = {}
|
operations[mathname] = {}
|
||||||
operations[mathname][name] = () => t => t[standardname]()
|
operations[mathname][name] = () => Returns(name, t => t[standardname]())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operations.zero = {}
|
operations.zero = {}
|
||||||
operations.zero[name] = () => t => creator()
|
operations.zero[name] = () => Returns(name, t => creator())
|
||||||
operations.one = {}
|
operations.one = {}
|
||||||
operations.one[name] = () => t => creator(1)
|
operations.one[name] = () => Returns(name, t => creator(1))
|
||||||
operations.conjugate = {}
|
operations.conjugate = {}
|
||||||
operations.conjugate[name] = () => t => t // or t.clone() ??
|
operations.conjugate[name] = () => Returns(name, t => t) // or t.clone() ??
|
||||||
|
|
||||||
const binarySignature = `${name},${name}`
|
const binarySignature = `${name},${name}`
|
||||||
for (const [mathname, standardname] of Object.entries(binaryOps)) {
|
for (const [mathname, spec] of Object.entries(binaryOps)) {
|
||||||
if (standardname in instance) {
|
if (spec[0] in instance) {
|
||||||
operations[mathname] = {}
|
operations[mathname] = {}
|
||||||
operations[mathname][binarySignature] = () => (t,u) => t[standardname](u)
|
operations[mathname][binarySignature] = () => Returns(
|
||||||
|
spec[1], (t,u) => t[spec[0]](u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ('operations' in overrides) {
|
if ('operations' in overrides) {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export const abs = {
|
export const abs = {
|
||||||
T: ({
|
T: ({
|
||||||
|
T,
|
||||||
'smaller(T,T)': lt,
|
'smaller(T,T)': lt,
|
||||||
'negate(T)': neg,
|
'negate(T)': neg,
|
||||||
'zero(T)': zr
|
'zero(T)': zr
|
||||||
}) => t => (smaller(t, zr(t)) ? neg(t) : t)
|
}) => Returns(T, t => (smaller(t, zr(t)) ? neg(t) : t))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
T: ({
|
T: ({
|
||||||
|
T,
|
||||||
'square(T)': sq,
|
'square(T)': sq,
|
||||||
'abs(T)': abval
|
'abs(T)': abval
|
||||||
}) => t => sq(abval(t))
|
}) => Returns(T, t => sq(abval(t)))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {adapted} from './Types/adapted.mjs'
|
import {adapted} from './Types/adapted.mjs'
|
||||||
import Fraction from 'fraction.js/bigfraction.js'
|
import Fraction from 'fraction.js/bigfraction.js'
|
||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './arithmetic.mjs'
|
export * from './arithmetic.mjs'
|
||||||
export * from './relational.mjs'
|
export * from './relational.mjs'
|
||||||
@ -8,15 +9,18 @@ export const fraction = adapted('Fraction', Fraction, {
|
|||||||
before: ['Complex'],
|
before: ['Complex'],
|
||||||
from: {number: n => new Fraction(n)},
|
from: {number: n => new Fraction(n)},
|
||||||
operations: {
|
operations: {
|
||||||
compare: {'Fraction,Fraction': () => (f,g) => new Fraction(f.compare(g))},
|
compare: {
|
||||||
|
'Fraction,Fraction': () => Returns(
|
||||||
|
'Fraction', (f,g) => new Fraction(f.compare(g)))
|
||||||
|
},
|
||||||
mod: {
|
mod: {
|
||||||
'Fraction,Fraction': () => (n,d) => {
|
'Fraction,Fraction': () => Returns('Fraction', (n,d) => {
|
||||||
// patch for "mathematician's modulus"
|
// patch for "mathematician's modulus"
|
||||||
// OK to use full public API of Fraction here
|
// OK to use full public API of Fraction here
|
||||||
const fmod = n.mod(d)
|
const fmod = n.mod(d)
|
||||||
if (fmod.s === -1n) return fmod.add(d.abs())
|
if (fmod.s === -1n) return fmod.add(d.abs())
|
||||||
return fmod
|
return fmod
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const divide = {
|
export const divide = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'invert(T)': invT
|
'invert(T)': invT
|
||||||
}) => (x, y) => multT(x, invT(y))
|
}) => Returns(T, (x, y) => multT(x, invT(y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
/* Note we do not use a template here so that we can explicitly control
|
/* Note we do not use a template here so that we can explicitly control
|
||||||
* which types this is instantiated for, namely the "integer" types, and
|
* which types this is instantiated for, namely the "integer" types, and
|
||||||
* not simply allow Pocomath to generate instances for any type it encounters.
|
* not simply allow Pocomath to generate instances for any type it encounters.
|
||||||
@ -7,14 +9,14 @@ export default function(type) {
|
|||||||
const producer = refs => {
|
const producer = refs => {
|
||||||
const modder = refs[`mod(${type},${type})`]
|
const modder = refs[`mod(${type},${type})`]
|
||||||
const zeroTester = refs[`isZero(${type})`]
|
const zeroTester = refs[`isZero(${type})`]
|
||||||
return (a,b) => {
|
return Returns(type, (a,b) => {
|
||||||
while (!zeroTester(b)) {
|
while (!zeroTester(b)) {
|
||||||
const r = modder(a,b)
|
const r = modder(a,b)
|
||||||
a = b
|
a = b
|
||||||
b = r
|
b = r
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
const retval = {}
|
const retval = {}
|
||||||
retval[`${type},${type}`] = producer
|
retval[`${type},${type}`] = producer
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
export function identity(x) {
|
import Returns from '../core/Returns.mjs'
|
||||||
return x
|
|
||||||
|
export function identityType(type) {
|
||||||
|
return () => Returns(type, x => x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function identitySubTypes(type) {
|
||||||
|
return ({T}) => Returns(T, x => x)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const identity = {T: ({T}) => Returns(T, x => x)}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
import {reducingOperation} from './reducingOperation.mjs'
|
import {reducingOperation} from './reducingOperation.mjs'
|
||||||
|
|
||||||
export const lcm = {
|
export const lcm = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'quotient(T,T)': quotT,
|
'quotient(T,T)': quotT,
|
||||||
'gcd(T,T)': gcdT
|
'gcd(T,T)': gcdT
|
||||||
}) => (a,b) => multT(quotT(a, gcdT(a,b)), b)
|
}) => Returns(T, (a,b) => multT(quotT(a, gcdT(a,b)), b))
|
||||||
}
|
}
|
||||||
Object.assign(lcm, reducingOperation)
|
Object.assign(lcm, reducingOperation)
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export const mean = {
|
export const mean = {
|
||||||
'...any': ({add, divide}) => args => divide(add(...args), args.length)
|
'...T': ({
|
||||||
|
T,
|
||||||
|
add,
|
||||||
|
'divide(T,NumInt)': div
|
||||||
|
}) => Returns(T, args => div(add(...args), args.length))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const mod = {
|
export const mod = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'subtract(T,T)': subT,
|
'subtract(T,T)': subT,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'quotient(T,T)': quotT
|
'quotient(T,T)': quotT
|
||||||
}) => (a,m) => subT(a, multT(m, quotT(a,m)))
|
}) => Returns(T, (a,m) => subT(a, multT(m, quotT(a,m))))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const quotient = {
|
export const quotient = {
|
||||||
'T,T': ({'floor(T)': flr, 'divide(T,T)':div}) => (n,d) => flr(div(n,d))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'floor(T)': flr,
|
||||||
|
'divide(T,T)': div
|
||||||
|
}) => Returns(T, (n,d) => flr(div(n,d)))
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/generic.mjs'
|
export * from './Types/generic.mjs'
|
||||||
|
|
||||||
export const reducingOperation = {
|
export const reducingOperation = {
|
||||||
'undefined': () => u => u,
|
'undefined': () => Returns('undefined', u => u),
|
||||||
'undefined,...any': () => (u, rest) => u,
|
'undefined,...any': () => Returns('undefined', (u, rest) => u),
|
||||||
'any,undefined': () => (x, u) => u,
|
'any,undefined': () => Returns('undefined', (x, u) => u),
|
||||||
'undefined,undefined': () => (u,v) => u,
|
'undefined,undefined': () => Returns('undefined', (u,v) => u),
|
||||||
any: () => x => x,
|
T: ({T}) => Returns(T, x => x),
|
||||||
|
// Unfortunately the type language of Pocomath is not (yet?) expressive
|
||||||
|
// enough to properly type the full reduction signature here:
|
||||||
'any,any,...any': ({
|
'any,any,...any': ({
|
||||||
self
|
self
|
||||||
}) => (a,b,rest) => [b, ...rest].reduce((x,y) => self(x,y), a)
|
}) => Returns('any', (a,b,rest) => [b, ...rest].reduce((x,y) => self(x,y), a))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +1,45 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const compare = {
|
export const compare = {
|
||||||
'undefined,undefined': () => () => 0
|
'undefined,undefined': () => Returns('NumInt', () => 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
'undefined': () => u => u === 0,
|
'undefined': () => Returns('boolean', u => u === 0),
|
||||||
T: ({'equal(T,T)': eq, 'zero(T)': zr}) => t => eq(t, zr(t))
|
T: ({
|
||||||
|
T,
|
||||||
|
'equal(T,T)': eq,
|
||||||
|
'zero(T)': zr
|
||||||
|
}) => Returns('boolean', t => eq(t, zr(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const equal = {
|
export const equal = {
|
||||||
'any,any': ({equalTT, joinTypes, Templates, typeOf}) => (x,y) => {
|
'any,any': ({
|
||||||
|
equalTT,
|
||||||
|
joinTypes,
|
||||||
|
Templates,
|
||||||
|
typeOf
|
||||||
|
}) => Returns('boolean', (x,y) => {
|
||||||
const resultant = joinTypes([typeOf(x), typeOf(y)], 'convert')
|
const resultant = joinTypes([typeOf(x), typeOf(y)], 'convert')
|
||||||
if (resultant === 'any' || resultant in Templates) {
|
if (resultant === 'any' || resultant in Templates) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return equalTT(x,y)
|
return equalTT(x,y)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'isZero(T)': isZ
|
'isZero(T)': isZ
|
||||||
}) => (x,y) => isZ(cmp(x,y)),
|
}) => Returns('boolean', (x,y) => isZ(cmp(x,y)))
|
||||||
// If templates were native to typed-function, we should be able to
|
// If templates were native to typed-function, we should be able to
|
||||||
// do something like:
|
// do something like:
|
||||||
// 'any,any': () => () => false // should only be hit for different types
|
// 'any,any': () => () => false // should only be hit for different types
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unequal = {
|
export const unequal = {
|
||||||
'any,any': ({equal}) => (x,y) => !(equal(x,y))
|
'any,any': ({equal}) => Returns('boolean', (x,y) => !(equal(x,y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const larger = {
|
export const larger = {
|
||||||
@ -36,7 +47,7 @@ export const larger = {
|
|||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'equalTT(T,T)' : eq
|
'equalTT(T,T)' : eq
|
||||||
}) => (x,y) => eq(cmp(x,y), uno(y))
|
}) => Returns('boolean', (x,y) => eq(cmp(x,y), uno(y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const largerEq = {
|
export const largerEq = {
|
||||||
@ -45,10 +56,10 @@ export const largerEq = {
|
|||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'isZero(T)' : isZ,
|
'isZero(T)' : isZ,
|
||||||
'equalTT(T,T)': eq
|
'equalTT(T,T)': eq
|
||||||
}) => (x,y) => {
|
}) => Returns('boolean', (x,y) => {
|
||||||
const c = cmp(x,y)
|
const c = cmp(x,y)
|
||||||
return isZ(c) || eq(c, uno(y))
|
return isZ(c) || eq(c, uno(y))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const smaller = {
|
export const smaller = {
|
||||||
@ -57,10 +68,10 @@ export const smaller = {
|
|||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'isZero(T)' : isZ,
|
'isZero(T)' : isZ,
|
||||||
unequal
|
unequal
|
||||||
}) => (x,y) => {
|
}) => Returns('boolean', (x,y) => {
|
||||||
const c = cmp(x,y)
|
const c = cmp(x,y)
|
||||||
return !isZ(c) && unequal(c, uno(y))
|
return !isZ(c) && unequal(c, uno(y))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const smallerEq = {
|
export const smallerEq = {
|
||||||
@ -68,5 +79,5 @@ export const smallerEq = {
|
|||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
unequal
|
unequal
|
||||||
}) => (x,y) => unequal(cmp(x,y), uno(y))
|
}) => Returns('boolean', (x,y) => unequal(cmp(x,y), uno(y)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'T,T': ({'round(T)': rnd, 'divide(T,T)':div}) => (n,d) => rnd(div(n,d))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'round(T)': rnd,
|
||||||
|
'divide(T,T)':div
|
||||||
|
}) => Returns(T, (n,d) => rnd(div(n,d)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const sign = {
|
export const sign = {
|
||||||
T: ({'compare(T,T)': cmp, 'zero(T)': Z}) => x => cmp(x, Z(x))
|
T: ({
|
||||||
|
T,
|
||||||
|
'compare(T,T)': cmp,
|
||||||
|
'zero(T)': Z
|
||||||
|
}) => Returns(T, x => cmp(x, Z(x)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/generic.mjs'
|
export * from './Types/generic.mjs'
|
||||||
|
|
||||||
export const sqrt = {undefined: () => () => undefined}
|
export const sqrt = {undefined: () => Returns('undefined', () => undefined)}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const square = {
|
export const square = {
|
||||||
T: ({'multiply(T,T)': multT}) => x => multT(x,x)
|
T: ({'multiply(T,T)': multT}) => Returns(
|
||||||
|
returnTypeOf(multT), x => multT(x,x))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const subtract = {
|
export const subtract = {
|
||||||
'T,T': ({'add(T,T)': addT, 'negate(T)': negT}) => (x,y) => addT(x, negT(y))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'add(T,T)': addT,
|
||||||
|
'negate(T)': negT
|
||||||
|
}) => Returns(T, (x,y) => addT(x, negT(y)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const abs = {number: () => n => Math.abs(n)}
|
export const abs = {'T:number': ({T}) => Returns(T, n => Math.abs(n))}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
/* Absolute value squared */
|
/* Absolute value squared */
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
number: ({'square(number)': sqn}) => n => sqn(n)
|
'T:number': ({T, 'square(T)': sqn}) => Returns(T, n => sqn(n))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const add = {'number,number': () => (m,n) => m+n}
|
export const add = {
|
||||||
|
// Note the below assumes that all subtypes of number that will be defined
|
||||||
|
// are closed under addition!
|
||||||
|
'T:number, T': ({T}) => Returns(T, (m,n) => m+n)
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
/* Lifted from mathjs/src/utils/number.js */
|
/* Lifted from mathjs/src/utils/number.js */
|
||||||
/**
|
/**
|
||||||
* Minimum number added to one that makes the result different than one
|
* Minimum number added to one that makes the result different than one
|
||||||
@ -48,5 +50,6 @@ function nearlyEqual (x, y, epsilon) {
|
|||||||
export const compare = {
|
export const compare = {
|
||||||
'number,number': ({
|
'number,number': ({
|
||||||
config
|
config
|
||||||
}) => (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1)
|
}) => Returns(
|
||||||
|
'NumInt', (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const invert = {number: () => n => 1/n}
|
export const invert = {number: () => Returns('number', n => 1/n)}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
number: () => n => n === 0,
|
'T:number': () => Returns('boolean', n => n === 0)
|
||||||
NumInt: () => n => n === 0 // necessary because of generic template
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const multiply = {'number,number': () => (m,n) => m*n}
|
export const multiply = {'T:number,T': ({T}) => Returns(T, (m,n) => m*n)}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import gcdType from '../generic/gcdType.mjs'
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
import {identity} from '../generic/identity.mjs'
|
import {identitySubTypes} from '../generic/identity.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ export {abs} from './abs.mjs'
|
|||||||
export {absquare} from './absquare.mjs'
|
export {absquare} from './absquare.mjs'
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
export {compare} from './compare.mjs'
|
export {compare} from './compare.mjs'
|
||||||
export const conjugate = {number: () => identity}
|
export const conjugate = {'T:number': identitySubTypes('number')}
|
||||||
export const gcd = gcdType('NumInt')
|
export const gcd = gcdType('NumInt')
|
||||||
export {invert} from './invert.mjs'
|
export {invert} from './invert.mjs'
|
||||||
export {isZero} from './isZero.mjs'
|
export {isZero} from './isZero.mjs'
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const negate = {number: () => n => -n}
|
export const negate = {
|
||||||
|
'T:number': ({T}) => Returns(T, n => -n)
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const one = {number: () => () => 1}
|
export const one = {number: () => Returns('NumInt', () => 1)}
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
const intquotient = () => (n,d) => {
|
export const quotient = {
|
||||||
|
'T:number,T': () => Returns('NumInt', (n,d) => {
|
||||||
if (d === 0) return d
|
if (d === 0) return d
|
||||||
return Math.floor(n/d)
|
return Math.floor(n/d)
|
||||||
}
|
})
|
||||||
|
|
||||||
export const quotient = {
|
|
||||||
// Hmmm, seem to need all of these because of the generic template version
|
|
||||||
// Should be a way around that
|
|
||||||
'NumInt,NumInt': intquotient,
|
|
||||||
'NumInt,number': intquotient,
|
|
||||||
'number,NumInt': intquotient,
|
|
||||||
'number,number': intquotient
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'number,number': () => (n,d) => {
|
'number,number': () => Returns('NumInt', (n,d) => {
|
||||||
if (d === 0) return d
|
if (d === 0) return d
|
||||||
return Math.round(n/d)
|
return Math.round(n/d)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
@ -6,12 +7,12 @@ export const sqrt = {
|
|||||||
'complex(number,number)': cplx,
|
'complex(number,number)': cplx,
|
||||||
'negate(number)': neg}) => {
|
'negate(number)': neg}) => {
|
||||||
if (config.predictable || !cplx) {
|
if (config.predictable || !cplx) {
|
||||||
return n => isNaN(n) ? NaN : Math.sqrt(n)
|
return Returns('number', n => isNaN(n) ? NaN : Math.sqrt(n))
|
||||||
}
|
}
|
||||||
return n => {
|
return Returns('number|Complex<number>', 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 cplx(0, Math.sqrt(neg(n)))
|
return cplx(0, Math.sqrt(neg(n)))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const zero = {number: () => () => 0}
|
export const zero = {number: () => Returns('NumInt', () => 0)}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
/* Note this is not a good algorithm for computing binomial coefficients,
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
|
/* Note this is _not_ a good algorithm for computing binomial coefficients,
|
||||||
* it's just for demonstration purposes
|
* it's just for demonstration purposes
|
||||||
*/
|
*/
|
||||||
export const choose = {
|
export const choose = {
|
||||||
'NumInt,NumInt': ({factorial}) => (n,k) => Number(
|
'NumInt,NumInt': ({factorial}) => Returns(
|
||||||
factorial(n) / (factorial(k)*factorial(n-k))),
|
'NumInt', (n,k) => Number(factorial(n) / (factorial(k)*factorial(n-k)))),
|
||||||
'bigint,bigint': ({
|
'bigint,bigint': ({
|
||||||
factorial
|
factorial
|
||||||
}) => (n,k) => factorial(n) / (factorial(k)*factorial(n-k))
|
}) => Returns('bigint', (n,k) => factorial(n) / (factorial(k)*factorial(n-k)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
export function factorial(n) {
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
|
/* Plain functions are OK, too, and they can be decorated with a return type
|
||||||
|
* just like an implementation.
|
||||||
|
*/
|
||||||
|
const factorial = Returns('bigint', function factorial(n) {
|
||||||
n = BigInt(n)
|
n = BigInt(n)
|
||||||
let prod = 1n
|
let prod = 1n
|
||||||
for (let i = n; i > 1n; --i) {
|
for (let i = n; i > 1n; --i) {
|
||||||
prod *= i
|
prod *= i
|
||||||
}
|
}
|
||||||
return prod
|
return prod
|
||||||
}
|
})
|
||||||
|
|
||||||
|
export {factorial}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
import {Complex} from '../complex/Types/Complex.mjs'
|
import {Complex} from '../complex/Types/Complex.mjs'
|
||||||
|
|
||||||
/* Note we don't **export** any types here, so that only the options
|
/* Note we don't **export** any types here, so that only the options
|
||||||
@ -5,26 +6,28 @@ import {Complex} from '../complex/Types/Complex.mjs'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const floor = {
|
export const floor = {
|
||||||
bigint: () => x => x,
|
/* Because Pocomath isn't part of typed-function, nor does it have access
|
||||||
NumInt: () => x => x, // Because Pocomath isn't part of typed-function, or
|
* to the real typed-function parse, we unfortunately can't coalesce the
|
||||||
'Complex<bigint>': () => x => x, // at least have access to the real
|
* first several implementations into one entry with type
|
||||||
// typed-function parse, we unfortunately can't coalesce these into one
|
* `bigint|NumInt|GaussianInteger` because then they couldn't
|
||||||
// entry with type `bigint|NumInt|GaussianInteger` because they couldn't
|
* be separately activated
|
||||||
// be separately activated then
|
*/
|
||||||
|
bigint: () => Returns('bigint', x => x),
|
||||||
|
NumInt: () => Returns('NumInt', x => x),
|
||||||
|
'Complex<bigint>': () => Returns('Complex<bigint>', x => x),
|
||||||
|
|
||||||
number: ({'equalTT(number,number)': eq}) => n => {
|
number: ({'equalTT(number,number)': eq}) => Returns('NumInt', n => {
|
||||||
if (eq(n, Math.round(n))) return Math.round(n)
|
if (eq(n, Math.round(n))) return Math.round(n)
|
||||||
return Math.floor(n)
|
return Math.floor(n)
|
||||||
},
|
}),
|
||||||
|
|
||||||
'Complex<T>': Complex.promoteUnary['Complex<T>'],
|
'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.
|
||||||
// Fraction: ({quotient}) => f => quotient(f.n, f.d), // oops have that now
|
|
||||||
BigNumber: ({
|
BigNumber: ({
|
||||||
'round(BigNumber)': rnd,
|
'round(BigNumber)': rnd,
|
||||||
'equal(BigNumber,BigNumber)': eq
|
'equal(BigNumber,BigNumber)': eq
|
||||||
}) => x => eq(x,round(x)) ? round(x) : x.floor()
|
}) => Returns('BigNumber', x => eq(x,round(x)) ? round(x) : x.floor())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
/* A template type representing a homeogeneous tuple of elements */
|
/* A template type representing a homeogeneous tuple of elements */
|
||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
import {Returns, returnTypeOf} from '../../core/Returns.mjs'
|
||||||
|
|
||||||
const Tuple = new PocomathInstance('Tuple')
|
const Tuple = new PocomathInstance('Tuple')
|
||||||
// First a base type that will generally not be used directly
|
|
||||||
Tuple.installType('Tuple', {
|
|
||||||
test: t => t && typeof t === 'object' && 'elts' in t && Array.isArray(t.elts)
|
|
||||||
})
|
|
||||||
// Now the template type that is the primary use of this
|
|
||||||
Tuple.installType('Tuple<T>', {
|
Tuple.installType('Tuple<T>', {
|
||||||
// We are assuming that any 'Type<T>' refines 'Type', so this is
|
// A test that "defines" the "base type", which is not really a type
|
||||||
// not necessary:
|
// (only fully instantiated types are added to the universe)
|
||||||
// refines: 'Tuple',
|
base: t => t && typeof t === 'object' && 'elts' in t && Array.isArray(t.elts),
|
||||||
// But we need there to be a way to determine the type of a tuple:
|
// The template portion of the test; it takes the test for T as
|
||||||
infer: ({typeOf, joinTypes}) => t => joinTypes(t.elts.map(typeOf)),
|
// input and returns the test for an entity _that already passes
|
||||||
// For the test, we can assume that t is already a base tuple,
|
// the base test_ to be a Tuple<T>:
|
||||||
// and we get the test for T as an input and we have to return
|
|
||||||
// the test for Tuple<T>
|
|
||||||
test: testT => t => t.elts.every(testT),
|
test: testT => t => t.elts.every(testT),
|
||||||
// These are only invoked for types U such that there is already
|
// And we need there to be a way to determine the (instantiation)
|
||||||
// a conversion from U to T, and that conversion is passed as an input
|
// type of an tuple (that has already passed the base test):
|
||||||
// and we have to return the conversion to Tuple<T>:
|
infer: ({typeOf, joinTypes}) => t => joinTypes(t.elts.map(typeOf)),
|
||||||
|
// Conversions. Parametrized conversions are only invoked for types
|
||||||
|
// U such that there is already a conversion from U to T, and that
|
||||||
|
// conversion is passed as an input, and we have to return the conversion
|
||||||
|
// function from the indicated template in terms of U to Tuple<T>:
|
||||||
from: {
|
from: {
|
||||||
'Tuple<U>': convert => tu => ({elts: tu.elts.map(convert)}),
|
'Tuple<U>': convert => tu => ({elts: tu.elts.map(convert)}),
|
||||||
// Here since there is no U it's a straight conversion:
|
// Here since there is no U it's a straight conversion:
|
||||||
@ -35,13 +34,20 @@ Tuple.promoteUnary = {
|
|||||||
'Tuple<T>': ({
|
'Tuple<T>': ({
|
||||||
'self(T)': me,
|
'self(T)': me,
|
||||||
tuple
|
tuple
|
||||||
}) => t => tuple(...(t.elts.map(x => me(x)))) // NOTE: this must use
|
}) => {
|
||||||
// the inner arrow function to drop additional arguments that Array.map
|
const compType = me.fromInstance.joinTypes(
|
||||||
// supplies, as otherwise the wrong signature of `me` might be used.
|
returnTypeOf(me).split('|'), 'convert')
|
||||||
|
return Returns(
|
||||||
|
`Tuple<${compType}>`, t => tuple(...(t.elts.map(x => me(x)))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinaryUnary = {
|
Tuple.promoteBinaryUnary = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => {
|
||||||
|
const compTypes = returnTypeOf(meB).split('|').concat(
|
||||||
|
returnTypeOf(meU).split('|'))
|
||||||
|
const compType = meU.fromInstance.joinTypes(compTypes, 'convert')
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
let i = -1
|
let i = -1
|
||||||
let result = []
|
let result = []
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -55,22 +61,30 @@ Tuple.promoteBinaryUnary = {
|
|||||||
else break
|
else break
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinary = {
|
Tuple.promoteBinary = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||||
|
const compType = meB.fromInstance.joinTypes(
|
||||||
|
returnTypeOf(meB).split('|'))
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
const lim = Math.max(s.elts.length, t.elts.length)
|
const lim = Math.max(s.elts.length, t.elts.length)
|
||||||
const result = []
|
const result = []
|
||||||
for (let i = 0; i < lim; ++i) {
|
for (let i = 0; i < lim; ++i) {
|
||||||
result.push(meB(s.elts[i], t.elts[i]))
|
result.push(meB(s.elts[i], t.elts[i]))
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple.promoteBinaryStrict = {
|
Tuple.promoteBinaryStrict = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||||
|
const compType = meB.fromInstance.joinTypes(
|
||||||
|
returnTypeOf(meB).split('|'))
|
||||||
|
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||||
if (s.elts.length !== t.elts.length) {
|
if (s.elts.length !== t.elts.length) {
|
||||||
throw new RangeError('Tuple length mismatch') // get name of self ??
|
throw new RangeError('Tuple length mismatch') // get name of self ??
|
||||||
}
|
}
|
||||||
@ -79,6 +93,7 @@ Tuple.promoteBinaryStrict = {
|
|||||||
result.push(meB(s.elts[i], t.elts[i]))
|
result.push(meB(s.elts[i], t.elts[i]))
|
||||||
}
|
}
|
||||||
return tuple(...result)
|
return tuple(...result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './Types/Tuple.mjs'
|
export * from './Types/Tuple.mjs'
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': me, 'length(Tuple)': len}) => (s,t) => {
|
'Tuple<T>,Tuple<T>': ({
|
||||||
|
'self(T,T)': me,
|
||||||
|
'length(Tuple)': len
|
||||||
|
}) => Returns('boolean', (s,t) => {
|
||||||
if (len(s) !== len(t)) return false
|
if (len(s) !== len(t)) return false
|
||||||
for (let i = 0; i < len(s); ++i) {
|
for (let i = 0; i < len(s); ++i) {
|
||||||
if (!me(s.elts[i], t.elts[i])) return false
|
if (!me(s.elts[i], t.elts[i])) return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export {Tuple} from './Types/Tuple.mjs'
|
export {Tuple} from './Types/Tuple.mjs'
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
'Tuple<T>': ({'self(T)': me}) => t => t.elts.every(e => me(e))
|
'Tuple<T>': ({'self(T)': me}) => Returns(
|
||||||
|
'boolean', t => t.elts.every(e => me(e)))
|
||||||
// Note we can't just say `every(me)` above since every invokes its
|
// Note we can't just say `every(me)` above since every invokes its
|
||||||
// callback with more arguments, which then violates typed-function's
|
// callback with more arguments, which then violates typed-function's
|
||||||
// signature for `me`
|
// signature for `me`
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export {Tuple} from './Types/Tuple.mjs'
|
export {Tuple} from './Types/Tuple.mjs'
|
||||||
|
|
||||||
export const length = {Tuple: () => t => t.elts.length}
|
export const length = {'Tuple<T>': () => Returns('NumInt', t => t.elts.length)}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export {Tuple} from './Types/Tuple.mjs'
|
export {Tuple} from './Types/Tuple.mjs'
|
||||||
|
|
||||||
/* The purpose of the template argument is to ensure that all of the args
|
/* The purpose of the template argument is to ensure that all of the args
|
||||||
* are convertible to the same type.
|
* are convertible to the same type.
|
||||||
*/
|
*/
|
||||||
export const tuple = {'...T': () => args => ({elts: args})}
|
export const tuple = {
|
||||||
|
'...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args}))
|
||||||
|
}
|
||||||
|
@ -20,9 +20,56 @@ describe('The default full pocomath instance "math"', () => {
|
|||||||
assert.strictEqual(math.typeOf({re: 6.28, im: 2.72}), 'Complex<number>')
|
assert.strictEqual(math.typeOf({re: 6.28, im: 2.72}), 'Complex<number>')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('can determine the return types of operations', () => {
|
||||||
|
assert.strictEqual(math.returnTypeOf('negate', 'number'), 'number')
|
||||||
|
assert.strictEqual(math.returnTypeOf('negate', 'NumInt'), 'NumInt')
|
||||||
|
math.negate(math.complex(1.2, 2.8)) // TODO: make this call unnecessary
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('negate', 'Complex<number>'), 'Complex<number>')
|
||||||
|
assert.strictEqual(math.returnTypeOf('add', 'number,number'), 'number')
|
||||||
|
assert.strictEqual(math.returnTypeOf('add', 'NumInt,NumInt'), 'NumInt')
|
||||||
|
assert.strictEqual(math.returnTypeOf('add', 'NumInt,number'), 'number')
|
||||||
|
assert.strictEqual(math.returnTypeOf('add', 'number,NumInt'), 'number')
|
||||||
|
assert.deepStrictEqual( // TODO: ditto
|
||||||
|
math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1))
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('add', 'Complex<number>,NumInt'), 'Complex<number>')
|
||||||
|
// The following is not actually what we want, but the Pocomath type
|
||||||
|
// language isn't powerful enough at this point to capture the true
|
||||||
|
// return type:
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('add', 'number,NumInt,Complex<number>'), 'any')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('chain', 'bigint'), 'Chain<bigint>')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('returnTypeOf', 'string,string'), 'string')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('conjugate', 'bigint'), 'bigint')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('gcd', 'bigint,bigint'), 'bigint')
|
||||||
|
math.identity(math.fraction(3,5)) // TODO: ditto
|
||||||
|
assert.strictEqual(math.returnTypeOf('identity', 'Fraction'), 'Fraction')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('quotient', 'bigint,bigint'), 'bigint')
|
||||||
|
math.abs(math.complex(2,1)) //TODO: ditto
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('abs','Complex<NumInt>'), 'number')
|
||||||
|
math.multiply(math.quaternion(1,1,1,1), math.quaternion(1,-1,1,-1)) // dit
|
||||||
|
const quatType = math.returnTypeOf(
|
||||||
|
'quaternion', 'NumInt,NumInt,NumInt,NumInt')
|
||||||
|
assert.strictEqual(quatType, 'Complex<Complex<NumInt>>')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('multiply', quatType + ',' + quatType), quatType)
|
||||||
|
assert.strictEqual(math.returnTypeOf('isZero', 'NumInt'), 'boolean')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('roundquotient', 'NumInt,number'), 'NumInt')
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('factorial', 'NumInt'), 'bigint')
|
||||||
|
})
|
||||||
|
|
||||||
it('can subtract numbers', () => {
|
it('can subtract numbers', () => {
|
||||||
assert.strictEqual(math.subtract(12, 5), 7)
|
assert.strictEqual(math.subtract(12, 5), 7)
|
||||||
//assert.strictEqual(math.subtract(3n, 1.5), 1.5)
|
assert.throws(() => math.subtract(3n, 1.5), 'TypeError')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can add numbers', () => {
|
it('can add numbers', () => {
|
||||||
|
@ -83,6 +83,7 @@ describe('complex', () => {
|
|||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.multiply(q0, math.quaternion(2, 1, 0.1, 0.1)),
|
math.multiply(q0, math.quaternion(2, 1, 0.1, 0.1)),
|
||||||
math.quaternion(1.9, 1.1, 2.1, -0.9))
|
math.quaternion(1.9, 1.1, 2.1, -0.9))
|
||||||
|
math.absquare(math.complex(1.25, 2.5)) //HACK: need absquare(Complex<number>)
|
||||||
assert.strictEqual(math.abs(q0), Math.sqrt(2))
|
assert.strictEqual(math.abs(q0), Math.sqrt(2))
|
||||||
assert.strictEqual(math.abs(q1), Math.sqrt(33)/4)
|
assert.strictEqual(math.abs(q1), Math.sqrt(33)/4)
|
||||||
})
|
})
|
||||||
|
8
test/core/_utils.mjs
Normal file
8
test/core/_utils.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import * as utils from '../../src/core/utils.mjs'
|
||||||
|
|
||||||
|
describe('typeListOfSignature', () => {
|
||||||
|
it('returns an empty list for the empty signature', () => {
|
||||||
|
assert.deepStrictEqual(utils.typeListOfSignature(''), [])
|
||||||
|
})
|
||||||
|
})
|
@ -135,13 +135,8 @@ describe('A custom instance', () => {
|
|||||||
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<number>')
|
'Merge to Complex<number>')
|
||||||
// The following is the current behavior, since 3 converts to 3+0i
|
assert.throws(
|
||||||
// which is technically the same Complex type as 3n+0ni.
|
() => inst.typeMerge(3, inst.complex(3n)), TypeError)
|
||||||
// This should clear up when Complex is templatized
|
|
||||||
assert.strictEqual(inst.typeMerge(3, inst.complex(3n)), 'Merge to Complex')
|
|
||||||
// But types that truly cannot be merged should throw a TypeError
|
|
||||||
// Should add a variation of this with a more usual type once there is
|
|
||||||
// one not interconvertible with others...
|
|
||||||
inst.install(genericSubtract)
|
inst.install(genericSubtract)
|
||||||
assert.throws(() => inst.typeMerge(3, undefined), TypeError)
|
assert.throws(() => inst.typeMerge(3, undefined), TypeError)
|
||||||
})
|
})
|
||||||
|
20
test/generic/_all.mjs
Normal file
20
test/generic/_all.mjs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import math from '../../src/pocomath.mjs'
|
||||||
|
|
||||||
|
describe('generic', () => {
|
||||||
|
it('calculates mean', () => {
|
||||||
|
assert.strictEqual(math.mean(1,2.5,3.25,4.75), 2.875)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('mean', 'number,number,number,number'),
|
||||||
|
'number'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('compares things', () => {
|
||||||
|
assert.strictEqual(math.larger(7n, 3n), true)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('larger', 'bigint,bigint'), 'boolean')
|
||||||
|
assert.strictEqual(math.smallerEq(7.2, 3), false)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('smallerEq', 'number,NumInt'), 'boolean')
|
||||||
|
})
|
||||||
|
})
|
@ -92,4 +92,10 @@ describe('fraction', () => {
|
|||||||
assert.deepStrictEqual(math.square(tf), math.fraction(9/16))
|
assert.deepStrictEqual(math.square(tf), math.fraction(9/16))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('knows the types of its operations', () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('ceiling', 'Fraction'), 'Fraction')
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('multiply', 'Fraction,Fraction'), 'Fraction')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -8,14 +8,12 @@ describe('tuple', () => {
|
|||||||
|
|
||||||
it('does not allow unification by converting consecutive arguments', () => {
|
it('does not allow unification by converting consecutive arguments', () => {
|
||||||
assert.throws(() => math.tuple(3, 5.2, 2n), /TypeError.*unif/)
|
assert.throws(() => math.tuple(3, 5.2, 2n), /TypeError.*unif/)
|
||||||
// Hence, the order matters in a slightly unfortunate way,
|
|
||||||
// but I think being a little ragged in these edge cases is OK:
|
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => math.tuple(3, 2n, math.complex(5.2)),
|
() => math.tuple(3, 2n, math.complex(5.2)),
|
||||||
/TypeError.*unif/)
|
/TypeError.*unif/)
|
||||||
assert.deepStrictEqual(
|
assert.throws(
|
||||||
math.tuple(3, math.complex(2n), 5.2),
|
() => math.tuple(3, math.complex(2n), 5.2),
|
||||||
{elts: [math.complex(3), math.complex(2n), math.complex(5.2)]})
|
/TypeError.*unif/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can be tested for zero and equality', () => {
|
it('can be tested for zero and equality', () => {
|
||||||
@ -56,6 +54,9 @@ describe('tuple', () => {
|
|||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.subtract(math.tuple(3n,4n,5n), math.tuple(2n,1n,0n)),
|
math.subtract(math.tuple(3n,4n,5n), math.tuple(2n,1n,0n)),
|
||||||
math.tuple(1n,3n,5n))
|
math.tuple(1n,3n,5n))
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('subtract', 'Tuple<bigint>,Tuple<bigint>'),
|
||||||
|
'Tuple<bigint>')
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => math.subtract(math.tuple(5,6), math.tuple(7)),
|
() => math.subtract(math.tuple(5,6), math.tuple(7)),
|
||||||
/RangeError/)
|
/RangeError/)
|
||||||
@ -106,9 +107,16 @@ describe('tuple', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('supports sqrt', () => {
|
it('supports sqrt', () => {
|
||||||
|
const mixedTuple = math.tuple(2, math.complex(0,2), 1.5)
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
math.sqrt(math.tuple(4,-4,2.25)),
|
mixedTuple,
|
||||||
math.tuple(2, math.complex(0,2), 1.5))
|
math.tuple(math.complex(2), math.complex(0,2), math.complex(1.5)))
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('tuple', 'NumInt, Complex<NumInt>, number'),
|
||||||
|
'Tuple<Complex<number>>')
|
||||||
|
assert.deepStrictEqual(math.sqrt(math.tuple(4,-4,2.25)), mixedTuple)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('sqrt', 'Tuple<NumInt>'), 'Tuple<Complex<number>>')
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user