All checks were successful
/ test (push) Successful in 17s
Adds all of the pocomath functions on Complex that do not depend on any unimplemented types or config properties, except quotient and roundquotient, where the design is murky. To get this working, adds some additional features: * Allows conversions to generic types, with the matched type determined from the return value of the built convertor * Adds predicate-based type patterns * Adds conversion from any non-complex type T to Complex(T) * Adds check for recursive loops in resolve (a key/signature depending on itself) Reviewed-on: #24 Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-committed-by: Glen Whitney <glen@studioinfinity.org>
83 lines
2.9 KiB
JavaScript
83 lines
2.9 KiB
JavaScript
import {Returns, Type} from '#core/Type.js'
|
|
import {match} from '#core/TypePatterns.js'
|
|
|
|
const isComplex = z => z && typeof z === 'object' && 're' in z && 'im' in z
|
|
|
|
const specializesTo = CType => CType.complex
|
|
|
|
function complexSpecialize(ComponentType) {
|
|
const compTest = ComponentType.test
|
|
const specTest = z => isComplex(z) && compTest(z.re) && compTest(z.im)
|
|
const typeName = `Complex(${ComponentType})`
|
|
if (ComponentType.concrete) {
|
|
const fromSpec = [match(
|
|
ComponentType, math => r => ({re: r, im: ComponentType.zero}))]
|
|
for (const {pattern, does} of ComponentType.from) {
|
|
fromSpec.push(match(
|
|
this.specialize(pattern.type),
|
|
math => {
|
|
const compConv = does(math)
|
|
return z => ({re: compConv(z.re), im: compConv(z.im)})
|
|
}))
|
|
}
|
|
const typeOptions = {from: fromSpec, typeName}
|
|
if ('zero' in ComponentType) {
|
|
typeOptions.zero = {re: ComponentType.zero, im: ComponentType.zero}
|
|
if ('one' in ComponentType) {
|
|
typeOptions.one = {re: ComponentType.one, im: ComponentType.zero}
|
|
}
|
|
}
|
|
if ('nan' in ComponentType) {
|
|
typeOptions.nan = {re: ComponentType.nan, im: ComponentType.nan}
|
|
}
|
|
const complexCompType = new Type(specTest, typeOptions)
|
|
complexCompType.Component = ComponentType
|
|
complexCompType.complex = true
|
|
return complexCompType
|
|
}
|
|
// wrapping a generic type in Complex
|
|
const cplx = this
|
|
|
|
const innerSpecialize = (...args) => {
|
|
const innerType = ComponentType.specialize(...args)
|
|
return cplx.specialize(innerType)
|
|
}
|
|
|
|
const innerSpecializesTo = CType => specializesTo(CType)
|
|
&& ComponentType.specializesTo(CType.Component)
|
|
|
|
const innerRefine = (z, typer) => {
|
|
const reType = ComponentType.refine(z.re, typer)
|
|
const imType = ComponentType.refine(z.im, typer)
|
|
if (reType === imType) return cplx.specialize(reType)
|
|
throw new TypeError(
|
|
'mixed-type Complex numbers disallowed '
|
|
+ `(real: ${reType}, imaginary: ${imType})`)
|
|
}
|
|
|
|
return new Type(specTest, {
|
|
specialize: innerSpecialize,
|
|
specializesTo: innerSpecializesTo,
|
|
refine: innerRefine,
|
|
typeName,
|
|
})
|
|
}
|
|
|
|
export const Complex = new Type(isComplex, {
|
|
specialize: complexSpecialize,
|
|
specializesTo,
|
|
from: [match( // can promote any non-complex type T to Complex(T) as needed
|
|
// but watch out, this should be tried late, because it can preclude
|
|
// other more reasonable conversions like bool => number.
|
|
T => !T.complex,
|
|
(math, T) => Returns(Complex(T), r => math.complex(r, math.zero(T)))
|
|
)],
|
|
refine: function(z, typer) {
|
|
const reType = typer(z.re)
|
|
const imType = typer(z.im)
|
|
if (reType === imType) return this.specialize(reType)
|
|
throw new TypeError(
|
|
'mixed-type Complex numbers disallowed '
|
|
+ `(real: ${reType}, imaginary: ${imType})`)
|
|
}
|
|
})
|