feat(return types): Add more return types for complex functions
These changes greatly increased the need for precision in generating implementations for signatures of operations whenever possible. So this commit also includes a refactor that basically caches all of the conversions of Pocomath implementations to typed-function implementatios so that they are more often externally available (i.e., not disrupted so much after invalidation).
This commit is contained in:
parent
bc434c7163
commit
a2f76a55b8
@ -3,7 +3,7 @@ export * from './Types/bigint.mjs'
|
||||
|
||||
export const divide = {
|
||||
'bigint,bigint': ({config, 'quotient(bigint,bigint)': quot}) => {
|
||||
if (config.predictable) return quot
|
||||
if (config.predictable) return Returns('bigint', (n,d) => quot(n,d))
|
||||
return Returns('bigint|undefined', (n, d) => {
|
||||
const q = n/d
|
||||
if (q * d == n) return q
|
||||
|
@ -6,7 +6,7 @@ const Complex = new PocomathInstance('Complex')
|
||||
Complex.installType('Complex', {
|
||||
test: z => z && typeof z === 'object' && 're' in z && 'im' in z
|
||||
})
|
||||
// Now the template type: Complex numbers are actually always homeogeneous
|
||||
// Now the template type: Complex numbers are actually always homogeneous
|
||||
// in their component types.
|
||||
Complex.installType('Complex<T>', {
|
||||
infer: ({typeOf, joinTypes}) => z => joinTypes([typeOf(z.re), typeOf(z.im)]),
|
||||
|
@ -1,10 +1,25 @@
|
||||
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||
export * from './Types/Complex.mjs'
|
||||
|
||||
export const abs = {
|
||||
'Complex<T>': ({
|
||||
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
|
||||
sqrt, // Unfortunately no notation yet for the needed signature
|
||||
'absquare(T)': baseabsq,
|
||||
'absquare(Complex<T>)': absq
|
||||
}) => z => sqrt(absq(z))
|
||||
}) => {
|
||||
const pm = sqrt.fromInstance
|
||||
if (typeof pm === 'undefined') {
|
||||
// Just checking for the dependencies, return value is irrelevant
|
||||
return undefined
|
||||
}
|
||||
const midType = returnTypeOf(baseabsq)
|
||||
const sqrtImp = pm.resolve('sqrt', midType, sqrt)
|
||||
let retType = returnTypeOf(sqrtImp)
|
||||
if (retType.includes('|')) {
|
||||
// This is a bit of a hack, as it relies on all implementations of
|
||||
// sqrt returning the "typical" return type as the first option
|
||||
retType = retType.split('|',1)[0]
|
||||
}
|
||||
return Returns(retType, z => sqrtImp(absq(z)))
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,31 @@
|
||||
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||
export * from './Types/Complex.mjs'
|
||||
|
||||
export const absquare = {
|
||||
'Complex<T>': ({
|
||||
add, // Calculation of exact type needed in add (underlying numeric of T)
|
||||
// is (currently) too involved for Pocomath
|
||||
add, // no easy way to write the needed signature; if T is number
|
||||
// it is number,number; but if T is Complex<bigint>, it is just
|
||||
// bigint,bigint. So unfortunately we depend on all of add, and
|
||||
// we extract the needed implementation below.
|
||||
'self(T)': absq
|
||||
}) => z => add(absq(z.re), absq(z.im))
|
||||
}) => {
|
||||
const pm = add.fromInstance
|
||||
if (typeof pm === 'undefined') {
|
||||
// Just checking the dependencies, return value irrelevant
|
||||
return undefined
|
||||
}
|
||||
const midType = returnTypeOf(absq)
|
||||
const addImp = pm.resolve('add', `${midType},${midType}`, add)
|
||||
return Returns(
|
||||
returnTypeOf(addImp), z => addImp(absq(z.re), absq(z.im)))
|
||||
}
|
||||
}
|
||||
|
||||
/* We could imagine notations that Pocomath could support that would simplify
|
||||
* the above, maybe something like
|
||||
* 'Complex<T>': ({
|
||||
* 'self(T): U': absq,
|
||||
* 'add(U,U):V': plus,
|
||||
* V
|
||||
* }) => Returns(V, z => plus(absq(z.re), absq(z.im)))
|
||||
*/
|
||||
|
@ -1,15 +1,18 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
export * from './Types/Complex.mjs'
|
||||
|
||||
export const multiply = {
|
||||
'Complex<T>,Complex<T>': ({
|
||||
T,
|
||||
'complex(T,T)': cplx,
|
||||
'add(T,T)': plus,
|
||||
'subtract(T,T)': sub,
|
||||
'self(T,T)': me,
|
||||
'conjugate(T)': conj // makes quaternion multiplication work
|
||||
}) => (w,z) => {
|
||||
return cplx(
|
||||
}) => Returns(
|
||||
`Complex<${T}>`,
|
||||
(w,z) => cplx(
|
||||
sub(me(w.re, z.re), me(conj(w.im), z.im)),
|
||||
plus(me(conj(w.re), z.im), me(w.im, z.re)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||
export * from './Types/Complex.mjs'
|
||||
|
||||
export const sqrt = {
|
||||
@ -12,29 +13,41 @@ export const sqrt = {
|
||||
'multiply(T,T)': mult,
|
||||
'self(T)': me,
|
||||
'divide(T,T)': div,
|
||||
'abs(Complex<T>)': absC,
|
||||
'absquare(Complex<T>)': absqC,
|
||||
'subtract(T,T)': sub
|
||||
}) => {
|
||||
let baseReturns = returnTypeOf(me)
|
||||
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]
|
||||
}
|
||||
|
||||
if (config.predictable) {
|
||||
return z => {
|
||||
return Returns(`Complex<${baseReturns}>`, 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(absC(z),z.re), reTwo))),
|
||||
me(div(sub(absC(z),z.re), reTwo))
|
||||
mult(sgn(z.im), me(div(plus(myabs, z.re), reTwo))),
|
||||
me(div(sub(myabs, z.re), reTwo))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return Returns(
|
||||
`Complex<${baseReturns}>|${baseReturns}|undefined`,
|
||||
z => {
|
||||
const reOne = uno(z.re)
|
||||
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
||||
const reTwo = plus(reOne, reOne)
|
||||
const myabs = me(absqC(z))
|
||||
const reSqrt = me(div(plus(myabs, z.re), reTwo))
|
||||
const imSqrt = me(div(sub(myabs, z.re), reTwo))
|
||||
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
||||
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
||||
}
|
||||
}
|
||||
return z => {
|
||||
const reOne = uno(z.re)
|
||||
if (isZ(z.im) && sgn(z.re) === reOne) return me(z.re)
|
||||
const reTwo = plus(reOne, reOne)
|
||||
const reSqrt = me(div(plus(absC(z),z.re), reTwo))
|
||||
const imSqrt = me(div(sub(absC(z),z.re), reTwo))
|
||||
if (reSqrt === undefined || imSqrt === undefined) return undefined
|
||||
return cplxB(mult(sgn(z.im), reSqrt), imSqrt)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ export default class PocomathInstance {
|
||||
'joinTypes',
|
||||
'name',
|
||||
'returnTypeOf',
|
||||
'resolve',
|
||||
'self',
|
||||
'subtypesOf',
|
||||
'supertypesOf',
|
||||
@ -49,7 +50,8 @@ export default class PocomathInstance {
|
||||
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
this._imps = {}
|
||||
this._imps = {} // Pocomath implementations, with dependencies
|
||||
this._TFimps = {} // typed-function implementations, dependencies resolved
|
||||
this._affects = {}
|
||||
this._typed = typed.create()
|
||||
this._typed.clear()
|
||||
@ -220,7 +222,6 @@ export default class PocomathInstance {
|
||||
if (details) {
|
||||
return returnTypeOf(details.fn, signature, this)
|
||||
}
|
||||
console.log('Checking return type of', operation)
|
||||
return returnTypeOf(this[operation], signature, this)
|
||||
})
|
||||
|
||||
@ -564,8 +565,28 @@ export default class PocomathInstance {
|
||||
`Conflicting definitions of ${signature} for ${name}`)
|
||||
}
|
||||
} else {
|
||||
// Must avoid aliasing into another instance:
|
||||
opImps[signature] = {uses: behavior.uses, does: behavior.does}
|
||||
/* Check if it's an ordinary non-template signature */
|
||||
let explicit = true
|
||||
for (const type of typesOfSignature(signature)) {
|
||||
for (const word of type.split(/[<>:\s]/)) {
|
||||
if (this._templateParam(word)) {
|
||||
explicit = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!explicit) break
|
||||
}
|
||||
opImps[signature] = {
|
||||
explicit,
|
||||
uses: behavior.uses,
|
||||
does: behavior.does
|
||||
}
|
||||
if (explicit) {
|
||||
opImps[signature].resolved = false
|
||||
} else {
|
||||
opImps[signature].hasInstantiations = {}
|
||||
opImps[signature].needsInstantiations = new Set()
|
||||
}
|
||||
for (const dep of behavior.uses) {
|
||||
const depname = dep.split('(', 1)[0]
|
||||
if (depname === 'self' || this._templateParam(depname)) {
|
||||
@ -604,11 +625,40 @@ export default class PocomathInstance {
|
||||
* Reset an operation to require creation of typed-function,
|
||||
* and if it has no implementations so far, set them up.
|
||||
*/
|
||||
_invalidate(name) {
|
||||
if (this._invalid.has(name)) return
|
||||
_invalidate(name, reason) {
|
||||
if (!(name in this._imps)) {
|
||||
this._imps[name] = {}
|
||||
}
|
||||
if (reason) {
|
||||
// Make sure no TF imps that depend on reason remain:
|
||||
for (const [signature, behavior] of Object.entries(this._imps[name])) {
|
||||
let invalidated = false
|
||||
if (reason.charAt(0) === ':') {
|
||||
const badType = reason.slice(1)
|
||||
if (signature.includes(badType)) invalidated = true
|
||||
} else {
|
||||
for (const dep of behavior.uses) {
|
||||
if (dep.includes(reason)) {
|
||||
invalidated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invalidated) {
|
||||
if (behavior.explicit) {
|
||||
if (behavior.resolved) delete this._TFimps[signature]
|
||||
behavior.resolved = false
|
||||
} else {
|
||||
for (const fullSig
|
||||
of Object.values(behavior.hasInstantiations)) {
|
||||
delete this._TFimps[fullSig]
|
||||
}
|
||||
behavior.hasInstantiations = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._invalid.has(name)) return
|
||||
this._invalid.add(name)
|
||||
this._invalidateDependents(name)
|
||||
const self = this
|
||||
@ -628,7 +678,7 @@ export default class PocomathInstance {
|
||||
_invalidateDependents(name) {
|
||||
if (name in this._affects) {
|
||||
for (const ancestor of this._affects[name]) {
|
||||
this._invalidate(ancestor)
|
||||
this._invalidate(ancestor, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -642,6 +692,10 @@ export default class PocomathInstance {
|
||||
if (!imps) {
|
||||
throw new SyntaxError(`No implementations for ${name}`)
|
||||
}
|
||||
if (!(this._TFimps[name])) {
|
||||
this._TFimps[name] = {}
|
||||
}
|
||||
const tf_imps = this._TFimps[name]
|
||||
/* Collect the entries we know the types for */
|
||||
const usableEntries = []
|
||||
for (const entry of Object.entries(imps)) {
|
||||
@ -664,20 +718,13 @@ export default class PocomathInstance {
|
||||
* in the midst of being reassembled
|
||||
*/
|
||||
Object.defineProperty(this, name, {configurable: true, value: 'limbo'})
|
||||
const tf_imps = {}
|
||||
for (const [rawSignature, behavior] of usableEntries) {
|
||||
/* Check if it's an ordinary non-template signature */
|
||||
let explicit = true
|
||||
for (const type of typesOfSignature(rawSignature)) {
|
||||
for (const word of type.split(/[<>:\s]/)) {
|
||||
if (this._templateParam(word)) {
|
||||
explicit = false
|
||||
break
|
||||
}
|
||||
if (behavior.explicit) {
|
||||
if (!(behavior.resolved)) {
|
||||
this._addTFimplementation(tf_imps, rawSignature, behavior)
|
||||
tf_imps[rawSignature]._pocoSignature = rawSignature
|
||||
behavior.resolved = true
|
||||
}
|
||||
}
|
||||
if (explicit) {
|
||||
this._addTFimplementation(tf_imps, rawSignature, behavior)
|
||||
continue
|
||||
}
|
||||
/* It's a template, have to instantiate */
|
||||
@ -693,12 +740,9 @@ export default class PocomathInstance {
|
||||
}
|
||||
}
|
||||
/* First, add the known instantiations, gathering all types needed */
|
||||
if (!('instantiations' in behavior)) {
|
||||
behavior.instantiations = new Set()
|
||||
if (ubType) behavior.instantiations.add(ubType)
|
||||
}
|
||||
if (ubType) behavior.needsInstantiations.add(ubType)
|
||||
let instantiationSet = new Set()
|
||||
for (const instType of behavior.instantiations) {
|
||||
for (const instType of behavior.needsInstantiations) {
|
||||
instantiationSet.add(instType)
|
||||
const otherTypes =
|
||||
ubType ? this.subtypesOf(instType) : this._priorTypes[instType]
|
||||
@ -710,6 +754,7 @@ export default class PocomathInstance {
|
||||
for (const instType of instantiationSet) {
|
||||
if (!(instType in this.Types)) continue
|
||||
if (this.Types[instType] === anySpec) continue
|
||||
if (instType in behavior.hasInstantiations) continue
|
||||
const signature =
|
||||
substituteInSignature(rawSignature, theTemplateParam, instType)
|
||||
/* Don't override an explicit implementation: */
|
||||
@ -742,10 +787,14 @@ export default class PocomathInstance {
|
||||
}
|
||||
this._addTFimplementation(
|
||||
tf_imps, signature, {uses, does: patch})
|
||||
tf_imps[signature]._pocoSignature = rawSignature
|
||||
tf_imps[signature]._pocoInstance = instType
|
||||
behavior.hasInstantiations[instType] = signature
|
||||
}
|
||||
/* Now add the catchall signature */
|
||||
/* (Not needed if if it's a bounded template) */
|
||||
if (ubType) continue
|
||||
if ('_catchall_' in behavior.hasInstantiations) continue
|
||||
let templateCall = `<${theTemplateParam}>`
|
||||
/* Relying here that the base of 'Foo<T>' is 'Foo': */
|
||||
let baseSignature = rawSignature.replaceAll(templateCall, '')
|
||||
@ -886,11 +935,11 @@ export default class PocomathInstance {
|
||||
/* Arrange that the desired instantiation will be there next
|
||||
* time so we don't have to go through that again for this type
|
||||
*/
|
||||
refs[theTemplateParam] = instantiateFor
|
||||
behavior.instantiations.add(instantiateFor)
|
||||
behavior.needsInstantiations.add(instantiateFor)
|
||||
self._invalidate(name)
|
||||
// And update refs because we now know the type we're instantiating
|
||||
// for:
|
||||
refs[theTemplateParam] = instantiateFor
|
||||
const innerRefs = {}
|
||||
for (const dep in simplifiedUses) {
|
||||
const simplifiedDep = simplifiedUses[dep]
|
||||
@ -903,7 +952,7 @@ export default class PocomathInstance {
|
||||
needsig, theTemplateParam, instantiateFor)
|
||||
let resname = simplifiedDep
|
||||
if (resname == 'self') resname = name
|
||||
innerRefs[dep] = self._pocoresolve(
|
||||
innerRefs[dep] = self.resolve(
|
||||
resname, subsig, refs[simplifiedDep])
|
||||
} else {
|
||||
innerRefs[dep] = refs[simplifiedDep]
|
||||
@ -913,13 +962,15 @@ export default class PocomathInstance {
|
||||
// Finally ready to make the call.
|
||||
const implementation = behavior.does(innerRefs)
|
||||
// We can access return type information here
|
||||
// And in particular, if it's a template, we should try to
|
||||
// And in particular, if it might be a template, we should try to
|
||||
// instantiate it:
|
||||
const returnType = returnTypeOf(implementation, wantSig, self)
|
||||
const instantiated = self._maybeInstantiate(returnType)
|
||||
if (instantiated) {
|
||||
const tempBase = instantiated.split('<',1)[0]
|
||||
self._invalidateDependents(':' + tempBase)
|
||||
for (const possibility of returnType.split('|')) {
|
||||
const instantiated = self._maybeInstantiate(possibility)
|
||||
if (instantiated) {
|
||||
const tempBase = instantiated.split('<',1)[0]
|
||||
self._invalidateDependents(':' + tempBase)
|
||||
}
|
||||
}
|
||||
return implementation(...args)
|
||||
}
|
||||
@ -932,6 +983,7 @@ export default class PocomathInstance {
|
||||
const outerUses = new Set(Object.values(simplifiedUses))
|
||||
this._addTFimplementation(
|
||||
tf_imps, signature, {uses: outerUses, does: patch})
|
||||
behavior.hasInstantiations._catchall_ = rawSignature
|
||||
}
|
||||
this._correctPartialSelfRefs(name, tf_imps)
|
||||
// Make sure we have all of the needed (template) types; and if they
|
||||
@ -946,9 +998,17 @@ export default class PocomathInstance {
|
||||
}
|
||||
}
|
||||
for (const badSig of badSigs) {
|
||||
const imp = tf_imps[badSig]
|
||||
delete tf_imps[badSig]
|
||||
const fromBehavior = this._imps[name][imp._pocoSignature]
|
||||
if (fromBehavior.explicit) {
|
||||
fromBehavior.resolved = false
|
||||
} else {
|
||||
delete fromBehavior.hasInstantiations[imp._pocoInstance]
|
||||
}
|
||||
}
|
||||
const tf = this._typed(name, tf_imps)
|
||||
Object.defineProperty(tf, 'fromInstance', {value: this})
|
||||
Object.defineProperty(this, name, {configurable: true, value: tf})
|
||||
return tf
|
||||
}
|
||||
@ -1006,13 +1066,20 @@ export default class PocomathInstance {
|
||||
}
|
||||
if (func === 'self') {
|
||||
if (needsig) {
|
||||
if (full_self_referential) {
|
||||
throw new SyntaxError(
|
||||
'typed-function does not support mixed full and '
|
||||
+ 'partial self-reference')
|
||||
}
|
||||
if (subsetOfKeys(typesOfSignature(needsig), this.Types)) {
|
||||
part_self_references.push(needsig)
|
||||
/* Maybe we can resolve the self reference without troubling
|
||||
* typed-function:
|
||||
*/
|
||||
if (needsig in imps && typeof imps[needsig] == 'function') {
|
||||
refs[dep] = imps[needsig]
|
||||
} else {
|
||||
if (full_self_referential) {
|
||||
throw new SyntaxError(
|
||||
'typed-function does not support mixed full and '
|
||||
+ 'partial self-reference')
|
||||
}
|
||||
if (subsetOfKeys(typesOfSignature(needsig), this.Types)) {
|
||||
part_self_references.push(needsig)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (part_self_references.length) {
|
||||
@ -1024,19 +1091,41 @@ export default class PocomathInstance {
|
||||
}
|
||||
} else {
|
||||
if (this[func] === 'limbo') {
|
||||
/* We are in the midst of bundling func, so have to use
|
||||
* an indirect reference to func. And given that, there's
|
||||
* really no helpful way to extract a specific signature
|
||||
/* We are in the midst of bundling func */
|
||||
let fallback = true
|
||||
/* So the first thing we can do is try the tf_imps we are
|
||||
* accumulating:
|
||||
*/
|
||||
const self = this
|
||||
refs[dep] = function () { // is this the most efficient?
|
||||
return self[func].apply(this, arguments)
|
||||
if (needsig) {
|
||||
const tempTF = this._typed('dummy_' + func, this._TFimps[func])
|
||||
let result = undefined
|
||||
try {
|
||||
result = this._typed.find(tempTF, needsig, {exact: true})
|
||||
} catch {}
|
||||
if (result) {
|
||||
refs[dep] = result
|
||||
fallback = false
|
||||
}
|
||||
}
|
||||
if (fallback) {
|
||||
/* Either we need the whole function or the signature
|
||||
* we need is not available yet, so we have to use
|
||||
* an indirect reference to func. And given that, there's
|
||||
* really no helpful way to extract a specific signature
|
||||
*/
|
||||
const self = this
|
||||
const redirect = function () { // is this the most efficient?
|
||||
return self[func].apply(this, arguments)
|
||||
}
|
||||
Object.defineProperty(redirect, 'name', {value: func})
|
||||
Object.defineProperty(redirect, 'fromInstance', {value: this})
|
||||
refs[dep] = redirect
|
||||
}
|
||||
} else {
|
||||
// can bundle up func, and grab its signature if need be
|
||||
let destination = this[func]
|
||||
if (destination && needsig) {
|
||||
destination = this._pocoresolve(func, needsig)
|
||||
destination = this.resolve(func, needsig)
|
||||
}
|
||||
refs[dep] = destination
|
||||
}
|
||||
@ -1076,7 +1165,8 @@ export default class PocomathInstance {
|
||||
_correctPartialSelfRefs(name, imps) {
|
||||
for (const aSignature in imps) {
|
||||
if (!(imps[aSignature].deferred)) continue
|
||||
const part_self_references = imps[aSignature].psr
|
||||
const deferral = imps[aSignature]
|
||||
const part_self_references = deferral.psr
|
||||
const corrected_self_references = []
|
||||
for (const neededSig of part_self_references) {
|
||||
// Have to find a match for neededSig among the other signatures
|
||||
@ -1098,8 +1188,8 @@ export default class PocomathInstance {
|
||||
+ `${name}(${neededSig})`)
|
||||
}
|
||||
}
|
||||
const refs = imps[aSignature].builtRefs
|
||||
const does = imps[aSignature].sigDoes
|
||||
const refs = deferral.builtRefs
|
||||
const does = deferral.sigDoes
|
||||
imps[aSignature] = this._typed.referTo(
|
||||
...corrected_self_references, (...impls) => {
|
||||
for (let i = 0; i < part_self_references.length; ++i) {
|
||||
@ -1110,6 +1200,8 @@ export default class PocomathInstance {
|
||||
return implementation
|
||||
}
|
||||
)
|
||||
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
||||
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,16 +1374,30 @@ export default class PocomathInstance {
|
||||
typedFunction = this[name]
|
||||
}
|
||||
let result = undefined
|
||||
if (!this._typed.isTypedFunction(typedFunction)) {
|
||||
return result
|
||||
const haveTF = this._typed.isTypedFunction(typedFunction)
|
||||
if (haveTF) {
|
||||
try {
|
||||
result = this._typed.findSignature(typedFunction, sig, {exact: true})
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
try {
|
||||
result = this._typed.findSignature(typedFunction, sig, {exact: true})
|
||||
} catch {
|
||||
}
|
||||
if (result) return result
|
||||
if (result || !(this._imps[name])) return result
|
||||
const foundsig = this._findSubtypeImpl(name, this._imps[name], sig)
|
||||
if (foundsig) return this._typed.findSignature(typedFunction, foundsig)
|
||||
if (foundsig) {
|
||||
if (haveTF) {
|
||||
return this._typed.findSignature(typedFunction, foundsig)
|
||||
}
|
||||
// We have an implementation but not a typed function. Do the best
|
||||
// we can:
|
||||
const foundImpl = this._imps[name][foundsig]
|
||||
const needs = {}
|
||||
for (const dep of foundImpl.uses) {
|
||||
const [base, sig] = dep.split('()')
|
||||
needs[dep] = this.resolve(base, sig)
|
||||
}
|
||||
const pseudoImpl = foundImpl.does(needs)
|
||||
return {fn: pseudoImpl, implementation: pseudoImpl}
|
||||
}
|
||||
const wantTypes = typeListOfSignature(sig)
|
||||
for (const [implSig, details]
|
||||
of typedFunction._typedFunctionData.signatureMap) {
|
||||
@ -1314,7 +1420,12 @@ export default class PocomathInstance {
|
||||
return result
|
||||
}
|
||||
|
||||
_pocoresolve(name, sig, typedFunction) {
|
||||
/* Returns a function that implements the operation with the given name
|
||||
* when called with the given signature. The optional third argument is
|
||||
* the typed function that provides the operation name, which can be
|
||||
* passed in for efficiency if it is already available.
|
||||
*/
|
||||
resolve = Returns('function', function (name, sig, typedFunction) {
|
||||
if (!this._typed.isTypedFunction(typedFunction)) {
|
||||
typedFunction = this[name]
|
||||
}
|
||||
@ -1323,6 +1434,6 @@ export default class PocomathInstance {
|
||||
// total punt, revert to typed-function resolution on every call;
|
||||
// hopefully this happens rarely:
|
||||
return typedFunction
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
|
||||
export const absquare = {
|
||||
T: ({
|
||||
T,
|
||||
'square(T)': sq,
|
||||
'abs(T)': abval
|
||||
}) => t => sq(abval(t))
|
||||
}) => Returns(T, t => sq(abval(t)))
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
import {Returns, returnTypeOf} from '../core/Returns.mjs'
|
||||
|
||||
export const square = {
|
||||
T: ({'multiply(T,T)': multT}) => x => multT(x,x)
|
||||
T: ({'multiply(T,T)': multT}) => Returns(
|
||||
returnTypeOf(multT), x => multT(x,x))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
export * from './Types/number.mjs'
|
||||
|
||||
/* Absolute value squared */
|
||||
export const absquare = {
|
||||
number: ({'square(number)': sqn}) => n => sqn(n)
|
||||
'T:number': ({T, 'square(T)': sqn}) => Returns(T, n => sqn(n))
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
|
||||
export * from './Types/number.mjs'
|
||||
|
||||
export const multiply = {'number,number': () => (m,n) => m*n}
|
||||
export const multiply = {'T:number,T': ({T}) => Returns(T, (m,n) => m*n)}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
export * from './Types/number.mjs'
|
||||
|
||||
export const sqrt = {
|
||||
@ -5,13 +6,13 @@ export const sqrt = {
|
||||
config,
|
||||
'complex(number,number)': cplx,
|
||||
'negate(number)': neg}) => {
|
||||
if (config.predictable || !cplx) {
|
||||
return n => isNaN(n) ? NaN : Math.sqrt(n)
|
||||
if (config.predictable || !cplx) {
|
||||
return Returns('number', n => isNaN(n) ? NaN : Math.sqrt(n))
|
||||
}
|
||||
return Returns('number|Complex<number>', n => {
|
||||
if (isNaN(n)) return NaN
|
||||
if (n >= 0) return Math.sqrt(n)
|
||||
return cplx(0, Math.sqrt(neg(n)))
|
||||
})
|
||||
}
|
||||
return n => {
|
||||
if (isNaN(n)) return NaN
|
||||
if (n >= 0) return Math.sqrt(n)
|
||||
return cplx(0, Math.sqrt(neg(n)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ describe('complex', () => {
|
||||
assert.deepStrictEqual(
|
||||
math.multiply(q0, math.quaternion(2, 1, 0.1, 0.1)),
|
||||
math.quaternion(1.9, 1.1, 2.1, -0.9))
|
||||
math.absquare(math.complex(1.25, 2.5)) //HACK: need absquare(Complex<number>)
|
||||
assert.strictEqual(math.abs(q0), Math.sqrt(2))
|
||||
assert.strictEqual(math.abs(q1), Math.sqrt(33)/4)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user