feat: Complete relational functions for vectors
Toward its goal, this commit also: * Adds a new section of logical functions, and defines `not`, `and`, `or` for all current types. * Extends `OneOf` choice/union type to allow argument types that are themselves `OneOf` types. * Adds a readable .toString() method for TypePatterns. * Defines negate (as a no-op) and isnan (as always true) for the Undefined type * Extends comparisons to the Undefined type (to handle comparing vectors of different lengths)
This commit is contained in:
parent
c3c2bbbf78
commit
edfba089e3
19 changed files with 238 additions and 19 deletions
|
@ -1,3 +1,4 @@
|
|||
export * as typeDefinition from './BooleanT.js'
|
||||
export * as logical from './logical.js'
|
||||
export * as type from './type.js'
|
||||
export * as utilities from './utils.js'
|
||||
|
|
9
src/boolean/logical.js
Normal file
9
src/boolean/logical.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import {BooleanT} from './BooleanT.js'
|
||||
import {Returns} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
|
||||
export const not = match(BooleanT, Returns(BooleanT, p => !p))
|
||||
export const and = match(
|
||||
[BooleanT, BooleanT], Returns(BooleanT, (p, q) => p && q))
|
||||
export const or = match(
|
||||
[BooleanT, BooleanT], Returns(BooleanT, (p, q) => p || q))
|
|
@ -71,21 +71,32 @@ NotAType._doNotMerge = true
|
|||
|
||||
const unionDirectory = new ArrayKeyedMap() // make sure only one of each union
|
||||
export const OneOf = (...types) => {
|
||||
if (!types.length) {
|
||||
throw new RangeError('cannot choose OneOf no types at all')
|
||||
}
|
||||
const nonType = types.findIndex(T => !(T instanceof Type))
|
||||
if (nonType >= 0) {
|
||||
throw new RangeError(
|
||||
`OneOf can only take type arguments, not ${types[nonType]}`)
|
||||
}
|
||||
const typeSet = new Set(types) // remove duplicates
|
||||
const typeSet = new Set() // remove duplicates:
|
||||
for (const type of types) {
|
||||
if (type.unifies) {
|
||||
type.unifies.forEach(t => typeSet.add(t))
|
||||
} else typeSet.add(type)
|
||||
}
|
||||
if (typeSet.size === 1) return types[0]
|
||||
const typeList = Array.from(typeSet).sort() // canonical order
|
||||
const generic = typeList.find(T => !T.concrete)
|
||||
if (generic) {
|
||||
throw new RangeError(`OneOf can only take concrete types, not ${generic}`)
|
||||
}
|
||||
if (!unionDirectory.has(typeList)) {
|
||||
unionDirectory.set(typeList, new Type(
|
||||
const unionType = new Type(
|
||||
t => typeList.some(T => T.test(t)),
|
||||
{typeName: typeList.join('|')}))
|
||||
{typeName: typeList.join('|')})
|
||||
unionType.unifies = typeList
|
||||
unionDirectory.set(typeList, unionType)
|
||||
}
|
||||
return unionDirectory.get(typeList)
|
||||
}
|
||||
|
|
|
@ -309,9 +309,9 @@ export class TypeDispatcher {
|
|||
let needItem = true
|
||||
let item
|
||||
let template
|
||||
let pattern
|
||||
if (imps.length) {
|
||||
for (const options of [{}, {convert: true}]) {
|
||||
let pattern
|
||||
for ([pattern, item] of imps) {
|
||||
let finalIndex
|
||||
;[finalIndex, template] = pattern.match(types, options)
|
||||
|
@ -327,11 +327,18 @@ export class TypeDispatcher {
|
|||
needItem = false
|
||||
item = this._fallbacks[key]
|
||||
template = types
|
||||
pattern = Passthru
|
||||
}
|
||||
if (needItem) {
|
||||
throw new ResolutionError(
|
||||
`no matching definition of '${key}' on '${types}'`)
|
||||
}
|
||||
/* The following message is often helpful in debugging, so left it
|
||||
in but commented out; likely at some point we should have some
|
||||
sort of trace facility that this would be a part of:
|
||||
*/
|
||||
// console.log(`RESOLVING ${key} on ${types} matches ${pattern}`)
|
||||
|
||||
// If this key is producing a non-function value, we're done
|
||||
if (!isPlainFunction(item)) {
|
||||
behave.set(bhvix, item)
|
||||
|
|
|
@ -9,6 +9,7 @@ export class TypePattern {
|
|||
throw new Error('Specific TypePatterns must implement sampleTypes')
|
||||
}
|
||||
equal(other) {return other.constructor === this.constructor}
|
||||
toString() {return 'Abstract Pattern (?!)'}
|
||||
}
|
||||
|
||||
class MatchTypePattern extends TypePattern {
|
||||
|
@ -34,6 +35,7 @@ class MatchTypePattern extends TypePattern {
|
|||
}
|
||||
sampleTypes() {return [this.type]}
|
||||
equal(other) {return super.equal(other) && this.type === other.type}
|
||||
toString() {return `Match(${this.type})`}
|
||||
}
|
||||
|
||||
class SequencePattern extends TypePattern {
|
||||
|
@ -64,6 +66,7 @@ class SequencePattern extends TypePattern {
|
|||
&& this.patterns.length === other.patterns.length
|
||||
&& this.patterns.every((elt, ix) => elt.equal(other.patterns[ix]))
|
||||
}
|
||||
toString() {return `[${this.patterns}]`}
|
||||
}
|
||||
|
||||
class PredicatePattern extends TypePattern {
|
||||
|
@ -84,6 +87,7 @@ class PredicatePattern extends TypePattern {
|
|||
equal(other) {
|
||||
return super.equal(other) && this.predicate === other.predicate
|
||||
}
|
||||
toString() {return `Test(${this.predicate})`}
|
||||
}
|
||||
|
||||
export const pattern = patternOrSpec => {
|
||||
|
@ -106,6 +110,7 @@ class AnyPattern extends TypePattern {
|
|||
: [-1, Undefined]
|
||||
}
|
||||
sampleTypes() {return [Undefined]}
|
||||
toString() {return 'Any'}
|
||||
}
|
||||
|
||||
export const Any = new AnyPattern()
|
||||
|
@ -132,6 +137,7 @@ class OptionalPattern extends TypePattern {
|
|||
equal(other) {
|
||||
return super.equal(other) && this.pattern.equal(other.pattern)
|
||||
}
|
||||
toString() {return `?${this.pattern}`}
|
||||
}
|
||||
|
||||
export const Optional = item => new OptionalPattern(item)
|
||||
|
@ -158,6 +164,7 @@ class MultiPattern extends TypePattern {
|
|||
equal(other) {
|
||||
return super.equal(other) && this.pattern.equal(other.pattern)
|
||||
}
|
||||
toString() {return `${this.pattern}*`}
|
||||
}
|
||||
|
||||
export const Multiple = item => new MultiPattern(item)
|
||||
|
@ -170,6 +177,7 @@ class PassthruPattern extends TypePattern {
|
|||
return [typeSequence.length, typeSequence.slice(position)]
|
||||
}
|
||||
sampleTypes() {return []}
|
||||
toString() {return 'Passthru'}
|
||||
}
|
||||
|
||||
export const Passthru = new PassthruPattern()
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './arithmetic.js'
|
||||
export * from './relational.js'
|
||||
export * from './utils.js'
|
||||
|
|
6
src/coretypes/arithmetic.js
Normal file
6
src/coretypes/arithmetic.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import {Returns, Undefined} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {boolnum} from '#number/helpers.js'
|
||||
|
||||
export const isnan = match(Undefined, boolnum(() => true))
|
||||
export const negate = match(Undefined, Returns(Undefined, () => undefined))
|
|
@ -48,7 +48,7 @@ describe('generic relational functions', () => {
|
|||
assert.throws(() => compare(false, NaN), TypeError)
|
||||
assert(isNaN(compare(NaN, NaN)))
|
||||
assert.throws(() => compare(true, false), TypeError)
|
||||
assert.throws(() => compare(undefined, -1), ResolutionError)
|
||||
assert.throws(() => compare(math.types.BooleanT, -1), ResolutionError)
|
||||
})
|
||||
it('determines the sign of numeric values', () => {
|
||||
const {sign} = math
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * as arithmetic from './arithmetic.js'
|
||||
export * as logical from './logical.js'
|
||||
export * as relational from './relational.js'
|
||||
export * as utilities from './utils.js'
|
||||
|
|
40
src/generic/logical.js
Normal file
40
src/generic/logical.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import {ReturnsAs} from './helpers.js'
|
||||
|
||||
import {OneOf, Returns} from '#core/Type.js'
|
||||
import {Any, Multiple, match} from '#core/TypePatterns.js'
|
||||
import {boolnum} from '#number/helpers.js'
|
||||
|
||||
export const not = match(Any, (math, T) => {
|
||||
const bool = math.boolean.resolve(T)
|
||||
return ReturnsAs(bool, t => !bool(t))
|
||||
})
|
||||
|
||||
export const and = [
|
||||
match([], boolnum(() => true)),
|
||||
match(Any, (_math, T) => Returns(T, t => t)),
|
||||
match([Any, Any], (math, [T, U]) => {
|
||||
const bool = math.boolean.resolve(T)
|
||||
return Returns(OneOf(T, U), (t, u) => bool(t) ? u : t)
|
||||
}),
|
||||
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])
|
||||
return ReturnsAs(
|
||||
andFirst, (t, u, v, rest) => andFirst(t, andRest(u, v, ...rest)))
|
||||
})
|
||||
]
|
||||
|
||||
export const or = [
|
||||
match([], boolnum(() => false)),
|
||||
match(Any, (_math, T) => Returns(T, t => t)),
|
||||
match([Any, Any], (math, [T, U]) => {
|
||||
const bool = math.boolean.resolve(T)
|
||||
return Returns(OneOf(T, U), (t, u) => bool(t) ? t : u)
|
||||
}),
|
||||
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])
|
||||
return ReturnsAs(
|
||||
orFirst, (t, u, v, rest) => orFirst(t, orRest(u, v, ...rest)))
|
||||
})
|
||||
]
|
|
@ -82,35 +82,42 @@ export const sign = match(Any, (math, T) => {
|
|||
|
||||
export const unequal = match(Passthru, (math, types) => {
|
||||
const eq = math.equal.resolve(types)
|
||||
return ReturnsAs(eq, (...args) => !eq(...args))
|
||||
const not = math.not.resolve(eq.returns)
|
||||
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])
|
||||
return boolnum((t, u) => !eq(t, u) && bigger(t, u))(math)
|
||||
const not = math.not.resolve(eq.returns)
|
||||
const and = math.and.resolve([not.returns, bigger.returns])
|
||||
return ReturnsAs(and, (t, u) => and(not(eq(t, u)), bigger(t, u)))
|
||||
})
|
||||
|
||||
export const isPositive = match(Any, (math, T) => {
|
||||
const zero = math.zero(T)
|
||||
const larger = math.larger.resolve([T, T])
|
||||
return boolnum(t => larger(t, zero))
|
||||
return ReturnsAs(larger, t => larger(t, zero))
|
||||
})
|
||||
|
||||
export const largerEq = match([Any, Any], (math, [T, U]) => {
|
||||
const eq = math.equal.resolve([T, U])
|
||||
const bigger = math.exceeds.resolve([T, U])
|
||||
return ReturnsAs(bigger, (t, u) => eq(t, u) || bigger(t, u))
|
||||
const or = math.or.resolve([eq.returns, bigger.returns])
|
||||
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])
|
||||
return boolnum((t, u) => !eq(t, u) && bigger(u, t))(math)
|
||||
const not = math.not.resolve(eq.returns)
|
||||
const and = math.and.resolve([not.returns, bigger.returns])
|
||||
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])
|
||||
return ReturnsAs(bigger, (t, u) => eq(t, u) || bigger(u, t))
|
||||
const or = math.or.resolve([eq.returns, bigger.returns])
|
||||
return ReturnsAs(or, (t, u) => or(eq(t, u), bigger(u, t)))
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export * as typeDefinition from './NumberT.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'
|
||||
export * as utils from './utils.js'
|
||||
|
|
6
src/number/logical.js
Normal file
6
src/number/logical.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import {boolnum} from './helpers.js'
|
||||
import {NumberT} from './NumberT.js'
|
||||
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
|
||||
export const not = match(NumberT, boolnum(a => !a))
|
|
@ -1,7 +1,9 @@
|
|||
import {match, Optional} from '#core/TypePatterns.js'
|
||||
import {boolnum} from './helpers.js'
|
||||
import {NumberT} from './NumberT.js'
|
||||
|
||||
import {Undefined} from '#core/Type.js'
|
||||
import {match, Optional} from '#core/TypePatterns.js'
|
||||
|
||||
// In nanomath, we take the point of view that two comparators are primitive:
|
||||
// indistinguishable(a, b, relTol, absTol), and exceeds(a, b). All others
|
||||
// are defined generically in terms of these. They typically return BooleanT,
|
||||
|
@ -32,4 +34,9 @@ export const indistinguishable = match(
|
|||
// Returns truthy if a (interpreted as completely precise) represents a
|
||||
// greater value than b (interpreted as completely precise). Note that even if
|
||||
// so, a and b might be indistinguishable() to some tolerances.
|
||||
export const exceeds = match([NumberT, NumberT], boolnum((a, b) => a > b))
|
||||
export const exceeds = [
|
||||
match([NumberT, NumberT], boolnum((a, b) => a > b)),
|
||||
// Needed to allow comparison of vectors of different lengths:
|
||||
match([Undefined, NumberT], boolnum(() => false)),
|
||||
match([NumberT, Undefined], boolnum(() => false))
|
||||
]
|
||||
|
|
|
@ -31,4 +31,17 @@ describe('Vector relational functions', () => {
|
|||
assert.deepStrictEqual(
|
||||
eq([[1, 2], [3, 4]], [[1, 3], [2, 4]]), [[t, f], [f, t]])
|
||||
})
|
||||
it('performs order comparisons elementwise', () => {
|
||||
const pyth = [3, 4, 5]
|
||||
const ans = [-1, 0, 1]
|
||||
const powers = [2, 4, 8]
|
||||
assert.deepStrictEqual(math.compare(pyth, [5, 4, 3]), ans)
|
||||
assert.deepStrictEqual(math.sign([-Infinity, 0, 3]), ans)
|
||||
assert.deepStrictEqual(math.unequal(pyth, [5, 4 + 1e-14, 5]), [t, f, f])
|
||||
assert.deepStrictEqual(math.larger(pyth, powers), [t, f, f])
|
||||
assert.deepStrictEqual(math.isPositive(ans), [f, f, t])
|
||||
assert.deepStrictEqual(math.largerEq(pyth, powers), [t, t, f])
|
||||
assert.deepStrictEqual(math.smaller(pyth, powers), [f, f, t])
|
||||
assert.deepStrictEqual(math.smallerEq(pyth, powers), [f, t, t])
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * as typeDefinition from './Vector.js'
|
||||
export * as logical from './logical.js'
|
||||
export * as relational from './relational.js'
|
||||
export * as type from './type.js'
|
||||
export * as utilities from './utils.js'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Vector} from './Vector.js'
|
||||
import {NotAType, Returns} from '#core/Type.js'
|
||||
import {match} from '#core/TypePatterns.js'
|
||||
import {NotAType, Returns, 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) {
|
||||
|
@ -12,4 +12,68 @@ export const promoteUnary = name => match(Vector, (math, V, strategy) => {
|
|||
return Returns(Vector(compOp.returns), 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)
|
||||
return Returns(
|
||||
Vector(compOp.returns), (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)
|
||||
return Returns(
|
||||
Vector(compOp.returns, (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)
|
||||
}
|
||||
return Returns(
|
||||
ReturnType,
|
||||
(v, w) => {
|
||||
const vInc = Number(v.length > 1)
|
||||
const wInc = Number(w.length >= v.length || w.length > 1)
|
||||
const retval = []
|
||||
let vIx = 0
|
||||
let wIx = 0
|
||||
while ((vInc && vIx < v.length)
|
||||
|| (wInc && wIx < w.length)
|
||||
) {
|
||||
if (vIx >= v.length) {
|
||||
retval.push(opNoV(undefined, w[wIx]))
|
||||
} else if (wIx >= w.length) {
|
||||
retval.push(opNoW(v[vIx], undefined))
|
||||
} else retval.push(compOp(v[vIx], w[wIx]))
|
||||
vIx += vInc
|
||||
wIx += wInc
|
||||
}
|
||||
return retval
|
||||
})
|
||||
})
|
||||
]
|
||||
|
|
7
src/vector/logical.js
Normal file
7
src/vector/logical.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import {promoteBinary, promoteUnary} from './helpers.js'
|
||||
|
||||
export const not = promoteUnary('not')
|
||||
export const and = promoteBinary('and')
|
||||
export const or = promoteBinary('or')
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import {Vector} from './Vector.js'
|
||||
import {promoteBinary} from './helpers.js'
|
||||
|
||||
import {NotAType, Returns, Undefined} from '#core/Type.js'
|
||||
import {Any, Optional, match} from '#core/TypePatterns.js'
|
||||
import {BooleanT} from '#boolean/BooleanT.js'
|
||||
|
@ -23,11 +25,21 @@ 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)))
|
||||
}
|
||||
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),
|
||||
|
@ -38,11 +50,21 @@ 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)))
|
||||
}
|
||||
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),
|
||||
|
@ -51,10 +73,15 @@ export const indistinguishable = [
|
|||
match([Vector, Vector, Optional([Any, Any])], (math, [V, W, T]) => {
|
||||
const VComp = V.Component
|
||||
const WComp = W.Component
|
||||
const inhomogeneous = VComp === NotAType || WComp === NotAType
|
||||
let same
|
||||
let sameNoV
|
||||
let sameNoW
|
||||
if (T.length === 0) { // no tolerances
|
||||
if (inhomogeneous) {
|
||||
same = math.indistinguishable
|
||||
sameNoV = same
|
||||
sameNoW = same
|
||||
} else if (T.length === 0) { // no tolerances
|
||||
same = math.indistinguishable.resolve([VComp, WComp])
|
||||
sameNoV = math.indistinguishable.resolve([Undefined, WComp])
|
||||
sameNoW = math.indistinguishable.resolve([VComp, Undefined])
|
||||
|
@ -65,7 +92,7 @@ export const indistinguishable = [
|
|||
sameNoW = math.indistinguishable.resolve([VComp, Undefined, RT, AT])
|
||||
}
|
||||
return Returns(
|
||||
Vector(same.returns),
|
||||
inhomogeneous ? Vector(NotAType) : Vector(same.returns),
|
||||
(v, w, [tol = [0, 0]]) => {
|
||||
const [rT, aT] = tol
|
||||
const vInc = Number(v.length > 1)
|
||||
|
@ -73,7 +100,6 @@ export const indistinguishable = [
|
|||
const retval = []
|
||||
let vIx = 0
|
||||
let wIx = 0
|
||||
let remainder = vIx < v.length || wIx < w.length
|
||||
while ((vInc && vIx < v.length)
|
||||
|| (wInc && wIx < w.length)
|
||||
) {
|
||||
|
@ -89,3 +115,6 @@ export const indistinguishable = [
|
|||
})
|
||||
})
|
||||
]
|
||||
|
||||
export const compare = promoteBinary('compare')
|
||||
export const exceeds = promoteBinary('exceeds')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue