refactor: Streamline publishing operations
Avoids clumsy naming properties by making the names the keys in an interface to which the signatures of all operations must be published. This also reduces the number of different symbols and avoids long lists of imports in the modules implementing multiple operations, which were redundant with the list of functions exported from such modules.
This commit is contained in:
parent
74e2aef524
commit
072b2a1f79
@ -1,47 +1,45 @@
|
|||||||
import {Complex, ComplexOp} from './type.js'
|
import {Complex} from './type.js'
|
||||||
import type {
|
import type {
|
||||||
AbsquareOp, AddOp, AddRealOp, ConjOp, ConservativeSqrtOp, DivideOp,
|
Dependencies, OpType, OpReturns, RealType, ZeroType
|
||||||
DivideByRealOp, MultiplyOp, ReciprocalOp, SqrtOp, SubtractOp,
|
|
||||||
UnaryMinusOp
|
|
||||||
} from '../interfaces/arithmetic.js'
|
|
||||||
import type {
|
|
||||||
NanOp, ReOp, ZeroOp, Depends, RealType, WithConstants, NaNType
|
|
||||||
} from '../interfaces/type.js'
|
} from '../interfaces/type.js'
|
||||||
import type {IsSquareOp, IsRealOp} from '../interfaces/predicate.js'
|
|
||||||
|
declare module "../interfaces/type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
// TODO: Make Dispatcher collapse operations that start with the same
|
||||||
|
// prefix up to a possible `_`
|
||||||
|
add_real: {params: [T, RealType<T>], returns: T}
|
||||||
|
divide_real: {params: [T, RealType<T>], returns: T}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const add =
|
export const add =
|
||||||
<T>(dep: Depends<AddOp<T>> & Depends<ComplexOp<T>>): AddOp<Complex<T>> =>
|
<T>(dep: Dependencies<'add' | 'complex', T>): OpType<'add', Complex<T>> =>
|
||||||
(w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im))
|
(w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im))
|
||||||
|
|
||||||
export const addReal =
|
export const add_real =
|
||||||
<T>(dep: Depends<AddRealOp<T>> & Depends<ComplexOp<T>>):
|
<T>(dep: Dependencies<'add_real' | 'complex', T>):
|
||||||
AddRealOp<Complex<T>> =>
|
OpType<'add_real', Complex<T>> =>
|
||||||
(z, r) => dep.complex(dep.addReal(z.re, r), z.im)
|
(z, r) => dep.complex(dep.add_real(z.re, r), z.im)
|
||||||
|
|
||||||
export const unaryMinus =
|
export const unaryMinus =
|
||||||
<T>(dep: Depends<UnaryMinusOp<T>> & Depends<ComplexOp<T>>):
|
<T>(dep: Dependencies<'unaryMinus' | 'complex', T>):
|
||||||
UnaryMinusOp<Complex<T>> =>
|
OpType<'unaryMinus', Complex<T>> =>
|
||||||
z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
|
z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
export const conj =
|
export const conj =
|
||||||
<T>(dep: Depends<UnaryMinusOp<T>>
|
<T>(dep: Dependencies<'unaryMinus' | 'conj' | 'complex', T>):
|
||||||
& Depends<ConjOp<T>>
|
OpType<'conj', Complex<T>> =>
|
||||||
& Depends<ComplexOp<T>>):
|
|
||||||
ConjOp<Complex<T>> =>
|
|
||||||
z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im))
|
z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
export const subtract =
|
export const subtract =
|
||||||
<T>(dep: Depends<SubtractOp<T>> & Depends<ComplexOp<T>>):
|
<T>(dep: Dependencies<'subtract' | 'complex', T>):
|
||||||
SubtractOp<Complex<T>> =>
|
OpType<'subtract', Complex<T>> =>
|
||||||
(w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
|
(w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
|
||||||
|
|
||||||
export const multiply =
|
export const multiply =
|
||||||
<T>(dep: Depends<AddOp<T>>
|
<T>(dep: Dependencies<
|
||||||
& Depends<SubtractOp<T>>
|
'add' | 'subtract' | 'multiply' | 'conj' | 'complex', T>):
|
||||||
& Depends<MultiplyOp<T>>
|
OpType<'multiply', Complex<T>> =>
|
||||||
& Depends<ConjOp<T>>
|
|
||||||
& Depends<ComplexOp<T>>):
|
|
||||||
MultiplyOp<Complex<T>> =>
|
|
||||||
(w, z) => {
|
(w, z) => {
|
||||||
const mult = dep.multiply
|
const mult = dep.multiply
|
||||||
const realpart = dep.subtract(
|
const realpart = dep.subtract(
|
||||||
@ -52,69 +50,48 @@ export const multiply =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const absquare =
|
export const absquare =
|
||||||
<T>(dep: Depends<AddOp<RealType<T>>> & Depends<AbsquareOp<T>>):
|
<T>(dep: Dependencies<'absquare', T>
|
||||||
AbsquareOp<Complex<T>> =>
|
& Dependencies<'add', OpReturns<'absquare', T>>):
|
||||||
|
OpType<'absquare', Complex<T>> =>
|
||||||
z => dep.add(dep.absquare(z.re), dep.absquare(z.im))
|
z => dep.add(dep.absquare(z.re), dep.absquare(z.im))
|
||||||
|
|
||||||
export const divideByReal =
|
export const divideByReal =
|
||||||
<T>(dep: Depends<DivideByRealOp<T>> & Depends<ComplexOp<T>>):
|
<T>(dep: Dependencies<'divide_real' | 'complex', T>):
|
||||||
DivideByRealOp<Complex<T>> =>
|
OpType<'divide_real', Complex<T>> =>
|
||||||
(z, r) => dep.complex(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r))
|
(z, r) => dep.complex(dep.divide_real(z.re, r), dep.divide_real(z.im, r))
|
||||||
|
|
||||||
export const reciprocal =
|
export const reciprocal =
|
||||||
<T>(dep: Depends<ConjOp<Complex<T>>>
|
<T>(dep: Dependencies<'conj' | 'absquare' | 'divide_real', Complex<T>>):
|
||||||
& Depends<AbsquareOp<Complex<T>>>
|
OpType<'reciprocal', Complex<T>> =>
|
||||||
& Depends<DivideByRealOp<Complex<T>>>):
|
z => dep.divide_real(dep.conj(z), dep.absquare(z))
|
||||||
ReciprocalOp<Complex<T>> =>
|
|
||||||
z => dep.divideByReal(dep.conj(z), dep.absquare(z))
|
|
||||||
|
|
||||||
export const divide =
|
export const divide =
|
||||||
<T>(dep: Depends<MultiplyOp<Complex<T>>>
|
<T>(dep: Dependencies<'multiply' | 'reciprocal', Complex<T>>):
|
||||||
& Depends<ReciprocalOp<Complex<T>>>):
|
OpType<'divide', Complex<T>> =>
|
||||||
DivideOp<Complex<T>> =>
|
|
||||||
(w, z) => dep.multiply(w, dep.reciprocal(z))
|
(w, z) => dep.multiply(w, dep.reciprocal(z))
|
||||||
|
|
||||||
export type ComplexSqrtOp<T> = {
|
|
||||||
op?: 'complexSqrt',
|
|
||||||
(a: T): Complex<WithConstants<T> | NaNType<WithConstants<T>>>
|
|
||||||
}
|
|
||||||
// Complex square root of a real type T
|
|
||||||
export const complexSqrt =
|
|
||||||
<T>(dep: Depends<ConservativeSqrtOp<T>>
|
|
||||||
& Depends<IsSquareOp<T>>
|
|
||||||
& Depends<UnaryMinusOp<T>>
|
|
||||||
& Depends<ComplexOp<WithConstants<T>>>
|
|
||||||
& Depends<ZeroOp<T>>
|
|
||||||
& Depends<NanOp<Complex<WithConstants<T>>>>): ComplexSqrtOp<T> =>
|
|
||||||
r => {
|
|
||||||
if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r))
|
|
||||||
const negative = dep.unaryMinus(r)
|
|
||||||
if (dep.isSquare(negative)) {
|
|
||||||
return dep.complex(dep.zero(r), dep.conservativeSqrt(negative))
|
|
||||||
}
|
|
||||||
// neither the real number or its negative is a square; could happen
|
|
||||||
// for example with bigint. So there is no square root. So we have to
|
|
||||||
// return the NaN of the type.
|
|
||||||
return dep.nan(dep.complex(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sqrt =
|
export const sqrt =
|
||||||
<T>(dep: Depends<IsRealOp<Complex<T>>>
|
<T>(dep:
|
||||||
& Depends<ComplexSqrtOp<T>>
|
Dependencies<
|
||||||
& Depends<ConservativeSqrtOp<RealType<T>>>
|
'conservativeSqrt' | 'add' | 'unaryMinus' | 'equal', RealType<T>>
|
||||||
& Depends<AbsquareOp<Complex<T>>>
|
& Dependencies<'zero' | 'add_real', T>
|
||||||
& Depends<AddRealOp<Complex<T>>>
|
& Dependencies<'complex', T | ZeroType<T>>
|
||||||
& Depends<DivideByRealOp<Complex<T>>>
|
& Dependencies<'absquare' | 're' | 'divide_real', Complex<T>>
|
||||||
& Depends<AddOp<RealType<T>>>
|
& {add_complex_real: OpType<'add_real', Complex<T>>}):
|
||||||
& Depends<ReOp<Complex<T>>>): SqrtOp<Complex<T>> =>
|
OpType<'sqrt', Complex<T>> =>
|
||||||
z => {
|
z => {
|
||||||
if (dep.isReal(z)) return dep.complexSqrt(z.re)
|
|
||||||
const myabs = dep.conservativeSqrt(dep.absquare(z))
|
const myabs = dep.conservativeSqrt(dep.absquare(z))
|
||||||
const num = dep.addReal(z, myabs)
|
|
||||||
const r = dep.re(z)
|
const r = dep.re(z)
|
||||||
|
const negr = dep.unaryMinus(r)
|
||||||
|
if (dep.equal(myabs, negr)) {
|
||||||
|
// pure imaginary square root; z.im already sero
|
||||||
|
return dep.complex(
|
||||||
|
dep.zero(z.re), dep.add_real(z.im, dep.conservativeSqrt(negr)))
|
||||||
|
}
|
||||||
|
const num = dep.add_complex_real(z, myabs)
|
||||||
const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r))
|
const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r))
|
||||||
const denom = dep.conservativeSqrt(denomsq)
|
const denom = dep.conservativeSqrt(denomsq)
|
||||||
return dep.divideByReal(num, denom)
|
return dep.divide_real(num, denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const conservativeSqrt = sqrt
|
export const conservativeSqrt = sqrt
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import {Complex} from './type.js'
|
import {Complex} from './type.js'
|
||||||
import {EqualOp} from '../interfaces/relational.js'
|
import type {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
import {AddOp} from '../interfaces/arithmetic.js'
|
|
||||||
import type {Depends} from '../interfaces/type.js'
|
|
||||||
import type {IsRealOp, IsSquareOp} from '../interfaces/predicate.js'
|
|
||||||
|
|
||||||
export const isReal =
|
export const isReal =
|
||||||
<T>(dep: Depends<AddOp<T>> & Depends<EqualOp<T>> & Depends<IsRealOp<T>>):
|
<T>(dep: Dependencies<'add' | 'equal' | 'isReal', T>):
|
||||||
IsRealOp<Complex<T>> =>
|
OpType<'isReal', Complex<T>> =>
|
||||||
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
||||||
|
|
||||||
export const isSquare: IsSquareOp<Complex<any>> = z => true // FIXME: not correct for Complex<bigint> once we get there
|
export const isSquare: OpType<'isSquare', Complex<any>> = z => true // FIXME: not correct for Complex<bigint> once we get there
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {Complex} from './type.js'
|
import {Complex} from './type.js'
|
||||||
import {Depends} from '../interfaces/type.js'
|
import {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
import {EqualOp} from '../interfaces/relational.js'
|
|
||||||
|
|
||||||
export const equal =
|
export const equal =
|
||||||
<T>(dep: Depends<EqualOp<T>>): EqualOp<Complex<T>> =>
|
<T>(dep: Dependencies<'equal', T>): OpType<'equal', Complex<T>> =>
|
||||||
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
joinTypes, typeOfDependency, Dependency,
|
joinTypes, typeOfDependency, Dependency,
|
||||||
} from '../core/Dispatcher.js'
|
} from '../core/Dispatcher.js'
|
||||||
import type {
|
import type {
|
||||||
OneOp, ZeroOp, NanOp, ReOp, ZeroType, OneType, NaNType, Depends
|
ZeroType, OneType, NaNType, Dependencies, OpType, OpReturns
|
||||||
} from '../interfaces/type.js'
|
} from '../interfaces/type.js'
|
||||||
|
|
||||||
export type Complex<T> = { re: T; im: T; }
|
export type Complex<T> = { re: T; im: T; }
|
||||||
@ -32,31 +32,34 @@ declare module "../interfaces/type" {
|
|||||||
real: RealType<R>
|
real: RealType<R>
|
||||||
} : never
|
} : never
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Operations<T> {
|
||||||
|
complex: {params: [T] | [T,T], returns: Complex<T>}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexOp<T> = {op?: 'complex', (a: T, b?: T): Complex<T>}
|
|
||||||
|
|
||||||
export const complex =
|
export const complex =
|
||||||
<T>(dep: Depends<ZeroOp<T>>): ComplexOp<T | ZeroType<T>> =>
|
<T>(dep: Dependencies<'zero', T>): OpType<'complex', T | ZeroType<T>> =>
|
||||||
(a, b) => ({re: a, im: b || dep.zero(a)})
|
(a, b) => ({re: a, im: b || dep.zero(a)})
|
||||||
|
|
||||||
export const zero =
|
export const zero =
|
||||||
<T>(dep: Depends<ZeroOp<T>> & Depends<ComplexOp<ZeroType<T>>>):
|
<T>(dep: Dependencies<'zero', T>
|
||||||
ZeroOp<Complex<T>> =>
|
& Dependencies<'complex', OpReturns<'zero', T>>):
|
||||||
|
OpType<'zero', Complex<T>> =>
|
||||||
z => dep.complex(dep.zero(z.re), dep.zero(z.im))
|
z => dep.complex(dep.zero(z.re), dep.zero(z.im))
|
||||||
|
|
||||||
export const one =
|
export const one =
|
||||||
<T>(dep: Depends<OneOp<T>>
|
<T>(dep: Dependencies<'one' | 'zero', T>
|
||||||
& Depends<ZeroOp<T>>
|
& Dependencies<'complex', OpReturns<'one' | 'zero', T>>):
|
||||||
& Depends<ComplexOp<ZeroType<T>|OneType<T>>>):
|
OpType<'one', Complex<T>> =>
|
||||||
OneOp<Complex<T>> =>
|
|
||||||
z => dep.complex(dep.one(z.re), dep.zero(z.im))
|
z => dep.complex(dep.one(z.re), dep.zero(z.im))
|
||||||
|
|
||||||
export const nan =
|
export const nan =
|
||||||
<T>(dep: Depends<NanOp<T>> & Depends<ComplexOp<NaNType<T>>>):
|
<T>(dep: Dependencies<'nan', T>
|
||||||
NanOp<Complex<T>> =>
|
& Dependencies<'complex', OpReturns<'nan', T>>):
|
||||||
|
OpType<'nan', Complex<T>> =>
|
||||||
z => dep.complex(dep.nan(z.re), dep.nan(z.im))
|
z => dep.complex(dep.nan(z.re), dep.nan(z.im))
|
||||||
|
|
||||||
export const re =
|
export const re =
|
||||||
<T>(dep: Depends<ReOp<T>>): ReOp<Complex<T>> =>
|
<T>(dep: Dependencies<'re', T>): OpType<'re', Complex<T>> =>
|
||||||
z => dep.re(z.re)
|
z => dep.re(z.re)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type {Depends} from '../interfaces/type.js'
|
import type {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
import type {MultiplyOp, SquareOp} from '../interfaces/arithmetic.js'
|
|
||||||
|
|
||||||
export const square =
|
export const square =
|
||||||
<T>(dep: Depends<MultiplyOp<T>>): SquareOp<T> =>
|
<T>(dep: Dependencies<'multiply', T>): OpType<'square', T> =>
|
||||||
z => dep.multiply(z, z)
|
z => dep.multiply(z, z)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Depends} from '../interfaces/type.js'
|
import {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
import type {EqualOp, UnequalOp} from '../interfaces/relational.js'
|
|
||||||
|
|
||||||
export const unequal = <T>(dep: Depends<EqualOp<T>>): UnequalOp<T> =>
|
export const unequal =
|
||||||
|
<T>(dep: Dependencies<'equal', T>): OpType<'unequal', T> =>
|
||||||
(x, y) => !dep.equal(x, y)
|
(x, y) => !dep.equal(x, y)
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import type {Complex} from '../Complex/type.js'
|
import type {Complex} from '../Complex/type.js'
|
||||||
import type {RealType, WithConstants, NaNType} from './type.js'
|
import type {RealType, WithConstants, NaNType} from './type.js'
|
||||||
|
|
||||||
// Note: right now I've added an 'Op' suddix,
|
type UnaryOperator<T> = {params: [T], returns: T}
|
||||||
// so it is clear that the type holds the function type of an operation
|
type BinaryOperator<T> = {params: [T, T], returns: T}
|
||||||
// This is not necessary though, it is just a naming convention.
|
declare module "./type" {
|
||||||
export type AddOp<T> = {op?: 'add', (a: T, b: T): T}
|
interface Operations<T> {
|
||||||
export type AddRealOp<T> = {op?: 'addReal', (a: T, b: RealType<T>): T}
|
add: BinaryOperator<T>
|
||||||
export type UnaryMinusOp<T> = {op?: 'unaryMinus', (a: T): T}
|
unaryMinus: UnaryOperator<T>
|
||||||
export type ConjOp<T> = {op?: 'conj', (a: T): T}
|
conj: UnaryOperator<T>
|
||||||
export type SubtractOp<T> = {op?: 'subtract', (a: T, b: T): T}
|
subtract: BinaryOperator<T>
|
||||||
export type MultiplyOp<T> = {op?: 'multiply', (a: T, b: T): T}
|
multiply: BinaryOperator<T>
|
||||||
export type AbsquareOp<T> = {op?: 'absquare', (a: T): RealType<T>}
|
square: UnaryOperator<T>
|
||||||
export type ReciprocalOp<T> = {op?: 'reciprocal', (a: T): T}
|
absquare: {params: [T], returns: RealType<T>}
|
||||||
export type DivideOp<T> = {op?: 'divide', (a: T, b: T): T}
|
reciprocal: UnaryOperator<T>
|
||||||
export type DivideByRealOp<T> = {op?: 'divideByReal', (a: T, b: RealType<T>): T}
|
divide: BinaryOperator<T>
|
||||||
export type ConservativeSqrtOp<T> = {op?: 'conservativeSqrt', (a: T): T}
|
conservativeSqrt: UnaryOperator<T>
|
||||||
export type SqrtOp<T> = {
|
sqrt: {
|
||||||
op?: 'sqrt',
|
params: [T],
|
||||||
(a: T): T extends Complex<infer R>
|
returns: T extends Complex<infer R>
|
||||||
? Complex<WithConstants<R> | NaNType<WithConstants<R>>>
|
? Complex<R | ZeroType<R>>
|
||||||
: T | Complex<T>
|
: T | Complex<T>
|
||||||
}
|
}
|
||||||
export type SquareOp<T> = {op?: 'square', (z: T): T}
|
}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,9 @@
|
|||||||
export type IsRealOp<T> = {op?: 'isReal', (a: T): boolean}
|
// Warning: a module must have something besides just a "declare module"
|
||||||
export type IsSquareOp<T> = {op?: 'isSquare', (a: T): boolean}
|
// section; otherwise it is ignored.
|
||||||
|
export type UnaryPredicate<T> = {params: [T], returns: boolean}
|
||||||
|
declare module "./type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
isReal: UnaryPredicate<T>
|
||||||
|
isSquare: UnaryPredicate<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,9 @@
|
|||||||
export type EqualOp<T> = {op?: 'equal', (a: T, b: T): boolean}
|
// Warning: a module must have something besides just a "declare module"
|
||||||
export type UnequalOp<T> = {op?: 'unequal', (a: T, b: T): boolean}
|
// section; otherwise it is ignored.
|
||||||
|
export type BinaryPredicate<T> = {params: [T, T], returns: boolean}
|
||||||
|
declare module "./type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
equal: BinaryPredicate<T>
|
||||||
|
unequal: BinaryPredicate<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
// Every typocomath type has some associated types; they need
|
||||||
|
// to be published as in the following interface. The key is the
|
||||||
|
// name of the type, and within the subinterface for that key,
|
||||||
|
// the type of the 'type' property is the actual TypeScript type
|
||||||
|
// we are associating the other properties to. Note the interface
|
||||||
|
// is generic with one parameter, corresponding to the fact that
|
||||||
|
// typocomath currently only allows types with a single generic parameter.
|
||||||
|
// This way, AssociatedTypes<SubType> can give the associated types
|
||||||
|
// for a generic type instantiated with SubType. That's not necessary for
|
||||||
|
// the 'undefined' type (or if you look in the `numbers` subdirectory,
|
||||||
|
// the 'number' type either) or any concrete type, but that's OK, the
|
||||||
|
// generic parameter doesn't hurt in those cases.
|
||||||
export interface AssociatedTypes<T> {
|
export interface AssociatedTypes<T> {
|
||||||
undefined: {
|
undefined: {
|
||||||
type: undefined
|
type: undefined
|
||||||
@ -9,24 +21,46 @@ export interface AssociatedTypes<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AssociatedTypeNames = keyof AssociatedTypes<unknown>['undefined']
|
type AssociatedTypeNames = keyof AssociatedTypes<unknown>['undefined']
|
||||||
export type Lookup<T, Name extends AssociatedTypeNames> = {
|
type ALookup<T, Name extends AssociatedTypeNames> = {
|
||||||
[K in keyof AssociatedTypes<T>]:
|
[K in keyof AssociatedTypes<T>]:
|
||||||
T extends AssociatedTypes<T>[K]['type'] ? AssociatedTypes<T>[K][Name] : never
|
T extends AssociatedTypes<T>[K]['type'] ? AssociatedTypes<T>[K][Name] : never
|
||||||
}[keyof AssociatedTypes<T>]
|
}[keyof AssociatedTypes<T>]
|
||||||
|
|
||||||
export type ZeroType<T> = Lookup<T, 'zero'>
|
export type ZeroType<T> = ALookup<T, 'zero'>
|
||||||
export type OneType<T> = Lookup<T, 'one'>
|
export type OneType<T> = ALookup<T, 'one'>
|
||||||
export type WithConstants<T> = T | ZeroType<T> | OneType<T>
|
export type WithConstants<T> = T | ZeroType<T> | OneType<T>
|
||||||
export type NaNType<T> = Lookup<T, 'nan'>
|
export type NaNType<T> = ALookup<T, 'nan'>
|
||||||
export type RealType<T> = Lookup<T, 'real'>
|
export type RealType<T> = ALookup<T, 'real'>
|
||||||
|
|
||||||
export type ZeroOp<T> = {op?: 'zero', (a: WithConstants<T>): ZeroType<T>}
|
// The global signature patterns for all operations need to be published in the
|
||||||
export type OneOp<T> = {op?: 'one', (a: WithConstants<T>): OneType<T>}
|
// following interface. Each key is the name of an operation (but note that
|
||||||
export type NanOp<T> = {op?: 'nan', (a: T|NaNType<T>): NaNType<T>}
|
// the Dispatcher will automatically merge operations that have the same
|
||||||
export type ReOp<T> = {op?: 're', (a: T): RealType<T>}
|
// name when the first underscore `_` and everything thereafter is stripped).
|
||||||
|
// The type of each key should be an interface with two properties: 'params'
|
||||||
|
// whose type is the type of the parameter list for the operation, and
|
||||||
|
// 'returns' whose type is the return type of the operation on those
|
||||||
|
// parameters. These types are generic in a parameter type T which should
|
||||||
|
// be interpreted as the type that the operation is supposed to "primarily"
|
||||||
|
// operate on, although note that some of the parameters and/or return types
|
||||||
|
// may depend on T rather than be exactly T.
|
||||||
|
// So note that the example 're' below provides essentially the same
|
||||||
|
// information that e.g.
|
||||||
|
// `type ReOp<T> = (t: T) => RealType<T>`
|
||||||
|
// would, but in a way that is much easier to manipulate in TypeScript,
|
||||||
|
// and it records the name of the operation as 're' also by virtue of the
|
||||||
|
// key 're' in the interface.
|
||||||
|
export interface Operations<T> {
|
||||||
|
zero: {params: [WithConstants<T>], returns: ZeroType<T>}
|
||||||
|
one: {params: [WithConstants<T>], returns: OneType<T>}
|
||||||
|
nan: {params: [T | NaNType<T>], returns: NaNType<T>}
|
||||||
|
re: {params: [T], returns: RealType<T>}
|
||||||
|
}
|
||||||
|
|
||||||
type NamedFunction = {op?: string, (...params: any[]): any}
|
type OpKey = keyof Operations<unknown>
|
||||||
export type Depends<FuncType extends NamedFunction> =
|
|
||||||
{[K in FuncType['op']]: FuncType}
|
export type OpReturns<Name extends OpKey, T> = Operations<T>[Name]['returns']
|
||||||
|
export type OpType<Name extends OpKey, T> =
|
||||||
|
(...args: Operations<T>[Name]['params']) => OpReturns<Name, T>
|
||||||
|
export type Dependencies<Name extends OpKey, T> = {[K in Name]: OpType<K, T>}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
import type {configDependency} from '../core/Config.js'
|
import type {configDependency} from '../core/Config.js'
|
||||||
import type {ComplexOp} from '../Complex/type.js'
|
import type {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
import type {
|
|
||||||
AddOp, ConjOp, SubtractOp, UnaryMinusOp, MultiplyOp,
|
|
||||||
AbsquareOp, ReciprocalOp, DivideOp, ConservativeSqrtOp, SqrtOp
|
|
||||||
} from '../interfaces/arithmetic.js'
|
|
||||||
import type {Depends} from '../interfaces/type.js'
|
|
||||||
|
|
||||||
export const add: AddOp<number> = (a, b) => a + b
|
export const add: OpType<'add', number> = (a, b) => a + b
|
||||||
export const addReal = add
|
export const unaryMinus: OpType<'unaryMinus', number> = a => -a
|
||||||
export const unaryMinus: UnaryMinusOp<number> = a => -a
|
export const conj: OpType<'conj', number> = a => a
|
||||||
export const conj: ConjOp<number> = a => a
|
export const subtract: OpType<'subtract', number> = (a, b) => a - b
|
||||||
export const subtract: SubtractOp<number> = (a, b) => a - b
|
export const multiply: OpType<'multiply', number> = (a, b) => a * b
|
||||||
export const multiply: MultiplyOp<number> = (a, b) => a * b
|
export const absquare: OpType<'absquare', number> = a => a * a
|
||||||
export const absquare: AbsquareOp<number> = a => a * a
|
export const reciprocal: OpType<'reciprocal', number> = a => 1 / a
|
||||||
export const reciprocal: ReciprocalOp<number> = a => 1 / a
|
export const divide: OpType<'divide', number> = (a, b) => a / b
|
||||||
export const divide: DivideOp<number> = (a, b) => a / b
|
|
||||||
export const divideByReal = divide
|
|
||||||
|
|
||||||
const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
|
const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
|
||||||
export const conservativeSqrt: ConservativeSqrtOp<number> = basicSqrt
|
export const conservativeSqrt: OpType<'conservativeSqrt', number> = basicSqrt
|
||||||
|
|
||||||
export const sqrt =
|
export const sqrt =
|
||||||
(dep: configDependency & Depends<ComplexOp<number>>): SqrtOp<number> => {
|
(dep: configDependency & Dependencies<'complex', number>):
|
||||||
|
OpType<'sqrt', number> => {
|
||||||
if (dep.config.predictable || !dep.complex) return basicSqrt
|
if (dep.config.predictable || !dep.complex) return basicSqrt
|
||||||
return a => {
|
return a => {
|
||||||
if (isNaN(a)) return NaN
|
if (isNaN(a)) return NaN
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { IsRealOp, IsSquareOp } from '../interfaces/predicate.js'
|
import type {OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const isReal: IsRealOp<number> = (a) => true
|
export const isReal: OpType<'isReal', number> = (a) => true
|
||||||
export const isSquare: IsSquareOp<number> = (a) => a >= 0
|
export const isSquare: OpType<'isSquare', number> = (a) => a >= 0
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import {Config} from '../core/Config.js'
|
import {configDependency} from '../core/Config.js'
|
||||||
import type {EqualOp} from '../interfaces/relational.js'
|
import {OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
||||||
|
|
||||||
export const equal =
|
export const equal =
|
||||||
(dep: {
|
(dep: configDependency): OpType<'equal', number> =>
|
||||||
config: Config
|
(x, y) => {
|
||||||
}): EqualOp<number> => (x, y) => {
|
|
||||||
const eps = dep.config.epsilon
|
const eps = dep.config.epsilon
|
||||||
if (eps === null || eps === undefined) return x === y
|
if (eps === null || eps === undefined) return x === y
|
||||||
if (x === y) return true
|
if (x === y) return true
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { OneOp, ZeroOp, NanOp, ReOp } from '../interfaces/type.js'
|
import type { OpType } from '../interfaces/type.js'
|
||||||
|
|
||||||
export const number_type = {
|
export const number_type = {
|
||||||
before: ['Complex'],
|
before: ['Complex'],
|
||||||
@ -18,9 +18,8 @@ declare module "../interfaces/type" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't like the redundancy of repeating 'zero' and 'ZeroOp', any
|
// I don't like the redundancy of repeating 'zero'; any way to eliminate that?
|
||||||
// way to eliminate that?
|
export const zero: OpType<'zero', number> = (a) => 0
|
||||||
export const zero: ZeroOp<number> = (a) => 0
|
export const one: OpType<'one', number> = (a) => 1
|
||||||
export const one: OneOp<number> = (a) => 1
|
export const nan: OpType<'nan', number> = (a) => NaN
|
||||||
export const nan: NanOp<number> = (a) => NaN
|
export const re: OpType<'re', number> = (a) => a
|
||||||
export const re: ReOp<number> = (a) => a
|
|
||||||
|
Loading…
Reference in New Issue
Block a user