Declare implementations and dependencies via standard interfaces for operations (#8)
Adds a new subdirectory `interfaces` where standard interfaces are defined. Additional interfaces for a given operation can be added with an `AliasOf` type operator. Provides type operators that give the return type, full function type, and the type of a dependency on, a given operator. Resolves #6. Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-authored-by: Jos de Jong <wjosdejong@gmail.com> Reviewed-on: #8
This commit is contained in:
parent
3fa216d1f4
commit
cc1e66c054
3
.gitignore
vendored
3
.gitignore
vendored
@ -129,6 +129,9 @@ dist
|
|||||||
# Stores VSCode versions used for testing VSCode extensions
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
.vscode-test
|
.vscode-test
|
||||||
|
|
||||||
|
# Webstorm
|
||||||
|
.idea
|
||||||
|
|
||||||
# yarn v2
|
# yarn v2
|
||||||
.yarn/cache
|
.yarn/cache
|
||||||
.yarn/unplugged
|
.yarn/unplugged
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
# typocomath
|
# typocomath
|
||||||
|
|
||||||
A final (?) prototype for a refactor of mathjs, culminating the picomath, pocomath, typomath series. Provides an extensible core with "fuzzy" types for its operations, that can at any time generate exact .d.ts file for its current state.
|
A final (?) prototype for a refactor of mathjs, culminating the picomath, pocomath, typomath series. Provides an extensible core with "fuzzy" types for its operations, that can at any time generate exact .d.ts file for its current state.
|
||||||
|
|
||||||
|
To build and run the prototype, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
npx tsc
|
||||||
|
node obj
|
||||||
|
```
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import {ForType} from '../core/Dispatcher.js'
|
|
||||||
import * as Complex from './native.js'
|
import * as Complex from './native.js'
|
||||||
|
import * as complex from './arithmetic.js'
|
||||||
|
|
||||||
|
export { complex }
|
||||||
|
|
||||||
export {Complex}
|
export {Complex}
|
||||||
|
|
||||||
declare module "../core/Dispatcher" {
|
|
||||||
interface ImplementationTypes extends ForType<'Complex', typeof Complex> {}
|
|
||||||
}
|
|
||||||
|
101
src/Complex/arithmetic.ts
Normal file
101
src/Complex/arithmetic.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import {Complex} from './type.js'
|
||||||
|
import type {
|
||||||
|
Dependencies, Signature, Returns, RealType, AliasOf
|
||||||
|
} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
declare module "../interfaces/type" {
|
||||||
|
interface Signatures<T> {
|
||||||
|
addReal: AliasOf<'add', (a: T, b: RealType<T>) => T>
|
||||||
|
divideReal: AliasOf<'divide', (a: T, b: RealType<T>) => T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const add =
|
||||||
|
<T>(dep: Dependencies<'add' | 'complex', T>): Signature<'add', Complex<T>> =>
|
||||||
|
(w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im))
|
||||||
|
|
||||||
|
export const addReal =
|
||||||
|
<T>(dep: Dependencies<'addReal' | 'complex', T>):
|
||||||
|
Signature<'addReal', Complex<T>> =>
|
||||||
|
(z, r) => dep.complex(dep.addReal(z.re, r), z.im)
|
||||||
|
|
||||||
|
export const unaryMinus =
|
||||||
|
<T>(dep: Dependencies<'unaryMinus' | 'complex', T>):
|
||||||
|
Signature<'unaryMinus', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
|
export const conj =
|
||||||
|
<T>(dep: Dependencies<'unaryMinus' | 'conj' | 'complex', T>):
|
||||||
|
Signature<'conj', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
|
export const subtract =
|
||||||
|
<T>(dep: Dependencies<'subtract' | 'complex', T>):
|
||||||
|
Signature<'subtract', Complex<T>> =>
|
||||||
|
(w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
|
||||||
|
|
||||||
|
export const multiply =
|
||||||
|
<T>(dep: Dependencies<
|
||||||
|
'add' | 'subtract' | 'multiply' | 'conj' | 'complex', T>):
|
||||||
|
Signature<'multiply', Complex<T>> =>
|
||||||
|
(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 =
|
||||||
|
<T>(dep: Dependencies<'absquare', T>
|
||||||
|
& Dependencies<'add', Returns<'absquare', T>>):
|
||||||
|
Signature<'absquare', Complex<T>> =>
|
||||||
|
z => dep.add(dep.absquare(z.re), dep.absquare(z.im))
|
||||||
|
|
||||||
|
export const divideReal =
|
||||||
|
<T>(dep: Dependencies<'divideReal' | 'complex', T>):
|
||||||
|
Signature<'divideReal', Complex<T>> =>
|
||||||
|
(z, r) => dep.complex(dep.divideReal(z.re, r), dep.divideReal(z.im, r))
|
||||||
|
|
||||||
|
export const reciprocal =
|
||||||
|
<T>(dep: Dependencies<'conj' | 'absquare' | 'divideReal', Complex<T>>):
|
||||||
|
Signature<'reciprocal', Complex<T>> =>
|
||||||
|
z => dep.divideReal(dep.conj(z), dep.absquare(z))
|
||||||
|
|
||||||
|
export const divide =
|
||||||
|
<T>(dep: Dependencies<'multiply' | 'reciprocal', Complex<T>>):
|
||||||
|
Signature<'divide', Complex<T>> =>
|
||||||
|
(w, z) => dep.multiply(w, dep.reciprocal(z))
|
||||||
|
|
||||||
|
// The dependencies are slightly tricky here, because there are three types
|
||||||
|
// involved: Complex<T>, T, and RealType<T>, all of which might be different,
|
||||||
|
// and we have to get it straight which operations we need on each type, and
|
||||||
|
// in fact, we need `addReal` on both T and Complex<T>, hence the dependency
|
||||||
|
// with a custom name, not generated via Dependencies<...>
|
||||||
|
export const sqrt =
|
||||||
|
<T>(dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType<T>>
|
||||||
|
& Dependencies<'zero' | 'complex', T>
|
||||||
|
& Dependencies<'absquare' | 're' | 'divideReal', Complex<T>>
|
||||||
|
& {
|
||||||
|
addTR: Signature<'addReal', T>,
|
||||||
|
addRR: Signature<'add', RealType<T>>,
|
||||||
|
addCR: Signature<'addReal', Complex<T>>
|
||||||
|
}):
|
||||||
|
Signature<'sqrt', Complex<T>> =>
|
||||||
|
z => {
|
||||||
|
const myabs = dep.conservativeSqrt(dep.absquare(z))
|
||||||
|
const r = dep.re(z)
|
||||||
|
const negr = dep.unaryMinus(r)
|
||||||
|
if (dep.equal(myabs, negr)) {
|
||||||
|
// pure imaginary square root; z.im already zero
|
||||||
|
return dep.complex(
|
||||||
|
dep.zero(z.re), dep.addTR(z.im, dep.conservativeSqrt(negr)))
|
||||||
|
}
|
||||||
|
const num = dep.addCR(z, myabs)
|
||||||
|
const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r))
|
||||||
|
const denom = dep.conservativeSqrt(denomsq)
|
||||||
|
return dep.divideReal(num, denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const conservativeSqrt = sqrt
|
9
src/Complex/predicate.ts
Normal file
9
src/Complex/predicate.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {Complex} from './type.js'
|
||||||
|
import type {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const isReal =
|
||||||
|
<T>(dep: Dependencies<'add' | 'equal' | 'isReal', T>):
|
||||||
|
Signature<'isReal', Complex<T>> =>
|
||||||
|
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
||||||
|
|
||||||
|
export const isSquare: Signature<'isSquare', Complex<any>> = z => true // FIXME: not correct for Complex<bigint> once we get there
|
6
src/Complex/relational.ts
Normal file
6
src/Complex/relational.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {Complex} from './type.js'
|
||||||
|
import {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const equal =
|
||||||
|
<T>(dep: Dependencies<'equal', T>): Signature<'equal', Complex<T>> =>
|
||||||
|
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
@ -1,22 +1,63 @@
|
|||||||
import {joinTypes, typeOfDependency, Dependency} from '../core/Dispatcher.js'
|
import {joinTypes, typeOfDependency} from '../core/Dispatcher.js'
|
||||||
|
import type {
|
||||||
|
ZeroType, OneType, NaNType, Dependencies, Signature, Returns
|
||||||
|
} from '../interfaces/type.js'
|
||||||
|
|
||||||
export type Complex<T> = {re: T; im: T;}
|
export type Complex<T> = { re: T; im: T; }
|
||||||
|
|
||||||
export const Complex_type = {
|
export const Complex_type = {
|
||||||
test: <T>(dep: {testT: (z: unknown) => z is T}) =>
|
test: <T>(dep: { testT: (z: unknown) => z is T }) =>
|
||||||
(z: unknown): z is Complex<T> =>
|
(z: unknown): z is Complex<T> =>
|
||||||
typeof z === 'object' && 're' in z && 'im' in z
|
typeof z === 'object' && z != null && 're' in z && 'im' in z
|
||||||
&& dep.testT(z.re) && dep.testT(z.im),
|
&& dep.testT(z['re']) && dep.testT(z['im']),
|
||||||
infer: (dep: typeOfDependency) =>
|
infer: (dep: typeOfDependency) =>
|
||||||
(z: Complex<unknown>) => joinTypes(dep.typeOf(z.re), dep.typeOf(z.im)),
|
(z: Complex<unknown>) => joinTypes(dep.typeOf(z.re), dep.typeOf(z.im)),
|
||||||
from: {
|
from: {
|
||||||
T: <T>(dep: Dependency<'zero', [T]>) => (t: T) =>
|
T: <T>(dep: Dependencies<'zero', T>) => (t: T) =>
|
||||||
({re: t, im: dep.zero(t)}),
|
({ re: t, im: dep.zero(t) }),
|
||||||
Complex: <U,T>(dep: {convert: (from: U) => T}) =>
|
Complex: <U, T>(dep: { convert: (from: U) => T }) =>
|
||||||
(z: Complex<U>) => ({re: dep.convert(z.re), im: dep.convert(z.im)})
|
(z: Complex<U>) => ({ re: dep.convert(z.re), im: dep.convert(z.im) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const complex_unary = <T>(dep: Dependency<'zero', [T]>) =>
|
declare module "../interfaces/type" {
|
||||||
(t: T) => ({re: t, im: dep.zero(t)})
|
interface AssociatedTypes<T> {
|
||||||
export const complex_binary = <T>(t: T, u: T) => ({re: t, im: u})
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Signatures<T> {
|
||||||
|
complex: ((re: T) => Complex<T>) | ((re: T, im: T) => Complex<T>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const complex =
|
||||||
|
<T>(dep: Dependencies<'zero', T>): Signature<'complex', T> =>
|
||||||
|
(a, b) => ({re: a, im: b || dep.zero(a)})
|
||||||
|
|
||||||
|
export const zero =
|
||||||
|
<T>(dep: Dependencies<'zero', T>
|
||||||
|
& Dependencies<'complex', Returns<'zero', T>>):
|
||||||
|
Signature<'zero', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.zero(z.re), dep.zero(z.im))
|
||||||
|
|
||||||
|
export const one =
|
||||||
|
<T>(dep: Dependencies<'one' | 'zero', T>
|
||||||
|
& Dependencies<'complex', Returns<'one' | 'zero', T>>):
|
||||||
|
Signature<'one', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.one(z.re), dep.zero(z.im))
|
||||||
|
|
||||||
|
export const nan =
|
||||||
|
<T>(dep: Dependencies<'nan', T>
|
||||||
|
& Dependencies<'complex', Returns<'nan', T>>):
|
||||||
|
Signature<'nan', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.nan(z.re), dep.nan(z.im))
|
||||||
|
|
||||||
|
export const re =
|
||||||
|
<T>(dep: Dependencies<'re', T>): Signature<'re', Complex<T>> =>
|
||||||
|
z => dep.re(z.re)
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * from './numbers/all.js'
|
export * from './numbers/all.js'
|
||||||
export * from './Complex/all.js'
|
export * from './Complex/all.js'
|
||||||
|
export * from './generic/all.js'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export type Config = {
|
export type Config = {
|
||||||
|
epsilon: number
|
||||||
predictable: boolean
|
predictable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,44 +10,18 @@
|
|||||||
type TypeName = string
|
type TypeName = string
|
||||||
type Parameter = TypeName
|
type Parameter = TypeName
|
||||||
type Signature = Parameter[]
|
type Signature = Parameter[]
|
||||||
|
type DependenciesType = Record<string, Function>
|
||||||
|
|
||||||
export interface ImplementationTypes {}
|
// A "canned" dependency for a builtin function:
|
||||||
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
||||||
|
|
||||||
// Helper for collecting implementations
|
// Utility needed in type definitions
|
||||||
// (Really just suffixes the type name onto the keys of exports)
|
|
||||||
export type ForType<T extends string, Exports> = keyof Exports extends string
|
|
||||||
? {[K in keyof Exports as `${K}_${T}`]: Exports[K]}
|
|
||||||
: never
|
|
||||||
|
|
||||||
//dummy implementation for now
|
//dummy implementation for now
|
||||||
export function joinTypes(a: TypeName, b: TypeName) {
|
export function joinTypes(a: TypeName, b: TypeName) {
|
||||||
if (a === b) return a
|
if (a === b) return a
|
||||||
return 'any'
|
return 'any'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build up to Dependency type lookup
|
|
||||||
*/
|
|
||||||
type DependenciesType = Record<string, Function>
|
|
||||||
|
|
||||||
type FinalShape<FuncType> =
|
|
||||||
FuncType extends (arg: DependenciesType) => Function
|
|
||||||
? ReturnType<FuncType> : FuncType
|
|
||||||
|
|
||||||
type BeginsWith<Name extends string> = `${Name}${string}`
|
|
||||||
|
|
||||||
type DependencyTypes<Ob, Name extends string, Params extends unknown[]> =
|
|
||||||
{[K in keyof Ob]: K extends BeginsWith<Name>
|
|
||||||
? FinalShape<Ob[K]> extends (...args: Params) => any
|
|
||||||
? FinalShape<Ob[K]>
|
|
||||||
: never
|
|
||||||
: never}
|
|
||||||
|
|
||||||
export type Dependency<Name extends string, Params extends unknown[]> =
|
|
||||||
{[N in Name]:
|
|
||||||
DependencyTypes<ImplementationTypes, N, Params>[keyof ImplementationTypes]}
|
|
||||||
|
|
||||||
// Now types used in the Dispatcher class itself
|
// Now types used in the Dispatcher class itself
|
||||||
|
|
||||||
type TypeSpecification = {
|
type TypeSpecification = {
|
||||||
|
1
src/generic/all.ts
Normal file
1
src/generic/all.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * as generic from './native.js'
|
5
src/generic/arithmetic.ts
Normal file
5
src/generic/arithmetic.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const square =
|
||||||
|
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
||||||
|
z => dep.multiply(z, z)
|
2
src/generic/native.ts
Normal file
2
src/generic/native.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './arithmetic.js'
|
||||||
|
export * from './relational.js'
|
5
src/generic/relational.ts
Normal file
5
src/generic/relational.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const unequal =
|
||||||
|
<T>(dep: Dependencies<'equal', T>): Signature<'unequal', T> =>
|
||||||
|
(x, y) => !dep.equal(x, y)
|
16
src/index.ts
16
src/index.ts
@ -2,3 +2,19 @@ import {Dispatcher} from './core/Dispatcher.js'
|
|||||||
import * as Specifications from './all.js'
|
import * as Specifications from './all.js'
|
||||||
|
|
||||||
export default new Dispatcher(Specifications)
|
export default new Dispatcher(Specifications)
|
||||||
|
|
||||||
|
import {Complex} from './Complex/type.js'
|
||||||
|
import {absquare as absquare_complex} from './Complex/arithmetic.js'
|
||||||
|
|
||||||
|
const mockRealAdd = (a: number, b: number) => a+b
|
||||||
|
const mockComplexAbsquare = (z: Complex<number>) => z.re*z.re + z.im*z.im
|
||||||
|
|
||||||
|
const quatAbsquare = absquare_complex({
|
||||||
|
add: mockRealAdd,
|
||||||
|
absquare: mockComplexAbsquare
|
||||||
|
})
|
||||||
|
|
||||||
|
const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}})
|
||||||
|
const typeTest: typeof myabs = 7 // check myabs is just a number
|
||||||
|
|
||||||
|
console.log('Result is', myabs)
|
||||||
|
18
src/interfaces/arithmetic.ts
Normal file
18
src/interfaces/arithmetic.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type {Complex} from '../Complex/type.js'
|
||||||
|
import type {RealType} from './type.js'
|
||||||
|
|
||||||
|
declare module "./type" {
|
||||||
|
interface Signatures<T> {
|
||||||
|
add: (a: T, b: T) => T
|
||||||
|
unaryMinus: (a: T) => T
|
||||||
|
conj: (a: T) => T
|
||||||
|
subtract: (a: T, b: T) => T
|
||||||
|
multiply: (a: T, b: T) => T
|
||||||
|
square: (a: T) => T
|
||||||
|
absquare: (a: T) => RealType<T>
|
||||||
|
reciprocal: (a: T) => T
|
||||||
|
divide: (a: T, b: T) => T
|
||||||
|
conservativeSqrt: (a: T) => T
|
||||||
|
sqrt: (a: T)=> T extends Complex<unknown> ? T : T | Complex<T>
|
||||||
|
}
|
||||||
|
}
|
10
src/interfaces/predicate.ts
Normal file
10
src/interfaces/predicate.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Warning: a module must have something besides just a "declare module"
|
||||||
|
// section; otherwise it is ignored.
|
||||||
|
export type UnaryPredicate<T> = (a: T) => boolean
|
||||||
|
|
||||||
|
declare module "./type" {
|
||||||
|
interface Signatures<T> {
|
||||||
|
isReal: (a: T) => boolean
|
||||||
|
isSquare: (a: T) => boolean
|
||||||
|
}
|
||||||
|
}
|
9
src/interfaces/relational.ts
Normal file
9
src/interfaces/relational.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Warning: a module must have something besides just a "declare module"
|
||||||
|
// section; otherwise it is ignored.
|
||||||
|
export type BinaryPredicate<T> = (a: T, b: T) => T
|
||||||
|
declare module "./type" {
|
||||||
|
interface Signatures<T> {
|
||||||
|
equal: (a: T, b: T) => boolean
|
||||||
|
unequal: (a: T, b: T) => boolean
|
||||||
|
}
|
||||||
|
}
|
75
src/interfaces/type.ts
Normal file
75
src/interfaces/type.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*****
|
||||||
|
* Every typocomath type has some associated types; they need
|
||||||
|
* to be published 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 generic 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) or any concrete type,
|
||||||
|
* but that's OK, the generic parameter doesn't hurt in those cases.
|
||||||
|
****/
|
||||||
|
|
||||||
|
export interface AssociatedTypes<T> {
|
||||||
|
undefined: {
|
||||||
|
type: undefined
|
||||||
|
zero: undefined
|
||||||
|
one: undefined
|
||||||
|
nan: undefined
|
||||||
|
real: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssociatedTypeNames = keyof AssociatedTypes<unknown>['undefined']
|
||||||
|
type ALookup<T, Name extends AssociatedTypeNames> = {
|
||||||
|
[K in keyof AssociatedTypes<T>]:
|
||||||
|
T extends AssociatedTypes<T>[K]['type'] ? AssociatedTypes<T>[K][Name] : never
|
||||||
|
}[keyof AssociatedTypes<T>]
|
||||||
|
|
||||||
|
// For everything to compile, zero and one must be subtypes of T:
|
||||||
|
export type ZeroType<T> = ALookup<T, 'zero'> & T
|
||||||
|
export type OneType<T> = ALookup<T, 'one'> & T
|
||||||
|
// But I believe 'nan' really might not be, like I think we will have to use
|
||||||
|
// 'undefined' for the nan of 'bigint', as it has nothing at all like NaN,
|
||||||
|
// so don't force it:
|
||||||
|
export type NaNType<T> = ALookup<T, 'nan'>
|
||||||
|
export type RealType<T> = ALookup<T, 'real'>
|
||||||
|
|
||||||
|
/*****
|
||||||
|
* The global signature patterns for all operations need to be published in the
|
||||||
|
* following interface. Each key is the name of an operation (but note that
|
||||||
|
* the Dispatcher will automatically merge operations that have the same
|
||||||
|
* 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 Signatures<T> {
|
||||||
|
zero: (a: T) => ZeroType<T>
|
||||||
|
one: (a: T) => OneType<T>
|
||||||
|
// nan needs to be able to operate on its own output for everything
|
||||||
|
// else to compile. That's why its parameter type is widened:
|
||||||
|
nan: (a: T | NaNType<T>) => NaNType<T>
|
||||||
|
re: (a: T) => RealType<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignatureKey<T> = keyof Signatures<T>
|
||||||
|
|
||||||
|
export type Signature<Name extends SignatureKey<T>, T> = Signatures<T>[Name]
|
||||||
|
export type Returns<Name extends SignatureKey<T>, T> = ReturnType<Signatures<T>[Name]>
|
||||||
|
export type Dependencies<Name extends SignatureKey<T>, T> = {[K in Name]: Signature<K, T>}
|
||||||
|
|
||||||
|
export type AliasOf<Name extends string, T> = T & {aliasOf?: Name}
|
@ -1,8 +1,3 @@
|
|||||||
import {ForType} from '../core/Dispatcher.js'
|
|
||||||
import * as numbers from './native.js'
|
import * as numbers from './native.js'
|
||||||
|
|
||||||
export {numbers}
|
export {numbers}
|
||||||
|
|
||||||
declare module "../core/Dispatcher" {
|
|
||||||
interface ImplementationTypes extends ForType<'numbers', typeof numbers> {}
|
|
||||||
}
|
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
import {configDependency} from '../core/Config.js'
|
import type {configDependency} from '../core/Config.js'
|
||||||
import {Dependency} from '../core/Dispatcher.js'
|
import type {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const add: Signature<'add', number> = (a, b) => a + b
|
||||||
|
export const unaryMinus: Signature<'unaryMinus', number> = a => -a
|
||||||
|
export const conj: Signature<'conj', number> = a => a
|
||||||
|
export const subtract: Signature<'subtract', number> = (a, b) => a - b
|
||||||
|
export const multiply: Signature<'multiply', number> = (a, b) => a * b
|
||||||
|
export const absquare: Signature<'absquare', number> = a => a * a
|
||||||
|
export const reciprocal: Signature<'reciprocal', number> = a => 1 / a
|
||||||
|
export const divide: Signature<'divide', number> = (a, b) => a / b
|
||||||
|
|
||||||
|
const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
|
||||||
|
export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt
|
||||||
|
|
||||||
export const add = (a: number, b: number) => a + b
|
|
||||||
export const unaryMinus = (a: number) => -a
|
|
||||||
export const subtract = (a: number, b: number) => a - b
|
|
||||||
export const multiply = (a: number, b: number) => a * b
|
|
||||||
export const divide = (a: number, b: number) => a / b
|
|
||||||
export const sqrt =
|
export const sqrt =
|
||||||
(dep: configDependency
|
(dep: configDependency & Dependencies<'complex', number>):
|
||||||
& Dependency<'complex', [number, number]>) => {
|
Signature<'sqrt', number> => {
|
||||||
if (dep.config.predictable || !dep.complex) {
|
if (dep.config.predictable || !dep.complex) return basicSqrt
|
||||||
return (a: number) => isNaN(a) ? NaN : Math.sqrt(a)
|
return a => {
|
||||||
}
|
if (isNaN(a)) return NaN
|
||||||
return (a: number) => {
|
if (a >= 0) return Math.sqrt(a)
|
||||||
if (isNaN(a)) return NaN
|
return dep.complex(0, Math.sqrt(unaryMinus(a)))
|
||||||
if (a >= 0) return Math.sqrt(a)
|
}
|
||||||
return dep.complex(0, Math.sqrt(unaryMinus(a)))
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
4
src/numbers/predicate.ts
Normal file
4
src/numbers/predicate.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import type {Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const isReal: Signature<'isReal', number> = (a) => true
|
||||||
|
export const isSquare: Signature<'isSquare', number> = (a) => a >= 0
|
21
src/numbers/relational.ts
Normal file
21
src/numbers/relational.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {configDependency} from '../core/Config.js'
|
||||||
|
import {Signature} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
||||||
|
|
||||||
|
export const equal =
|
||||||
|
(dep: configDependency): Signature<'equal', number> =>
|
||||||
|
(x, y) => {
|
||||||
|
const eps = dep.config.epsilon
|
||||||
|
if (eps === null || eps === undefined) return x === y
|
||||||
|
if (x === y) return true
|
||||||
|
if (isNaN(x) || isNaN(y)) return false
|
||||||
|
|
||||||
|
if (isFinite(x) && isFinite(y)) {
|
||||||
|
const diff = Math.abs(x - y)
|
||||||
|
if (diff < DBL_EPSILON) return true
|
||||||
|
return diff <= Math.max(Math.abs(x), Math.abs(y)) * eps
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -1,7 +1,25 @@
|
|||||||
|
import type { Signature } from '../interfaces/type.js'
|
||||||
|
|
||||||
export const number_type = {
|
export const number_type = {
|
||||||
before: ['Complex'],
|
before: ['Complex'],
|
||||||
test: (n: unknown): n is number => typeof n === 'number',
|
test: (n: unknown): n is number => typeof n === 'number',
|
||||||
from: {string: s => +s}
|
from: { string: (s: string) => +s }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const zero = (a: number) => 0
|
declare module "../interfaces/type" {
|
||||||
|
interface AssociatedTypes<T> {
|
||||||
|
numbers: {
|
||||||
|
type: number
|
||||||
|
zero: 0
|
||||||
|
one: 1
|
||||||
|
nan: typeof NaN
|
||||||
|
real: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't like the redundancy of repeating 'zero'; any way to eliminate that?
|
||||||
|
export const zero: Signature<'zero', number> = (a) => 0
|
||||||
|
export const one: Signature<'one', number> = (a) => 1
|
||||||
|
export const nan: Signature<'nan', number> = (a) => NaN
|
||||||
|
export const re: Signature<'re', number> = (a) => a
|
||||||
|
Loading…
Reference in New Issue
Block a user