feat: Start adding vector arithmetic
So far, abs, add, norm, normsq, and sum are supported. To get them to work, also implements the following: * refactor: Use ReturnType function rather than just accessing .returns * feat: distinguish marking a function as a behavior from its return type * refactor: Rename `NotAType` to `Unknown` because it must be made closer to a bona fide type for the sake of inhomogeneous vectors * feat: make resolving a TypeDispatcher method on a type vector including `Unknown` into a no-op; that simplifies a number of generic behaviors * feat: add `haszero` method parallel to `hasnan` * feat: track the Vector nesting depth of Vector specializations
This commit is contained in:
parent
ec97b0e20a
commit
cb3a93dd1c
26 changed files with 238 additions and 171 deletions
|
@ -1,7 +1,7 @@
|
|||
import assert from 'assert'
|
||||
import math from '#nanomath'
|
||||
import {Complex} from '../Complex.js'
|
||||
import {OneOf, ReturnTyping} from '#core/Type.js'
|
||||
import {OneOf, ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
import {NumberT} from '#number/NumberT.js'
|
||||
|
||||
|
||||
|
@ -38,13 +38,13 @@ describe('complex type operations', () => {
|
|||
})
|
||||
it('computes cis of an angle', () => {
|
||||
const cis = math.cis.resolve(NumberT)
|
||||
assert.strictEqual(cis.returns, OneOf(Complex(NumberT), NumberT))
|
||||
assert.strictEqual(ReturnType(cis), OneOf(Complex(NumberT), NumberT))
|
||||
assert.strictEqual(cis(0), 1)
|
||||
assert.strictEqual(cis(Math.PI), -1)
|
||||
assert(math.equal(cis(Math.PI/3), cplx(0.5, Math.sqrt(3)/2)))
|
||||
math.config.returnTyping = ReturnTyping.full
|
||||
const ccis = math.cis.resolve(NumberT)
|
||||
assert.strictEqual(ccis.returns, Complex(NumberT))
|
||||
assert.strictEqual(ReturnType(ccis), Complex(NumberT))
|
||||
const one = ccis(0)
|
||||
assert(one !== 1)
|
||||
assert(math.equal(one, 1))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Complex} from './Complex.js'
|
||||
import {maybeComplex, promoteBinary, promoteUnary} from './helpers.js'
|
||||
import {ResolutionError} from '#core/helpers.js'
|
||||
import {Returns, ReturnTyping} from '#core/Type.js'
|
||||
import {Returns, ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {ReturnsAs} from '#generic/helpers.js'
|
||||
|
||||
|
@ -9,7 +9,7 @@ const {conservative, full, free} = ReturnTyping
|
|||
|
||||
export const normsq = match(Complex, (math, C, strategy) => {
|
||||
const compNormsq = math.normsq.resolve(C.Component, full)
|
||||
const R = compNormsq.returns
|
||||
const R = ReturnType(compNormsq)
|
||||
const add = math.add.resolve([R,R], strategy)
|
||||
return ReturnsAs(add, z => add(compNormsq(z.re), compNormsq(z.im)))
|
||||
})
|
||||
|
@ -19,19 +19,21 @@ export const add = promoteBinary('add')
|
|||
export const conj = match(Complex, (math, C, strategy) => {
|
||||
const neg = math.negate.resolve(C.Component, full)
|
||||
const compConj = math.conj.resolve(C.Component, full)
|
||||
const cplx = maybeComplex(math, strategy, compConj.returns, neg.returns)
|
||||
const cplx = maybeComplex(
|
||||
math, strategy, ReturnType(compConj), ReturnType(neg))
|
||||
return ReturnsAs(cplx, z => cplx(compConj(z.re), neg(z.im)))
|
||||
})
|
||||
|
||||
export const divide = [
|
||||
match([Complex, T => !T.complex], (math, [C, R], strategy) => {
|
||||
const div = math.divide.resolve([C.Component, R], full)
|
||||
const cplx = maybeComplex(math, strategy, div.returns, div.returns)
|
||||
const NewComp = ReturnType(div)
|
||||
const cplx = maybeComplex(math, strategy, NewComp, NewComp)
|
||||
return ReturnsAs(cplx, (z, r) => cplx(div(z.re, r), div(z.im, r)))
|
||||
}),
|
||||
match([Complex, Complex], (math, [W, Z], strategy) => {
|
||||
const inv = math.invert.resolve(Z, full)
|
||||
const mult = math.multiply.resolve([W, inv.returns], strategy)
|
||||
const mult = math.multiply.resolve([W, ReturnType(inv)], strategy)
|
||||
return ReturnsAs(mult, (w, z) => mult(w, inv(z)))
|
||||
})
|
||||
]
|
||||
|
@ -39,8 +41,9 @@ export const divide = [
|
|||
export const invert = match(Complex, (math, C, strategy) => {
|
||||
const conj = math.conj.resolve(C, full)
|
||||
const normsq = math.normsq.resolve(C, full)
|
||||
const div = math.divide.resolve([C.Component, normsq.returns], full)
|
||||
const cplx = maybeComplex(math, strategy, div.returns, div.returns)
|
||||
const div = math.divide.resolve([C.Component, ReturnType(normsq)], full)
|
||||
const NewComp = ReturnType(div)
|
||||
const cplx = maybeComplex(math, strategy, NewComp, NewComp)
|
||||
return ReturnsAs(cplx, z => {
|
||||
const c = conj(z)
|
||||
const d = normsq(z)
|
||||
|
@ -53,7 +56,8 @@ export const invert = match(Complex, (math, C, strategy) => {
|
|||
export const multiply = [
|
||||
match([T => !T.complex, Complex], (math, [R, C], strategy) => {
|
||||
const mult = math.multiply.resolve([R, C.Component], full)
|
||||
const cplx = maybeComplex(math, strategy, mult.returns, mult.returns)
|
||||
const NewComp = ReturnType(mult)
|
||||
const cplx = maybeComplex(math, strategy, NewComp, NewComp)
|
||||
return ReturnsAs(cplx, (r, z) => cplx(mult(r, z.re), mult(r, z.im)))
|
||||
}),
|
||||
match([Complex, T => !T.complex], (math, [C, R], strategy) => {
|
||||
|
@ -62,15 +66,19 @@ export const multiply = [
|
|||
}),
|
||||
match([Complex, Complex], (math, [W, Z], strategy) => {
|
||||
const conj = math.conj.resolve(W.Component, full)
|
||||
if (conj.returns !== W.Component) {
|
||||
if (ReturnType(conj) !== W.Component) {
|
||||
throw new ResolutionError(
|
||||
`conjugation on ${W.Component} returns type (${conj.returns})`)
|
||||
`conjugation on ${W.Component} returns different type `
|
||||
+ `(${ReturnType(conj)})`)
|
||||
}
|
||||
const mWZ = math.multiply.resolve([W.Component, Z.Component], full)
|
||||
const mZW = math.multiply.resolve([Z.Component, W.Component], full)
|
||||
const sub = math.subtract.resolve([mWZ.returns, mZW.returns], full)
|
||||
const add = math.add.resolve([mWZ.returns, mZW.returns], full)
|
||||
const cplx = maybeComplex(math, strategy, sub.returns, add.returns)
|
||||
const TWZ = ReturnType(mWZ)
|
||||
const TZW = ReturnType(mZW)
|
||||
const sub = math.subtract.resolve([TWZ, TZW], full)
|
||||
const add = math.add.resolve([TWZ, TZW], full)
|
||||
const cplx = maybeComplex(
|
||||
math, strategy, ReturnType(sub), ReturnType(add))
|
||||
return ReturnsAs(cplx, (w, z) => {
|
||||
const real = sub(mWZ( w.re, z.re), mZW(z.im, conj(w.im)))
|
||||
const imag = add(mWZ(conj(w.re), z.im), mZW(z.re, w.im))
|
||||
|
@ -85,7 +93,7 @@ export const negate = promoteUnary('negate')
|
|||
// integer coordinates.
|
||||
export const sqrt = match(Complex, (math, C, strategy) => {
|
||||
const re = math.re.resolve(C)
|
||||
const R = re.returns
|
||||
const R = ReturnType(re)
|
||||
const isReal = math.isReal.resolve(C)
|
||||
// dependencies for the real case:
|
||||
const zComp = math.zero(C.Component)
|
||||
|
@ -98,8 +106,8 @@ export const sqrt = match(Complex, (math, C, strategy) => {
|
|||
const cplx = math.complex.resolve([C.Component, C.Component], full)
|
||||
// additional dependencies for the complex case
|
||||
const abs = math.abs.resolve(C, full)
|
||||
if (abs.returns !== R) {
|
||||
throw new TypeError(`abs on ${C} returns ${abs.returns}, not ${R}`)
|
||||
if (ReturnType(abs) !== R) {
|
||||
throw new TypeError(`abs on ${C} returns ${ReturnType(abs)}, not ${R}`)
|
||||
}
|
||||
const addRR = math.add.resolve([R, R], conservative)
|
||||
const twoR = addRR(oneR, oneR)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Complex} from './Complex.js'
|
||||
import {ReturnTyping} from '#core/Type.js'
|
||||
import {ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {ReturnsAs} from '#generic/helpers.js'
|
||||
|
||||
|
@ -8,7 +8,7 @@ const {free, full} = ReturnTyping
|
|||
export const maybeComplex = (math, strategy, Real, Imag) => {
|
||||
if (strategy !== free) return math.complex.resolve([Real, Imag], strategy)
|
||||
const cplx = math.complex.resolve([Real, Imag], full)
|
||||
const prune = math.pruneImaginary.resolve(cplx.returns, full)
|
||||
const prune = math.pruneImaginary.resolve(ReturnType(cplx), full)
|
||||
return ReturnsAs(prune, (r, m) => prune(cplx(r, m)))
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ export const promoteUnary = (name, overrideStrategy) => match(
|
|||
(math, C, strategy) => {
|
||||
const compOp = math.resolve(name, C.Component, full)
|
||||
if (overrideStrategy) strategy = overrideStrategy
|
||||
const cplx = maybeComplex(math, strategy, compOp.returns, compOp.returns)
|
||||
const NewComp = ReturnType(compOp)
|
||||
const cplx = maybeComplex(math, strategy, NewComp, NewComp)
|
||||
return ReturnsAs(cplx, z => cplx(compOp(z.re), compOp(z.im)))
|
||||
})
|
||||
|
||||
|
@ -32,7 +33,8 @@ export const promoteBinary = name => match(
|
|||
[Complex, Complex],
|
||||
(math, [W, Z], strategy) => {
|
||||
const compOp = math.resolve(name, [W.Component, Z.Component], full)
|
||||
const cplx = maybeComplex(math, strategy, compOp.returns, compOp.returns)
|
||||
const NewComp = ReturnType(compOp)
|
||||
const cplx = maybeComplex(math, strategy, NewComp, NewComp)
|
||||
return ReturnsAs(
|
||||
cplx, (w, z) => cplx(compOp(w.re, z.re), compOp(w.im, z.im)))
|
||||
})
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import {Complex} from './Complex.js'
|
||||
import {OneOf, Returns, ReturnTyping, TypeOfTypes} from "#core/Type.js"
|
||||
import {
|
||||
OneOf, Returns, ReturnType, ReturnTyping, TypeOfTypes
|
||||
} from "#core/Type.js"
|
||||
import {Any, match} from "#core/TypePatterns.js"
|
||||
import {BooleanT} from '#boolean/BooleanT.js'
|
||||
import {NumberT} from '#number/NumberT.js'
|
||||
|
@ -32,7 +34,7 @@ export const complex = [
|
|||
export const arg = // [ // enable when we have atan2 in mathjs
|
||||
// match(Complex, (math, C) => {
|
||||
// const re = math.re.resolve(C)
|
||||
// const R = re.returns
|
||||
// const R = ReturnType(re)
|
||||
// const im = math.im.resolve(C)
|
||||
// const abs = math.abs.resolve(C)
|
||||
// const atan2 = math.atan2.resolve([R, R], conservative)
|
||||
|
@ -54,11 +56,11 @@ export const associate = match(
|
|||
}
|
||||
const eq = math.equal.resolve([W, Z], full)
|
||||
const neg = math.negate.resolve(Z, full)
|
||||
const eqN = math.equal.resolve([W, neg.returns], full)
|
||||
const eqN = math.equal.resolve([W, ReturnType(neg)], full)
|
||||
const mult = math.multiply.resolve([Z, Z], full)
|
||||
const eqM = math.equal.resolve([W, mult.returns], full)
|
||||
const negM = math.negate.resolve(mult.returns, full)
|
||||
const eqNM = math.equal.resolve([W, negM.returns], full)
|
||||
const eqM = math.equal.resolve([W, ReturnType(mult)], full)
|
||||
const negM = math.negate.resolve(ReturnType(mult), full)
|
||||
const eqNM = math.equal.resolve([W, ReturnType(negM)], full)
|
||||
const iZ = math.complex(math.zero(Z.Component), math.one(Z.Component))
|
||||
return Returns(BooleanT, (w, z) => {
|
||||
if (eq(w, z) || eqN(w, neg(z))) return true
|
||||
|
|
|
@ -13,12 +13,16 @@ As of this writing, the only two types required to be in a TypeDispatcher are
|
|||
Undefined (the type inhabited only by `undefined`) and TypeOfTypes (the type
|
||||
inhabited exactly by Type objects).
|
||||
|
||||
There is also a constant NotAType which is the type-world analogue of NaN for
|
||||
numbers. It is occasionally used for the rare behavior that truly does not
|
||||
There is also a constant Unknown which is the type that is used when it is
|
||||
not possible in advance to determine what the type of an entity is or will
|
||||
be. It is, for example, used for the occasional behavior that truly does not
|
||||
return any particular type, such as the method `zero` that takes a Type and
|
||||
returns its zero element. However, it does not really work as a Type, and in
|
||||
particular, do _not_ merge it into any TypeDispatcher -- it will disrupt the
|
||||
type and method resolution process.
|
||||
returns its zero element. It is also used for the component type of instances
|
||||
of containers, like Vector, that have inhomogeneous contents -- and therefore,
|
||||
as the return type of `sum` on a `Vector(Unknown)`. However, it lacks much of
|
||||
the machinery of other Type entities. In particular, do _not_ attempt to merge
|
||||
Unknown as a type into any TypeDispatcher -- it will disrupt the type and
|
||||
method resolution process.
|
||||
|
||||
## Core methods
|
||||
|
||||
|
|
|
@ -66,8 +66,8 @@ export const Undefined = new Type(
|
|||
t => typeof t === 'undefined',
|
||||
{zero: undefined, one: undefined, nan: undefined})
|
||||
export const TypeOfTypes = new Type(t => t instanceof Type)
|
||||
export const NotAType = new Type(() => true) // Danger, do not merge!
|
||||
NotAType._doNotMerge = true
|
||||
export const Unknown = new Type(() => true) // Danger, do not merge!
|
||||
Unknown._doNotMerge = true
|
||||
|
||||
const unionDirectory = new ArrayKeyedMap() // make sure only one of each union
|
||||
export const OneOf = (...types) => {
|
||||
|
@ -101,7 +101,8 @@ export const OneOf = (...types) => {
|
|||
return unionDirectory.get(typeList)
|
||||
}
|
||||
|
||||
export const Returns = (type, f) => (f.returns = type, f)
|
||||
export const Returns = (type, f) => (f.returns = type, f.isBehavior = true, f)
|
||||
export const ReturnType = f => f.returns ?? Unknown
|
||||
|
||||
export const whichType = typs => Returns(TypeOfTypes, item => {
|
||||
for (const type of Object.values(typs)) {
|
||||
|
|
|
@ -3,7 +3,9 @@ import ArrayKeyedMap from 'array-keyed-map'
|
|||
import {ResolutionError, isPlainFunction, isPlainObject} from './helpers.js'
|
||||
import {Implementations, ImplementationsGenerator} from './Implementations.js'
|
||||
import {bootstrapTypes} from './type.js'
|
||||
import {Returns, ReturnTyping, whichType, Type} from './Type.js'
|
||||
import {
|
||||
Returns, ReturnType, ReturnTyping, Unknown, Type, whichType
|
||||
} from './Type.js'
|
||||
import {
|
||||
matched, needsCollection, Passthru, Matcher, match
|
||||
} from './TypePatterns.js'
|
||||
|
@ -253,7 +255,7 @@ export class TypeDispatcher {
|
|||
if ('actual' in template) { // incorporate conversion
|
||||
let convert = template.convertor
|
||||
// Check if it's a factory:
|
||||
if (!convert.returns) convert = convert(this, template.actual)
|
||||
if (!convert.isBehavior) convert = convert(this, template.actual)
|
||||
extractor = args => convert(args[from])
|
||||
}
|
||||
return state ? extractor : args => [extractor(args)]
|
||||
|
@ -273,6 +275,14 @@ export class TypeDispatcher {
|
|||
throw new ReferenceError(`no method or value for key '${key}'`)
|
||||
}
|
||||
if (!Array.isArray(types)) types = [types]
|
||||
if (types.some(T => T === Unknown)) {
|
||||
if (!strategy) return this[key]
|
||||
const thisTypeOf = whichType(this.types)
|
||||
return (...args) => {
|
||||
const types = args.map(thisTypeOf)
|
||||
return this.resolve(key, types, strategy)(...args)
|
||||
}
|
||||
}
|
||||
if (!strategy) {
|
||||
// Avoid recursing on obtaining config
|
||||
if (key === 'config') strategy = ReturnTyping.free
|
||||
|
@ -380,12 +390,7 @@ export class TypeDispatcher {
|
|||
|
||||
finalBehavior = theBehavior
|
||||
if (typeof theBehavior === 'function') {
|
||||
const returning = theBehavior.returns
|
||||
if (!returning) {
|
||||
throw new TypeError(
|
||||
`No return type specified for ${key} on ${types} with`
|
||||
+ ` return typing ${ReturnTyping.name(strategy)}`)
|
||||
}
|
||||
const returning = ReturnType(theBehavior)
|
||||
if (needsCollection(template)) {
|
||||
// have to wrap the behavior to collect the actual arguments
|
||||
// in the way corresponding to the template. Generating that
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Type, Undefined} from './Type.js'
|
||||
import {ReturnType, Type, Undefined, Unknown} from './Type.js'
|
||||
import {isPlainFunction} from './helpers.js'
|
||||
|
||||
export class TypePattern {
|
||||
|
@ -189,8 +189,10 @@ export const matched = (template, math) => {
|
|||
}
|
||||
if (template.matched) {
|
||||
let convert = template.convertor
|
||||
if (!convert.returns) convert = convert(math, template.actual)
|
||||
return convert.returns || template.matched
|
||||
if (!convert.isBehavior) convert = convert(math, template.actual)
|
||||
const ConvertsTo = ReturnType(convert)
|
||||
if (ConvertsTo !== Unknown) return ConvertsTo
|
||||
return template.matched
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import assert from 'assert'
|
|||
import math from '#nanomath'
|
||||
import {NumberT} from '#number/NumberT.js'
|
||||
|
||||
import {Returns, ReturnTyping} from '../Type.js'
|
||||
import {Returns, ReturnType, ReturnTyping} from '../Type.js'
|
||||
import {isPlainFunction} from '../helpers.js'
|
||||
|
||||
describe('Core types', () => {
|
||||
|
@ -36,7 +36,7 @@ describe('Core types', () => {
|
|||
it('provides a return-value labeling', () => {
|
||||
const labeledF = Returns(math.types.Undefined, () => undefined)
|
||||
assert.strictEqual(typeof labeledF, 'function')
|
||||
assert.strictEqual(labeledF.returns, math.types.Undefined)
|
||||
assert.strictEqual(ReturnType(labeledF), math.types.Undefined)
|
||||
assert(isPlainFunction(labeledF))
|
||||
})
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as numbers from '#number/all.js'
|
|||
import {NumberT} from '#number/NumberT.js'
|
||||
import {ResolutionError} from "#core/helpers.js"
|
||||
import {match, Any} from "#core/TypePatterns.js"
|
||||
import {NotAType, Returns, ReturnTyping} from "#core/Type.js"
|
||||
import {Returns, ReturnType, ReturnTyping, Unknown} from "#core/Type.js"
|
||||
import {plain} from "#number/helpers.js"
|
||||
|
||||
describe('TypeDispatcher', () => {
|
||||
|
@ -18,7 +18,7 @@ describe('TypeDispatcher', () => {
|
|||
const {NumberT, TypeOfTypes, Undefined} = incremental.types
|
||||
assert(NumberT.test(7))
|
||||
assert.strictEqual(incremental.add(-1.5, 0.5), -1)
|
||||
assert.throws(() => incremental.add(7, undefined), ResolutionError)
|
||||
assert.throws(() => incremental.add(7, NumberT), ResolutionError)
|
||||
// Make Undefined act like zero:
|
||||
incremental.merge({add: [
|
||||
match([Undefined, Any], (_m, [_U, T]) => Returns(T, (_a, b) => b)),
|
||||
|
@ -26,11 +26,11 @@ describe('TypeDispatcher', () => {
|
|||
]})
|
||||
assert.strictEqual(incremental.add(7, undefined), 7)
|
||||
assert.strictEqual(
|
||||
incremental.resolve('add', [TypeOfTypes, Undefined]).returns,
|
||||
ReturnType(incremental.resolve('add', [TypeOfTypes, Undefined])),
|
||||
TypeOfTypes)
|
||||
assert.strictEqual(incremental.add(undefined, -3.25), -3.25)
|
||||
assert.strictEqual(
|
||||
incremental.add.resolve([Undefined, NumberT]).returns,
|
||||
ReturnType(incremental.add.resolve([Undefined, NumberT])),
|
||||
NumberT)
|
||||
// Oops, changed my mind ;-), make it work like NaN with numbers:
|
||||
const alwaysNaN = Returns(NumberT, () => NaN)
|
||||
|
@ -40,7 +40,7 @@ describe('TypeDispatcher', () => {
|
|||
]})
|
||||
assert(isNaN(incremental.add(undefined, -3.25)))
|
||||
assert.strictEqual(
|
||||
incremental.add.resolve([Undefined, NumberT]).returns,
|
||||
ReturnType(incremental.add.resolve([Undefined, NumberT])),
|
||||
NumberT)
|
||||
assert.strictEqual(incremental.isnan(NaN), 1)
|
||||
incremental.merge(booleans)
|
||||
|
@ -68,9 +68,9 @@ describe('TypeDispatcher', () => {
|
|||
assert(!bgn._behaviors.negate.has([ReturnTyping.free, BooleanT]))
|
||||
assert.strictEqual(bgn.negate(true), -2)
|
||||
})
|
||||
it('disallows merging NotAType', () => {
|
||||
it('disallows merging Unknown', () => {
|
||||
const doomed = new TypeDispatcher()
|
||||
assert.throws(() => doomed.merge({NaT: NotAType}), TypeError)
|
||||
assert.throws(() => doomed.merge({Unknown}), TypeError)
|
||||
})
|
||||
it('supports generic types', () => {
|
||||
assert.throws(() => NumberT(0), TypeError)
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
import {NotAType, Returns, TypeOfTypes} from '#core/Type.js'
|
||||
import {Returns, TypeOfTypes, Unknown} from '#core/Type.js'
|
||||
import {match,Any} from "#core/TypePatterns.js"
|
||||
import {boolnum} from "#number/helpers.js"
|
||||
|
||||
export const haszero = [
|
||||
match(Any, (math, T) => {
|
||||
const answer = math.haszero(T)
|
||||
return Returns(math.typeOf(answer), () => answer)
|
||||
}),
|
||||
match(TypeOfTypes, boolnum(T => 'zero' in T))
|
||||
]
|
||||
|
||||
export const zero = [
|
||||
match(Any, (math, T) => {
|
||||
const z = math.zero(T)
|
||||
return Returns(T, () => z)
|
||||
}),
|
||||
match(TypeOfTypes, Returns(NotAType, t => {
|
||||
if ('zero' in t) return t.zero
|
||||
throw new RangeError(`type '${t}' has no zero element`)
|
||||
match(TypeOfTypes, Returns(Unknown, T => {
|
||||
if ('zero' in T) return T.zero
|
||||
throw new RangeError(`type '${T}' has no zero element`)
|
||||
}))
|
||||
]
|
||||
|
||||
|
@ -18,10 +26,10 @@ export const one = [
|
|||
const unit = math.one(T)
|
||||
return Returns(T, () => unit)
|
||||
}),
|
||||
match(TypeOfTypes, Returns(NotAType, t => {
|
||||
if ('one' in t) return t.one
|
||||
match(TypeOfTypes, Returns(Unknown, T => {
|
||||
if ('one' in T) return T.one
|
||||
throw new RangeError(
|
||||
`type '${t}' has no unit element designated as "one"`)
|
||||
`type '${T}' has no unit element designated as "one"`)
|
||||
}))
|
||||
]
|
||||
|
||||
|
@ -30,7 +38,7 @@ export const hasnan = [
|
|||
const answer = math.hasnan(T)
|
||||
return Returns(math.typeOf(answer), () => answer)
|
||||
}),
|
||||
match(TypeOfTypes, boolnum(t => 'nan' in t))
|
||||
match(TypeOfTypes, boolnum(T => 'nan' in T))
|
||||
]
|
||||
|
||||
export const nan = [
|
||||
|
@ -38,9 +46,9 @@ export const nan = [
|
|||
const notanum = math.nan(T)
|
||||
return Returns(T, () => notanum)
|
||||
}),
|
||||
match(TypeOfTypes, Returns(NotAType, t => {
|
||||
if ('nan' in t) return t.nan
|
||||
match(TypeOfTypes, Returns(Unknown, T => {
|
||||
if ('nan' in T) return T.nan
|
||||
throw new RangeError(
|
||||
`type '${t}' has no "not a number" element`)
|
||||
`type '${T}' has no "not a number" element`)
|
||||
}))
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import assert from 'assert'
|
||||
import math from '#nanomath'
|
||||
import {ReturnTyping} from '#core/Type.js'
|
||||
import {ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
|
||||
const {Complex, NumberT} = math.types
|
||||
|
||||
|
@ -8,7 +8,7 @@ describe('generic arithmetic', () => {
|
|||
it('squares anything', () => {
|
||||
const sq = math.square
|
||||
assert.strictEqual(sq(7), 49)
|
||||
assert.strictEqual(math.square.resolve([NumberT]).returns, NumberT)
|
||||
assert.strictEqual(ReturnType(math.square.resolve([NumberT])), NumberT)
|
||||
assert.deepStrictEqual(sq(math.complex(3, 4)), math.complex(-7, 24))
|
||||
const eyes = math.complex(0, 2)
|
||||
assert.strictEqual(sq(eyes), -4)
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import {ReturnsAs} from './helpers.js'
|
||||
|
||||
import {Returns, ReturnTyping} from '#core/Type.js'
|
||||
import {Returns, ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
import {match, Any} from '#core/TypePatterns.js'
|
||||
|
||||
export const norm = match(Any, (math, T) => {
|
||||
const normsq = math.normsq.resolve(T)
|
||||
const sqrt = math.sqrt.resolve(normsq.returns, ReturnTyping.conservative)
|
||||
const sqrt = math.sqrt.resolve(
|
||||
ReturnType(normsq), ReturnTyping.conservative)
|
||||
return ReturnsAs(sqrt, t => sqrt(normsq(t)))
|
||||
})
|
||||
|
||||
export const abs = norm // coincide for most types (scalars)
|
||||
|
||||
export const conj = match(Any, (_math, T) => Returns(T, t => t))
|
||||
|
||||
export const square = match(Any, (math, T, strategy) => {
|
||||
const mult = math.multiply.resolve([T, T], strategy)
|
||||
return Returns(mult.returns, t => mult(t, t))
|
||||
return ReturnsAs(mult, t => mult(t, t))
|
||||
})
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
export const ReturnsAs = (g, f) => (f.returns = g.returns, f)
|
||||
import {Returns, ReturnType} from '#core/Type.js'
|
||||
|
||||
export const ReturnsAs = (g, f) => Returns(ReturnType(g), f)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {ReturnsAs} from './helpers.js'
|
||||
|
||||
import {OneOf, Returns} from '#core/Type.js'
|
||||
import {OneOf, Returns, ReturnType} from '#core/Type.js'
|
||||
import {Any, Multiple, match} from '#core/TypePatterns.js'
|
||||
import {boolnum} from '#number/helpers.js'
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const and = [
|
|||
}),
|
||||
match([Any, Any, Any, Multiple(Any)], (math, [T, U, V, Rest]) => {
|
||||
const andRest = math.and.resolve([U, V, ...Rest])
|
||||
const andFirst = math.and.resolve([T, andRest.returns])
|
||||
const andFirst = math.and.resolve([T, ReturnType(andRest)])
|
||||
return ReturnsAs(
|
||||
andFirst, (t, u, v, rest) => andFirst(t, andRest(u, v, ...rest)))
|
||||
})
|
||||
|
@ -33,7 +33,7 @@ export const or = [
|
|||
}),
|
||||
match([Any, Any, Any, Multiple(Any)], (math, [T, U, V, Rest]) => {
|
||||
const orRest = math.or.resolve([U, V, ...Rest])
|
||||
const orFirst = math.and.resolve([T, orRest.returns])
|
||||
const orFirst = math.and.resolve([T, ReturnType(orRest)])
|
||||
return ReturnsAs(
|
||||
orFirst, (t, u, v, rest) => orFirst(t, orRest(u, v, ...rest)))
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {ReturnsAs} from './helpers.js'
|
||||
import {Returns, ReturnTyping} from '#core/Type.js'
|
||||
import {Returns, ReturnType, ReturnTyping} from '#core/Type.js'
|
||||
import {Any, Passthru, match, matched} from '#core/TypePatterns.js'
|
||||
import {boolnum} from '#number/helpers.js'
|
||||
|
||||
|
@ -82,15 +82,15 @@ export const sign = match(Any, (math, T) => {
|
|||
|
||||
export const unequal = match(Passthru, (math, types) => {
|
||||
const eq = math.equal.resolve(types)
|
||||
const not = math.not.resolve(eq.returns)
|
||||
const not = math.not.resolve(ReturnType(eq))
|
||||
return ReturnsAs(not, (...args) => not(eq(...args)))
|
||||
})
|
||||
|
||||
export const larger = match([Any, Any], (math, [T, U]) => {
|
||||
const eq = math.equal.resolve([T, U])
|
||||
const bigger = math.exceeds.resolve([T, U])
|
||||
const not = math.not.resolve(eq.returns)
|
||||
const and = math.and.resolve([not.returns, bigger.returns])
|
||||
const not = math.not.resolve(ReturnType(eq))
|
||||
const and = math.and.resolve([ReturnType(not), ReturnType(bigger)])
|
||||
return ReturnsAs(and, (t, u) => and(not(eq(t, u)), bigger(t, u)))
|
||||
})
|
||||
|
||||
|
@ -103,21 +103,21 @@ export const isPositive = match(Any, (math, T) => {
|
|||
export const largerEq = match([Any, Any], (math, [T, U]) => {
|
||||
const eq = math.equal.resolve([T, U])
|
||||
const bigger = math.exceeds.resolve([T, U])
|
||||
const or = math.or.resolve([eq.returns, bigger.returns])
|
||||
const or = math.or.resolve([ReturnType(eq), ReturnType(bigger)])
|
||||
return ReturnsAs(or, (t, u) => or(eq(t, u), bigger(t, u)))
|
||||
})
|
||||
|
||||
export const smaller = match([Any, Any], (math, [T, U]) => {
|
||||
const eq = math.equal.resolve([T, U])
|
||||
const bigger = math.exceeds.resolve([U, T])
|
||||
const not = math.not.resolve(eq.returns)
|
||||
const and = math.and.resolve([not.returns, bigger.returns])
|
||||
const not = math.not.resolve(ReturnType(eq))
|
||||
const and = math.and.resolve([ReturnType(not), ReturnType(bigger)])
|
||||
return ReturnsAs(and, (t, u) => and(not(eq(t, u)), bigger(u, t)))
|
||||
})
|
||||
|
||||
export const smallerEq = match([Any, Any], (math, [T, U]) => {
|
||||
const eq = math.equal.resolve([T, U])
|
||||
const bigger = math.exceeds.resolve([U, T])
|
||||
const or = math.or.resolve([eq.returns, bigger.returns])
|
||||
const or = math.or.resolve([ReturnType(eq), ReturnType(bigger)])
|
||||
return ReturnsAs(or, (t, u) => or(eq(t, u), bigger(u, t)))
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {plain} from './helpers.js'
|
||||
import {NumberT} from './NumberT.js'
|
||||
import {OneOf, Returns, ReturnTyping} from '#core/Type.js'
|
||||
import {OneOf, Returns, ReturnTyping, Undefined} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {Complex} from '#complex/Complex.js'
|
||||
|
||||
|
@ -9,7 +9,11 @@ const {conservative, full} = ReturnTyping
|
|||
export const abs = plain(Math.abs)
|
||||
export const norm = abs
|
||||
export const normsq = plain(a => a*a)
|
||||
export const add = plain((a, b) => a + b)
|
||||
export const add = [
|
||||
plain((a, b) => a + b),
|
||||
match([Undefined, NumberT], Returns(NumberT, () => NaN)),
|
||||
match([NumberT, Undefined], Returns(NumberT, () => NaN))
|
||||
]
|
||||
export const divide = plain((a, b) => a / b)
|
||||
export const cbrt = plain(a => {
|
||||
if (a === 0) return a
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NotAType, Type} from '#core/Type.js'
|
||||
import {Type, Unknown} from '#core/Type.js'
|
||||
|
||||
const isVector = v => Array.isArray(v)
|
||||
|
||||
|
@ -12,7 +12,7 @@ export const Vector = new Type(isVector, {
|
|||
if ('zero' in CompType) typeOptions.zero = [CompType.zero]
|
||||
const vectorCompType = new Type(specTest, typeOptions)
|
||||
vectorCompType.Component = CompType
|
||||
vectorCompType.vector = true
|
||||
vectorCompType.vectorDepth = (CompType.vectorDepth ?? 0) + 1
|
||||
return vectorCompType
|
||||
}
|
||||
// Wrapping a generic type in Vector
|
||||
|
@ -37,12 +37,12 @@ export const Vector = new Type(isVector, {
|
|||
}
|
||||
})
|
||||
},
|
||||
specializesTo: VT => VT.vector,
|
||||
specializesTo: VT => 'vectorDepth' in VT && VT.vectorDepth > 0,
|
||||
refine(v, typer) {
|
||||
if (!v.length) return this.specialize(NotAType) // what else could we do?
|
||||
if (!v.length) return this.specialize(Unknown) // what else could we do?
|
||||
const eltTypes = v.map(elt => typer(elt))
|
||||
let compType = eltTypes[0]
|
||||
if (eltTypes.some(T => T !== compType)) compType = NotAType
|
||||
if (eltTypes.some(T => T !== compType)) compType = Unknown
|
||||
return this.specialize(compType)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import assert from 'assert'
|
||||
import {Vector} from '../Vector.js'
|
||||
import {NotAType} from '#core/Type.js'
|
||||
import {Unknown} from '#core/Type.js'
|
||||
import math from '#nanomath'
|
||||
|
||||
describe('Vector Type', () => {
|
||||
|
@ -14,7 +14,7 @@ describe('Vector Type', () => {
|
|||
const cplx = math.complex
|
||||
assert.strictEqual(typ([3, 4]), Vector(NumberT))
|
||||
assert.strictEqual(typ([true, false]), Vector(BooleanT))
|
||||
assert.strictEqual(typ([3, false]), Vector(NotAType))
|
||||
assert.strictEqual(typ([3, false]), Vector(Unknown))
|
||||
assert.strictEqual(typ([cplx(3, 4), cplx(5)]), Vector(Complex(NumberT)))
|
||||
assert.strictEqual(typ([[3, 4], [5]]), Vector(Vector(NumberT)))
|
||||
})
|
||||
|
|
33
src/vector/__test__/arithmetic.spec.js
Normal file
33
src/vector/__test__/arithmetic.spec.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import assert from 'assert'
|
||||
import math from '#nanomath'
|
||||
|
||||
describe('Vector arithmetic functions', () => {
|
||||
const cplx = math.complex
|
||||
const cV = [cplx(3, 4), cplx(4, -3), cplx(-3, -4)]
|
||||
it('distributes abs elementwise', () => {
|
||||
const abs = math.abs
|
||||
assert.deepStrictEqual(abs([-3, 4, -5]), [3, 4, 5])
|
||||
assert.deepStrictEqual(abs(cV), [5, 5, 5])
|
||||
assert.deepStrictEqual(abs([true, -4, cplx(4, 3)]), [1, 4, 5])
|
||||
})
|
||||
it('computes the norm of a vector or matrix', () => {
|
||||
const norm = math.norm
|
||||
assert.strictEqual(norm([-3, 4, -5]), Math.sqrt(50))
|
||||
assert.strictEqual(norm([[1, 2], [4, 2]]), 5)
|
||||
assert.strictEqual(norm(cV), Math.sqrt(75))
|
||||
})
|
||||
it('adds vectors and matrices', () => {
|
||||
const add = math.add
|
||||
assert.deepStrictEqual(add([-3, 4, -5], [8, 0, 2]), [5, 4, -3])
|
||||
assert.deepStrictEqual(
|
||||
add([[1, 2], [4, 2]], [[0, -1], [3, -4]]), [[1, 1], [7, -2]])
|
||||
assert.deepStrictEqual(
|
||||
add([[1, 2], [4, 2]], [0, -1]), [[1, 1], [4, 1]])
|
||||
})
|
||||
it('computes the sum of a vector', () => {
|
||||
const sum = math.sum
|
||||
assert.strictEqual(sum([-3, 4, -5]), -4)
|
||||
assert.deepStrictEqual(sum(cV), cplx(4, -3))
|
||||
assert.strictEqual(sum([4, true, -2]), 3)
|
||||
})
|
||||
})
|
|
@ -1,5 +1,5 @@
|
|||
import assert from 'assert'
|
||||
import {NotAType} from '#core/Type.js'
|
||||
import {ReturnType, Unknown} from '#core/Type.js'
|
||||
import math from '#nanomath'
|
||||
|
||||
describe('Vector type functions', () => {
|
||||
|
@ -8,9 +8,11 @@ describe('Vector type functions', () => {
|
|||
const {BooleanT, NumberT, Vector} = math.types
|
||||
assert.deepStrictEqual(vec(3, 4, 5), [3, 4, 5])
|
||||
assert.strictEqual(
|
||||
vec.resolve([NumberT, NumberT, NumberT]).returns, Vector(NumberT))
|
||||
ReturnType(vec.resolve([NumberT, NumberT, NumberT])),
|
||||
Vector(NumberT))
|
||||
assert.deepStrictEqual(vec(3, true), [3, true])
|
||||
assert.strictEqual(
|
||||
vec.resolve([NumberT, BooleanT]).returns, Vector(NotAType))
|
||||
ReturnType(vec.resolve([NumberT, BooleanT])),
|
||||
Vector(Unknown))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * as typeDefinition from './Vector.js'
|
||||
export * as arithmetic from './arithmetic.js'
|
||||
export * as logical from './logical.js'
|
||||
export * as relational from './relational.js'
|
||||
export * as type from './type.js'
|
||||
|
|
33
src/vector/arithmetic.js
Normal file
33
src/vector/arithmetic.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {promoteBinary, promoteUnary} from './helpers.js'
|
||||
import {Vector} from './Vector.js'
|
||||
|
||||
import {ReturnType} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {ReturnsAs} from '#generic/helpers.js'
|
||||
|
||||
export const normsq = match(Vector, (math, V) => {
|
||||
const compNormsq = math.normsq.resolve(V.Component)
|
||||
const sum = math.sum.resolve(Vector(ReturnType(compNormsq)))
|
||||
return ReturnsAs(sum, v => sum(v.map(compNormsq)))
|
||||
})
|
||||
// abs and norm differ only on Vector (and perhaps other collections) --
|
||||
// norm computes overall by the generic formula, whereas abs distributes
|
||||
// elementwise:
|
||||
export const abs = promoteUnary('abs')
|
||||
export const add = promoteBinary('add')
|
||||
|
||||
export const sum = match(Vector, (math, V) => {
|
||||
const add = math.add.resolve([V.Component, V.Component])
|
||||
const haszero = math.haszero(V.Component)
|
||||
const zero = haszero ? math.zero(V.Component) : undefined
|
||||
return ReturnsAs(add, v => {
|
||||
if (v.length === 0) {
|
||||
if (haszero) return zero
|
||||
throw new TypeError(`Can't sum empty ${V}: no zero element`)
|
||||
}
|
||||
let ix = 0
|
||||
let retval = v[ix]
|
||||
while (++ix < v.length) retval = add(retval, v[ix])
|
||||
return retval
|
||||
})
|
||||
})
|
|
@ -1,61 +1,44 @@
|
|||
import {Vector} from './Vector.js'
|
||||
import {NotAType, Returns, Undefined} from '#core/Type.js'
|
||||
import {Returns, ReturnType, Undefined} from '#core/Type.js'
|
||||
import {Any, match} from '#core/TypePatterns.js'
|
||||
|
||||
export const promoteUnary = name => match(Vector, (math, V, strategy) => {
|
||||
if (V.Component === NotAType) {
|
||||
// have to resolve element by element :-(
|
||||
return Returns(V, v => v.map(
|
||||
elt => math.resolve(name, math.typeOf(elt), strategy)(elt)))
|
||||
}
|
||||
const compOp = math.resolve(name, V.Component, strategy)
|
||||
return Returns(Vector(compOp.returns), v => v.map(elt => compOp(elt)))
|
||||
return Returns(Vector(ReturnType(compOp)), v => v.map(elt => compOp(elt)))
|
||||
})
|
||||
|
||||
export const promoteBinary = name => [
|
||||
match([Vector, Any], (math, [V, E], strategy) => {
|
||||
const VComp = V.Component
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (v, e) => v.map(
|
||||
f => math.resolve(name, [math.typeOf(f), E], strategy)(f, e)))
|
||||
}
|
||||
const compOp = math.resolve(name, [VComp, E], strategy)
|
||||
const compOp = math.resolve(name, [V.Component, E], strategy)
|
||||
return Returns(
|
||||
Vector(compOp.returns), (v, e) => v.map(f => compOp(f, e)))
|
||||
Vector(ReturnType(compOp)), (v, e) => v.map(f => compOp(f, e)))
|
||||
}),
|
||||
match([Any, Vector], (math, [E, V], strategy) => {
|
||||
const VComp = V.Component
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (e, v) => v.map(
|
||||
f => math.resolve(name, [E, math.typeOf(f)], strategy)(e, f)))
|
||||
}
|
||||
const compOp = math.resolve(name, [E, VComp], strategy)
|
||||
const compOp = math.resolve(name, [E, V.Component], strategy)
|
||||
return Returns(
|
||||
Vector(compOp.returns, (e, v) => v.map(f => compOp(e, f))))
|
||||
Vector(ReturnType(compOp)), (e, v) => v.map(f => compOp(e, f)))
|
||||
}),
|
||||
match([Vector, Vector], (math, [V, W], strategy) => {
|
||||
const VComp = V.Component
|
||||
const WComp = W.Component
|
||||
let compOp
|
||||
let opNoV
|
||||
let opNoW
|
||||
let ReturnType
|
||||
if (VComp === NotAType || WComp === NotAType) {
|
||||
const typ = math.typeOf
|
||||
compOp = (v, w) => {
|
||||
return math.resolve(name, [typ(v), typ(w)], strategy)(v, w)
|
||||
}
|
||||
opNoV = compOp
|
||||
opNoW = compOp
|
||||
ReturnType = Vector(NotAType)
|
||||
} else {
|
||||
compOp = math.resolve(name, [VComp, WComp], strategy)
|
||||
opNoV = math.resolve(name, [Undefined, WComp], strategy)
|
||||
opNoW = math.resolve(name, [VComp, Undefined], strategy)
|
||||
ReturnType = Vector(compOp.returns)
|
||||
// special case: if the vector nesting depths do not match,
|
||||
// we operate between the elements of the deeper one and the entire
|
||||
// more shallow one:
|
||||
if (V.vectorDepth > W.vectorDepth) {
|
||||
const compOp = math.resolve(name, [VComp, W], strategy)
|
||||
return Returns(
|
||||
Vector(ReturnType(compOp)), (v, w) => v.map(f => compOp(f, w)))
|
||||
}
|
||||
if (V.vectorDepth < W.vectorDepth) {
|
||||
const compOp = math.resolve(name, [V, WComp], strategy)
|
||||
return Returns(
|
||||
Vector(ReturnType(compOp)), (v, w) => w.map(f => compOp(v, f)))
|
||||
}
|
||||
const compOp = math.resolve(name, [VComp, WComp], strategy)
|
||||
const opNoV = math.resolve(name, [Undefined, WComp], strategy)
|
||||
const opNoW = math.resolve(name, [VComp, Undefined], strategy)
|
||||
return Returns(
|
||||
ReturnType,
|
||||
Vector(ReturnType(compOp)),
|
||||
(v, w) => {
|
||||
const vInc = Number(v.length > 1)
|
||||
const wInc = Number(w.length >= v.length || w.length > 1)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Vector} from './Vector.js'
|
||||
import {promoteBinary} from './helpers.js'
|
||||
|
||||
import {NotAType, Returns, Undefined} from '#core/Type.js'
|
||||
import {Unknown, Returns, ReturnType, Undefined} from '#core/Type.js'
|
||||
import {Any, Optional, match} from '#core/TypePatterns.js'
|
||||
import {BooleanT} from '#boolean/BooleanT.js'
|
||||
|
||||
|
@ -9,11 +9,6 @@ export const deepEqual = [
|
|||
match([Vector, Any], Returns(BooleanT, () => false)),
|
||||
match([Any, Vector], Returns(BooleanT, () => false)),
|
||||
match([Vector, Vector], (math, [V, W]) => {
|
||||
if (V.Component === NotAType || W.Component === NotAType) {
|
||||
return Returns(BooleanT, (v, w) => v === w
|
||||
|| (v.length === w.length
|
||||
&& v.every((e, i) => math.deepEqual(e, w[i]))))
|
||||
}
|
||||
const compDeep = math.deepEqual.resolve([V.Component, W.Component])
|
||||
return Returns(BooleanT, (v,w) => v === w
|
||||
|| (v.length === w.length
|
||||
|
@ -25,24 +20,14 @@ export const indistinguishable = [
|
|||
match([Vector, Any, Optional([Any, Any])], (math, [V, E, T]) => {
|
||||
const VComp = V.Component
|
||||
if (T.length === 0) { // no tolerances
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (v, e) => v.map(f => {
|
||||
return math.indistinguishable.resolve([math.typeOf(f), E])(f, e)
|
||||
}))
|
||||
}
|
||||
const same = math.indistinguishable.resolve([VComp, E])
|
||||
return Returns(
|
||||
Vector(same.returns), (v, e) => v.map(f => same(f, e)))
|
||||
Vector(ReturnType(same)), (v, e) => v.map(f => same(f, e)))
|
||||
}
|
||||
const [[RT, AT]] = T
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (v, e, [[rT, aT]]) => v.map(f => {
|
||||
return math.indistinguishable(f, e, rT, aT)
|
||||
}))
|
||||
}
|
||||
const same = math.indistinguishable.resolve([VComp, E, RT, AT])
|
||||
return Returns(
|
||||
Vector(same.returns),
|
||||
Vector(ReturnType(same)),
|
||||
(v, e, [[rT, aT]]) => v.map(f => same(f, e, rT, aT)))
|
||||
}),
|
||||
match([Any, Vector, Optional([Any, Any])], (math, [E, V, T]) => {
|
||||
|
@ -50,30 +35,20 @@ export const indistinguishable = [
|
|||
// same is symmetric, even though it probably is
|
||||
const VComp = V.Component
|
||||
if (T.length === 0) { // no tolerances
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (e, v) => v.map(f => {
|
||||
return math.indistinguishable.resolve([E, math.typeOf(f)])(e, f)
|
||||
}))
|
||||
}
|
||||
const same = math.indistinguishable.resolve([E, VComp])
|
||||
return Returns(
|
||||
Vector(same.returns), (e, v) => v.map(f => same(e, f)))
|
||||
Vector(ReturnType(same)), (e, v) => v.map(f => same(e, f)))
|
||||
}
|
||||
const [[RT, AT]] = T
|
||||
if (VComp === NotAType) {
|
||||
return Returns(V, (e, v, [[rT, aT]]) => v.map(f => {
|
||||
return math.indistiguishable(e, f, rT, aT)
|
||||
}))
|
||||
}
|
||||
const same = math.indistinguishable.resolve([E, VComp, RT, AT])
|
||||
return Returns(
|
||||
Vector(same.returns),
|
||||
Vector(ReturnType(same)),
|
||||
(e, v, [[rT, aT]]) => v.map(f => same(e, f, rT, aT)))
|
||||
}),
|
||||
match([Vector, Vector, Optional([Any, Any])], (math, [V, W, T]) => {
|
||||
const VComp = V.Component
|
||||
const WComp = W.Component
|
||||
const inhomogeneous = VComp === NotAType || WComp === NotAType
|
||||
const inhomogeneous = VComp === Unknown || WComp === Unknown
|
||||
let same
|
||||
let sameNoV
|
||||
let sameNoW
|
||||
|
@ -92,7 +67,7 @@ export const indistinguishable = [
|
|||
sameNoW = math.indistinguishable.resolve([VComp, Undefined, RT, AT])
|
||||
}
|
||||
return Returns(
|
||||
inhomogeneous ? Vector(NotAType) : Vector(same.returns),
|
||||
inhomogeneous ? Vector(Unknown) : Vector(ReturnType(same)),
|
||||
(v, w, [tol = [0, 0]]) => {
|
||||
const [rT, aT] = tol
|
||||
const vInc = Number(v.length > 1)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {Vector} from './Vector.js'
|
||||
import {NotAType, Returns} from '#core/Type.js'
|
||||
import {Returns, Unknown} from '#core/Type.js'
|
||||
import {Any, Multiple, match} from '#core/TypePatterns.js'
|
||||
|
||||
export const vector = match(Multiple(Any), (math, [TV]) => {
|
||||
if (!TV.length) return Returns(Vector(NotAType), () => [])
|
||||
if (!TV.length) return Returns(Vector(Unknown), () => [])
|
||||
let CompType = TV[0]
|
||||
if (TV.some(T => T !== CompType)) CompType = NotAType
|
||||
if (TV.some(T => T !== CompType)) CompType = Unknown
|
||||
return Returns(Vector(CompType), v => v)
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue