Compare commits

...

15 Commits

Author SHA1 Message Date
Jos de Jong 8c65387ef0 chore: add a TODO 2023-09-21 11:55:59 +02:00
Jos de Jong 5d256a468f Fix function name Square 2023-09-14 19:44:15 +02:00
Jos de Jong 1c48e57ea1 Move the TODO's up 2023-09-14 19:32:30 +02:00
Jos de Jong a4f5af3ea5 Defined the signature of the function that is being created too 2023-09-14 19:31:21 +02:00
Jos de Jong 7dc26ea297 get template literals working with multiple dependencies 2023-09-14 17:42:42 +02:00
Jos de Jong 722ee05a0a resolve any number of arguments 2023-09-14 14:51:49 +02:00
Jos de Jong 11792e0435 experiment inferring TS types from a JS template 2022-12-24 20:17:36 +01:00
Jos de Jong a5848125e4 fix and test absquare for quaternion 2022-12-23 17:18:24 +01:00
Jos de Jong 60ce6212b4 convert code to type aliases 2022-12-23 13:52:56 +01:00
Jos de Jong 04024a2a8d fix a TS issue 2022-12-23 12:22:41 +01:00
Jos de Jong cbd1719227 experiment: convert all implementations to plain types 2022-12-23 11:27:39 +01:00
Glen Whitney 8c06c8f36e feat: Add generic operation `square` and numeric `unequal` (#4)
Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: #4
2022-12-22 16:12:36 +00:00
Glen Whitney fbec410c42 feat: Implement complex arithmetic through sqrt
Together with any auxiliary functions needed for that goal. Also
  strives to ensure the same functions are being defined for
  number and for `Complex<T>`.
2022-12-22 00:14:58 -05:00
Glen Whitney d55776655f refactor: Convenience type operator for specifying concrete signatures 2022-12-21 11:41:25 -05:00
Glen Whitney 1eb73be2fa refactor: entirely new scheme for specifying return types 2022-12-21 00:18:42 -05:00
24 changed files with 500 additions and 75 deletions

4
.gitignore vendored
View File

@ -3,6 +3,7 @@
# Typescript
# emitted code
obj
build
# ---> Node
# Logs
@ -129,6 +130,9 @@ dist
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Webstiorm
.idea
# yarn v2
.yarn/cache
.yarn/unplugged

View File

@ -1,3 +1,3 @@
# 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.

View File

@ -4,7 +4,7 @@
description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept',
main: 'index.ts',
scripts: {
test: 'echo "Error: no test specified" && exit 1',
go: 'tsc && node build/infer.js'
},
keywords: [
'math',

View File

@ -1,14 +1,17 @@
lockfileVersion: 5.4
lockfileVersion: '6.0'
specifiers:
typescript: ^4.9.3
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
devDependencies:
typescript: 4.9.3
typescript:
specifier: ^4.9.3
version: 4.9.3
packages:
/typescript/4.9.3:
/typescript@4.9.3:
resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==}
engines: {node: '>=4.2.0'}
hasBin: true

View File

@ -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
View 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
View 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

View 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)

View File

@ -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)

View File

@ -1,2 +1,3 @@
export * from './numbers/all.js'
export * from './Complex/all.js'
export * from './generic/all.js'

View File

@ -1,4 +1,5 @@
export type Config = {
epsilon: number
predictable: boolean
}

View File

@ -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
View File

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

View 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)

View File

@ -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)

88
src/infer.ts Normal file
View File

@ -0,0 +1,88 @@
/**
* Idea: instead of writing TypeScript, and inferring the JS-pocomath signature
* from TS that via a TypeScript plugin, we can maybe do this the other way
* around: take the JS-pocomath signature as base (and source of truth), and
* infer TypeScript interfaces from that using infer in template literal types.
*/
// TODO: get generics working
// TODO: how to pass config?
const create = createFactory<{
number: number
bigint: bigint
string: string
any: any
}>()
// These are our string based interfaces, which we can use both in typed-function and in TypeScript:
const Multiply = 'multiply(number,number)=>number'
const Square = 'square(number)=>number'
const Zero = 'zero(number)=>number'
// TODO: turn a generic string like `(T,T)=>T` into a concrete one like `(number,number)=>number`
// const MultiplyNumber = ResolveGeneric<'multiply', number>
const createSquare = create(Square, [Multiply, Zero], dep =>
x => dep.multiply(x, x)
)
// the code works in JS, and works in TS
const multiply = (a: number, b: number) => a * b
const zero = (a: number) => 0
const square = createSquare({ multiply, zero })
console.log('square', square(8)) // 64
function createFactory<BaseTypes extends Record<string, unknown>>() {
type BaseTypeNames = string & keyof BaseTypes
type ResolveType<TypeName extends BaseTypeNames> = BaseTypes[TypeName]
type Value<K> = K
type ResolveArguments<S extends string> = S extends ''
? []
: S extends `${infer Arg extends BaseTypeNames},${infer Tail}`
? [ResolveType<Arg>, ...ResolveArguments<Tail>]
: S extends `${infer Arg extends BaseTypeNames}`
? [ResolveType<Arg>]
: never
type DependencyRecord<FnType> =
FnType extends Value<infer K>
? K extends `${infer Name}(${infer Args})=>${infer ReturnType extends BaseTypeNames}`
? Record<Name, (...args: ResolveArguments<Args>) => ResolveType<ReturnType>>
: never
: never
type CreatedFunctionType<FnType> =
FnType extends Value<infer K>
? K extends `${infer Name}(${infer Args})=>${infer ReturnType extends BaseTypeNames}`
? (...args: ResolveArguments<Args>) => ResolveType<ReturnType>
: never
: never
// inspired by: https://stackoverflow.com/questions/68391632/infer-type-from-array-literal
type DependenciesRecord<
Arr extends Array<Value<any>>,
Result extends Record<string, any> = {}
> = Arr extends []
? Result
: Arr extends [infer H, ...infer Tail]
? Tail extends Array<Value<any>>
? H extends Value<any>
? DependenciesRecord<Tail, Result & DependencyRecord<H>>
: never
: never
: never
return function create<K extends string, Dependencies extends Value<K>[], W extends Value<K>>(
signature: W,
dependencies: [...Dependencies],
callback: (deps: DependenciesRecord<[...Dependencies]>) => CreatedFunctionType<W>
) {
console.log('Creating typed-function with', { signature, dependencies })
// TODO: create a typed-function for real
return callback
}
}

View 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

View File

@ -0,0 +1,3 @@
export type FnEqual<T> = (a: T, b: T) => boolean
export type FnUnequal<T> = (a: T, b: T) => boolean

View File

@ -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> {}
}

View File

@ -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
View 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
View 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)

View File

@ -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

View File

@ -2,6 +2,6 @@
"compilerOptions": {
"target": "ES2022",
"rootDir": "./src",
"outDir": "./obj"
"outDir": "./build"
}
}