Compare commits
11 Commits
main
...
approach4.
Author | SHA1 | Date | |
---|---|---|---|
6d63d23498 | |||
072b2a1f79 | |||
74e2aef524 | |||
a5848125e4 | |||
60ce6212b4 | |||
04024a2a8d | |||
cbd1719227 | |||
8c06c8f36e | |||
fbec410c42 | |||
d55776655f | |||
1eb73be2fa |
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
|
||||||
|
|
||||||
|
# Webstiorm
|
||||||
|
.idea
|
||||||
|
|
||||||
# yarn v2
|
# yarn v2
|
||||||
.yarn/cache
|
.yarn/cache
|
||||||
.yarn/unplugged
|
.yarn/unplugged
|
||||||
|
@ -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> {}
|
|
||||||
}
|
|
||||||
|
102
src/Complex/arithmetic.ts
Normal file
102
src/Complex/arithmetic.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import {Complex} from './type.js'
|
||||||
|
import type {
|
||||||
|
Dependencies, OpType, OpReturns, RealType, ZeroType
|
||||||
|
} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
declare module "../interfaces/type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
// TODO: Make Dispatcher collapse operations that match
|
||||||
|
// after removing any `_...` suffixes; the following should be
|
||||||
|
// additional dispatches for add and divide, not separate
|
||||||
|
// operations, in the final mathjs bundle.
|
||||||
|
add_real: {params: [T, RealType<T>], returns: T}
|
||||||
|
divide_real: {params: [T, RealType<T>], returns: T}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const add =
|
||||||
|
<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))
|
||||||
|
|
||||||
|
export const add_real =
|
||||||
|
<T>(dep: Dependencies<'add_real' | 'complex', T>):
|
||||||
|
OpType<'add_real', Complex<T>> =>
|
||||||
|
(z, r) => dep.complex(dep.add_real(z.re, r), z.im)
|
||||||
|
|
||||||
|
export const unaryMinus =
|
||||||
|
<T>(dep: Dependencies<'unaryMinus' | 'complex', T>):
|
||||||
|
OpType<'unaryMinus', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
|
export const conj =
|
||||||
|
<T>(dep: Dependencies<'unaryMinus' | 'conj' | 'complex', T>):
|
||||||
|
OpType<'conj', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im))
|
||||||
|
|
||||||
|
export const subtract =
|
||||||
|
<T>(dep: Dependencies<'subtract' | 'complex', T>):
|
||||||
|
OpType<'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>):
|
||||||
|
OpType<'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', OpReturns<'absquare', T>>):
|
||||||
|
OpType<'absquare', Complex<T>> =>
|
||||||
|
z => dep.add(dep.absquare(z.re), dep.absquare(z.im))
|
||||||
|
|
||||||
|
export const divideByReal =
|
||||||
|
<T>(dep: Dependencies<'divide_real' | 'complex', T>):
|
||||||
|
OpType<'divide_real', Complex<T>> =>
|
||||||
|
(z, r) => dep.complex(dep.divide_real(z.re, r), dep.divide_real(z.im, r))
|
||||||
|
|
||||||
|
export const reciprocal =
|
||||||
|
<T>(dep: Dependencies<'conj' | 'absquare' | 'divide_real', Complex<T>>):
|
||||||
|
OpType<'reciprocal', Complex<T>> =>
|
||||||
|
z => dep.divide_real(dep.conj(z), dep.absquare(z))
|
||||||
|
|
||||||
|
export const divide =
|
||||||
|
<T>(dep: Dependencies<'multiply' | 'reciprocal', Complex<T>>):
|
||||||
|
OpType<'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 `add_real` on both T and Complex<T>, hence the dependency
|
||||||
|
// with a custom name, not generated via Dependencies<...>
|
||||||
|
export const sqrt =
|
||||||
|
<T>(dep: Dependencies<'add' | 'equal' | 'conservativeSqrt' | 'unaryMinus',
|
||||||
|
RealType<T>>
|
||||||
|
& Dependencies<'zero' | 'add_real' | 'complex', T>
|
||||||
|
& Dependencies<'absquare' | 're' | 'divide_real', Complex<T>>
|
||||||
|
& {add_complex_real: OpType<'add_real', Complex<T>>}):
|
||||||
|
OpType<'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.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 denom = dep.conservativeSqrt(denomsq)
|
||||||
|
return dep.divide_real(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, OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const isReal =
|
||||||
|
<T>(dep: Dependencies<'add' | 'equal' | 'isReal', T>):
|
||||||
|
OpType<'isReal', Complex<T>> =>
|
||||||
|
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
||||||
|
|
||||||
|
export const isSquare: OpType<'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, OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const equal =
|
||||||
|
<T>(dep: Dependencies<'equal', T>): OpType<'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, OpType, OpReturns
|
||||||
|
} 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 Operations<T> {
|
||||||
|
complex: {params: [T] | [T,T], returns: Complex<T>}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const complex =
|
||||||
|
<T>(dep: Dependencies<'zero', T>): OpType<'complex', T> =>
|
||||||
|
(a, b) => ({re: a, im: b || dep.zero(a)})
|
||||||
|
|
||||||
|
export const zero =
|
||||||
|
<T>(dep: Dependencies<'zero', T>
|
||||||
|
& Dependencies<'complex', OpReturns<'zero', T>>):
|
||||||
|
OpType<'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', OpReturns<'one' | 'zero', T>>):
|
||||||
|
OpType<'one', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.one(z.re), dep.zero(z.im))
|
||||||
|
|
||||||
|
export const nan =
|
||||||
|
<T>(dep: Dependencies<'nan', T>
|
||||||
|
& Dependencies<'complex', OpReturns<'nan', T>>):
|
||||||
|
OpType<'nan', Complex<T>> =>
|
||||||
|
z => dep.complex(dep.nan(z.re), dep.nan(z.im))
|
||||||
|
|
||||||
|
export const re =
|
||||||
|
<T>(dep: Dependencies<'re', T>): OpType<'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, OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const square =
|
||||||
|
<T>(dep: Dependencies<'multiply', T>): OpType<'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, OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const unequal =
|
||||||
|
<T>(dep: Dependencies<'equal', T>): OpType<'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)
|
||||||
|
23
src/interfaces/arithmetic.ts
Normal file
23
src/interfaces/arithmetic.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type {Complex} from '../Complex/type.js'
|
||||||
|
import type {RealType} from './type.js'
|
||||||
|
|
||||||
|
type UnaryOperator<T> = {params: [T], returns: T}
|
||||||
|
type BinaryOperator<T> = {params: [T, T], returns: T}
|
||||||
|
declare module "./type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
add: BinaryOperator<T>
|
||||||
|
unaryMinus: UnaryOperator<T>
|
||||||
|
conj: UnaryOperator<T>
|
||||||
|
subtract: BinaryOperator<T>
|
||||||
|
multiply: BinaryOperator<T>
|
||||||
|
square: UnaryOperator<T>
|
||||||
|
absquare: {params: [T], returns: RealType<T>}
|
||||||
|
reciprocal: UnaryOperator<T>
|
||||||
|
divide: BinaryOperator<T>
|
||||||
|
conservativeSqrt: UnaryOperator<T>
|
||||||
|
sqrt: {
|
||||||
|
params: [T],
|
||||||
|
returns: T extends Complex<any> ? T : T | Complex<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/interfaces/predicate.ts
Normal file
9
src/interfaces/predicate.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 UnaryPredicate<T> = {params: [T], returns: boolean}
|
||||||
|
declare module "./type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
isReal: UnaryPredicate<T>
|
||||||
|
isSquare: UnaryPredicate<T>
|
||||||
|
}
|
||||||
|
}
|
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> = {params: [T, T], returns: boolean}
|
||||||
|
declare module "./type" {
|
||||||
|
interface Operations<T> {
|
||||||
|
equal: BinaryPredicate<T>
|
||||||
|
unequal: BinaryPredicate<T>
|
||||||
|
}
|
||||||
|
}
|
74
src/interfaces/type.ts
Normal file
74
src/interfaces/type.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*****
|
||||||
|
* 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 Operations<T> {
|
||||||
|
zero: {params: [T], returns: ZeroType<T>}
|
||||||
|
one: {params: [T], returns: 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: {params: [T | NaNType<T>], returns: NaNType<T>}
|
||||||
|
re: {params: [T], returns: RealType<T>}
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpKey = keyof Operations<unknown>
|
||||||
|
|
||||||
|
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,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,18 +1,23 @@
|
|||||||
import {configDependency} from '../core/Config.js'
|
import type {configDependency} from '../core/Config.js'
|
||||||
import {Dependency} from '../core/Dispatcher.js'
|
import type {Dependencies, OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const add: OpType<'add', number> = (a, b) => a + b
|
||||||
|
export const unaryMinus: OpType<'unaryMinus', number> = a => -a
|
||||||
|
export const conj: OpType<'conj', number> = a => a
|
||||||
|
export const subtract: OpType<'subtract', number> = (a, b) => a - b
|
||||||
|
export const multiply: OpType<'multiply', number> = (a, b) => a * b
|
||||||
|
export const absquare: OpType<'absquare', number> = a => a * a
|
||||||
|
export const reciprocal: OpType<'reciprocal', number> = a => 1 / a
|
||||||
|
export const divide: OpType<'divide', number> = (a, b) => a / b
|
||||||
|
|
||||||
|
const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
|
||||||
|
export const conservativeSqrt: OpType<'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]>) => {
|
OpType<'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 => {
|
||||||
}
|
|
||||||
return (a: number) => {
|
|
||||||
if (isNaN(a)) return NaN
|
if (isNaN(a)) return NaN
|
||||||
if (a >= 0) return Math.sqrt(a)
|
if (a >= 0) return Math.sqrt(a)
|
||||||
return dep.complex(0, Math.sqrt(unaryMinus(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 {OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
export const isReal: OpType<'isReal', number> = (a) => true
|
||||||
|
export const isSquare: OpType<'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 {OpType} from '../interfaces/type.js'
|
||||||
|
|
||||||
|
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
||||||
|
|
||||||
|
export const equal =
|
||||||
|
(dep: configDependency): OpType<'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 { OpType } 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: OpType<'zero', number> = (a) => 0
|
||||||
|
export const one: OpType<'one', number> = (a) => 1
|
||||||
|
export const nan: OpType<'nan', number> = (a) => NaN
|
||||||
|
export const re: OpType<'re', number> = (a) => a
|
||||||
|
Loading…
Reference in New Issue
Block a user