refactor: tighter universal interface types

This commit is contained in:
Glen Whitney 2022-12-24 00:41:35 -05:00
parent a5848125e4
commit 74e2aef524
16 changed files with 251 additions and 214 deletions

View File

@ -1,103 +1,96 @@
import {Complex, complex_binary, FnComplexUnary} from './type.js' import {Complex, ComplexOp} from './type.js'
import type { import type {
FnAbsSquare, AbsquareOp, AddOp, AddRealOp, ConjOp, ConservativeSqrtOp, DivideOp,
FnAdd, DivideByRealOp, MultiplyOp, ReciprocalOp, SqrtOp, SubtractOp,
FnAddReal, UnaryMinusOp
FnConj, FnConservativeSqrt, FnDivide, } from '../interfaces/arithmetic.js'
FnDivideByReal, FnIsReal, FnIsSquare, import type {
FnMultiply, FnNaN, FnRe, FnReciprocal, FnSqrt, NanOp, ReOp, ZeroOp, Depends, RealType, WithConstants, NaNType
FnSubtract, } from '../interfaces/type.js'
FnUnaryMinus, FnZero import type {IsSquareOp, IsRealOp} from '../interfaces/predicate.js'
} from '../interfaces/arithmetic'
export const add = export const add =
<T>(dep: { <T>(dep: Depends<AddOp<T>> & Depends<ComplexOp<T>>): AddOp<Complex<T>> =>
add: FnAdd<T> (w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im))
}): FnAdd<Complex<T>> =>
(w, z) => complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im))
export const addReal = export const addReal =
<T>(dep: { <T>(dep: Depends<AddRealOp<T>> & Depends<ComplexOp<T>>):
addReal: FnAddReal<T, T> AddRealOp<Complex<T>> =>
}): FnAddReal<Complex<T>, T> => (z, r) => dep.complex(dep.addReal(z.re, r), z.im)
(z, r) => complex_binary(dep.addReal(z.re, r), z.im)
export const unaryMinus = export const unaryMinus =
<T>(dep: { <T>(dep: Depends<UnaryMinusOp<T>> & Depends<ComplexOp<T>>):
unaryMinus: FnUnaryMinus<T> UnaryMinusOp<Complex<T>> =>
}): FnUnaryMinus<Complex<T>> => z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
(z) => complex_binary(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
export const conj = export const conj =
<T>(dep: { <T>(dep: Depends<UnaryMinusOp<T>>
unaryMinus: FnUnaryMinus<T>, & Depends<ConjOp<T>>
conj: FnConj<T> & Depends<ComplexOp<T>>):
}) : FnConj<Complex<T>> => ConjOp<Complex<T>> =>
(z) => complex_binary(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: { <T>(dep: Depends<SubtractOp<T>> & Depends<ComplexOp<T>>):
subtract: FnSubtract<T> SubtractOp<Complex<T>> =>
}): FnSubtract<Complex<T>> => (w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
(w, z) => complex_binary(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
export const multiply = export const multiply =
<T>(dep: { <T>(dep: Depends<AddOp<T>>
add: FnAdd<T>, & Depends<SubtractOp<T>>
subtract: FnSubtract<T>, & Depends<MultiplyOp<T>>
multiply: FnMultiply<T>, & Depends<ConjOp<T>>
conj: FnConj<T> & Depends<ComplexOp<T>>):
}) => MultiplyOp<Complex<T>> =>
(w, z) => { (w, z) => {
const mult = dep.multiply const mult = dep.multiply
const realpart = dep.subtract(mult(w.re, z.re), mult(dep.conj(w.im), z.im)) const realpart = dep.subtract(
const imagpart = dep.add(mult(dep.conj(w.re), z.im), mult(w.im, z.re)) mult( w.re, z.re), mult(dep.conj(w.im), z.im))
return complex_binary(realpart, imagpart) const imagpart = dep.add(
mult(dep.conj(w.re), z.im), mult( w.im, z.re))
return dep.complex(realpart, imagpart)
} }
export const absquare = export const absquare =
<T, U>(dep: { <T>(dep: Depends<AddOp<RealType<T>>> & Depends<AbsquareOp<T>>):
add: FnAdd<U>, AbsquareOp<Complex<T>> =>
absquare: FnAbsSquare<T, U> z => dep.add(dep.absquare(z.re), dep.absquare(z.im))
}): FnAbsSquare<Complex<T>, U> =>
(z) => dep.add(dep.absquare(z.re), dep.absquare(z.im))
export const divideByReal = export const divideByReal =
<T>(dep: { <T>(dep: Depends<DivideByRealOp<T>> & Depends<ComplexOp<T>>):
divideByReal: FnDivideByReal<T, T> DivideByRealOp<Complex<T>> =>
}): FnDivideByReal<Complex<T>, T> => (z, r) => dep.complex(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r))
(z, r) => complex_binary(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r))
export const reciprocal = export const reciprocal =
<T>(dep: { <T>(dep: Depends<ConjOp<Complex<T>>>
conj: FnConj<Complex<T>>, & Depends<AbsquareOp<Complex<T>>>
absquare: FnAbsSquare<Complex<T>, T>, & Depends<DivideByRealOp<Complex<T>>>):
divideByReal: FnDivideByReal<Complex<T>, T> ReciprocalOp<Complex<T>> =>
}): FnReciprocal<Complex<T>> => z => dep.divideByReal(dep.conj(z), dep.absquare(z))
(z) => dep.divideByReal(dep.conj(z), dep.absquare(z))
export const divide = export const divide =
<T>(dep: { <T>(dep: Depends<MultiplyOp<Complex<T>>>
multiply: FnMultiply<Complex<T>>, & Depends<ReciprocalOp<Complex<T>>>):
reciprocal: FnReciprocal<Complex<T>>, DivideOp<Complex<T>> =>
}): FnDivide<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 = export const complexSqrt =
<T>(dep: { <T>(dep: Depends<ConservativeSqrtOp<T>>
conservativeSqrt: FnConservativeSqrt<T>, & Depends<IsSquareOp<T>>
isSquare: FnIsSquare<T>, & Depends<UnaryMinusOp<T>>
complex: FnComplexUnary<T>, & Depends<ComplexOp<WithConstants<T>>>
unaryMinus: FnUnaryMinus<T>, & Depends<ZeroOp<T>>
zero: FnZero<T>, & Depends<NanOp<Complex<WithConstants<T>>>>): ComplexSqrtOp<T> =>
nan: FnNaN<Complex<T>> r => {
}) =>
(r) => {
if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r)) if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r))
const negative = dep.unaryMinus(r) const negative = dep.unaryMinus(r)
if (dep.isSquare(negative)) { if (dep.isSquare(negative)) {
return complex_binary( return dep.complex(dep.zero(r), dep.conservativeSqrt(negative))
dep.zero(r), dep.conservativeSqrt(negative))
} }
// neither the real number or its negative is a square; could happen // 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 // for example with bigint. So there is no square root. So we have to
@ -106,17 +99,15 @@ export const complexSqrt =
} }
export const sqrt = export const sqrt =
<T>(dep: { <T>(dep: Depends<IsRealOp<Complex<T>>>
isReal: FnIsReal<Complex<T>>, & Depends<ComplexSqrtOp<T>>
complexSqrt: FnSqrt<T>, & Depends<ConservativeSqrtOp<RealType<T>>>
conservativeSqrt: FnConservativeSqrt<T>, & Depends<AbsquareOp<Complex<T>>>
absquare: FnAbsSquare<Complex<T>, T>, & Depends<AddRealOp<Complex<T>>>
addReal: FnAddReal<Complex<T>, T>, & Depends<DivideByRealOp<Complex<T>>>
divideByReal: FnDivideByReal<Complex<T>, T>, & Depends<AddOp<RealType<T>>>
add: FnAdd<T>, & Depends<ReOp<Complex<T>>>): SqrtOp<Complex<T>> =>
re: FnRe<Complex<T>, T>, z => {
}) =>
(z: Complex<T>): Complex<T> | T => {
if (dep.isReal(z)) return dep.complexSqrt(z.re) 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 num = dep.addReal(z, myabs)

View File

@ -1,14 +1,12 @@
import { Complex } from './type.js' import {Complex} from './type.js'
import {FnEqual} from '../interfaces/relational' import {EqualOp} from '../interfaces/relational.js'
import {FnAdd, FnIsReal, FnIsSquare} from '../interfaces/arithmetic' 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: { <T>(dep: Depends<AddOp<T>> & Depends<EqualOp<T>> & Depends<IsRealOp<T>>):
equal: FnEqual<T>, IsRealOp<Complex<T>> =>
add: FnAdd<T>, z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
isReal: FnIsReal<T>
}): FnIsReal<Complex<T>> =>
(z) => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
export const isSquare = export const isSquare: IsSquareOp<Complex<any>> = z => true // FIXME: not correct for Complex<bigint> once we get there
<T>(): FnIsSquare<Complex<T>> => (z) => true // FIXME: not correct for Complex<bigint> once we get there

View File

@ -1,8 +1,7 @@
import { Complex } from './type.js' import {Complex} from './type.js'
import {FnEqual} from '../interfaces/relational' import {Depends} from '../interfaces/type.js'
import {EqualOp} from '../interfaces/relational.js'
export const equal = export const equal =
<T>(dep: { <T>(dep: Depends<EqualOp<T>>): EqualOp<Complex<T>> =>
equal: FnEqual<T>
}): FnEqual<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)

View File

@ -1,7 +1,9 @@
import { import {
joinTypes, typeOfDependency, Dependency, joinTypes, typeOfDependency, Dependency,
} from '../core/Dispatcher.js' } 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<T> = { re: T; im: T; } export type Complex<T> = { re: T; im: T; }
@ -20,39 +22,41 @@ export const Complex_type = {
} }
} }
export type FnComplexUnary<T> = (t: T) => Complex<T> declare module "../interfaces/type" {
interface AssociatedTypes<T> {
Complex: T extends Complex<infer R> ? {
type: Complex<R>
zero: Complex<ZeroType<R>>
one: Complex<OneType<R> | ZeroType<R>>
nan: Complex<NaNType<R>>
real: RealType<R>
} : never
}
}
export const complex_unary = export type ComplexOp<T> = {op?: 'complex', (a: T, b?: T): Complex<T>}
<T>(dep: {
zero: FnZero<T>
}): FnComplexUnary<T> =>
(t) => ({ re: t, im: dep.zero(t) })
export type FnComplexBinary<T> = (re: T, im: T) => Complex<T> export const complex =
<T>(dep: Depends<ZeroOp<T>>): ComplexOp<T | ZeroType<T>> =>
export const complex_binary = <T>(t: T, u: T): Complex<T> => ({ re: t, im: u }) (a, b) => ({re: a, im: b || dep.zero(a)})
export const zero = export const zero =
<T>(dep: { <T>(dep: Depends<ZeroOp<T>> & Depends<ComplexOp<ZeroType<T>>>):
zero: FnZero<T> ZeroOp<Complex<T>> =>
}): FnZero<Complex<T>> => z => dep.complex(dep.zero(z.re), dep.zero(z.im))
(z) => complex_binary(dep.zero(z.re), dep.zero(z.im))
export const one = export const one =
<T>(dep: { <T>(dep: Depends<OneOp<T>>
zero: FnZero<T>, & Depends<ZeroOp<T>>
one: FnOne<T> & Depends<ComplexOp<ZeroType<T>|OneType<T>>>):
}): FnOne<Complex<T>> => OneOp<Complex<T>> =>
(z) => complex_binary(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: { <T>(dep: Depends<NanOp<T>> & Depends<ComplexOp<NaNType<T>>>):
nan: FnNaN<T> NanOp<Complex<T>> =>
}): FnNaN<Complex<T>> => z => dep.complex(dep.nan(z.re), dep.nan(z.im))
(z) => complex_binary(dep.nan(z.re), dep.nan(z.im))
export const re = export const re =
<T>(dep: { <T>(dep: Depends<ReOp<T>>): ReOp<Complex<T>> =>
re: FnRe<T,T> z => dep.re(z.re)
}): FnRe<Complex<T>, T> =>
(z) => dep.re(z.re)

View File

@ -1,3 +1 @@
import * as generic from './arithmetic.js' export * as generic from './native.js'
export { generic }

View File

@ -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 = export const square =
<T>(dep: { <T>(dep: Depends<MultiplyOp<T>>): SquareOp<T> =>
multiply: FnMultiply<T> z => dep.multiply(z, z)
}): FnSquare<T> =>
(z) => dep.multiply(z, z)

2
src/generic/native.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './arithmetic.js'
export * from './relational.js'

View File

@ -0,0 +1,5 @@
import {Depends} from '../interfaces/type.js'
import type {EqualOp, UnequalOp} from '../interfaces/relational.js'
export const unequal = <T>(dep: Depends<EqualOp<T>>): UnequalOp<T> =>
(x, y) => !dep.equal(x, y)

View File

@ -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 'Op' suddix,
// so it is clear that the type holds the function type of an operation
// Note: right now I've added an 'Fn*' prefix,
// so it is clear that the type hold a function type definition
// This is not necessary though, it is just a naming convention. // This is not necessary though, it is just a naming convention.
export type FnAdd<T> = (a: T, b: T) => T export type AddOp<T> = {op?: 'add', (a: T, b: T): T}
export type FnAddReal<T, U> = (a: T, b: U) => T export type AddRealOp<T> = {op?: 'addReal', (a: T, b: RealType<T>): T}
export type FnUnaryMinus<T> = (a: T) => T export type UnaryMinusOp<T> = {op?: 'unaryMinus', (a: T): T}
export type FnConj<T> = (a: T) => T export type ConjOp<T> = {op?: 'conj', (a: T): T}
export type FnSubtract<T> = (a: T, b: T) => T export type SubtractOp<T> = {op?: 'subtract', (a: T, b: T): T}
export type FnMultiply<T> = (a: T, b: T) => T export type MultiplyOp<T> = {op?: 'multiply', (a: T, b: T): T}
export type FnAbsSquare<T, U> = (a: T) => U export type AbsquareOp<T> = {op?: 'absquare', (a: T): RealType<T>}
export type FnReciprocal<T> = (a: T) => T export type ReciprocalOp<T> = {op?: 'reciprocal', (a: T): T}
export type FnDivide<T> = (a: T, b: T) => T export type DivideOp<T> = {op?: 'divide', (a: T, b: T): T}
export type FnDivideByReal<T, U> = (a: T, b: U) => T export type DivideByRealOp<T> = {op?: 'divideByReal', (a: T, b: RealType<T>): T}
export type FnConservativeSqrt<T> = (a: T) => T export type ConservativeSqrtOp<T> = {op?: 'conservativeSqrt', (a: T): T}
export type FnSqrt<T> = (a: T) => T | Complex<T> export type SqrtOp<T> = {
export type FnSquare<T> = (z: T) => T op?: 'sqrt',
(a: T): T extends Complex<infer R>
? Complex<WithConstants<R> | NaNType<WithConstants<R>>>
: T | Complex<T>
}
export type SquareOp<T> = {op?: 'square', (z: T): T}
export type FnIsReal<T> = (a: T) => boolean
export type FnIsSquare<T> = (a: T) => boolean
export type FnZero<T> = (a: T) => T
export type FnOne<T> = (a: T) => T
export type FnNaN<T> = (a: T) => T
export type FnRe<T, U> = (a: T) => U

View File

@ -0,0 +1,2 @@
export type IsRealOp<T> = {op?: 'isReal', (a: T): boolean}
export type IsSquareOp<T> = {op?: 'isSquare', (a: T): boolean}

View File

@ -1,3 +1,2 @@
export type EqualOp<T> = {op?: 'equal', (a: T, b: T): boolean}
export type FnEqual<T> = (a: T, b: T) => boolean export type UnequalOp<T> = {op?: 'unequal', (a: T, b: T): boolean}
export type FnUnequal<T> = (a: T, b: T) => boolean

32
src/interfaces/type.ts Normal file
View File

@ -0,0 +1,32 @@
export interface AssociatedTypes<T> {
undefined: {
type: undefined
zero: undefined
one: undefined
nan: undefined
real: undefined
}
}
type AssociatedTypeNames = keyof AssociatedTypes<unknown>['undefined']
export type Lookup<T, Name extends AssociatedTypeNames> = {
[K in keyof AssociatedTypes<T>]:
T extends AssociatedTypes<T>[K]['type'] ? AssociatedTypes<T>[K][Name] : never
}[keyof AssociatedTypes<T>]
export type ZeroType<T> = Lookup<T, 'zero'>
export type OneType<T> = Lookup<T, 'one'>
export type WithConstants<T> = T | ZeroType<T> | OneType<T>
export type NaNType<T> = Lookup<T, 'nan'>
export type RealType<T> = Lookup<T, 'real'>
export type ZeroOp<T> = {op?: 'zero', (a: WithConstants<T>): ZeroType<T>}
export type OneOp<T> = {op?: 'one', (a: WithConstants<T>): OneType<T>}
export type NanOp<T> = {op?: 'nan', (a: T|NaNType<T>): NaNType<T>}
export type ReOp<T> = {op?: 're', (a: T): RealType<T>}
type NamedFunction = {op?: string, (...params: any[]): any}
export type Depends<FuncType extends NamedFunction> =
{[K in FuncType['op']]: FuncType}

View File

@ -1,26 +1,28 @@
import { Config } from '../core/Config.js' import type {configDependency} from '../core/Config.js'
import type { FnComplexBinary } from '../Complex/type.js' import type {ComplexOp} from '../Complex/type.js'
import { FnAdd, FnConj, FnSubtract, FnUnaryMinus, FnMultiply, FnAbsSquare, FnReciprocal, FnDivide, FnConservativeSqrt, FnSqrt } from '../interfaces/arithmetic.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<number> = (a, b) => a + b export const add: AddOp<number> = (a, b) => a + b
export const addReal = add export const addReal = add
export const unaryMinus: FnUnaryMinus<number> = (a) => -a export const unaryMinus: UnaryMinusOp<number> = a => -a
export const conj: FnConj<number> = (a) => a export const conj: ConjOp<number> = a => a
export const subtract: FnSubtract<number> = (a, b) => a - b export const subtract: SubtractOp<number> = (a, b) => a - b
export const multiply: FnMultiply<number> = (a, b) => a * b export const multiply: MultiplyOp<number> = (a, b) => a * b
export const absquare: FnAbsSquare<number, number> = (a) => a * a export const absquare: AbsquareOp<number> = a => a * a
export const reciprocal: FnReciprocal<number> = (a) => 1 / a export const reciprocal: ReciprocalOp<number> = a => 1 / a
export const divide: FnDivide<number> = (a, b) => a / b export const divide: DivideOp<number> = (a, b) => a / b
export const divideByReal = divide export const divideByReal = divide
export const conservativeSqrt: FnConservativeSqrt<number> = (a) => isNaN(a) ? NaN : Math.sqrt(a) const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
export const conservativeSqrt: ConservativeSqrtOp<number> = basicSqrt
export const sqrt = export const sqrt =
(dep: { (dep: configDependency & Depends<ComplexOp<number>>): SqrtOp<number> => {
config: Config, if (dep.config.predictable || !dep.complex) return basicSqrt
complex: FnComplexBinary<number>
}): FnSqrt<number> => {
if (dep.config.predictable || !dep.complex) return conservativeSqrt
return a => { return a => {
if (isNaN(a)) return NaN if (isNaN(a)) return NaN
if (a >= 0) return Math.sqrt(a) if (a >= 0) return Math.sqrt(a)

View File

@ -1,4 +1,4 @@
import type { FnIsReal, FnIsSquare } from "../interfaces/arithmetic" import type { IsRealOp, IsSquareOp } from '../interfaces/predicate.js'
export const isReal: FnIsReal<number> = (a) => true export const isReal: IsRealOp<number> = (a) => true
export const isSquare: FnIsSquare<number> = (a) => a >= 0 export const isSquare: IsSquareOp<number> = (a) => a >= 0

View File

@ -1,12 +1,12 @@
import { Config } from '../core/Config.js' import {Config} from '../core/Config.js'
import type { FnEqual, FnUnequal } from '../interfaces/relational.js' import type {EqualOp} from '../interfaces/relational.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: {
config: Config config: Config
}): FnEqual<number> => (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
@ -20,8 +20,3 @@ export const equal =
return false return false
} }
export const unequal = (dep: {
equal: FnEqual<number>
}): FnUnequal<number> =>
(x, y) => !dep.equal(x, y)

View File

@ -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 = { export const number_type = {
before: ['Complex'], before: ['Complex'],
@ -6,7 +6,21 @@ export const number_type = {
from: { string: (s: string) => +s } from: { string: (s: string) => +s }
} }
export const zero: FnZero<number> = (a) => 0 declare module "../interfaces/type" {
export const one: FnOne<number> = (a) => 1 interface AssociatedTypes<T> {
export const nan: FnNaN<number> = (a) => NaN numbers: {
export const re: FnRe<number, number> = (a) => a 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<number> = (a) => 0
export const one: OneOp<number> = (a) => 1
export const nan: NanOp<number> = (a) => NaN
export const re: ReOp<number> = (a) => a