feat(number): Provide return types for all operations

This commit is contained in:
Glen Whitney 2022-08-29 21:18:59 -04:00
parent 3957ae8adf
commit 1ee6da4294
9 changed files with 75 additions and 70 deletions

View File

@ -3,6 +3,7 @@ export * from './Types/Complex.mjs'
export const equalTT = {
'Complex<T>,Complex<T>': ({
T,
'self(T,T)': me
}) => Returns('boolean', (w, z) => me(w.re, z.re) && me(w.im, z.im)),
// NOTE: Although I do not understand exactly why, with typed-function@3.0's

View File

@ -800,26 +800,9 @@ export default class PocomathInstance {
/* First, add the known instantiations, gathering all types needed */
if (ubType) behavior.needsInstantiations.add(ubType)
let instantiationSet = new Set()
for (const instType of behavior.needsInstantiations) {
instantiationSet.add(instType)
const otherTypes =
ubType ? this.subtypesOf(instType) : this._priorTypes[instType]
for (const other of otherTypes) {
instantiationSet.add(other)
}
}
/* Prevent other existing signatures from blocking use of top-level
* templates via conversions:
*/
let baseSignature = rawSignature.replaceAll(templateCall, '')
/* Any remaining template params are top-level */
const signature = substituteInSignature(
baseSignature, theTemplateParam, 'any')
const hasTopLevel = (signature !== baseSignature)
if (!ubType && hasTopLevel) {
// collect upper-bound types
const ubTypes = new Set()
const ubTypes = new Set()
if (!ubType) {
// Collect all upper-bound types for this signature
for (const othersig in imps) {
const thisUB = upperBounds.exec(othersig)
if (thisUB) ubTypes.add(thisUB[2])
@ -839,6 +822,27 @@ export default class PocomathInstance {
}
}
}
}
for (const instType of behavior.needsInstantiations) {
instantiationSet.add(instType)
const otherTypes =
ubType ? this.subtypesOf(instType) : this._priorTypes[instType]
for (const other of otherTypes) {
if (!(this._atOrBelowSomeType(other, ubTypes))) {
instantiationSet.add(other)
}
}
}
/* Prevent other existing signatures from blocking use of top-level
* templates via conversions:
*/
let baseSignature = rawSignature.replaceAll(templateCall, '')
/* Any remaining template params are top-level */
const signature = substituteInSignature(
baseSignature, theTemplateParam, 'any')
const hasTopLevel = (signature !== baseSignature)
if (!ubType && hasTopLevel) {
for (const othersig in imps) {
let basesig = othersig.replaceAll(templateCall, '')
const testsig = substituteInSignature(
@ -859,23 +863,9 @@ export default class PocomathInstance {
for (const possibility of otherTypeCollection) {
for (const convtype of this._priorTypes[possibility]) {
if (this.isSubtypeOf(convtype, possibility)) continue
if (ubTypes.has(convtype)) continue
let belowUB = false
for (const anUB of ubTypes) {
if (anUB in this.Templates) {
if (convtype.slice(0, anUB.length) === anUB) {
belowUB = true
break
}
} else {
if (this.isSubtypeOf(convtype, anUB)) {
belowUB = true
break
}
}
if (!(this._atOrBelowSomeType(convtype, ubTypes))) {
instantiationSet.add(convtype)
}
if (belowUB) continue
instantiationSet.add(convtype)
}
}
}
@ -943,22 +933,7 @@ export default class PocomathInstance {
throw new SyntaxError(
`Cannot find template parameter in ${rawSignature}`)
}
/* And eliminate template parameters from the dependencies */
const simplifiedUses = {}
for (const dep of behavior.uses) {
let [func, needsig] = dep.split(/[()]/)
if (needsig) {
const subsig = substituteInSignature(
needsig, theTemplateParam, '')
if (subsig === needsig) {
simplifiedUses[dep] = dep
} else {
simplifiedUses[dep] = func
}
} else {
simplifiedUses[dep] = dep
}
}
/* Now build the catchall implementation */
const self = this
/* For return type annotation, we may have to fix this to
@ -1122,6 +1097,28 @@ export default class PocomathInstance {
return tf
}
/* Takes a type and a set of types and returns true if the type
* is a subtype of some type in the set.
*/
_atOrBelowSomeType(type, typeSet) {
if (typeSet.has(type)) return true
let belowSome = false
for (const anUB of typeSet) {
if (anUB in this.Templates) {
if (type.slice(0, anUB.length) === anUB) {
belowSome = true
break
}
} else {
if (this.isSubtypeOf(type, anUB)) {
belowSome = true
break
}
}
}
return belowSome
}
/* Takes an arbitrary type and performs an instantiation if necessary.
* @param {string} type The type to instantiate
* @param {string | bool | undefined }

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
/* Lifted from mathjs/src/utils/number.js */
/**
* Minimum number added to one that makes the result different than one
@ -48,5 +50,6 @@ function nearlyEqual (x, y, epsilon) {
export const compare = {
'number,number': ({
config
}) => (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1)
}) => Returns(
'NumInt', (x,y) => nearlyEqual(x, y, config.epsilon) ? 0 : (x > y ? 1 : -1))
}

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs'
export const invert = {number: () => n => 1/n}
export const invert = {number: () => Returns('number', n => 1/n)}

View File

@ -1,6 +1,6 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs'
export const isZero = {
number: () => n => n === 0,
NumInt: () => n => n === 0 // necessary because of generic template
'T:number': () => Returns('boolean', n => n === 0)
}

View File

@ -1,3 +1,5 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs'
export const one = {number: () => () => 1}
export const one = {number: () => Returns('NumInt', () => 1)}

View File

@ -1,15 +1,10 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs'
const intquotient = () => (n,d) => {
if (d === 0) return d
return Math.floor(n/d)
}
export const quotient = {
// Hmmm, seem to need all of these because of the generic template version
// Should be a way around that
'NumInt,NumInt': intquotient,
'NumInt,number': intquotient,
'number,NumInt': intquotient,
'number,number': intquotient
'T:number,T': () => Returns('NumInt', (n,d) => {
if (d === 0) return d
return Math.floor(n/d)
})
}

View File

@ -1,8 +1,10 @@
import Returns from '../core/Returns.mjs'
export * from './Types/number.mjs'
export const roundquotient = {
'number,number': () => (n,d) => {
'number,number': () => Returns('NumInt', (n,d) => {
if (d === 0) return d
return Math.round(n/d)
}
})
}

View File

@ -60,6 +60,9 @@ describe('The default full pocomath instance "math"', () => {
assert.strictEqual(quatType, 'Complex<Complex<NumInt>>')
assert.strictEqual(
math.returnTypeOf('multiply', quatType + ',' + quatType), quatType)
assert.strictEqual(math.returnTypeOf('isZero', 'NumInt'), 'boolean')
assert.strictEqual(
math.returnTypeOf('roundquotient', 'NumInt,number'), 'NumInt')
})
it('can subtract numbers', () => {