feat(quaternion): Add convenience quaternion creator function #48
6
src/bigint/absquare.mjs
Normal file
6
src/bigint/absquare.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
|
/* Absolute value squared */
|
||||||
|
export const absquare = {
|
||||||
|
bigint: ({'square(bigint)': sqb}) => b => sqb(b)
|
||||||
|
}
|
@ -3,6 +3,7 @@ import {identity} from '../generic/identity.mjs'
|
|||||||
|
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
|
export {absquare} from './absquare.mjs'
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
export {compare} from './compare.mjs'
|
export {compare} from './compare.mjs'
|
||||||
export const conjugate = {bigint: () => identity}
|
export const conjugate = {bigint: () => identity}
|
||||||
|
@ -2,7 +2,9 @@ export * from './Types/Complex.mjs'
|
|||||||
|
|
||||||
export const abs = {
|
export const abs = {
|
||||||
'Complex<T>': ({
|
'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
|
'absquare(Complex<T>)': absq
|
||||||
}) => z => sqt(absq(z))
|
}) => z => sqrt(absq(z))
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ export * from './Types/Complex.mjs'
|
|||||||
|
|
||||||
export const absquare = {
|
export const absquare = {
|
||||||
'Complex<T>': ({
|
'Complex<T>': ({
|
||||||
'add(T,T)': plus,
|
add, // Calculation of exact type needed in add (underlying numeric of T)
|
||||||
'square(T)': sqr
|
// is (currently) too involved for Pocomath
|
||||||
}) => z => plus(sqr(z.re), sqr(z.im))
|
'self(T)': absq
|
||||||
|
}) => z => add(absq(z.re), absq(z.im))
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ export {invert} from './invert.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 {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'
|
||||||
|
5
src/complex/quaternion.mjs
Normal file
5
src/complex/quaternion.mjs
Normal 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))
|
||||||
|
}
|
@ -64,6 +64,7 @@ export default class PocomathInstance {
|
|||||||
*/
|
*/
|
||||||
this._priorTypes = {}
|
this._priorTypes = {}
|
||||||
this._seenTypes = new Set() // all types that have occurred in a signature
|
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._invalid = new Set() // methods that are currently invalid
|
||||||
this._config = {predictable: false, epsilon: 1e-12}
|
this._config = {predictable: false, epsilon: 1e-12}
|
||||||
const self = this
|
const self = this
|
||||||
@ -336,6 +337,7 @@ export default class PocomathInstance {
|
|||||||
let nextSuper = type
|
let nextSuper = type
|
||||||
while (nextSuper) {
|
while (nextSuper) {
|
||||||
if (this._priorTypes[nextSuper].has(from)) break
|
if (this._priorTypes[nextSuper].has(from)) break
|
||||||
|
if (from === nextSuper) break
|
||||||
this._typed.addConversion(
|
this._typed.addConversion(
|
||||||
{from, to: nextSuper, convert: spec.from[from]})
|
{from, to: nextSuper, convert: spec.from[from]})
|
||||||
this._invalidateDependents(':' + nextSuper)
|
this._invalidateDependents(':' + nextSuper)
|
||||||
@ -360,12 +362,16 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
let nextSuper = to
|
let nextSuper = to
|
||||||
while (nextSuper) {
|
while (nextSuper) {
|
||||||
|
if (type === nextSuper) break
|
||||||
|
try { // may already be a conversion, and no way to ask
|
||||||
this._typed.addConversion({
|
this._typed.addConversion({
|
||||||
from: type,
|
from: type,
|
||||||
to: nextSuper,
|
to: nextSuper,
|
||||||
convert: this.Types[to].from[fromtype]
|
convert: this.Types[to].from[fromtype]
|
||||||
})
|
})
|
||||||
this._invalidateDependents(':' + nextSuper)
|
this._invalidateDependents(':' + nextSuper)
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
this._priorTypes[nextSuper].add(type)
|
this._priorTypes[nextSuper].add(type)
|
||||||
nextSuper = this.Types[nextSuper].refines
|
nextSuper = this.Types[nextSuper].refines
|
||||||
}
|
}
|
||||||
@ -609,6 +615,14 @@ export default class PocomathInstance {
|
|||||||
substituteInSig(rawSignature, theTemplateParam, instType)
|
substituteInSig(rawSignature, theTemplateParam, instType)
|
||||||
/* Don't override an explicit implementation: */
|
/* Don't override an explicit implementation: */
|
||||||
if (signature in imps) continue
|
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()
|
const uses = new Set()
|
||||||
for (const dep of behavior.uses) {
|
for (const dep of behavior.uses) {
|
||||||
if (this._templateParam(dep)) continue
|
if (this._templateParam(dep)) continue
|
||||||
@ -718,6 +732,10 @@ export default class PocomathInstance {
|
|||||||
+ 'supertype of at least one of them')
|
+ '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 */
|
/* Generate the list of actual wanted types */
|
||||||
const wantTypes = parTypes.map(type => substituteInSig(
|
const wantTypes = parTypes.map(type => substituteInSig(
|
||||||
type, theTemplateParam, instantiateFor))
|
type, theTemplateParam, instantiateFor))
|
||||||
@ -790,6 +808,28 @@ export default class PocomathInstance {
|
|||||||
tf_imps, signature, {uses: outerUses, does: patch})
|
tf_imps, signature, {uses: outerUses, does: patch})
|
||||||
}
|
}
|
||||||
this._correctPartialSelfRefs(name, tf_imps)
|
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)
|
const tf = this._typed(name, tf_imps)
|
||||||
Object.defineProperty(this, name, {configurable: true, value: tf})
|
Object.defineProperty(this, name, {configurable: true, value: tf})
|
||||||
return tf
|
return tf
|
||||||
@ -928,8 +968,8 @@ export default class PocomathInstance {
|
|||||||
* in the instance.
|
* in the instance.
|
||||||
*/
|
*/
|
||||||
_ensureTemplateTypes(template, type) {
|
_ensureTemplateTypes(template, type) {
|
||||||
let [base, arg] = template.split('<', 2)
|
const base = template.split('<', 1)[0]
|
||||||
arg = arg.slice(0,-1)
|
const arg = template.slice(base.length + 1, -1)
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Implementation error in _ensureTemplateTypes', template, type)
|
'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
|
/* Maybe add the instantiation of template type base with argument tyoe
|
||||||
* instantiator to the Types of this instance, if it hasn't happened already.
|
* 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) {
|
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}>`
|
const wantsType = `${base}<${instantiator}>`
|
||||||
if (wantsType in this.Types) return false
|
if (wantsType in this.Types) return false
|
||||||
// OK, need to generate the type from the template
|
// OK, need to generate the type from the template
|
||||||
|
6
src/number/absquare.mjs
Normal file
6
src/number/absquare.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
|
/* Absolute value squared */
|
||||||
|
export const absquare = {
|
||||||
|
number: ({'square(number)': sqn}) => n => sqn(n)
|
||||||
|
}
|
@ -4,6 +4,7 @@ import {identity} from '../generic/identity.mjs'
|
|||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export {abs} from './abs.mjs'
|
export {abs} from './abs.mjs'
|
||||||
|
export {absquare} from './absquare.mjs'
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
export {compare} from './compare.mjs'
|
export {compare} from './compare.mjs'
|
||||||
export const conjugate = {number: () => identity}
|
export const conjugate = {number: () => identity}
|
||||||
|
@ -68,4 +68,23 @@ describe('complex', () => {
|
|||||||
assert.strictEqual(math.floor(gi), gi) // literally a no-op
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user