From de52c041e5ab25f37025e9ebdc4232fbc61349a1 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 26 Nov 2022 17:28:19 -0500 Subject: [PATCH] feat(polynomialRoot): Initial implementation (linear only) --- src/complex/native.mjs | 2 + src/core/PocomathInstance.mjs | 180 ++++++++++++++++++++-------------- src/tuple/tuple.mjs | 2 +- test/complex/_all.mjs | 5 + 4 files changed, 116 insertions(+), 73 deletions(-) diff --git a/src/complex/native.mjs b/src/complex/native.mjs index 93c26e4..db81c53 100644 --- a/src/complex/native.mjs +++ b/src/complex/native.mjs @@ -9,9 +9,11 @@ export {conjugate} from './conjugate.mjs' export {equalTT} from './equalTT.mjs' export {gcd} from './gcd.mjs' export {invert} from './invert.mjs' +export {isReal} from './isReal.mjs' export {isZero} from './isZero.mjs' export {multiply} from './multiply.mjs' export {negate} from './negate.mjs' +export {polynomialRoot} from './polynomialRoot.mjs' export {quaternion} from './quaternion.mjs' export {quotient} from './quotient.mjs' export {roundquotient} from './roundquotient.mjs' diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index 4b46752..17b65ae 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -87,6 +87,7 @@ function substituteInSignature(signature, parameter, type) { return sig.replaceAll(pattern, type) } +const UniversalType = 'ground' // name for a type that matches anything let lastWhatToDo = null // used in an infinite descent check export default class PocomathInstance { @@ -128,12 +129,13 @@ export default class PocomathInstance { // its onMismatch function, below: this._metaTyped = typed.create() this._metaTyped.clear() + this._metaTyped.addTypes([{name: UniversalType, test: () => true}]) + // And these are the meta bindings: (I think we don't need separate // invalidation for them as they are only accessed through a main call.) this._meta = {} // The resulting typed-functions this._metaTFimps = {} // and their implementations const me = this - const myTyped = this._typed this._typed.onMismatch = (name, args, sigs) => { if (me._invalid.has(name)) { // rebuild implementation and try again @@ -523,6 +525,10 @@ export default class PocomathInstance { } } } + // Need to metafy ground types + if (type === base) { + this._metafy(type) + } // update the typeOf function const imp = {} imp[type] = {uses: new Set(), does: () => Returns('string', () => type)} @@ -640,7 +646,7 @@ export default class PocomathInstance { } // install the "base type" in the meta universe: - let beforeType = 'any' + let beforeType = UniversalType for (const other of spec.before || []) { if (other in this.templates) { beforeType = other @@ -648,6 +654,13 @@ export default class PocomathInstance { } } this._metaTyped.addTypes([{name: base, test: spec.base}], beforeType) + // Add conversions to the base type: + if (spec.from && spec.from[theTemplateParam]) { + for (const ground of this._metafiedTypes) { + this._metaTyped.addConversion( + {from: ground, to: base, convert: spec.from[theTemplateParam]}) + } + } this._instantiationsOf[base] = new Set() // update the typeOf function @@ -881,7 +894,7 @@ export default class PocomathInstance { basesig, theTemplateParam, '') if (testsig === basesig) { // that is not also top-level - for (const templateType of typeListOfSignature(basesig)) { + for (let templateType of typeListOfSignature(basesig)) { if (templateType.slice(0,3) === '...') { templateType = templateType.slice(3) } @@ -908,7 +921,7 @@ export default class PocomathInstance { let baseSignature = rawSignature.replaceAll(templateCall, '') /* Any remaining template params are top-level */ const signature = substituteInSignature( - baseSignature, theTemplateParam, 'any') + baseSignature, theTemplateParam, UniversalType) const hasTopLevel = (signature !== baseSignature) if (!ubType && hasTopLevel) { for (const othersig in imps) { @@ -939,7 +952,6 @@ export default class PocomathInstance { } } } - for (const instType of instantiationSet) { this._instantiateTemplateImplementation(name, rawSignature, instType) } @@ -1018,9 +1030,10 @@ export default class PocomathInstance { usedConversions = true instantiateFor = self.joinTypes(argTypes, usedConversions) if (instantiateFor === 'any') { + let argDisplay = args.map(toString).join(', ') throw TypeError( `In call to ${name}, no type unifies arguments ` - + args.toString() + '; of types ' + argTypes.toString() + + argDisplay + '; of types ' + argTypes.toString() + '; note each consecutive pair must unify to a ' + 'supertype of at least one of them') } @@ -1042,7 +1055,9 @@ export default class PocomathInstance { for (j = 0; j < parTypes.length; ++j) { if (wantTypes[j] !== parTypes[j] && parTypes[j].includes('<')) { // actually used the param and is a template - self._ensureTemplateTypes(parTypes[j], instantiateFor) + const strippedType = parTypes[j].substr( + parTypes[j].lastIndexOf('.') + 1) + self._ensureTemplateTypes(strippedType, instantiateFor) } } @@ -1117,11 +1132,13 @@ export default class PocomathInstance { if (Object.keys(tf_imps).length > 0) { tf = this._typed(name, tf_imps) tf.fromInstance = this + tf.isMeta = false } let metaTF if (Object.keys(meta_imps).length > 0) { metaTF = this._metaTyped(name, meta_imps) metaTF.fromInstance = this + metaTF.isMeta = true } this._meta[name] = metaTF @@ -1325,6 +1342,14 @@ export default class PocomathInstance { if (destination && needsig) { destination = this.resolve(func, needsig) } + if (!destination) { + // Unresolved reference. This is allowed so that + // you can bundle up just some portions of the library, + // but let's warn. + console.log( + 'WARNING: No definition found for dependency', + dep, 'needed by a function with signature', signature) + } refs[dep] = destination } } @@ -1364,76 +1389,85 @@ export default class PocomathInstance { } _correctPartialSelfRefs(name, imps) { - for (const aSignature in imps) { - if (!(imps[aSignature].deferred)) continue - const deferral = imps[aSignature] - const part_self_references = deferral.psr - const corrected_self_references = [] - const remaining_self_references = [] - const refs = deferral.builtRefs - for (const neededSig of part_self_references) { - // Have to find a match for neededSig among the other signatures - // of this function. That's a job for typed-function, but we will - // try here: - if (neededSig in imps) { // the easy case - corrected_self_references.push(neededSig) - remaining_self_references.push(neededSig) - continue - } - // No exact match, try to get one that matches with - // subtypes since the whole conversion thing in typed-function - // is too complicated to reproduce - let foundSig = this._findSubtypeImpl(name, imps, neededSig) - if (foundSig) { - corrected_self_references.push(foundSig) - remaining_self_references.push(neededSig) - } else { - // Maybe it's a template instance we don't yet have - foundSig = this._findSubtypeImpl( - name, this._imps[name], neededSig) + let sawDeferral = true + while (sawDeferral) { + // We might generate some new partial self references in resolving + // the previously existing ones, so looping through the signatures + // once is not enough; we have to keep looping until there are no + // more deferrals + sawDeferral = false + for (const aSignature in imps) { + if (!(imps[aSignature].deferred)) continue + sawDeferral = true + const deferral = imps[aSignature] + const part_self_references = deferral.psr + const corrected_self_references = [] + const remaining_self_references = [] + const refs = deferral.builtRefs + for (const neededSig of part_self_references) { + // Have to find a match for neededSig among the other signatures + // of this function. That's a job for typed-function, but we will + // try here: + if (neededSig in imps) { // the easy case + corrected_self_references.push(neededSig) + remaining_self_references.push(neededSig) + continue + } + // No exact match, try to get one that matches with + // subtypes since the whole conversion thing in typed-function + // is too complicated to reproduce + let foundSig = this._findSubtypeImpl(name, imps, neededSig) if (foundSig) { - const match = this._pocoFindSignature(name, neededSig) - const neededTemplate = match.fn._pocoSignature - const neededInstance = whichSigInstance( - neededSig, neededTemplate) - const neededImplementation = - this._instantiateTemplateImplementation( - name, neededTemplate, neededInstance) - if (!neededImplementation) { - refs[`self(${neededSig})`] = match.implementation - } else { - if (typeof neededImplementation === 'function') { - refs[`self(${neededSig})`] = neededImplementation - } else { - corrected_self_references.push(neededSig) - remaining_self_references.push(neededSig) - } - } + corrected_self_references.push(foundSig) + remaining_self_references.push(neededSig) } else { - throw new Error( - 'Implement inexact self-reference in typed-function for ' - + `${name}(${neededSig})`) + // Maybe it's a template instance we don't yet have + foundSig = this._findSubtypeImpl( + name, this._imps[name], neededSig) + if (foundSig) { + const match = this._pocoFindSignature(name, neededSig) + const neededTemplate = match.fn._pocoSignature + const neededInstance = whichSigInstance( + neededSig, neededTemplate) + const neededImplementation = + this._instantiateTemplateImplementation( + name, neededTemplate, neededInstance) + if (!neededImplementation) { + refs[`self(${neededSig})`] = match.implementation + } else { + if (typeof neededImplementation === 'function') { + refs[`self(${neededSig})`] = neededImplementation + } else { + corrected_self_references.push(neededSig) + remaining_self_references.push(neededSig) + } + } + } else { + throw new Error( + 'Implement inexact self-reference in typed-function for ' + + `${name}(${neededSig})`) + } } } - } - const does = deferral.sigDoes - if (remaining_self_references.length > 0) { - imps[aSignature] = this._typed.referTo( - ...corrected_self_references, (...impls) => { - for (let i = 0; i < remaining_self_references.length; ++i) { - refs[`self(${remaining_self_references[i]})`] = impls[i] + const does = deferral.sigDoes + if (remaining_self_references.length > 0) { + imps[aSignature] = this._typed.referTo( + ...corrected_self_references, (...impls) => { + for (let i = 0; i < remaining_self_references.length; ++i) { + refs[`self(${remaining_self_references[i]})`] = impls[i] + } + const implementation = does(refs) + // What will we do with the return type info in here? + return implementation } - const implementation = does(refs) - // What will we do with the return type info in here? - return implementation - } - ) - } else { - imps[aSignature] = does(refs) + ) + } else { + imps[aSignature] = does(refs) + } + imps[aSignature]._pocoSignature = deferral._pocoSignature + imps[aSignature]._pocoInstance = deferral._pocoInstance + imps[aSignature].fromInstance = deferral.fromInstance } - imps[aSignature]._pocoSignature = deferral._pocoSignature - imps[aSignature]._pocoInstance = deferral._pocoInstance - imps[aSignature].fromInstance = deferral.fromInstance } } @@ -1550,7 +1584,7 @@ export default class PocomathInstance { const otherTypeList = typeListOfSignature(otherSig) if (typeList.length !== otherTypeList.length) continue let allMatch = true - let paramBound = 'any' + let paramBound = UniversalType for (let k = 0; k < typeList.length; ++k) { let myType = typeList[k] let otherType = otherTypeList[k] @@ -1577,6 +1611,7 @@ export default class PocomathInstance { theTemplateParam, paramBound) } if (otherType === 'any') continue + if (otherType === UniversalType) continue if (myType === otherType) continue if (otherType in this.Templates) { const [myBase] = splitTemplate(myType) @@ -1608,6 +1643,7 @@ export default class PocomathInstance { typedFunction = this[name] } const haveTF = this._typed.isTypedFunction(typedFunction) + && !(typedFunction.isMeta) if (haveTF) { // First try a direct match let result diff --git a/src/tuple/tuple.mjs b/src/tuple/tuple.mjs index 9cd0c65..8467176 100644 --- a/src/tuple/tuple.mjs +++ b/src/tuple/tuple.mjs @@ -6,5 +6,5 @@ export {Tuple} from './Types/Tuple.mjs' * are convertible to the same type. */ export const tuple = { - '...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args})) + '...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args})) } diff --git a/test/complex/_all.mjs b/test/complex/_all.mjs index 413503c..a14872e 100644 --- a/test/complex/_all.mjs +++ b/test/complex/_all.mjs @@ -45,6 +45,11 @@ describe('complex', () => { assert.ok(!(math.equal(math.complex(45n, 3n), 45n))) }) + it('tests for reality', () => { + assert.ok(math.isReal(math.complex(3, 0))) + assert.ok(!(math.isReal(math.complex(3, 2)))) + }) + it('computes gcd', () => { assert.deepStrictEqual( math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)),