feat: Return type annotations (#53)
Provides the infrastructure to allow annotating the return types of functions, and does so for essentially every operation in the system (the only known exceptions being add, multiply, etc., on arbitrarily many arguments). One main infrastructure enhancements are bounded template types, e.g. `T:number` being a template parameter where T can take on the type `number` or any subtype thereof. A main internal enhancement is that base template types are no longer added to the typed universe; rather, there is a secondary, "meta" typed universe where they live. The primary point/purpose of this change is then the necessary search order for implementations can be much better modeled by typed-function's search order, using the `onMismatch` facility to redirect the search from fully instantiated implementations to the generic catchall implementations for each template (these catchalls live in the meta universe). Numerous other small improvements and bugfixes were encountered along the way. Co-authored-by: Glen Whitney <glen@studioinfinity.org> Reviewed-on: #53
This commit is contained in:
parent
207ac4330b
commit
31add66f4c
@ -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