2025-04-25 14:17:34 +00:00
|
|
|
import {Returns, Type} from '#core/Type.js'
|
|
|
|
import {match} from '#core/TypePatterns.js'
|
2025-04-22 01:48:51 +00:00
|
|
|
|
|
|
|
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) {
|
refactor: change onType to match and take only one pattern and result (#22)
Pursuant to #12. Besides changing the name of onType to match, and only allowing one pattern and result in `match()`,
this PR also arranges that in place of an onType with lots of alternating PATTERN, VALUE, PATTERN, VALUE arguments, one now exports an _array_ of `match(PATTERN, VALUE)` items.
Doesn't quite fully resolve #12, because there is still the question of whether `match(...)` can be left out for a behavior that literally matches anything (current behavior), or whether `match(Passthru, behavior)` should be required for such cases.
Reviewed-on: https://code.studioinfinity.org/StudioInfinity/nanomath/pulls/22
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
2025-04-22 05:01:21 +00:00
|
|
|
const fromSpec = [match(
|
2025-04-26 00:20:30 -07:00
|
|
|
ComponentType, () => r => ({re: r, im: ComponentType.zero}))]
|
refactor: change onType to match and take only one pattern and result (#22)
Pursuant to #12. Besides changing the name of onType to match, and only allowing one pattern and result in `match()`,
this PR also arranges that in place of an onType with lots of alternating PATTERN, VALUE, PATTERN, VALUE arguments, one now exports an _array_ of `match(PATTERN, VALUE)` items.
Doesn't quite fully resolve #12, because there is still the question of whether `match(...)` can be left out for a behavior that literally matches anything (current behavior), or whether `match(Passthru, behavior)` should be required for such cases.
Reviewed-on: https://code.studioinfinity.org/StudioInfinity/nanomath/pulls/22
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
2025-04-22 05:01:21 +00:00
|
|
|
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)})
|
|
|
|
}))
|
2025-04-22 01:48:51 +00:00
|
|
|
}
|
refactor: change onType to match and take only one pattern and result (#22)
Pursuant to #12. Besides changing the name of onType to match, and only allowing one pattern and result in `match()`,
this PR also arranges that in place of an onType with lots of alternating PATTERN, VALUE, PATTERN, VALUE arguments, one now exports an _array_ of `match(PATTERN, VALUE)` items.
Doesn't quite fully resolve #12, because there is still the question of whether `match(...)` can be left out for a behavior that literally matches anything (current behavior), or whether `match(Passthru, behavior)` should be required for such cases.
Reviewed-on: https://code.studioinfinity.org/StudioInfinity/nanomath/pulls/22
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
2025-04-22 05:01:21 +00:00
|
|
|
const typeOptions = {from: fromSpec, typeName}
|
2025-04-22 01:48:51 +00:00
|
|
|
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,
|
2025-04-25 14:17:34 +00:00
|
|
|
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)))
|
|
|
|
)],
|
2025-04-22 01:48:51 +00:00
|
|
|
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})`)
|
|
|
|
}
|
|
|
|
})
|