feat(polynomialRoot): Progress commit, quadratic implemented
This commit is contained in:
parent
de52c041e5
commit
c4b1ac9045
7
src/complex/isReal.mjs
Normal file
7
src/complex/isReal.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const isReal = {
|
||||||
|
'Complex<T>': ({'equal(T,T)': eq, 'add(T,T)': plus}) => Returns(
|
||||||
|
'boolean', z => eq(z.re, plus(z.re, z.im)))
|
||||||
|
}
|
@ -18,5 +18,6 @@ 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'
|
||||||
export {sqrt} from './sqrt.mjs'
|
export {sqrt} from './sqrt.mjs'
|
||||||
|
export {sqrtc} from './sqrtc.mjs'
|
||||||
export {zero} from './zero.mjs'
|
export {zero} from './zero.mjs'
|
||||||
|
|
||||||
|
65
src/complex/polynomialRoot.mjs
Normal file
65
src/complex/polynomialRoot.mjs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const polynomialRoot = {
|
||||||
|
'Complex<T>,...Complex<T>': ({
|
||||||
|
T,
|
||||||
|
'tuple(...Complex<T>)': tupCplx,
|
||||||
|
'tuple(...T)': tupReal,
|
||||||
|
'isZero(Complex<T>)': zero,
|
||||||
|
'complex(T)': C,
|
||||||
|
'multiply(Complex<T>,Complex<T>)': mul,
|
||||||
|
'divide(Complex<T>,Complex<T>)': div,
|
||||||
|
'negate(Complex<T>)': neg,
|
||||||
|
'isReal(Complex<T>)': real,
|
||||||
|
'equalTT(Complex<T>, Complex<T>)': eq,
|
||||||
|
'subtract(Complex<T>, Complex<T>)': sub,
|
||||||
|
'sqrtc(Complex<T>)': sqt,
|
||||||
|
}) => Returns(`Tuple<${T}>|Tuple<Complex<${T}>>`, (constant, rest) => {
|
||||||
|
// helper to convert results to appropriate tuple type
|
||||||
|
const typedTup = arr => {
|
||||||
|
if (arr.every(real)) {
|
||||||
|
return tupReal.apply(tupReal, arr.map(z => z.re))
|
||||||
|
}
|
||||||
|
return tupCplx.apply(tupCplx, arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
const coeffs = [constant, ...rest]
|
||||||
|
while (coeffs.length > 0 && zero(coeffs[coeffs.length - 1])) {
|
||||||
|
coeffs.pop()
|
||||||
|
}
|
||||||
|
if (coeffs.length < 2) {
|
||||||
|
}
|
||||||
|
switch (coeffs.length) {
|
||||||
|
case 0: case 1:
|
||||||
|
throw new RangeError(
|
||||||
|
`Polynomial [${constant}, ${rest}] must have at least one`
|
||||||
|
+ 'non-zero non-constant coefficient')
|
||||||
|
case 2: // linear
|
||||||
|
return typedTup([neg(div(coeffs[0], coeffs[1]))])
|
||||||
|
case 3: { // quadratic
|
||||||
|
const [c, b, a] = coeffs
|
||||||
|
console.log('solving', a, b, c)
|
||||||
|
const denom = mul(C(2), a)
|
||||||
|
const d1 = mul(b, b)
|
||||||
|
const d2 = mul(C(4), mul(a, c))
|
||||||
|
console.log('Whoa', denom, d1, d2)
|
||||||
|
if (eq(d1, d2)) {
|
||||||
|
console.log('Hello', b, denom, div(neg(b), denom))
|
||||||
|
return typedTup([div(neg(b), denom)])
|
||||||
|
}
|
||||||
|
let discriminant = sqt(sub(d1, d2))
|
||||||
|
console.log('Uhoh', discriminant)
|
||||||
|
console.log('Roots', div(sub(discriminant, b), denom), div(sub(neg(discriminant), b), denom))
|
||||||
|
return typedTup([
|
||||||
|
div(sub(discriminant, b), denom),
|
||||||
|
div(sub(neg(discriminant), b), denom)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new RangeError(
|
||||||
|
'only implemented for cubic or lower-order polynomials, '
|
||||||
|
+ `not ${JSON.stringify(coeffs)}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -4,49 +4,30 @@ export * from './Types/Complex.mjs'
|
|||||||
export const sqrt = {
|
export const sqrt = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
config,
|
config,
|
||||||
|
'sqrtc(Complex<T>)': predictableSqrt,
|
||||||
'isZero(T)': isZ,
|
'isZero(T)': isZ,
|
||||||
'sign(T)': sgn,
|
|
||||||
'one(T)': uno,
|
|
||||||
'add(T,T)': plus,
|
|
||||||
'complex(T)': cplxU,
|
|
||||||
'complex(T,T)': cplxB,
|
|
||||||
'multiply(T,T)': mult,
|
|
||||||
'self(T)': me,
|
|
||||||
'divide(T,T)': div,
|
|
||||||
'absquare(Complex<T>)': absqC,
|
|
||||||
'subtract(T,T)': sub
|
|
||||||
}) => {
|
}) => {
|
||||||
let baseReturns = returnTypeOf(me)
|
if (config.checkingDependency) return undefined
|
||||||
if (baseReturns.includes('|')) {
|
const complexReturns = returnTypeOf(predictableSqrt)
|
||||||
// Bit of a hack, because it is relying on other implementations
|
const baseReturns = complexReturns.slice(8, -1); // Complex<WhatWeWant>
|
||||||
// to list the "typical" value of sqrt first
|
|
||||||
baseReturns = baseReturns.split('|', 1)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.predictable) {
|
if (config.predictable) {
|
||||||
return Returns(`Complex<${baseReturns}>`, z => {
|
return Returns(complexReturns, z => predictableSqrt(z))
|
||||||
const reOne = uno(z.re)
|
|
||||||
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(me(z.re))
|
|
||||||
const reTwo = plus(reOne, reOne)
|
|
||||||
const myabs = me(absqC(z))
|
|
||||||
return cplxB(
|
|
||||||
mult(sgn(z.im), me(div(plus(myabs, z.re), reTwo))),
|
|
||||||
me(div(sub(myabs, z.re), reTwo))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Returns(
|
return Returns(
|
||||||
`Complex<${baseReturns}>|${baseReturns}|undefined`,
|
`Complex<${baseReturns}>|${baseReturns}|undefined`,
|
||||||
z => {
|
z => {
|
||||||
const reOne = uno(z.re)
|
let complexSqrt
|
||||||
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
try {
|
||||||
const reTwo = plus(reOne, reOne)
|
complexSqrt = predictableSqrt(z)
|
||||||
const myabs = me(absqC(z))
|
} catch (e) {
|
||||||
const reSqrt = me(div(plus(myabs, z.re), reTwo))
|
return undefined
|
||||||
const imSqrt = me(div(sub(myabs, z.re), reTwo))
|
}
|
||||||
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
if (complexSqrt.re === undefined || complexSqrt.im === undefined) {
|
||||||
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
return undefined
|
||||||
|
}
|
||||||
|
if (isZ(complexSqrt.im)) return complexSqrt.re
|
||||||
|
return complexSqrt
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
42
src/complex/sqrtc.mjs
Normal file
42
src/complex/sqrtc.mjs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const sqrtc = {
|
||||||
|
'Complex<T>': ({
|
||||||
|
T,
|
||||||
|
'isZero(T)': isZ,
|
||||||
|
'sign(T)': sgn,
|
||||||
|
'one(T)': uno,
|
||||||
|
'add(T,T)': plus,
|
||||||
|
'complex(T)': cplxU,
|
||||||
|
'complex(T,T)': cplxB,
|
||||||
|
'multiply(T,T)': mult,
|
||||||
|
'sqrt(T)': sqt,
|
||||||
|
'divide(T,T)': div,
|
||||||
|
'absquare(Complex<T>)': absqC,
|
||||||
|
'subtract(T,T)': sub
|
||||||
|
}) => {
|
||||||
|
if (isZ.checkingDependency) return undefined
|
||||||
|
let baseReturns = returnTypeOf(sqt)
|
||||||
|
if (baseReturns.includes('|')) {
|
||||||
|
// Bit of a hack, because it is relying on other implementations
|
||||||
|
// to list the "typical" value of sqrt first
|
||||||
|
baseReturns = baseReturns.split('|', 1)[0]
|
||||||
|
}
|
||||||
|
return Returns(`Complex<${baseReturns}>`, z => {
|
||||||
|
const reOne = uno(z.re)
|
||||||
|
if (isZ(z.im) && sgn(z.re) === reOne) return cplxU(sqt(z.re))
|
||||||
|
const myabs = sqt(absqC(z))
|
||||||
|
const reTwo = plus(reOne, reOne)
|
||||||
|
const reQuot = div(plus(myabs, z.re), reTwo)
|
||||||
|
const imQuot = div(sub(myabs, z.re), reTwo)
|
||||||
|
if (reQuot === undefined || imQuot === undefined) {
|
||||||
|
throw new TypeError(`Cannot compute sqrt of ${z.re} + {z.im}i`)
|
||||||
|
}
|
||||||
|
return cplxB(
|
||||||
|
mult(sgn(z.im), sqt(div(plus(myabs, z.re), reTwo))),
|
||||||
|
sqt(div(sub(myabs, z.re), reTwo))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,12 @@ import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
|
|||||||
|
|
||||||
const anySpec = {} // fixed dummy specification of 'any' type
|
const anySpec = {} // fixed dummy specification of 'any' type
|
||||||
|
|
||||||
|
/* Like `.some(predicate)` but for collections */
|
||||||
|
function exists(collection, predicate) {
|
||||||
|
for (const item of collection) if (predicate(item)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Template/signature parsing stuff; should probably be moved to a
|
/* Template/signature parsing stuff; should probably be moved to a
|
||||||
* separate file, but it's a bit interleaved at the moment
|
* separate file, but it's a bit interleaved at the moment
|
||||||
*/
|
*/
|
||||||
@ -98,6 +104,7 @@ export default class PocomathInstance {
|
|||||||
static reserved = new Set([
|
static reserved = new Set([
|
||||||
'chain',
|
'chain',
|
||||||
'config',
|
'config',
|
||||||
|
'convert',
|
||||||
'importDependencies',
|
'importDependencies',
|
||||||
'install',
|
'install',
|
||||||
'installType',
|
'installType',
|
||||||
@ -138,8 +145,21 @@ export default class PocomathInstance {
|
|||||||
const me = this
|
const me = this
|
||||||
this._typed.onMismatch = (name, args, sigs) => {
|
this._typed.onMismatch = (name, args, sigs) => {
|
||||||
if (me._invalid.has(name)) {
|
if (me._invalid.has(name)) {
|
||||||
|
if (this._fixing === name) {
|
||||||
|
this._fixingCount += 1
|
||||||
|
if (this._fixingCount > this._maxDepthSeen + 2) {
|
||||||
|
throw new ReferenceError(
|
||||||
|
`Infinite descent rebuilding ${name} on ${args}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._fixingCount = 0
|
||||||
|
}
|
||||||
// rebuild implementation and try again
|
// rebuild implementation and try again
|
||||||
return me[name](...args)
|
const lastFixing = this._fixing
|
||||||
|
this._fixing = name
|
||||||
|
const value = me[name](...args)
|
||||||
|
this._fix = lastFixing
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
const metaversion = me._meta[name]
|
const metaversion = me._meta[name]
|
||||||
if (metaversion) {
|
if (metaversion) {
|
||||||
@ -185,6 +205,8 @@ export default class PocomathInstance {
|
|||||||
this._plainFunctions = new Set() // the names of the plain functions
|
this._plainFunctions = new Set() // the names of the plain functions
|
||||||
this._chainRepository = {} // place to store chainified functions
|
this._chainRepository = {} // place to store chainified functions
|
||||||
this.joinTypes = this.joinTypes.bind(me)
|
this.joinTypes = this.joinTypes.bind(me)
|
||||||
|
// Provide access to typed function conversion:
|
||||||
|
this.convert = this._typed.convert.bind(this._typed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -763,45 +785,39 @@ export default class PocomathInstance {
|
|||||||
/**
|
/**
|
||||||
* Reset an operation to require creation of typed-function,
|
* Reset an operation to require creation of typed-function,
|
||||||
* and if it has no implementations so far, set them up.
|
* and if it has no implementations so far, set them up.
|
||||||
|
* name is the name of the operation, badType is a type that has been
|
||||||
|
* invalidated, and reasons is a set of specific operations/signatures
|
||||||
|
* that have been invalidated
|
||||||
*/
|
*/
|
||||||
_invalidate(name, reason) {
|
_invalidate(name, badType = '', reasons = new Set()) {
|
||||||
if (!(name in this._imps)) {
|
if (!(name in this._imps)) {
|
||||||
this._imps[name] = {}
|
this._imps[name] = {}
|
||||||
this._TFimps[name] = {}
|
this._TFimps[name] = {}
|
||||||
this._metaTFimps[name] = {}
|
this._metaTFimps[name] = {}
|
||||||
}
|
}
|
||||||
if (reason) {
|
// Go through each TF imp and invalidate it if need be
|
||||||
// Make sure no TF imps that depend on reason remain:
|
for (const [signature, imp] of Object.entries(this._TFimps[name])) {
|
||||||
for (const [signature, behavior] of Object.entries(this._imps[name])) {
|
if (imp.deferred
|
||||||
let invalidated = false
|
|| (badType && signature.includes(badType))
|
||||||
if (reason.charAt(0) === ':') {
|
|| exists(imp.uses, dep => {
|
||||||
const badType = reason.slice(1)
|
const [func, sig] = dep.split(/[()]/)
|
||||||
if (signature.includes(badType)) invalidated = true
|
return reasons.has(dep)
|
||||||
} else {
|
|| (reasons.has(func) && !(sig in this._TFimps[func]))
|
||||||
for (const dep of behavior.uses) {
|
})) {
|
||||||
if (dep.includes(reason)) {
|
// Invalidate this implementation:
|
||||||
invalidated = true
|
delete this._TFimps[name][signature]
|
||||||
break
|
const behavior = imp.fromBehavior
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (invalidated) {
|
|
||||||
if (behavior.explicit) {
|
if (behavior.explicit) {
|
||||||
if (behavior.resolved) delete this._TFimps[signature]
|
|
||||||
behavior.resolved = false
|
behavior.resolved = false
|
||||||
} else {
|
} else {
|
||||||
for (const fullSig
|
delete behavior.hasInstantiations[imp.instance]
|
||||||
of Object.values(behavior.hasInstantiations)) {
|
|
||||||
delete this._TFimps[fullSig]
|
|
||||||
}
|
|
||||||
behavior.hasInstantiations = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
reasons.add(`${name}(${signature})`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._invalid.has(name)) return
|
if (this._invalid.has(name)) return
|
||||||
this._invalid.add(name)
|
this._invalid.add(name)
|
||||||
this._invalidateDependents(name)
|
this._invalidateDependents(name, badType, reasons)
|
||||||
const self = this
|
const self = this
|
||||||
Object.defineProperty(this, name, {
|
Object.defineProperty(this, name, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
@ -815,11 +831,14 @@ export default class PocomathInstance {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate all the dependents of a given property of the instance
|
* Invalidate all the dependents of a given property of the instance
|
||||||
|
* reasons is a set of invalidated signatures
|
||||||
*/
|
*/
|
||||||
_invalidateDependents(name) {
|
_invalidateDependents(name, badType, reasons = new Set()) {
|
||||||
|
if (name.charAt(0) === ':') badType = name.slice(1)
|
||||||
|
else reasons.add(name)
|
||||||
if (name in this._affects) {
|
if (name in this._affects) {
|
||||||
for (const ancestor of this._affects[name]) {
|
for (const ancestor of this._affects[name]) {
|
||||||
this._invalidate(ancestor, name)
|
this._invalidate(ancestor, badType, reasons)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -860,7 +879,7 @@ export default class PocomathInstance {
|
|||||||
for (const [rawSignature, behavior] of usableEntries) {
|
for (const [rawSignature, behavior] of usableEntries) {
|
||||||
if (behavior.explicit) {
|
if (behavior.explicit) {
|
||||||
if (!(behavior.resolved)) {
|
if (!(behavior.resolved)) {
|
||||||
this._addTFimplementation(tf_imps, rawSignature, behavior)
|
this._addTFimplementation(name, tf_imps, rawSignature, behavior)
|
||||||
tf_imps[rawSignature]._pocoSignature = rawSignature
|
tf_imps[rawSignature]._pocoSignature = rawSignature
|
||||||
behavior.resolved = true
|
behavior.resolved = true
|
||||||
}
|
}
|
||||||
@ -880,11 +899,18 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
/* First, add the known instantiations, gathering all types needed */
|
/* First, add the known instantiations, gathering all types needed */
|
||||||
if (ubType) behavior.needsInstantiations.add(ubType)
|
if (ubType) behavior.needsInstantiations.add(ubType)
|
||||||
|
const nargs = typeListOfSignature(rawSignature).length
|
||||||
let instantiationSet = new Set()
|
let instantiationSet = new Set()
|
||||||
const ubTypes = new Set()
|
const ubTypes = new Set()
|
||||||
if (!ubType) {
|
if (!ubType) {
|
||||||
// Collect all upper-bound types for this signature
|
// Collect all upper-bound types for this signature
|
||||||
for (const othersig in imps) {
|
for (const othersig in imps) {
|
||||||
|
const otherNargs = typeListOfSignature(othersig).length
|
||||||
|
if (nargs !== otherNargs) {
|
||||||
|
// crude criterion that it won't match, that ignores
|
||||||
|
// rest args, but hopefully OK for prototype
|
||||||
|
continue
|
||||||
|
}
|
||||||
const thisUB = upperBounds.exec(othersig)
|
const thisUB = upperBounds.exec(othersig)
|
||||||
if (thisUB) ubTypes.add(thisUB[2])
|
if (thisUB) ubTypes.add(thisUB[2])
|
||||||
let basesig = othersig.replaceAll(templateCall, '')
|
let basesig = othersig.replaceAll(templateCall, '')
|
||||||
@ -914,7 +940,6 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent other existing signatures from blocking use of top-level
|
/* Prevent other existing signatures from blocking use of top-level
|
||||||
* templates via conversions:
|
* templates via conversions:
|
||||||
*/
|
*/
|
||||||
@ -953,7 +978,8 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const instType of instantiationSet) {
|
for (const instType of instantiationSet) {
|
||||||
this._instantiateTemplateImplementation(name, rawSignature, instType)
|
this._instantiateTemplateImplementation(
|
||||||
|
name, rawSignature, instType)
|
||||||
}
|
}
|
||||||
/* Now add the catchall signature */
|
/* Now add the catchall signature */
|
||||||
/* (Not needed if if it's a bounded template) */
|
/* (Not needed if if it's a bounded template) */
|
||||||
@ -1008,6 +1034,7 @@ export default class PocomathInstance {
|
|||||||
`Type inference failed for argument ${j} of ${name}`)
|
`Type inference failed for argument ${j} of ${name}`)
|
||||||
}
|
}
|
||||||
if (argType === 'any') {
|
if (argType === 'any') {
|
||||||
|
console.log('INCOMPATIBLE ARGUMENTS are', args)
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
`In call to ${name}, `
|
`In call to ${name}, `
|
||||||
+ 'incompatible template arguments:'
|
+ 'incompatible template arguments:'
|
||||||
@ -1065,12 +1092,19 @@ export default class PocomathInstance {
|
|||||||
// But possibly since this resolution was grabbed, the proper
|
// But possibly since this resolution was grabbed, the proper
|
||||||
// instantiation has been added (like if there are multiple
|
// instantiation has been added (like if there are multiple
|
||||||
// uses in the implementation of another method.
|
// uses in the implementation of another method.
|
||||||
if (!(behavior.needsInstantiations.has(instantiateFor))) {
|
let whatToDo
|
||||||
behavior.needsInstantiations.add(instantiateFor)
|
if (!(instantiateFor in behavior.hasInstantiations)) {
|
||||||
|
const newImp = self._instantiateTemplateImplementation(
|
||||||
|
name, rawSignature, instantiateFor)
|
||||||
|
if (newImp) {
|
||||||
|
whatToDo = {fn: newImp, implementation: newImp}
|
||||||
|
}
|
||||||
self._invalidate(name)
|
self._invalidate(name)
|
||||||
}
|
}
|
||||||
const brandNewMe = self[name]
|
const brandNewMe = self[name]
|
||||||
const whatToDo = self._typed.resolve(brandNewMe, args)
|
const betterToDo = self._typed.resolve(brandNewMe, args)
|
||||||
|
whatToDo = betterToDo || whatToDo
|
||||||
|
|
||||||
// We can access return type information here
|
// We can access return type information here
|
||||||
// And in particular, if it might be a template, we should try to
|
// And in particular, if it might be a template, we should try to
|
||||||
// instantiate it:
|
// instantiate it:
|
||||||
@ -1084,8 +1118,13 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
if (whatToDo === lastWhatToDo) {
|
if (whatToDo === lastWhatToDo) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Infinite recursion in resolving $name called on`
|
`Infinite recursion in resolving ${name} called on `
|
||||||
+ args.map(x => x.toString()).join(','))
|
+ args.map(x =>
|
||||||
|
(typeof x === 'object'
|
||||||
|
? JSON.stringify(x)
|
||||||
|
: x.toString())
|
||||||
|
).join(', ')
|
||||||
|
+ ` inferred to be ${wantSig}`)
|
||||||
}
|
}
|
||||||
lastWhatToDo = whatToDo
|
lastWhatToDo = whatToDo
|
||||||
const retval = whatToDo.implementation(...args)
|
const retval = whatToDo.implementation(...args)
|
||||||
@ -1103,7 +1142,9 @@ export default class PocomathInstance {
|
|||||||
// correct return type a priori. Deferring because unclear what
|
// correct return type a priori. Deferring because unclear what
|
||||||
// aspects will be merged into typed-function.
|
// aspects will be merged into typed-function.
|
||||||
this._addTFimplementation(
|
this._addTFimplementation(
|
||||||
meta_imps, signature, {uses: new Set(), does: patch})
|
name, meta_imps, signature,
|
||||||
|
{uses: new Set(), does: patch},
|
||||||
|
behavior)
|
||||||
behavior.resolved = true
|
behavior.resolved = true
|
||||||
}
|
}
|
||||||
this._correctPartialSelfRefs(name, tf_imps)
|
this._correctPartialSelfRefs(name, tf_imps)
|
||||||
@ -1112,6 +1153,9 @@ export default class PocomathInstance {
|
|||||||
// ditch the signature:
|
// ditch the signature:
|
||||||
const badSigs = new Set()
|
const badSigs = new Set()
|
||||||
for (const sig in tf_imps) {
|
for (const sig in tf_imps) {
|
||||||
|
if (!tf_imps[sig].uses) {
|
||||||
|
throw new ReferenceError(`MONKEY WRENCH: ${name} ${sig}`)
|
||||||
|
}
|
||||||
for (const type of typeListOfSignature(sig)) {
|
for (const type of typeListOfSignature(sig)) {
|
||||||
if (this._maybeInstantiate(type) === undefined) {
|
if (this._maybeInstantiate(type) === undefined) {
|
||||||
badSigs.add(sig)
|
badSigs.add(sig)
|
||||||
@ -1232,10 +1276,12 @@ export default class PocomathInstance {
|
|||||||
return behavior.does(innerRefs)
|
return behavior.does(innerRefs)
|
||||||
}
|
}
|
||||||
const tf_imps = this._TFimps[name]
|
const tf_imps = this._TFimps[name]
|
||||||
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
this._addTFimplementation(
|
||||||
|
name, tf_imps, signature, {uses, does: patch}, behavior, instanceType)
|
||||||
tf_imps[signature]._pocoSignature = templateSignature
|
tf_imps[signature]._pocoSignature = templateSignature
|
||||||
tf_imps[signature]._pocoInstance = instanceType
|
tf_imps[signature]._pocoInstance = instanceType
|
||||||
behavior.hasInstantiations[instanceType] = signature
|
behavior.hasInstantiations[instanceType] = signature
|
||||||
|
behavior.needsInstantiations.add(instanceType) // once we have it, keep it
|
||||||
return tf_imps[signature]
|
return tf_imps[signature]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,10 +1289,17 @@ export default class PocomathInstance {
|
|||||||
* to typed-function implementations and inserts the result into plain
|
* to typed-function implementations and inserts the result into plain
|
||||||
* object imps
|
* object imps
|
||||||
*/
|
*/
|
||||||
_addTFimplementation(imps, signature, behavior) {
|
_addTFimplementation(
|
||||||
const {uses, does} = behavior
|
name, imps, signature, specificBehavior, fromImp, asInstance)
|
||||||
|
{
|
||||||
|
if (!fromImp) fromImp = specificBehavior
|
||||||
|
const {uses, does} = specificBehavior
|
||||||
if (uses.length === 0) {
|
if (uses.length === 0) {
|
||||||
const implementation = does()
|
const implementation = does()
|
||||||
|
implementation.uses = uses
|
||||||
|
implementation.fromInstance = this
|
||||||
|
implementation.fromBehavior = fromImp
|
||||||
|
implementation.instance = asInstance
|
||||||
// could do something with return type information here
|
// could do something with return type information here
|
||||||
imps[signature] = implementation
|
imps[signature] = implementation
|
||||||
return
|
return
|
||||||
@ -1274,7 +1327,8 @@ export default class PocomathInstance {
|
|||||||
*/
|
*/
|
||||||
if (needsig in imps && typeof imps[needsig] == 'function') {
|
if (needsig in imps && typeof imps[needsig] == 'function') {
|
||||||
refs[dep] = imps[needsig]
|
refs[dep] = imps[needsig]
|
||||||
} else {
|
continue
|
||||||
|
}
|
||||||
if (full_self_referential) {
|
if (full_self_referential) {
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
'typed-function does not support mixed full and '
|
'typed-function does not support mixed full and '
|
||||||
@ -1284,8 +1338,14 @@ export default class PocomathInstance {
|
|||||||
const mergedTypes = Object.assign(
|
const mergedTypes = Object.assign(
|
||||||
{}, this.Types, this.Templates)
|
{}, this.Types, this.Templates)
|
||||||
if (subsetOfKeys(needTypes, mergedTypes)) {
|
if (subsetOfKeys(needTypes, mergedTypes)) {
|
||||||
part_self_references.push(needsig)
|
func = name // just resolve it in limbo
|
||||||
}
|
} else {
|
||||||
|
// uses an unknown type, so will get an undefined impl
|
||||||
|
console.log(
|
||||||
|
'WARNING: partial self-reference for', name, 'to',
|
||||||
|
needsig, 'uses an unknown type')
|
||||||
|
refs[dep] = undefined
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (part_self_references.length) {
|
if (part_self_references.length) {
|
||||||
@ -1294,35 +1354,21 @@ export default class PocomathInstance {
|
|||||||
+ 'partial self-reference')
|
+ 'partial self-reference')
|
||||||
}
|
}
|
||||||
full_self_referential = true
|
full_self_referential = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (this[func] === 'limbo') {
|
if (this[func] === 'limbo') {
|
||||||
/* We are in the midst of bundling func */
|
/* We are in the midst of bundling func (which may be ourself) */
|
||||||
let fallback = true
|
|
||||||
/* So the first thing we can do is try the tf_imps we are
|
/* So the first thing we can do is try the tf_imps we are
|
||||||
* accumulating:
|
* accumulating:
|
||||||
*/
|
*/
|
||||||
if (needsig) {
|
if (needsig) {
|
||||||
let typedUniverse
|
const candidate = this.resolve(func, needsig)
|
||||||
let tempTF
|
if (typeof candidate === 'function') {
|
||||||
if (Object.keys(this._TFimps[func]).length > 0) {
|
refs[dep] = candidate
|
||||||
typedUniverse = this._typed
|
continue
|
||||||
tempTF = typedUniverse('dummy_' + func, this._TFimps[func])
|
|
||||||
} else {
|
|
||||||
typedUniverse = this._metaTyped
|
|
||||||
tempTF = typedUniverse(
|
|
||||||
'dummy_' + func, this._metaTFimps[func])
|
|
||||||
}
|
|
||||||
let result = undefined
|
|
||||||
try {
|
|
||||||
result = typedUniverse.find(tempTF, needsig, {exact: true})
|
|
||||||
} catch {}
|
|
||||||
if (result) {
|
|
||||||
refs[dep] = result
|
|
||||||
fallback = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fallback) {
|
|
||||||
/* Either we need the whole function or the signature
|
/* Either we need the whole function or the signature
|
||||||
* we need is not available yet, so we have to use
|
* we need is not available yet, so we have to use
|
||||||
* an indirect reference to func. And given that, there's
|
* an indirect reference to func. And given that, there's
|
||||||
@ -1335,11 +1381,11 @@ export default class PocomathInstance {
|
|||||||
Object.defineProperty(redirect, 'name', {value: func})
|
Object.defineProperty(redirect, 'name', {value: func})
|
||||||
Object.defineProperty(redirect, 'fromInstance', {value: this})
|
Object.defineProperty(redirect, 'fromInstance', {value: this})
|
||||||
refs[dep] = redirect
|
refs[dep] = redirect
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// can bundle up func, and grab its signature if need be
|
// can bundle up func, and grab its signature if need be
|
||||||
let destination = this[func]
|
let destination = this[func]
|
||||||
if (destination && needsig) {
|
if (needsig) {
|
||||||
destination = this.resolve(func, needsig)
|
destination = this.resolve(func, needsig)
|
||||||
}
|
}
|
||||||
if (!destination) {
|
if (!destination) {
|
||||||
@ -1348,21 +1394,29 @@ export default class PocomathInstance {
|
|||||||
// but let's warn.
|
// but let's warn.
|
||||||
console.log(
|
console.log(
|
||||||
'WARNING: No definition found for dependency',
|
'WARNING: No definition found for dependency',
|
||||||
dep, 'needed by a function with signature', signature)
|
dep, 'needed by', name, '(', signature, ')')
|
||||||
}
|
}
|
||||||
refs[dep] = destination
|
refs[dep] = destination
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (full_self_referential) {
|
if (full_self_referential) {
|
||||||
imps[signature] = this._typed.referToSelf(self => {
|
imps[signature] = this._typed.referToSelf(self => {
|
||||||
refs.self = self
|
refs.self = self
|
||||||
const implementation = does(refs)
|
const implementation = does(refs)
|
||||||
Object.defineProperty(implementation, 'name', {value: does.name})
|
Object.defineProperty(implementation, 'name', {value: does.name})
|
||||||
implementation.fromInstance = this
|
implementation.fromInstance = this
|
||||||
|
implementation.uses = uses
|
||||||
|
implementation.instance = asInstance
|
||||||
|
implementation.fromBehavior = fromImp
|
||||||
// What are we going to do with the return type info in here?
|
// What are we going to do with the return type info in here?
|
||||||
return implementation
|
return implementation
|
||||||
})
|
})
|
||||||
|
imps[signature].uses = uses
|
||||||
|
if (!uses) {
|
||||||
|
throw new ReferenceError(`NANH uses for ${signature} from ${fromImp}`)
|
||||||
|
}
|
||||||
|
imps[signature].fromInstance = this
|
||||||
|
imps[signature].instance = asInstance
|
||||||
|
imps[signature].fromBehavior = fromImp
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (part_self_references.length) {
|
if (part_self_references.length) {
|
||||||
@ -1377,13 +1431,19 @@ export default class PocomathInstance {
|
|||||||
deferred: true,
|
deferred: true,
|
||||||
builtRefs: refs,
|
builtRefs: refs,
|
||||||
sigDoes: does,
|
sigDoes: does,
|
||||||
|
sigUses: uses,
|
||||||
fromInstance: this,
|
fromInstance: this,
|
||||||
|
fromBehavior: fromImp,
|
||||||
|
instance: asInstance,
|
||||||
psr: part_self_references
|
psr: part_self_references
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const implementation = does(refs)
|
const implementation = does(refs)
|
||||||
implementation.fromInstance = this
|
implementation.fromInstance = this
|
||||||
|
implementation.fromBehavior = fromImp
|
||||||
|
implementation.instance = asInstance
|
||||||
|
implementation.uses = uses
|
||||||
// could do something with return type information here?
|
// could do something with return type information here?
|
||||||
imps[signature] = implementation
|
imps[signature] = implementation
|
||||||
}
|
}
|
||||||
@ -1457,6 +1517,10 @@ export default class PocomathInstance {
|
|||||||
refs[`self(${remaining_self_references[i]})`] = impls[i]
|
refs[`self(${remaining_self_references[i]})`] = impls[i]
|
||||||
}
|
}
|
||||||
const implementation = does(refs)
|
const implementation = does(refs)
|
||||||
|
implementation.fromInstance = deferral.fromInstance
|
||||||
|
implementation.fromBehavior = deferral.fromBehavior
|
||||||
|
implementation.instance = deferral.instance
|
||||||
|
implementation.uses = deferral.sigUses
|
||||||
// What will we do with the return type info in here?
|
// What will we do with the return type info in here?
|
||||||
return implementation
|
return implementation
|
||||||
}
|
}
|
||||||
@ -1467,6 +1531,9 @@ export default class PocomathInstance {
|
|||||||
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
||||||
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
||||||
imps[aSignature].fromInstance = deferral.fromInstance
|
imps[aSignature].fromInstance = deferral.fromInstance
|
||||||
|
imps[aSignature].fromBehavior = deferral.fromBehavior
|
||||||
|
imps[aSignature].instance = deferral.instance
|
||||||
|
imps[aSignature].uses = deferral.sigUses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1576,7 +1643,8 @@ export default class PocomathInstance {
|
|||||||
return wantsType
|
return wantsType
|
||||||
})
|
})
|
||||||
|
|
||||||
_findSubtypeImpl(name, imps, neededSig) {
|
_findSubtypeImpl(name, imps, neededSig, raw = false) {
|
||||||
|
const detemplate = !raw
|
||||||
if (neededSig in imps) return neededSig
|
if (neededSig in imps) return neededSig
|
||||||
let foundSig = false
|
let foundSig = false
|
||||||
const typeList = typeListOfSignature(neededSig)
|
const typeList = typeListOfSignature(neededSig)
|
||||||
@ -1589,16 +1657,16 @@ export default class PocomathInstance {
|
|||||||
let myType = typeList[k]
|
let myType = typeList[k]
|
||||||
let otherType = otherTypeList[k]
|
let otherType = otherTypeList[k]
|
||||||
if (otherType === theTemplateParam) {
|
if (otherType === theTemplateParam) {
|
||||||
otherTypeList[k] = paramBound
|
if (detemplate) otherTypeList[k] = paramBound
|
||||||
otherType = paramBound
|
otherType = paramBound
|
||||||
}
|
}
|
||||||
if (otherType === restTemplateParam) {
|
if (otherType === restTemplateParam) {
|
||||||
otherTypeList[k] = `...${paramBound}`
|
if (detemplate) otherTypeList[k] = `...${paramBound}`
|
||||||
otherType = paramBound
|
otherType = paramBound
|
||||||
}
|
}
|
||||||
const adjustedOtherType = otherType.replaceAll(templateCall, '')
|
const adjustedOtherType = otherType.replaceAll(templateCall, '')
|
||||||
if (adjustedOtherType !== otherType) {
|
if (adjustedOtherType !== otherType) {
|
||||||
otherTypeList[k] = adjustedOtherType
|
if (detemplate) otherTypeList[k] = adjustedOtherType
|
||||||
otherType = adjustedOtherType
|
otherType = adjustedOtherType
|
||||||
}
|
}
|
||||||
if (myType.slice(0,3) === '...') myType = myType.slice(3)
|
if (myType.slice(0,3) === '...') myType = myType.slice(3)
|
||||||
@ -1607,9 +1675,11 @@ export default class PocomathInstance {
|
|||||||
if (otherBound) {
|
if (otherBound) {
|
||||||
paramBound = otherBound[2]
|
paramBound = otherBound[2]
|
||||||
otherType = paramBound
|
otherType = paramBound
|
||||||
|
if (detemplate) {
|
||||||
otherTypeList[k] = otherBound[1].replaceAll(
|
otherTypeList[k] = otherBound[1].replaceAll(
|
||||||
theTemplateParam, paramBound)
|
theTemplateParam, paramBound)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (otherType === 'any') continue
|
if (otherType === 'any') continue
|
||||||
if (otherType === UniversalType) continue
|
if (otherType === UniversalType) continue
|
||||||
if (myType === otherType) continue
|
if (myType === otherType) continue
|
||||||
@ -1619,7 +1689,7 @@ export default class PocomathInstance {
|
|||||||
if (this.instantiateTemplate(otherType, myType)) {
|
if (this.instantiateTemplate(otherType, myType)) {
|
||||||
let dummy
|
let dummy
|
||||||
dummy = this[name] // for side effects
|
dummy = this[name] // for side effects
|
||||||
return this._findSubtypeImpl(name, this._imps[name], neededSig)
|
return this._findSubtypeImpl(name, this._imps[name], neededSig, raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(otherType in this.Types)) {
|
if (!(otherType in this.Types)) {
|
||||||
@ -1658,6 +1728,10 @@ export default class PocomathInstance {
|
|||||||
of typedFunction._typedFunctionData.signatureMap) {
|
of typedFunction._typedFunctionData.signatureMap) {
|
||||||
let allMatched = true
|
let allMatched = true
|
||||||
const implTypes = typeListOfSignature(implSig)
|
const implTypes = typeListOfSignature(implSig)
|
||||||
|
if (implTypes.length > wantTypes.length) {
|
||||||
|
// Not enough arguments for that implementation
|
||||||
|
continue
|
||||||
|
}
|
||||||
for (let i = 0; i < wantTypes.length; ++i) {
|
for (let i = 0; i < wantTypes.length; ++i) {
|
||||||
const implIndex = Math.min(i, implTypes.length - 1)
|
const implIndex = Math.min(i, implTypes.length - 1)
|
||||||
let implType = implTypes[implIndex]
|
let implType = implTypes[implIndex]
|
||||||
@ -1682,7 +1756,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(this._imps[name])) return undefined
|
if (!(this._imps[name])) return undefined
|
||||||
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig)
|
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig, 'raw')
|
||||||
if (foundsig) {
|
if (foundsig) {
|
||||||
if (haveTF) {
|
if (haveTF) {
|
||||||
try {
|
try {
|
||||||
@ -1690,19 +1764,74 @@ export default class PocomathInstance {
|
|||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const instantiationMatcher =
|
||||||
|
'^'
|
||||||
|
+ substituteInSignature(foundsig, theTemplateParam, '(.*)')
|
||||||
|
.replaceAll(UniversalType, '(.*)')
|
||||||
|
+ '$'
|
||||||
|
const instanceMatch = sig.match(instantiationMatcher)
|
||||||
|
let possibleInstantiator = false
|
||||||
|
if (instanceMatch) {
|
||||||
|
possibleInstantiator = instanceMatch[1]
|
||||||
|
for (let i = 2; i < instanceMatch.length; ++i) {
|
||||||
|
if (possibleInstantiator !== instanceMatch[i]) {
|
||||||
|
possibleInstantiator = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (possibleInstantiator) {
|
||||||
|
const behavior = this._imps[name][foundsig]
|
||||||
|
let newInstance
|
||||||
|
if (behavior) {
|
||||||
|
if (!(possibleInstantiator in behavior.hasInstantiations)) {
|
||||||
|
newInstance = this._instantiateTemplateImplementation(
|
||||||
|
name, foundsig, possibleInstantiator)
|
||||||
|
} else {
|
||||||
|
// OK, so we actually have the instantiation. Let's get it
|
||||||
|
newInstance = this._TFimps[name][sig]
|
||||||
|
}
|
||||||
|
// But we may not have taken advantage of conversions
|
||||||
|
this._invalidate(name)
|
||||||
|
const tryAgain = this[name]
|
||||||
|
let betterInstance
|
||||||
|
if (this._typed.isTypedFunction(tryAgain)) {
|
||||||
|
betterInstance = this._typed.findSignature(tryAgain, sig)
|
||||||
|
}
|
||||||
|
if (betterInstance) {
|
||||||
|
newInstance = betterInstance
|
||||||
|
} else {
|
||||||
|
newInstance = {
|
||||||
|
fn: newInstance,
|
||||||
|
implementation: newInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newInstance) return newInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const catchallSig = this._findSubtypeImpl(name, this._imps[name], sig)
|
||||||
|
if (catchallSig !== foundsig) {
|
||||||
try {
|
try {
|
||||||
return this._metaTyped.findSignature(this._meta[name], foundsig)
|
return this._metaTyped.findSignature(
|
||||||
|
this._meta[name], catchallSig)
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// We have an implementation but not a typed function. Do the best
|
// We have an implementation but not a typed function. Do the best
|
||||||
// we can:
|
// we can:
|
||||||
const foundImpl = this._imps[name][foundsig]
|
const restoredSig = foundsig.replaceAll('ground', theTemplateParam)
|
||||||
|
const foundImpl = this._imps[name][restoredSig]
|
||||||
const needs = {}
|
const needs = {}
|
||||||
for (const dep of foundImpl.uses) {
|
for (const dep of foundImpl.uses) {
|
||||||
const [base, sig] = dep.split('()')
|
const [base, sig] = dep.split(/[()]/)
|
||||||
|
if (sig) {
|
||||||
needs[dep] = this.resolve(base, sig)
|
needs[dep] = this.resolve(base, sig)
|
||||||
|
} else {
|
||||||
|
needs[dep] = this[dep]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const pseudoImpl = foundImpl.does(needs)
|
const pseudoImpl = foundImpl.does(needs)
|
||||||
|
pseudoImpl.fromInstance = this
|
||||||
return {fn: pseudoImpl, implementation: pseudoImpl}
|
return {fn: pseudoImpl, implementation: pseudoImpl}
|
||||||
}
|
}
|
||||||
// Hmm, no luck. Make sure bundle is up-to-date and retry:
|
// Hmm, no luck. Make sure bundle is up-to-date and retry:
|
||||||
|
@ -6,7 +6,7 @@ export function dependencyExtractor(destinationSet) {
|
|||||||
return new Proxy({}, {
|
return new Proxy({}, {
|
||||||
get: (target, property) => {
|
get: (target, property) => {
|
||||||
destinationSet.add(property)
|
destinationSet.add(property)
|
||||||
return {}
|
return {checkingDependency: true}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,5 @@ export * from './Types/number.mjs'
|
|||||||
export const add = {
|
export const add = {
|
||||||
// Note the below assumes that all subtypes of number that will be defined
|
// Note the below assumes that all subtypes of number that will be defined
|
||||||
// are closed under addition!
|
// are closed under addition!
|
||||||
'T:number, T': ({T}) => Returns(T, (m,n) => m+n)
|
'T:number,T': ({T}) => Returns(T, (m,n) => m+n)
|
||||||
}
|
}
|
||||||
|
34
test/complex/_polynomialRoot.mjs
Normal file
34
test/complex/_polynomialRoot.mjs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import math from '../../src/pocomath.mjs'
|
||||||
|
|
||||||
|
describe('polynomialRoot', () => {
|
||||||
|
it('should solve a linear equation with real coefficients', function () {
|
||||||
|
assert.deepEqual(math.polynomialRoot(6, 3), math.tuple(-2))
|
||||||
|
assert.deepEqual(
|
||||||
|
math.polynomialRoot(math.complex(-3, 2), 2),
|
||||||
|
math.tuple(math.complex(1.5, -1)))
|
||||||
|
assert.deepEqual(
|
||||||
|
math.polynomialRoot(math.complex(3, 1), math.complex(-1, -1)),
|
||||||
|
math.tuple(math.complex(2, -1)))
|
||||||
|
})
|
||||||
|
// Should be safe now to capture the functions:
|
||||||
|
const complex = math.complex
|
||||||
|
const pRoot = math.polynomialRoot
|
||||||
|
const tup = math.tuple
|
||||||
|
it('should solve a quadratic equation with a double root', function () {
|
||||||
|
assert.deepEqual(pRoot(4, 4, 1), tup(-2))
|
||||||
|
assert.deepEqual(
|
||||||
|
pRoot(complex(0, 2), complex(2, 2), 1), tup(complex(-1, -1)))
|
||||||
|
})
|
||||||
|
it('should solve a quadratic with two distinct roots', function () {
|
||||||
|
assert.deepEqual(pRoot(-3, 2, 1), tup(1, -3))
|
||||||
|
assert.deepEqual(pRoot(-2, 0, 1), tup(math.sqrt(2), -math.sqrt(2)))
|
||||||
|
assert.deepEqual(
|
||||||
|
pRoot(4, 2, 1),
|
||||||
|
tup(complex(-1, math.sqrt(3)), complex(-1, -math.sqrt(3))))
|
||||||
|
assert.deepEqual(
|
||||||
|
pRoot(complex(3, 1), -3, 1), tup(complex(1, 1), complex(2, -1)))
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user