diff --git a/src/Complex/arithmetic.ts b/src/Complex/arithmetic.ts index a9d0d42..de0e16d 100644 --- a/src/Complex/arithmetic.ts +++ b/src/Complex/arithmetic.ts @@ -1,129 +1,120 @@ -import {Complex, complex_binary, FnComplexUnary} from './type.js' +import {Complex, ComplexOp} from './type.js' import type { - FnAbsSquare, - FnAdd, - FnAddReal, - FnConj, FnConservativeSqrt, FnDivide, - FnDivideByReal, FnIsReal, FnIsSquare, - FnMultiply, FnNaN, FnRe, FnReciprocal, FnSqrt, - FnSubtract, - FnUnaryMinus, FnZero -} from '../interfaces/arithmetic' + AbsquareOp, AddOp, AddRealOp, ConjOp, ConservativeSqrtOp, DivideOp, + DivideByRealOp, MultiplyOp, ReciprocalOp, SqrtOp, SubtractOp, + UnaryMinusOp +} from '../interfaces/arithmetic.js' +import type { + NanOp, ReOp, ZeroOp, Depends, RealType, WithConstants, NaNType +} from '../interfaces/type.js' +import type {IsSquareOp, IsRealOp} from '../interfaces/predicate.js' export const add = - (dep: { - add: FnAdd - }): FnAdd> => - (w, z) => complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im)) + (dep: Depends> & Depends>): AddOp> => + (w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im)) export const addReal = - (dep: { - addReal: FnAddReal - }): FnAddReal, T> => - (z, r) => complex_binary(dep.addReal(z.re, r), z.im) + (dep: Depends> & Depends>): + AddRealOp> => + (z, r) => dep.complex(dep.addReal(z.re, r), z.im) export const unaryMinus = - (dep: { - unaryMinus: FnUnaryMinus - }): FnUnaryMinus> => - (z) => complex_binary(dep.unaryMinus(z.re), dep.unaryMinus(z.im)) + (dep: Depends> & Depends>): + UnaryMinusOp> => + z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im)) export const conj = - (dep: { - unaryMinus: FnUnaryMinus, - conj: FnConj - }) : FnConj> => - (z) => complex_binary(dep.conj(z.re), dep.unaryMinus(z.im)) + (dep: Depends> + & Depends> + & Depends>): + ConjOp> => + z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im)) export const subtract = - (dep: { - subtract: FnSubtract - }): FnSubtract> => - (w, z) => complex_binary(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im)) + (dep: Depends> & Depends>): + SubtractOp> => + (w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im)) export const multiply = - (dep: { - add: FnAdd, - subtract: FnSubtract, - multiply: FnMultiply, - conj: FnConj - }) => - (w, z) => { - const mult = dep.multiply - const realpart = dep.subtract(mult(w.re, z.re), mult(dep.conj(w.im), z.im)) - const imagpart = dep.add(mult(dep.conj(w.re), z.im), mult(w.im, z.re)) - return complex_binary(realpart, imagpart) - } + (dep: Depends> + & Depends> + & Depends> + & Depends> + & Depends>): + MultiplyOp> => + (w, z) => { + const mult = dep.multiply + const realpart = dep.subtract( + mult( w.re, z.re), mult(dep.conj(w.im), z.im)) + const imagpart = dep.add( + mult(dep.conj(w.re), z.im), mult( w.im, z.re)) + return dep.complex(realpart, imagpart) + } export const absquare = - (dep: { - add: FnAdd, - absquare: FnAbsSquare - }): FnAbsSquare, U> => - (z) => dep.add(dep.absquare(z.re), dep.absquare(z.im)) + (dep: Depends>> & Depends>): + AbsquareOp> => + z => dep.add(dep.absquare(z.re), dep.absquare(z.im)) export const divideByReal = - (dep: { - divideByReal: FnDivideByReal - }): FnDivideByReal, T> => - (z, r) => complex_binary(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r)) + (dep: Depends> & Depends>): + DivideByRealOp> => + (z, r) => dep.complex(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r)) export const reciprocal = - (dep: { - conj: FnConj>, - absquare: FnAbsSquare, T>, - divideByReal: FnDivideByReal, T> - }): FnReciprocal> => - (z) => dep.divideByReal(dep.conj(z), dep.absquare(z)) + (dep: Depends>> + & Depends>> + & Depends>>): + ReciprocalOp> => + z => dep.divideByReal(dep.conj(z), dep.absquare(z)) export const divide = - (dep: { - multiply: FnMultiply>, - reciprocal: FnReciprocal>, - }): FnDivide> => - (w, z) => dep.multiply(w, dep.reciprocal(z)) + (dep: Depends>> + & Depends>>): + DivideOp> => + (w, z) => dep.multiply(w, dep.reciprocal(z)) +export type ComplexSqrtOp = { + op?: 'complexSqrt', + (a: T): Complex | NaNType>> +} +// Complex square root of a real type T export const complexSqrt = - (dep: { - conservativeSqrt: FnConservativeSqrt, - isSquare: FnIsSquare, - complex: FnComplexUnary, - unaryMinus: FnUnaryMinus, - zero: FnZero, - nan: FnNaN> - }) => - (r) => { - if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r)) - const negative = dep.unaryMinus(r) - if (dep.isSquare(negative)) { - return complex_binary( - 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)) + (dep: Depends> + & Depends> + & Depends> + & Depends>> + & Depends> + & Depends>>>): ComplexSqrtOp => + 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 = - (dep: { - isReal: FnIsReal>, - complexSqrt: FnSqrt, - conservativeSqrt: FnConservativeSqrt, - absquare: FnAbsSquare, T>, - addReal: FnAddReal, T>, - divideByReal: FnDivideByReal, T>, - add: FnAdd, - re: FnRe, T>, - }) => - (z: Complex): Complex | T => { - if (dep.isReal(z)) return dep.complexSqrt(z.re) - const myabs = dep.conservativeSqrt(dep.absquare(z)) - const num = dep.addReal(z, myabs) - const r = dep.re(z) - const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r)) - const denom = dep.conservativeSqrt(denomsq) - return dep.divideByReal(num, denom) - } + (dep: Depends>> + & Depends> + & Depends>> + & Depends>> + & Depends>> + & Depends>> + & Depends>> + & Depends>>): SqrtOp> => + z => { + if (dep.isReal(z)) return dep.complexSqrt(z.re) + const myabs = dep.conservativeSqrt(dep.absquare(z)) + const num = dep.addReal(z, myabs) + const r = dep.re(z) + const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r)) + const denom = dep.conservativeSqrt(denomsq) + return dep.divideByReal(num, denom) + } export const conservativeSqrt = sqrt diff --git a/src/Complex/predicate.ts b/src/Complex/predicate.ts index 557ea83..cddd383 100644 --- a/src/Complex/predicate.ts +++ b/src/Complex/predicate.ts @@ -1,14 +1,12 @@ -import { Complex } from './type.js' -import {FnEqual} from '../interfaces/relational' -import {FnAdd, FnIsReal, FnIsSquare} from '../interfaces/arithmetic' +import {Complex} from './type.js' +import {EqualOp} from '../interfaces/relational.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 = - (dep: { - equal: FnEqual, - add: FnAdd, - isReal: FnIsReal - }): FnIsReal> => - (z) => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im)) + (dep: Depends> & Depends> & Depends>): + IsRealOp> => + z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im)) -export const isSquare = - (): FnIsSquare> => (z) => true // FIXME: not correct for Complex once we get there +export const isSquare: IsSquareOp> = z => true // FIXME: not correct for Complex once we get there diff --git a/src/Complex/relational.ts b/src/Complex/relational.ts index 9807d71..89db758 100644 --- a/src/Complex/relational.ts +++ b/src/Complex/relational.ts @@ -1,8 +1,7 @@ -import { Complex } from './type.js' -import {FnEqual} from '../interfaces/relational' +import {Complex} from './type.js' +import {Depends} from '../interfaces/type.js' +import {EqualOp} from '../interfaces/relational.js' export const equal = - (dep: { - equal: FnEqual - }): FnEqual> => - (w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im) + (dep: Depends>): EqualOp> => + (w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im) diff --git a/src/Complex/type.ts b/src/Complex/type.ts index 3b4e82d..f995f92 100644 --- a/src/Complex/type.ts +++ b/src/Complex/type.ts @@ -1,7 +1,9 @@ import { joinTypes, typeOfDependency, Dependency, } from '../core/Dispatcher.js' -import type {FnNaN, FnOne, FnRe, FnZero} from '../interfaces/arithmetic.js' +import type { + OneOp, ZeroOp, NanOp, ReOp, ZeroType, OneType, NaNType, Depends +} from '../interfaces/type.js' export type Complex = { re: T; im: T; } @@ -20,39 +22,41 @@ export const Complex_type = { } } -export type FnComplexUnary = (t: T) => Complex +declare module "../interfaces/type" { + interface AssociatedTypes { + Complex: T extends Complex ? { + type: Complex + zero: Complex> + one: Complex | ZeroType> + nan: Complex> + real: RealType + } : never + } +} -export const complex_unary = - (dep: { - zero: FnZero - }): FnComplexUnary => - (t) => ({ re: t, im: dep.zero(t) }) +export type ComplexOp = {op?: 'complex', (a: T, b?: T): Complex} -export type FnComplexBinary = (re: T, im: T) => Complex - -export const complex_binary = (t: T, u: T): Complex => ({ re: t, im: u }) +export const complex = + (dep: Depends>): ComplexOp> => + (a, b) => ({re: a, im: b || dep.zero(a)}) export const zero = - (dep: { - zero: FnZero - }): FnZero> => - (z) => complex_binary(dep.zero(z.re), dep.zero(z.im)) + (dep: Depends> & Depends>>): + ZeroOp> => + z => dep.complex(dep.zero(z.re), dep.zero(z.im)) export const one = - (dep: { - zero: FnZero, - one: FnOne - }): FnOne> => - (z) => complex_binary(dep.one(z.re), dep.zero(z.im)) + (dep: Depends> + & Depends> + & Depends|OneType>>): + OneOp> => + z => dep.complex(dep.one(z.re), dep.zero(z.im)) export const nan = - (dep: { - nan: FnNaN - }): FnNaN> => - (z) => complex_binary(dep.nan(z.re), dep.nan(z.im)) + (dep: Depends> & Depends>>): + NanOp> => + z => dep.complex(dep.nan(z.re), dep.nan(z.im)) export const re = - (dep: { - re: FnRe - }): FnRe, T> => - (z) => dep.re(z.re) + (dep: Depends>): ReOp> => + z => dep.re(z.re) diff --git a/src/generic/all.ts b/src/generic/all.ts index 1b1b8a4..78fa222 100644 --- a/src/generic/all.ts +++ b/src/generic/all.ts @@ -1,3 +1 @@ -import * as generic from './arithmetic.js' - -export { generic } +export * as generic from './native.js' diff --git a/src/generic/arithmetic.ts b/src/generic/arithmetic.ts index a1379df..a7cf2b7 100644 --- a/src/generic/arithmetic.ts +++ b/src/generic/arithmetic.ts @@ -1,7 +1,6 @@ -import type { FnMultiply, FnSquare } from "../interfaces/arithmetic" +import type {Depends} from '../interfaces/type.js' +import type {MultiplyOp, SquareOp} from '../interfaces/arithmetic.js' export const square = - (dep: { - multiply: FnMultiply - }): FnSquare => - (z) => dep.multiply(z, z) + (dep: Depends>): SquareOp => + z => dep.multiply(z, z) diff --git a/src/generic/native.ts b/src/generic/native.ts new file mode 100644 index 0000000..b8290ae --- /dev/null +++ b/src/generic/native.ts @@ -0,0 +1,2 @@ +export * from './arithmetic.js' +export * from './relational.js' diff --git a/src/generic/relational.ts b/src/generic/relational.ts new file mode 100644 index 0000000..f47c392 --- /dev/null +++ b/src/generic/relational.ts @@ -0,0 +1,5 @@ +import {Depends} from '../interfaces/type.js' +import type {EqualOp, UnequalOp} from '../interfaces/relational.js' + +export const unequal = (dep: Depends>): UnequalOp => + (x, y) => !dep.equal(x, y) diff --git a/src/interfaces/arithmetic.ts b/src/interfaces/arithmetic.ts index 91b59bf..80e1155 100644 --- a/src/interfaces/arithmetic.ts +++ b/src/interfaces/arithmetic.ts @@ -1,28 +1,25 @@ -// shared interfaces +import type {Complex} from '../Complex/type.js' +import type {RealType, WithConstants, NaNType} from './type.js' -import { Complex } from "../Complex/type" - -// Note: right now I've added an 'Fn*' prefix, -// so it is clear that the type hold a function type definition +// Note: right now I've added an 'Op' suddix, +// so it is clear that the type holds the function type of an operation // This is not necessary though, it is just a naming convention. -export type FnAdd = (a: T, b: T) => T -export type FnAddReal = (a: T, b: U) => T -export type FnUnaryMinus = (a: T) => T -export type FnConj = (a: T) => T -export type FnSubtract = (a: T, b: T) => T -export type FnMultiply = (a: T, b: T) => T -export type FnAbsSquare = (a: T) => U -export type FnReciprocal = (a: T) => T -export type FnDivide = (a: T, b: T) => T -export type FnDivideByReal = (a: T, b: U) => T -export type FnConservativeSqrt = (a: T) => T -export type FnSqrt = (a: T) => T | Complex -export type FnSquare = (z: T) => T +export type AddOp = {op?: 'add', (a: T, b: T): T} +export type AddRealOp = {op?: 'addReal', (a: T, b: RealType): T} +export type UnaryMinusOp = {op?: 'unaryMinus', (a: T): T} +export type ConjOp = {op?: 'conj', (a: T): T} +export type SubtractOp = {op?: 'subtract', (a: T, b: T): T} +export type MultiplyOp = {op?: 'multiply', (a: T, b: T): T} +export type AbsquareOp = {op?: 'absquare', (a: T): RealType} +export type ReciprocalOp = {op?: 'reciprocal', (a: T): T} +export type DivideOp = {op?: 'divide', (a: T, b: T): T} +export type DivideByRealOp = {op?: 'divideByReal', (a: T, b: RealType): T} +export type ConservativeSqrtOp = {op?: 'conservativeSqrt', (a: T): T} +export type SqrtOp = { + op?: 'sqrt', + (a: T): T extends Complex + ? Complex | NaNType>> + : T | Complex +} +export type SquareOp = {op?: 'square', (z: T): T} -export type FnIsReal = (a: T) => boolean -export type FnIsSquare = (a: T) => boolean - -export type FnZero = (a: T) => T -export type FnOne = (a: T) => T -export type FnNaN = (a: T) => T -export type FnRe = (a: T) => U diff --git a/src/interfaces/predicate.ts b/src/interfaces/predicate.ts new file mode 100644 index 0000000..37a69ea --- /dev/null +++ b/src/interfaces/predicate.ts @@ -0,0 +1,2 @@ +export type IsRealOp = {op?: 'isReal', (a: T): boolean} +export type IsSquareOp = {op?: 'isSquare', (a: T): boolean} diff --git a/src/interfaces/relational.ts b/src/interfaces/relational.ts index 2865f77..29529ff 100644 --- a/src/interfaces/relational.ts +++ b/src/interfaces/relational.ts @@ -1,3 +1,2 @@ - -export type FnEqual = (a: T, b: T) => boolean -export type FnUnequal = (a: T, b: T) => boolean +export type EqualOp = {op?: 'equal', (a: T, b: T): boolean} +export type UnequalOp = {op?: 'unequal', (a: T, b: T): boolean} diff --git a/src/interfaces/type.ts b/src/interfaces/type.ts new file mode 100644 index 0000000..1d7e103 --- /dev/null +++ b/src/interfaces/type.ts @@ -0,0 +1,32 @@ +export interface AssociatedTypes { + undefined: { + type: undefined + zero: undefined + one: undefined + nan: undefined + real: undefined + } +} + +type AssociatedTypeNames = keyof AssociatedTypes['undefined'] +export type Lookup = { + [K in keyof AssociatedTypes]: + T extends AssociatedTypes[K]['type'] ? AssociatedTypes[K][Name] : never +}[keyof AssociatedTypes] + +export type ZeroType = Lookup +export type OneType = Lookup +export type WithConstants = T | ZeroType | OneType +export type NaNType = Lookup +export type RealType = Lookup + +export type ZeroOp = {op?: 'zero', (a: WithConstants): ZeroType} +export type OneOp = {op?: 'one', (a: WithConstants): OneType} +export type NanOp = {op?: 'nan', (a: T|NaNType): NaNType} +export type ReOp = {op?: 're', (a: T): RealType} + +type NamedFunction = {op?: string, (...params: any[]): any} +export type Depends = + {[K in FuncType['op']]: FuncType} + + diff --git a/src/numbers/arithmetic.ts b/src/numbers/arithmetic.ts index 7142797..6a05fe8 100644 --- a/src/numbers/arithmetic.ts +++ b/src/numbers/arithmetic.ts @@ -1,26 +1,28 @@ -import { Config } from '../core/Config.js' -import type { FnComplexBinary } from '../Complex/type.js' -import { FnAdd, FnConj, FnSubtract, FnUnaryMinus, FnMultiply, FnAbsSquare, FnReciprocal, FnDivide, FnConservativeSqrt, FnSqrt } from '../interfaces/arithmetic.js' +import type {configDependency} from '../core/Config.js' +import type {ComplexOp} from '../Complex/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: FnAdd = (a, b) => a + b +export const add: AddOp = (a, b) => a + b export const addReal = add -export const unaryMinus: FnUnaryMinus = (a) => -a -export const conj: FnConj = (a) => a -export const subtract: FnSubtract = (a, b) => a - b -export const multiply: FnMultiply = (a, b) => a * b -export const absquare: FnAbsSquare = (a) => a * a -export const reciprocal: FnReciprocal = (a) => 1 / a -export const divide: FnDivide = (a, b) => a / b +export const unaryMinus: UnaryMinusOp = a => -a +export const conj: ConjOp = a => a +export const subtract: SubtractOp = (a, b) => a - b +export const multiply: MultiplyOp = (a, b) => a * b +export const absquare: AbsquareOp = a => a * a +export const reciprocal: ReciprocalOp = a => 1 / a +export const divide: DivideOp = (a, b) => a / b export const divideByReal = divide -export const conservativeSqrt: FnConservativeSqrt = (a) => isNaN(a) ? NaN : Math.sqrt(a) +const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a) +export const conservativeSqrt: ConservativeSqrtOp = basicSqrt export const sqrt = - (dep: { - config: Config, - complex: FnComplexBinary - }): FnSqrt => { - if (dep.config.predictable || !dep.complex) return conservativeSqrt + (dep: configDependency & Depends>): SqrtOp => { + if (dep.config.predictable || !dep.complex) return basicSqrt return a => { if (isNaN(a)) return NaN if (a >= 0) return Math.sqrt(a) diff --git a/src/numbers/predicate.ts b/src/numbers/predicate.ts index 4015c55..2018e56 100644 --- a/src/numbers/predicate.ts +++ b/src/numbers/predicate.ts @@ -1,4 +1,4 @@ -import type { FnIsReal, FnIsSquare } from "../interfaces/arithmetic" +import type { IsRealOp, IsSquareOp } from '../interfaces/predicate.js' -export const isReal: FnIsReal = (a) => true -export const isSquare: FnIsSquare = (a) => a >= 0 +export const isReal: IsRealOp = (a) => true +export const isSquare: IsSquareOp = (a) => a >= 0 diff --git a/src/numbers/relational.ts b/src/numbers/relational.ts index ae9a63d..5dbb3c5 100644 --- a/src/numbers/relational.ts +++ b/src/numbers/relational.ts @@ -1,12 +1,12 @@ -import { Config } from '../core/Config.js' -import type { FnEqual, FnUnequal } from '../interfaces/relational.js' +import {Config} from '../core/Config.js' +import type {EqualOp} from '../interfaces/relational.js' const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16 export const equal = (dep: { config: Config - }): FnEqual => (x, y) => { + }): EqualOp => (x, y) => { const eps = dep.config.epsilon if (eps === null || eps === undefined) return x === y if (x === y) return true @@ -20,8 +20,3 @@ export const equal = return false } - -export const unequal = (dep: { - equal: FnEqual -}): FnUnequal => - (x, y) => !dep.equal(x, y) diff --git a/src/numbers/type.ts b/src/numbers/type.ts index 77336ef..ff1ff61 100644 --- a/src/numbers/type.ts +++ b/src/numbers/type.ts @@ -1,4 +1,4 @@ -import type { FnNaN, FnOne, FnZero, FnRe } from "../interfaces/arithmetic" +import type { OneOp, ZeroOp, NanOp, ReOp } from '../interfaces/type.js' export const number_type = { before: ['Complex'], @@ -6,7 +6,21 @@ export const number_type = { from: { string: (s: string) => +s } } -export const zero: FnZero = (a) => 0 -export const one: FnOne = (a) => 1 -export const nan: FnNaN = (a) => NaN -export const re: FnRe = (a) => a +declare module "../interfaces/type" { + interface AssociatedTypes { + numbers: { + type: number + zero: 0 + one: 1 + nan: typeof NaN + real: number + } + } +} + +// I don't like the redundancy of repeating 'zero' and 'ZeroOp', any +// way to eliminate that? +export const zero: ZeroOp = (a) => 0 +export const one: OneOp = (a) => 1 +export const nan: NanOp = (a) => NaN +export const re: ReOp = (a) => a