feat(polynomialRoot): Initial implementation (linear only)

This commit is contained in:
Glen Whitney 2022-11-26 17:28:19 -05:00
parent 31add66f4c
commit de52c041e5
4 changed files with 116 additions and 73 deletions

View File

@ -9,9 +9,11 @@ export {conjugate} from './conjugate.mjs'
export {equalTT} from './equalTT.mjs' export {equalTT} from './equalTT.mjs'
export {gcd} from './gcd.mjs' export {gcd} from './gcd.mjs'
export {invert} from './invert.mjs' export {invert} from './invert.mjs'
export {isReal} from './isReal.mjs'
export {isZero} from './isZero.mjs' export {isZero} from './isZero.mjs'
export {multiply} from './multiply.mjs' export {multiply} from './multiply.mjs'
export {negate} from './negate.mjs' export {negate} from './negate.mjs'
export {polynomialRoot} from './polynomialRoot.mjs'
export {quaternion} from './quaternion.mjs' export {quaternion} from './quaternion.mjs'
export {quotient} from './quotient.mjs' export {quotient} from './quotient.mjs'
export {roundquotient} from './roundquotient.mjs' export {roundquotient} from './roundquotient.mjs'

View File

@ -87,6 +87,7 @@ function substituteInSignature(signature, parameter, type) {
return sig.replaceAll(pattern, 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 let lastWhatToDo = null // used in an infinite descent check
export default class PocomathInstance { export default class PocomathInstance {
@ -128,12 +129,13 @@ export default class PocomathInstance {
// its onMismatch function, below: // its onMismatch function, below:
this._metaTyped = typed.create() this._metaTyped = typed.create()
this._metaTyped.clear() this._metaTyped.clear()
this._metaTyped.addTypes([{name: UniversalType, test: () => true}])
// And these are the meta bindings: (I think we don't need separate // 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.) // invalidation for them as they are only accessed through a main call.)
this._meta = {} // The resulting typed-functions this._meta = {} // The resulting typed-functions
this._metaTFimps = {} // and their implementations this._metaTFimps = {} // and their implementations
const me = this const me = this
const myTyped = this._typed
this._typed.onMismatch = (name, args, sigs) => { this._typed.onMismatch = (name, args, sigs) => {
if (me._invalid.has(name)) { if (me._invalid.has(name)) {
// rebuild implementation and try again // 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 // update the typeOf function
const imp = {} const imp = {}
imp[type] = {uses: new Set(), does: () => Returns('string', () => type)} 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: // install the "base type" in the meta universe:
let beforeType = 'any' let beforeType = UniversalType
for (const other of spec.before || []) { for (const other of spec.before || []) {
if (other in this.templates) { if (other in this.templates) {
beforeType = other beforeType = other
@ -648,6 +654,13 @@ export default class PocomathInstance {
} }
} }
this._metaTyped.addTypes([{name: base, test: spec.base}], beforeType) 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() this._instantiationsOf[base] = new Set()
// update the typeOf function // update the typeOf function
@ -881,7 +894,7 @@ export default class PocomathInstance {
basesig, theTemplateParam, '') basesig, theTemplateParam, '')
if (testsig === basesig) { if (testsig === basesig) {
// that is not also top-level // that is not also top-level
for (const templateType of typeListOfSignature(basesig)) { for (let templateType of typeListOfSignature(basesig)) {
if (templateType.slice(0,3) === '...') { if (templateType.slice(0,3) === '...') {
templateType = templateType.slice(3) templateType = templateType.slice(3)
} }
@ -908,7 +921,7 @@ export default class PocomathInstance {
let baseSignature = rawSignature.replaceAll(templateCall, '') let baseSignature = rawSignature.replaceAll(templateCall, '')
/* Any remaining template params are top-level */ /* Any remaining template params are top-level */
const signature = substituteInSignature( const signature = substituteInSignature(
baseSignature, theTemplateParam, 'any') baseSignature, theTemplateParam, UniversalType)
const hasTopLevel = (signature !== baseSignature) const hasTopLevel = (signature !== baseSignature)
if (!ubType && hasTopLevel) { if (!ubType && hasTopLevel) {
for (const othersig in imps) { for (const othersig in imps) {
@ -939,7 +952,6 @@ export default class PocomathInstance {
} }
} }
} }
for (const instType of instantiationSet) { for (const instType of instantiationSet) {
this._instantiateTemplateImplementation(name, rawSignature, instType) this._instantiateTemplateImplementation(name, rawSignature, instType)
} }
@ -1018,9 +1030,10 @@ export default class PocomathInstance {
usedConversions = true usedConversions = true
instantiateFor = self.joinTypes(argTypes, usedConversions) instantiateFor = self.joinTypes(argTypes, usedConversions)
if (instantiateFor === 'any') { if (instantiateFor === 'any') {
let argDisplay = args.map(toString).join(', ')
throw TypeError( throw TypeError(
`In call to ${name}, no type unifies arguments ` `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 ' + '; note each consecutive pair must unify to a '
+ 'supertype of at least one of them') + 'supertype of at least one of them')
} }
@ -1042,7 +1055,9 @@ export default class PocomathInstance {
for (j = 0; j < parTypes.length; ++j) { for (j = 0; j < parTypes.length; ++j) {
if (wantTypes[j] !== parTypes[j] && parTypes[j].includes('<')) { if (wantTypes[j] !== parTypes[j] && parTypes[j].includes('<')) {
// actually used the param and is a template // 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) { if (Object.keys(tf_imps).length > 0) {
tf = this._typed(name, tf_imps) tf = this._typed(name, tf_imps)
tf.fromInstance = this tf.fromInstance = this
tf.isMeta = false
} }
let metaTF let metaTF
if (Object.keys(meta_imps).length > 0) { if (Object.keys(meta_imps).length > 0) {
metaTF = this._metaTyped(name, meta_imps) metaTF = this._metaTyped(name, meta_imps)
metaTF.fromInstance = this metaTF.fromInstance = this
metaTF.isMeta = true
} }
this._meta[name] = metaTF this._meta[name] = metaTF
@ -1325,6 +1342,14 @@ export default class PocomathInstance {
if (destination && needsig) { if (destination && needsig) {
destination = this.resolve(func, 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 refs[dep] = destination
} }
} }
@ -1364,76 +1389,85 @@ export default class PocomathInstance {
} }
_correctPartialSelfRefs(name, imps) { _correctPartialSelfRefs(name, imps) {
for (const aSignature in imps) { let sawDeferral = true
if (!(imps[aSignature].deferred)) continue while (sawDeferral) {
const deferral = imps[aSignature] // We might generate some new partial self references in resolving
const part_self_references = deferral.psr // the previously existing ones, so looping through the signatures
const corrected_self_references = [] // once is not enough; we have to keep looping until there are no
const remaining_self_references = [] // more deferrals
const refs = deferral.builtRefs sawDeferral = false
for (const neededSig of part_self_references) { for (const aSignature in imps) {
// Have to find a match for neededSig among the other signatures if (!(imps[aSignature].deferred)) continue
// of this function. That's a job for typed-function, but we will sawDeferral = true
// try here: const deferral = imps[aSignature]
if (neededSig in imps) { // the easy case const part_self_references = deferral.psr
corrected_self_references.push(neededSig) const corrected_self_references = []
remaining_self_references.push(neededSig) const remaining_self_references = []
continue const refs = deferral.builtRefs
} for (const neededSig of part_self_references) {
// No exact match, try to get one that matches with // Have to find a match for neededSig among the other signatures
// subtypes since the whole conversion thing in typed-function // of this function. That's a job for typed-function, but we will
// is too complicated to reproduce // try here:
let foundSig = this._findSubtypeImpl(name, imps, neededSig) if (neededSig in imps) { // the easy case
if (foundSig) { corrected_self_references.push(neededSig)
corrected_self_references.push(foundSig) remaining_self_references.push(neededSig)
remaining_self_references.push(neededSig) continue
} else { }
// Maybe it's a template instance we don't yet have // No exact match, try to get one that matches with
foundSig = this._findSubtypeImpl( // subtypes since the whole conversion thing in typed-function
name, this._imps[name], neededSig) // is too complicated to reproduce
let foundSig = this._findSubtypeImpl(name, imps, neededSig)
if (foundSig) { if (foundSig) {
const match = this._pocoFindSignature(name, neededSig) corrected_self_references.push(foundSig)
const neededTemplate = match.fn._pocoSignature remaining_self_references.push(neededSig)
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 { } else {
throw new Error( // Maybe it's a template instance we don't yet have
'Implement inexact self-reference in typed-function for ' foundSig = this._findSubtypeImpl(
+ `${name}(${neededSig})`) 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
const does = deferral.sigDoes if (remaining_self_references.length > 0) {
if (remaining_self_references.length > 0) { imps[aSignature] = this._typed.referTo(
imps[aSignature] = this._typed.referTo( ...corrected_self_references, (...impls) => {
...corrected_self_references, (...impls) => { for (let i = 0; i < remaining_self_references.length; ++i) {
for (let i = 0; i < remaining_self_references.length; ++i) { refs[`self(${remaining_self_references[i]})`] = impls[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? } else {
return implementation imps[aSignature] = does(refs)
} }
) imps[aSignature]._pocoSignature = deferral._pocoSignature
} else { imps[aSignature]._pocoInstance = deferral._pocoInstance
imps[aSignature] = does(refs) 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) const otherTypeList = typeListOfSignature(otherSig)
if (typeList.length !== otherTypeList.length) continue if (typeList.length !== otherTypeList.length) continue
let allMatch = true let allMatch = true
let paramBound = 'any' let paramBound = UniversalType
for (let k = 0; k < typeList.length; ++k) { for (let k = 0; k < typeList.length; ++k) {
let myType = typeList[k] let myType = typeList[k]
let otherType = otherTypeList[k] let otherType = otherTypeList[k]
@ -1577,6 +1611,7 @@ export default class PocomathInstance {
theTemplateParam, paramBound) theTemplateParam, paramBound)
} }
if (otherType === 'any') continue if (otherType === 'any') continue
if (otherType === UniversalType) continue
if (myType === otherType) continue if (myType === otherType) continue
if (otherType in this.Templates) { if (otherType in this.Templates) {
const [myBase] = splitTemplate(myType) const [myBase] = splitTemplate(myType)
@ -1608,6 +1643,7 @@ export default class PocomathInstance {
typedFunction = this[name] typedFunction = this[name]
} }
const haveTF = this._typed.isTypedFunction(typedFunction) const haveTF = this._typed.isTypedFunction(typedFunction)
&& !(typedFunction.isMeta)
if (haveTF) { if (haveTF) {
// First try a direct match // First try a direct match
let result let result

View File

@ -6,5 +6,5 @@ export {Tuple} from './Types/Tuple.mjs'
* are convertible to the same type. * are convertible to the same type.
*/ */
export const tuple = { export const tuple = {
'...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args})) '...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args}))
} }

View File

@ -45,6 +45,11 @@ describe('complex', () => {
assert.ok(!(math.equal(math.complex(45n, 3n), 45n))) 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', () => { it('computes gcd', () => {
assert.deepStrictEqual( assert.deepStrictEqual(
math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)), math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)),