feat(quaternion): Add convenience quaternion creator function #48

Merged
glen merged 1 commits from quaternion into main 2022-08-07 03:27:46 +00:00
10 changed files with 104 additions and 16 deletions

6
src/bigint/absquare.mjs Normal file
View File

@ -0,0 +1,6 @@
export * from './Types/bigint.mjs'
/* Absolute value squared */
export const absquare = {
bigint: ({'square(bigint)': sqb}) => b => sqb(b)
}

View File

@ -3,6 +3,7 @@ import {identity} from '../generic/identity.mjs'
export * from './Types/bigint.mjs'
export {absquare} from './absquare.mjs'
export {add} from './add.mjs'
export {compare} from './compare.mjs'
export const conjugate = {bigint: () => identity}

View File

@ -2,7 +2,9 @@ export * from './Types/Complex.mjs'
export const abs = {
'Complex<T>': ({
'sqrt(T)': sqt,
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
'absquare(Complex<T>)': absq
}) => z => sqt(absq(z))
}) => z => sqrt(absq(z))
}

View File

@ -2,7 +2,8 @@ export * from './Types/Complex.mjs'
export const absquare = {
'Complex<T>': ({
'add(T,T)': plus,
'square(T)': sqr
}) => z => plus(sqr(z.re), sqr(z.im))
add, // Calculation of exact type needed in add (underlying numeric of T)
// is (currently) too involved for Pocomath
'self(T)': absq
}) => z => add(absq(z.re), absq(z.im))
}

View File

@ -12,6 +12,7 @@ export {invert} from './invert.mjs'
export {isZero} from './isZero.mjs'
export {multiply} from './multiply.mjs'
export {negate} from './negate.mjs'
export {quaternion} from './quaternion.mjs'
export {quotient} from './quotient.mjs'
export {roundquotient} from './roundquotient.mjs'
export {sqrt} from './sqrt.mjs'

View File

@ -0,0 +1,5 @@
export * from './Types/Complex.mjs'
export const quaternion = {
'T,T,T,T': ({complex}) => (r,i,j,k) => complex(complex(r,j), complex(i,k))
}

View File

@ -64,6 +64,7 @@ export default class PocomathInstance {
*/
this._priorTypes = {}
this._seenTypes = new Set() // all types that have occurred in a signature
this._maxDepthSeen = 1 // deepest template nesting we've actually encountered
this._invalid = new Set() // methods that are currently invalid
this._config = {predictable: false, epsilon: 1e-12}
const self = this
@ -336,6 +337,7 @@ export default class PocomathInstance {
let nextSuper = type
while (nextSuper) {
if (this._priorTypes[nextSuper].has(from)) break
if (from === nextSuper) break
this._typed.addConversion(
{from, to: nextSuper, convert: spec.from[from]})
this._invalidateDependents(':' + nextSuper)
@ -360,12 +362,16 @@ export default class PocomathInstance {
}
let nextSuper = to
while (nextSuper) {
if (type === nextSuper) break
try { // may already be a conversion, and no way to ask
this._typed.addConversion({
from: type,
to: nextSuper,
convert: this.Types[to].from[fromtype]
})
this._invalidateDependents(':' + nextSuper)
} catch {
}
this._priorTypes[nextSuper].add(type)
nextSuper = this.Types[nextSuper].refines
}
@ -609,6 +615,14 @@ export default class PocomathInstance {
substituteInSig(rawSignature, theTemplateParam, instType)
/* Don't override an explicit implementation: */
if (signature in imps) continue
/* Don't go too deep */
let maxdepth = 0
for (const argType in typeListOfSignature(signature)) {
const depth = argType.split('<').length
if (depth > maxdepth) maxdepth = depth
}
if (maxdepth > this._maxDepthSeen + 1) continue
/* All right, go ahead and instantiate */
const uses = new Set()
for (const dep of behavior.uses) {
if (this._templateParam(dep)) continue
@ -718,6 +732,10 @@ export default class PocomathInstance {
+ 'supertype of at least one of them')
}
}
const depth = instantiateFor.split('<').length
if (depth > self._maxDepthSeen) {
self._maxDepthSeen = depth
}
/* Generate the list of actual wanted types */
const wantTypes = parTypes.map(type => substituteInSig(
type, theTemplateParam, instantiateFor))
@ -790,6 +808,28 @@ export default class PocomathInstance {
tf_imps, signature, {uses: outerUses, does: patch})
}
this._correctPartialSelfRefs(name, tf_imps)
// Make sure we have all of the needed (template) types; and if they
// can't be added (because they have been instantiated too deep),
// ditch the signature:
const badSigs = new Set()
for (const sig in tf_imps) {
for (const type of typeListOfSignature(sig)) {
if (type.includes('<')) {
// it's a template type, turn it into a template and an arg
let base = type.split('<',1)[0]
const arg = type.slice(base.length+1, -1)
if (base.slice(0,3) === '...') {
base = base.slice(3)
}
if (this.instantiateTemplate(base, arg) === undefined) {
badSigs.add(sig)
}
}
}
}
for (const badSig of badSigs) {
delete tf_imps[badSig]
}
const tf = this._typed(name, tf_imps)
Object.defineProperty(this, name, {configurable: true, value: tf})
return tf
@ -928,8 +968,8 @@ export default class PocomathInstance {
* in the instance.
*/
_ensureTemplateTypes(template, type) {
let [base, arg] = template.split('<', 2)
arg = arg.slice(0,-1)
const base = template.split('<', 1)[0]
const arg = template.slice(base.length + 1, -1)
if (!arg) {
throw new Error(
'Implementation error in _ensureTemplateTypes', template, type)
@ -951,9 +991,15 @@ export default class PocomathInstance {
/* Maybe add the instantiation of template type base with argument tyoe
* instantiator to the Types of this instance, if it hasn't happened already.
* Returns the name of the type if added, false otherwise.
* Returns the name of the type if added, false if it was already there,
* and undefined if the type is declined (because of being nested too deep).
*/
instantiateTemplate(base, instantiator) {
const depth = instantiator.split('<').length
if (depth > this._maxDepthSeen ) {
// don't bother with types much deeper thant we have seen
return undefined
}
const wantsType = `${base}<${instantiator}>`
if (wantsType in this.Types) return false
// OK, need to generate the type from the template

6
src/number/absquare.mjs Normal file
View File

@ -0,0 +1,6 @@
export * from './Types/number.mjs'
/* Absolute value squared */
export const absquare = {
number: ({'square(number)': sqn}) => n => sqn(n)
}

View File

@ -4,6 +4,7 @@ import {identity} from '../generic/identity.mjs'
export * from './Types/number.mjs'
export {abs} from './abs.mjs'
export {absquare} from './absquare.mjs'
export {add} from './add.mjs'
export {compare} from './compare.mjs'
export const conjugate = {number: () => identity}

View File

@ -68,4 +68,23 @@ describe('complex', () => {
assert.strictEqual(math.floor(gi), gi) // literally a no-op
})
it('performs rudimentary quaternion calculations', () => {
const q0 = math.quaternion(1, 0, 1, 0)
const q1 = math.quaternion(1, 0.5, 0.5, 0.75)
assert.deepStrictEqual(
q1,
math.complex(math.complex(1, 0.5), math.complex(0.5, 0.75)))
assert.deepStrictEqual(
math.add(q0,q1),
math.quaternion(2, 0.5, 1.5, 0.75))
assert.deepStrictEqual(
math.multiply(q0, q1),
math.quaternion(0.5, 1.25, 1.5, 0.25))
assert.deepStrictEqual(
math.multiply(q0, math.quaternion(2, 1, 0.1, 0.1)),
math.quaternion(1.9, 1.1, 2.1, -0.9))
assert.strictEqual(math.abs(q0), Math.sqrt(2))
assert.strictEqual(math.abs(q1), Math.sqrt(33)/4)
})
})