diff --git a/src/bigint/absquare.mjs b/src/bigint/absquare.mjs new file mode 100644 index 0000000..4c2040a --- /dev/null +++ b/src/bigint/absquare.mjs @@ -0,0 +1,6 @@ +export * from './Types/bigint.mjs' + +/* Absolute value squared */ +export const absquare = { + bigint: ({'square(bigint)': sqb}) => b => sqb(b) +} diff --git a/src/bigint/native.mjs b/src/bigint/native.mjs index 6458912..6cc76d4 100644 --- a/src/bigint/native.mjs +++ b/src/bigint/native.mjs @@ -3,6 +3,7 @@ import {identity} from '../generic/identity.mjs' export * from './Types/bigint.mjs' +export {absquare} from './absquare.mjs' export {add} from './add.mjs' export {compare} from './compare.mjs' export const conjugate = {bigint: () => identity} diff --git a/src/complex/abs.mjs b/src/complex/abs.mjs index 47fc88d..536b8b4 100644 --- a/src/complex/abs.mjs +++ b/src/complex/abs.mjs @@ -1,8 +1,10 @@ export * from './Types/Complex.mjs' export const abs = { - 'Complex': ({ - 'sqrt(T)': sqt, - 'absquare(Complex)': absq - }) => z => sqt(absq(z)) + 'Complex': ({ + sqrt, // Calculation of the type needed in the square root (the + // underlying numeric type of T, whatever T is, is beyond Pocomath's + // (current) template abilities, so punt and just do full resolution + 'absquare(Complex)': absq + }) => z => sqrt(absq(z)) } diff --git a/src/complex/absquare.mjs b/src/complex/absquare.mjs index 913124b..bb4677f 100644 --- a/src/complex/absquare.mjs +++ b/src/complex/absquare.mjs @@ -2,7 +2,8 @@ export * from './Types/Complex.mjs' export const absquare = { 'Complex': ({ - 'add(T,T)': plus, - 'square(T)': sqr - }) => z => plus(sqr(z.re), sqr(z.im)) + add, // Calculation of exact type needed in add (underlying numeric of T) + // is (currently) too involved for Pocomath + 'self(T)': absq + }) => z => add(absq(z.re), absq(z.im)) } diff --git a/src/complex/native.mjs b/src/complex/native.mjs index 420ce88..93c26e4 100644 --- a/src/complex/native.mjs +++ b/src/complex/native.mjs @@ -12,6 +12,7 @@ export {invert} from './invert.mjs' export {isZero} from './isZero.mjs' export {multiply} from './multiply.mjs' export {negate} from './negate.mjs' +export {quaternion} from './quaternion.mjs' export {quotient} from './quotient.mjs' export {roundquotient} from './roundquotient.mjs' export {sqrt} from './sqrt.mjs' diff --git a/src/complex/quaternion.mjs b/src/complex/quaternion.mjs new file mode 100644 index 0000000..4f35d30 --- /dev/null +++ b/src/complex/quaternion.mjs @@ -0,0 +1,5 @@ +export * from './Types/Complex.mjs' + +export const quaternion = { + 'T,T,T,T': ({complex}) => (r,i,j,k) => complex(complex(r,j), complex(i,k)) +} diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index b01c41f..bb013d1 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -64,6 +64,7 @@ export default class PocomathInstance { */ this._priorTypes = {} this._seenTypes = new Set() // all types that have occurred in a signature + this._maxDepthSeen = 1 // deepest template nesting we've actually encountered this._invalid = new Set() // methods that are currently invalid this._config = {predictable: false, epsilon: 1e-12} const self = this @@ -336,6 +337,7 @@ export default class PocomathInstance { let nextSuper = type while (nextSuper) { if (this._priorTypes[nextSuper].has(from)) break + if (from === nextSuper) break this._typed.addConversion( {from, to: nextSuper, convert: spec.from[from]}) this._invalidateDependents(':' + nextSuper) @@ -360,12 +362,16 @@ export default class PocomathInstance { } let nextSuper = to while (nextSuper) { - this._typed.addConversion({ - from: type, - to: nextSuper, - convert: this.Types[to].from[fromtype] - }) - this._invalidateDependents(':' + nextSuper) + if (type === nextSuper) break + try { // may already be a conversion, and no way to ask + this._typed.addConversion({ + from: type, + to: nextSuper, + convert: this.Types[to].from[fromtype] + }) + this._invalidateDependents(':' + nextSuper) + } catch { + } this._priorTypes[nextSuper].add(type) nextSuper = this.Types[nextSuper].refines } @@ -609,6 +615,14 @@ export default class PocomathInstance { substituteInSig(rawSignature, theTemplateParam, instType) /* Don't override an explicit implementation: */ if (signature in imps) continue + /* Don't go too deep */ + let maxdepth = 0 + for (const argType in typeListOfSignature(signature)) { + const depth = argType.split('<').length + if (depth > maxdepth) maxdepth = depth + } + if (maxdepth > this._maxDepthSeen + 1) continue + /* All right, go ahead and instantiate */ const uses = new Set() for (const dep of behavior.uses) { if (this._templateParam(dep)) continue @@ -718,6 +732,10 @@ export default class PocomathInstance { + 'supertype of at least one of them') } } + const depth = instantiateFor.split('<').length + if (depth > self._maxDepthSeen) { + self._maxDepthSeen = depth + } /* Generate the list of actual wanted types */ const wantTypes = parTypes.map(type => substituteInSig( type, theTemplateParam, instantiateFor)) @@ -790,6 +808,28 @@ export default class PocomathInstance { tf_imps, signature, {uses: outerUses, does: patch}) } this._correctPartialSelfRefs(name, tf_imps) + // Make sure we have all of the needed (template) types; and if they + // can't be added (because they have been instantiated too deep), + // ditch the signature: + const badSigs = new Set() + for (const sig in tf_imps) { + for (const type of typeListOfSignature(sig)) { + if (type.includes('<')) { + // it's a template type, turn it into a template and an arg + let base = type.split('<',1)[0] + const arg = type.slice(base.length+1, -1) + if (base.slice(0,3) === '...') { + base = base.slice(3) + } + if (this.instantiateTemplate(base, arg) === undefined) { + badSigs.add(sig) + } + } + } + } + for (const badSig of badSigs) { + delete tf_imps[badSig] + } const tf = this._typed(name, tf_imps) Object.defineProperty(this, name, {configurable: true, value: tf}) return tf @@ -928,8 +968,8 @@ export default class PocomathInstance { * in the instance. */ _ensureTemplateTypes(template, type) { - let [base, arg] = template.split('<', 2) - arg = arg.slice(0,-1) + const base = template.split('<', 1)[0] + const arg = template.slice(base.length + 1, -1) if (!arg) { throw new Error( 'Implementation error in _ensureTemplateTypes', template, type) @@ -951,9 +991,15 @@ export default class PocomathInstance { /* Maybe add the instantiation of template type base with argument tyoe * instantiator to the Types of this instance, if it hasn't happened already. - * Returns the name of the type if added, false otherwise. + * Returns the name of the type if added, false if it was already there, + * and undefined if the type is declined (because of being nested too deep). */ instantiateTemplate(base, instantiator) { + const depth = instantiator.split('<').length + if (depth > this._maxDepthSeen ) { + // don't bother with types much deeper thant we have seen + return undefined + } const wantsType = `${base}<${instantiator}>` if (wantsType in this.Types) return false // OK, need to generate the type from the template diff --git a/src/number/absquare.mjs b/src/number/absquare.mjs new file mode 100644 index 0000000..d6ab55a --- /dev/null +++ b/src/number/absquare.mjs @@ -0,0 +1,6 @@ +export * from './Types/number.mjs' + +/* Absolute value squared */ +export const absquare = { + number: ({'square(number)': sqn}) => n => sqn(n) +} diff --git a/src/number/native.mjs b/src/number/native.mjs index d095574..6746408 100644 --- a/src/number/native.mjs +++ b/src/number/native.mjs @@ -4,6 +4,7 @@ import {identity} from '../generic/identity.mjs' export * from './Types/number.mjs' export {abs} from './abs.mjs' +export {absquare} from './absquare.mjs' export {add} from './add.mjs' export {compare} from './compare.mjs' export const conjugate = {number: () => identity} diff --git a/test/complex/_all.mjs b/test/complex/_all.mjs index 801aa99..531a28d 100644 --- a/test/complex/_all.mjs +++ b/test/complex/_all.mjs @@ -68,4 +68,23 @@ describe('complex', () => { assert.strictEqual(math.floor(gi), gi) // literally a no-op }) + it('performs rudimentary quaternion calculations', () => { + const q0 = math.quaternion(1, 0, 1, 0) + const q1 = math.quaternion(1, 0.5, 0.5, 0.75) + assert.deepStrictEqual( + q1, + math.complex(math.complex(1, 0.5), math.complex(0.5, 0.75))) + assert.deepStrictEqual( + math.add(q0,q1), + math.quaternion(2, 0.5, 1.5, 0.75)) + assert.deepStrictEqual( + math.multiply(q0, q1), + math.quaternion(0.5, 1.25, 1.5, 0.25)) + assert.deepStrictEqual( + math.multiply(q0, math.quaternion(2, 1, 0.1, 0.1)), + math.quaternion(1.9, 1.1, 2.1, -0.9)) + assert.strictEqual(math.abs(q0), Math.sqrt(2)) + assert.strictEqual(math.abs(q1), Math.sqrt(33)/4) + }) + })