diff --git a/src/coretypes/relational.js b/src/coretypes/relational.js index a45cadf..896d7c8 100644 --- a/src/coretypes/relational.js +++ b/src/coretypes/relational.js @@ -1,8 +1,14 @@ import {TypeOfTypes, Undefined} from '#core/Type.js' -import {match} from '#core/TypePatterns.js' +import {Any, Multiple, match} from '#core/TypePatterns.js' import {boolnum} from '#number/helpers.js' export const indistinguishable = [ - match([Undefined, Undefined], boolnum(() => true)), - match([TypeOfTypes, TypeOfTypes], boolnum((t, u) => t === u)) + // I don't think there's any other type that should be indistinguishable + // from undefined: + match([Undefined, Any, Multiple(Any)], boolnum(() => false)), + match([Any, Undefined, Multiple(Any)], boolnum(() => false)), + match([Undefined, Undefined, Multiple(Any)], boolnum(() => true)), + match( + [TypeOfTypes, TypeOfTypes, Multiple(Any)], + boolnum((t, u) => t === u)) ] diff --git a/src/vector/__test__/relational.spec.js b/src/vector/__test__/relational.spec.js index 308ec80..3637b86 100644 --- a/src/vector/__test__/relational.spec.js +++ b/src/vector/__test__/relational.spec.js @@ -1,16 +1,34 @@ import assert from 'assert' import math from '#nanomath' +const t = true +const f = false describe('Vector relational functions', () => { it('can test two vectors for deep equality', () => { const dEq = math.deepEqual const pyth = [3, 4, 5] - assert.strictEqual(dEq(pyth, [3, 4, 5]), true) - assert.strictEqual(dEq(pyth, [3, 4, 6]), false) - assert.strictEqual(dEq(pyth, [3, 4, [5]]), false) - assert.strictEqual(dEq(3, 3 + 1e-13), true) - assert.strictEqual(dEq(pyth, [3+1e-13, 4-1e-13, 5]), true) - assert.strictEqual(dEq([pyth, pyth], [pyth, [3, 4, 5]]), true) - assert.strictEqual(dEq([3], 3), false) + assert.strictEqual(dEq(pyth, [3, 4, 5]), t) + assert.strictEqual(dEq(pyth, [3, 4, 6]), f) + assert.strictEqual(dEq(pyth, [3, 4, [5]]), f) + assert.strictEqual(dEq(3, 3 + 1e-13), t) + assert.strictEqual(dEq(pyth, [3+1e-13, 4-1e-13, 5]), t) + assert.strictEqual(dEq([pyth, pyth], [pyth, [3, 4, 5]]), t) + assert.strictEqual(dEq([3], 3), f) + }) + it('performs equal elementwise', () => { + const eq = math.equal + const pyth = [3, 4, 5] + assert.deepStrictEqual(eq(pyth, 4), [f, t, f]) + assert.deepStrictEqual(eq(5, pyth), [f, f, t]) + assert.deepStrictEqual(eq(pyth, pyth), [t, t, t]) + assert.deepStrictEqual(eq(pyth, [3]), [t, f, f]) + assert.deepStrictEqual(eq([4 + 1e-14], pyth), [f, t, f]) + assert.deepStrictEqual(eq(pyth, [3, 4]), [t, t, f]) + assert.deepStrictEqual(eq([3, 2], pyth), [t, f, f]) + assert.deepStrictEqual(eq([pyth, pyth], [3, 4]), [[t, f, f], [f, t, f]]) + assert.deepStrictEqual( + eq([pyth, pyth], [[5], pyth]), [[f, f, t], [t, t, t]]) + assert.deepStrictEqual( + eq([[1, 2], [3, 4]], [[1, 3], [2, 4]]), [[t, f], [f, t]]) }) }) diff --git a/src/vector/relational.js b/src/vector/relational.js index 11272ed..23e5835 100644 --- a/src/vector/relational.js +++ b/src/vector/relational.js @@ -1,6 +1,6 @@ import {Vector} from './Vector.js' -import {NotAType, Returns} from '#core/Type.js' -import {Any, match} from '#core/TypePatterns.js' +import {NotAType, Returns, Undefined} from '#core/Type.js' +import {Any, Optional, match} from '#core/TypePatterns.js' import {BooleanT} from '#boolean/BooleanT.js' export const deepEqual = [ @@ -18,3 +18,74 @@ export const deepEqual = [ && v.every((e, i) => compDeep(e, w[i])))) }) ] + +export const indistinguishable = [ + match([Vector, Any, Optional([Any, Any])], (math, [V, E, T]) => { + const VComp = V.Component + if (T.length === 0) { // no tolerances + const same = math.indistinguishable.resolve([VComp, E]) + return Returns( + Vector(same.returns), (v, e) => v.map(f => same(f, e))) + } + const [[RT, AT]] = T + const same = math.indistinguishable.resolve([VComp, E, RT, AT]) + return Returns( + Vector(same.returns), + (v, e, [[rT, aT]]) => v.map(f => same(f, e, rT, aT))) + }), + match([Any, Vector, Optional([Any, Any])], (math, [E, V, T]) => { + // reimplement to get other order in same so as not to assume + // same is symmetric, even though it probably is + const VComp = V.Component + if (T.length === 0) { // no tolerances + const same = math.indistinguishable.resolve([E, VComp]) + return Returns( + Vector(same.returns), (e, v) => v.map(f => same(e, f))) + } + const [[RT, AT]] = T + const same = math.indistinguishable.resolve([E, VComp, RT, AT]) + return Returns( + Vector(same.returns), + (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 + let same + let sameNoV + let sameNoW + if (T.length === 0) { // no tolerances + same = math.indistinguishable.resolve([VComp, WComp]) + sameNoV = math.indistinguishable.resolve([Undefined, WComp]) + sameNoW = math.indistinguishable.resolve([VComp, Undefined]) + } else { + const [[RT, AT]] = T + same = math.indistinguishable.resolve([VComp, WComp, RT, AT]) + sameNoV = math.indistinguishable.resolve([Undefined, WComp, RT, AT]) + sameNoW = math.indistinguishable.resolve([VComp, Undefined, RT, AT]) + } + return Returns( + Vector(same.returns), + (v, w, [tol = [0, 0]]) => { + const [rT, aT] = tol + const vInc = Number(v.length > 1) + const wInc = Number(w.length >= v.length || w.length > 1) + 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) + ) { + if (vIx >= v.length) { + retval.push(sameNoV(undefined, w[wIx], rT, aT)) + } else if (wIx >= w.length) { + retval.push(sameNoW(v[vIx], undefined, rT, aT)) + } else retval.push(same(v[vIx], w[wIx], rT, aT)) + vIx += vInc + wIx += wInc + } + return retval + }) + }) +]