Compare commits
8 Commits
main
...
approach4_
Author | SHA1 | Date | |
---|---|---|---|
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
|
||||
.vscode-test
|
||||
|
||||
# Webstiorm
|
||||
.idea
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
|
@ -1,8 +1,6 @@
|
||||
import {ForType} from '../core/Dispatcher.js'
|
||||
import * as Complex from './native.js'
|
||||
import * as complex from './arithmetic.js'
|
||||
|
||||
export { complex }
|
||||
|
||||
export {Complex}
|
||||
|
||||
declare module "../core/Dispatcher" {
|
||||
interface ImplementationTypes extends ForType<'Complex', typeof Complex> {}
|
||||
}
|
||||
|
129
src/Complex/arithmetic.ts
Normal file
129
src/Complex/arithmetic.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import {Complex, complex_binary, FnComplexUnary} 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'
|
||||
|
||||
export const add =
|
||||
<T>(dep: {
|
||||
add: FnAdd<T>
|
||||
}): FnAdd<Complex<T>> =>
|
||||
(w, z) => complex_binary(dep.add(w.re, z.re), dep.add(w.im, z.im))
|
||||
|
||||
export const addReal =
|
||||
<T>(dep: {
|
||||
addReal: FnAddReal<T, T>
|
||||
}): FnAddReal<Complex<T>, T> =>
|
||||
(z, r) => complex_binary(dep.addReal(z.re, r), z.im)
|
||||
|
||||
export const unaryMinus =
|
||||
<T>(dep: {
|
||||
unaryMinus: FnUnaryMinus<T>
|
||||
}): FnUnaryMinus<Complex<T>> =>
|
||||
(z) => complex_binary(dep.unaryMinus(z.re), dep.unaryMinus(z.im))
|
||||
|
||||
export const conj =
|
||||
<T>(dep: {
|
||||
unaryMinus: FnUnaryMinus<T>,
|
||||
conj: FnConj<T>
|
||||
}) : FnConj<Complex<T>> =>
|
||||
(z) => complex_binary(dep.conj(z.re), dep.unaryMinus(z.im))
|
||||
|
||||
export const subtract =
|
||||
<T>(dep: {
|
||||
subtract: FnSubtract<T>
|
||||
}): FnSubtract<Complex<T>> =>
|
||||
(w, z) => complex_binary(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im))
|
||||
|
||||
export const multiply =
|
||||
<T>(dep: {
|
||||
add: FnAdd<T>,
|
||||
subtract: FnSubtract<T>,
|
||||
multiply: FnMultiply<T>,
|
||||
conj: FnConj<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 complex_binary(realpart, imagpart)
|
||||
}
|
||||
|
||||
export const absquare =
|
||||
<T, U>(dep: {
|
||||
add: FnAdd<U>,
|
||||
absquare: FnAbsSquare<T, U>
|
||||
}): FnAbsSquare<Complex<T>, U> =>
|
||||
(z) => dep.add(dep.absquare(z.re), dep.absquare(z.im))
|
||||
|
||||
export const divideByReal =
|
||||
<T>(dep: {
|
||||
divideByReal: FnDivideByReal<T, T>
|
||||
}): FnDivideByReal<Complex<T>, T> =>
|
||||
(z, r) => complex_binary(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r))
|
||||
|
||||
export const reciprocal =
|
||||
<T>(dep: {
|
||||
conj: FnConj<Complex<T>>,
|
||||
absquare: FnAbsSquare<Complex<T>, T>,
|
||||
divideByReal: FnDivideByReal<Complex<T>, T>
|
||||
}): FnReciprocal<Complex<T>> =>
|
||||
(z) => dep.divideByReal(dep.conj(z), dep.absquare(z))
|
||||
|
||||
export const divide =
|
||||
<T>(dep: {
|
||||
multiply: FnMultiply<Complex<T>>,
|
||||
reciprocal: FnReciprocal<Complex<T>>,
|
||||
}): FnDivide<Complex<T>> =>
|
||||
(w, z) => dep.multiply(w, dep.reciprocal(z))
|
||||
|
||||
export const complexSqrt =
|
||||
<T>(dep: {
|
||||
conservativeSqrt: FnConservativeSqrt<T>,
|
||||
isSquare: FnIsSquare<T>,
|
||||
complex: FnComplexUnary<T>,
|
||||
unaryMinus: FnUnaryMinus<T>,
|
||||
zero: FnZero<T>,
|
||||
nan: FnNaN<Complex<T>>
|
||||
}) =>
|
||||
(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))
|
||||
}
|
||||
|
||||
export const sqrt =
|
||||
<T>(dep: {
|
||||
isReal: FnIsReal<Complex<T>>,
|
||||
complexSqrt: FnSqrt<T>,
|
||||
conservativeSqrt: FnConservativeSqrt<T>,
|
||||
absquare: FnAbsSquare<Complex<T>, T>,
|
||||
addReal: FnAddReal<Complex<T>, T>,
|
||||
divideByReal: FnDivideByReal<Complex<T>, T>,
|
||||
add: FnAdd<T>,
|
||||
re: FnRe<Complex<T>, T>,
|
||||
}) =>
|
||||
(z: Complex<T>): Complex<T> | 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)
|
||||
}
|
||||
|
||||
export const conservativeSqrt = sqrt
|
14
src/Complex/predicate.ts
Normal file
14
src/Complex/predicate.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Complex } from './type.js'
|
||||
import {FnEqual} from '../interfaces/relational'
|
||||
import {FnAdd, FnIsReal, FnIsSquare} from '../interfaces/arithmetic'
|
||||
|
||||
export const isReal =
|
||||
<T>(dep: {
|
||||
equal: FnEqual<T>,
|
||||
add: FnAdd<T>,
|
||||
isReal: FnIsReal<T>
|
||||
}): FnIsReal<Complex<T>> =>
|
||||
(z) => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
||||
|
||||
export const isSquare =
|
||||
<T>(): FnIsSquare<Complex<T>> => (z) => true // FIXME: not correct for Complex<bigint> once we get there
|
8
src/Complex/relational.ts
Normal file
8
src/Complex/relational.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Complex } from './type.js'
|
||||
import {FnEqual} from '../interfaces/relational'
|
||||
|
||||
export const equal =
|
||||
<T>(dep: {
|
||||
equal: FnEqual<T>
|
||||
}): FnEqual<Complex<T>> =>
|
||||
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
@ -1,22 +1,58 @@
|
||||
import {joinTypes, typeOfDependency, Dependency} from '../core/Dispatcher.js'
|
||||
import {
|
||||
joinTypes, typeOfDependency, Dependency,
|
||||
} from '../core/Dispatcher.js'
|
||||
import type {FnNaN, FnOne, FnRe, FnZero} from '../interfaces/arithmetic.js'
|
||||
|
||||
export type Complex<T> = {re: T; im: T;}
|
||||
export type Complex<T> = { re: T; im: T; }
|
||||
|
||||
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> =>
|
||||
typeof z === 'object' && 're' in z && 'im' in z
|
||||
&& dep.testT(z.re) && dep.testT(z.im),
|
||||
typeof z === 'object' && z != null && 're' in z && 'im' in z
|
||||
&& dep.testT(z['re']) && dep.testT(z['im']),
|
||||
infer: (dep: typeOfDependency) =>
|
||||
(z: Complex<unknown>) => joinTypes(dep.typeOf(z.re), dep.typeOf(z.im)),
|
||||
from: {
|
||||
T: <T>(dep: Dependency<'zero', [T]>) => (t: T) =>
|
||||
({re: t, im: dep.zero(t)}),
|
||||
Complex: <U,T>(dep: {convert: (from: U) => T}) =>
|
||||
(z: Complex<U>) => ({re: dep.convert(z.re), im: dep.convert(z.im)})
|
||||
({ re: t, im: dep.zero(t) }),
|
||||
Complex: <U, T>(dep: { convert: (from: U) => T }) =>
|
||||
(z: Complex<U>) => ({ re: dep.convert(z.re), im: dep.convert(z.im) })
|
||||
}
|
||||
}
|
||||
|
||||
export const complex_unary = <T>(dep: Dependency<'zero', [T]>) =>
|
||||
(t: T) => ({re: t, im: dep.zero(t)})
|
||||
export const complex_binary = <T>(t: T, u: T) => ({re: t, im: u})
|
||||
export type FnComplexUnary<T> = (t: T) => Complex<T>
|
||||
|
||||
export const complex_unary =
|
||||
<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_binary = <T>(t: T, u: T): Complex<T> => ({ re: t, im: u })
|
||||
|
||||
export const zero =
|
||||
<T>(dep: {
|
||||
zero: FnZero<T>
|
||||
}): FnZero<Complex<T>> =>
|
||||
(z) => complex_binary(dep.zero(z.re), dep.zero(z.im))
|
||||
|
||||
export const one =
|
||||
<T>(dep: {
|
||||
zero: FnZero<T>,
|
||||
one: FnOne<T>
|
||||
}): FnOne<Complex<T>> =>
|
||||
(z) => complex_binary(dep.one(z.re), dep.zero(z.im))
|
||||
|
||||
export const nan =
|
||||
<T>(dep: {
|
||||
nan: FnNaN<T>
|
||||
}): FnNaN<Complex<T>> =>
|
||||
(z) => complex_binary(dep.nan(z.re), dep.nan(z.im))
|
||||
|
||||
export const re =
|
||||
<T>(dep: {
|
||||
re: FnRe<T,T>
|
||||
}): FnRe<Complex<T>, T> =>
|
||||
(z) => dep.re(z.re)
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from './numbers/all.js'
|
||||
export * from './Complex/all.js'
|
||||
export * from './generic/all.js'
|
||||
|
@ -1,4 +1,5 @@
|
||||
export type Config = {
|
||||
epsilon: number
|
||||
predictable: boolean
|
||||
}
|
||||
|
||||
|
@ -9,16 +9,62 @@
|
||||
|
||||
type TypeName = string
|
||||
type Parameter = TypeName
|
||||
type Signature = Parameter[]
|
||||
type InputSignature = Parameter[]
|
||||
type DependenciesType = Record<string, Function>
|
||||
|
||||
export interface ImplementationTypes {}
|
||||
export type typeOfDependency = {typeOf: (x: unknown) => TypeName}
|
||||
|
||||
// Helper for collecting implementations
|
||||
// (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
|
||||
// All of the implementations must publish descriptions of their
|
||||
// return types into the following interface, using the format
|
||||
// described just below:
|
||||
export interface ReturnTypes<Params> {}
|
||||
|
||||
/*****
|
||||
To describe one implementation for a hypothetical operation `foo`, there
|
||||
should be a property of the interface whose name starts with `foo` and whose
|
||||
next character, if any, is an underscore. The type of this property
|
||||
must be the return type of that implementation when Params matches the
|
||||
parameter types of the implementation, and `never` otherwise.
|
||||
Thus to describe an implementation that takes a number and a string and
|
||||
returns a boolean, for example, you could write
|
||||
```
|
||||
declare module "Dispatcher" {
|
||||
interface ReturnTypes<Params> {
|
||||
foo_example: Params extends [number, string] ? boolean : never
|
||||
}
|
||||
}
|
||||
```
|
||||
If there is another, generic implementation that takes one argument
|
||||
of any type and returns a Vector of that type, you can say
|
||||
```
|
||||
...
|
||||
foo_generic: Params extends [infer T] ? Vector<T> : never
|
||||
...
|
||||
```
|
||||
In practice, each subdirectory corresponding to a type, like Complex,
|
||||
defines an interface, like `ComplexReturn<Params>` for the implementations
|
||||
in that subdirectory, which can mostly be defined without suffixes because
|
||||
there's typically just a single implementation within that domain.
|
||||
Then the module responsible for collating all of the implementations for
|
||||
that type inserts all of the properties of that interface into `ReturnTypes`
|
||||
suitably suffixed to avoid collisions.
|
||||
|
||||
One might think that simply defining an implementation for `foo`
|
||||
of type `(n: number, s: string) => boolean` would provide all of the same
|
||||
information as the type of the key `foo_example` in the ReturnTypes
|
||||
interface above, but in practice TypeScript has challenges in extracting
|
||||
types relating to functions. (In particular, there is no
|
||||
way to get the specialized return type of a generic function when it is
|
||||
called on aguments whose specific types match the generic parameters.)
|
||||
Hence the need for this additional mechanism to specify return types, in
|
||||
a way readily suited for TypeScript type computations.
|
||||
*****/
|
||||
|
||||
// Helpers for specifying signatures
|
||||
|
||||
// A basic signature with concrete types
|
||||
export type Signature<CandidateParams, ActualParams, Returns> =
|
||||
CandidateParams extends ActualParams ? Returns : never
|
||||
|
||||
//dummy implementation for now
|
||||
export function joinTypes(a: TypeName, b: TypeName) {
|
||||
@ -26,27 +72,27 @@ export function joinTypes(a: TypeName, b: TypeName) {
|
||||
return 'any'
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up to Dependency type lookup
|
||||
*/
|
||||
type DependenciesType = Record<string, Function>
|
||||
// Used to filter keys that match a given operation name
|
||||
type BeginsWith<Name extends string> = Name | `${Name}_${string}`
|
||||
|
||||
type FinalShape<FuncType> =
|
||||
FuncType extends (arg: DependenciesType) => Function
|
||||
? ReturnType<FuncType> : FuncType
|
||||
// Look up the return type of an implementation based on its name
|
||||
// and the parameters it takes
|
||||
export type ImpReturns<Name extends string, Params> =
|
||||
{[K in keyof ReturnTypes<Params>]: K extends BeginsWith<Name>
|
||||
? ReturnTypes<Params>[K] : never}[keyof ReturnTypes<Params>]
|
||||
|
||||
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}
|
||||
// The type of an implementation (with dependencies satisfied,
|
||||
// based on its name and the parameters it takes
|
||||
export type ImpType<Name extends string, Params extends unknown[]> =
|
||||
(...args: Params) => ImpReturns<Name, Params>
|
||||
|
||||
// The type of a dependency on an implementation based on its name
|
||||
// and the parameters it takes (just a simple object with one property
|
||||
// named the same as the operation, of value type equal to the type of
|
||||
// that implementation. These can be `&`ed together in case of multiple
|
||||
// dependencies:
|
||||
export type Dependency<Name extends string, Params extends unknown[]> =
|
||||
{[N in Name]:
|
||||
DependencyTypes<ImplementationTypes, N, Params>[keyof ImplementationTypes]}
|
||||
{[N in Name]: ImpType<N, Params>}
|
||||
|
||||
// Now types used in the Dispatcher class itself
|
||||
|
||||
@ -64,9 +110,9 @@ type SpecificationsGroup = Record<string, SpecObject>
|
||||
export class Dispatcher {
|
||||
installSpecification(
|
||||
name: string,
|
||||
signature: Signature,
|
||||
signature: InputSignature,
|
||||
returns: TypeName,
|
||||
dependencies: Record<string, Signature>,
|
||||
dependencies: Record<string, InputSignature>,
|
||||
behavior: Function // possible todo: constrain this type based
|
||||
// on the signature, return type, and dependencies. Not sure if
|
||||
// that's really possible, though.
|
||||
|
3
src/generic/all.ts
Normal file
3
src/generic/all.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import * as generic from './arithmetic.js'
|
||||
|
||||
export { generic }
|
7
src/generic/arithmetic.ts
Normal file
7
src/generic/arithmetic.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { FnMultiply, FnSquare } from "../interfaces/arithmetic"
|
||||
|
||||
export const square =
|
||||
<T>(dep: {
|
||||
multiply: FnMultiply<T>
|
||||
}): FnSquare<T> =>
|
||||
(z) => dep.multiply(z, z)
|
16
src/index.ts
16
src/index.ts
@ -2,3 +2,19 @@ import {Dispatcher} from './core/Dispatcher.js'
|
||||
import * as Specifications from './all.js'
|
||||
|
||||
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)
|
||||
|
28
src/interfaces/arithmetic.ts
Normal file
28
src/interfaces/arithmetic.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// shared interfaces
|
||||
|
||||
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
|
||||
// This is not necessary though, it is just a naming convention.
|
||||
export type FnAdd<T> = (a: T, b: T) => T
|
||||
export type FnAddReal<T, U> = (a: T, b: U) => T
|
||||
export type FnUnaryMinus<T> = (a: T) => T
|
||||
export type FnConj<T> = (a: T) => T
|
||||
export type FnSubtract<T> = (a: T, b: T) => T
|
||||
export type FnMultiply<T> = (a: T, b: T) => T
|
||||
export type FnAbsSquare<T, U> = (a: T) => U
|
||||
export type FnReciprocal<T> = (a: T) => T
|
||||
export type FnDivide<T> = (a: T, b: T) => T
|
||||
export type FnDivideByReal<T, U> = (a: T, b: U) => T
|
||||
export type FnConservativeSqrt<T> = (a: T) => T
|
||||
export type FnSqrt<T> = (a: T) => T | Complex<T>
|
||||
export type FnSquare<T> = (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
|
3
src/interfaces/relational.ts
Normal file
3
src/interfaces/relational.ts
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
export type FnEqual<T> = (a: T, b: T) => boolean
|
||||
export type FnUnequal<T> = (a: T, b: T) => boolean
|
@ -1,8 +1,3 @@
|
||||
import {ForType} from '../core/Dispatcher.js'
|
||||
import * as numbers from './native.js'
|
||||
|
||||
export {numbers}
|
||||
|
||||
declare module "../core/Dispatcher" {
|
||||
interface ImplementationTypes extends ForType<'numbers', typeof numbers> {}
|
||||
}
|
||||
|
@ -1,20 +1,29 @@
|
||||
import {configDependency} from '../core/Config.js'
|
||||
import {Dependency} from '../core/Dispatcher.js'
|
||||
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'
|
||||
|
||||
export const add: FnAdd<number> = (a, b) => a + b
|
||||
export const addReal = add
|
||||
export const unaryMinus: FnUnaryMinus<number> = (a) => -a
|
||||
export const conj: FnConj<number> = (a) => a
|
||||
export const subtract: FnSubtract<number> = (a, b) => a - b
|
||||
export const multiply: FnMultiply<number> = (a, b) => a * b
|
||||
export const absquare: FnAbsSquare<number, number> = (a) => a * a
|
||||
export const reciprocal: FnReciprocal<number> = (a) => 1 / a
|
||||
export const divide: FnDivide<number> = (a, b) => a / b
|
||||
export const divideByReal = divide
|
||||
|
||||
export const conservativeSqrt: FnConservativeSqrt<number> = (a) => isNaN(a) ? NaN : Math.sqrt(a)
|
||||
|
||||
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 =
|
||||
(dep: configDependency
|
||||
& Dependency<'complex', [number, number]>) => {
|
||||
if (dep.config.predictable || !dep.complex) {
|
||||
return (a: number) => isNaN(a) ? NaN : Math.sqrt(a)
|
||||
}
|
||||
return (a: number) => {
|
||||
if (isNaN(a)) return NaN
|
||||
if (a >= 0) return Math.sqrt(a)
|
||||
return dep.complex(0, Math.sqrt(unaryMinus(a)))
|
||||
}
|
||||
}
|
||||
(dep: {
|
||||
config: Config,
|
||||
complex: FnComplexBinary<number>
|
||||
}): FnSqrt<number> => {
|
||||
if (dep.config.predictable || !dep.complex) return conservativeSqrt
|
||||
return a => {
|
||||
if (isNaN(a)) return NaN
|
||||
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 { FnIsReal, FnIsSquare } from "../interfaces/arithmetic"
|
||||
|
||||
export const isReal: FnIsReal<number> = (a) => true
|
||||
export const isSquare: FnIsSquare<number> = (a) => a >= 0
|
27
src/numbers/relational.ts
Normal file
27
src/numbers/relational.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Config } from '../core/Config.js'
|
||||
import type { FnEqual, FnUnequal } from '../interfaces/relational.js'
|
||||
|
||||
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
||||
|
||||
export const equal =
|
||||
(dep: {
|
||||
config: Config
|
||||
}): FnEqual<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
|
||||
}
|
||||
|
||||
export const unequal = (dep: {
|
||||
equal: FnEqual<number>
|
||||
}): FnUnequal<number> =>
|
||||
(x, y) => !dep.equal(x, y)
|
@ -1,7 +1,12 @@
|
||||
import type { FnNaN, FnOne, FnZero, FnRe } from "../interfaces/arithmetic"
|
||||
|
||||
export const number_type = {
|
||||
before: ['Complex'],
|
||||
test: (n: unknown): n is number => typeof n === 'number',
|
||||
from: {string: s => +s}
|
||||
from: { string: (s: string) => +s }
|
||||
}
|
||||
|
||||
export const zero = (a: number) => 0
|
||||
export const zero: FnZero<number> = (a) => 0
|
||||
export const one: FnOne<number> = (a) => 1
|
||||
export const nan: FnNaN<number> = (a) => NaN
|
||||
export const re: FnRe<number, number> = (a) => a
|
||||
|
Loading…
Reference in New Issue
Block a user