feat: Implement subtypes
This should eventually be moved into typed-function itself, but for now it can be implemented on top of the existing typed-function. Uses subtypes to define (and error-check) gcd and lcm, which are only defined for integer arguments. Resolves #36.
This commit is contained in:
parent
4d38f4161c
commit
c429c19dfe
@ -1,20 +1,12 @@
|
|||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const divide = {
|
export const divide = {
|
||||||
'bigint,bigint': ({config, 'sign(bigint)': sgn}) => {
|
'bigint,bigint': ({config, 'quotient(bigint,bigint)': quot}) => {
|
||||||
if (config.predictable) {
|
if (config.predictable) return quot
|
||||||
return (n, d) => {
|
return (n, d) => {
|
||||||
if (sgn(n) === sgn(d)) return n/d
|
const q = n/d
|
||||||
const quot = n/d
|
if (q * d == n) return q
|
||||||
if (quot * d == n) return quot
|
|
||||||
return quot - 1n
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return (n, d) => {
|
|
||||||
const quot = n/d
|
|
||||||
if (quot * d == n) return quot
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
3
src/bigint/isZero.mjs
Normal file
3
src/bigint/isZero.mjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
|
export const isZero = {bigint: () => b => b === 0n}
|
@ -1,5 +1,3 @@
|
|||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export const multiply = {
|
export const multiply = {'bigint,bigint': () => (a,b) => a*b}
|
||||||
'...bigint': () => multiplicands => multiplicands.reduce((x,y) => x*y, 1n)
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
|
|
||||||
export * from './Types/bigint.mjs'
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
export {divide} from './divide.mjs'
|
export {divide} from './divide.mjs'
|
||||||
|
export const gcd = gcdType('bigint')
|
||||||
|
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 {one} from './one.mjs'
|
export {one} from './one.mjs'
|
||||||
export {sign} from './sign.mjs'
|
export {sign} from './sign.mjs'
|
||||||
|
export {quotient} from './quotient.mjs'
|
||||||
|
export {roundquotient} from './roundquotient.mjs'
|
||||||
export {sqrt} from './sqrt.mjs'
|
export {sqrt} from './sqrt.mjs'
|
||||||
export {zero} from './zero.mjs'
|
export {zero} from './zero.mjs'
|
||||||
|
13
src/bigint/quotient.mjs
Normal file
13
src/bigint/quotient.mjs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
|
/* Returns the best integer approximation to n/d */
|
||||||
|
export const quotient = {
|
||||||
|
'bigint,bigint': ({'sign(bigint)': sgn}) => (n, d) => {
|
||||||
|
const dSgn = sgn(d)
|
||||||
|
if (dSgn === 0n) return 0n
|
||||||
|
if (sgn(n) === dSgn) return n/d
|
||||||
|
const quot = n/d
|
||||||
|
if (quot * d == n) return quot
|
||||||
|
return quot - 1n
|
||||||
|
}
|
||||||
|
}
|
15
src/bigint/roundquotient.mjs
Normal file
15
src/bigint/roundquotient.mjs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export * from './Types/bigint.mjs'
|
||||||
|
|
||||||
|
/* Returns the closest integer approximation to n/d */
|
||||||
|
export const roundquotient = {
|
||||||
|
'bigint,bigint': ({'sign(bigint)': sgn}) => (n, d) => {
|
||||||
|
const dSgn = sgn(d)
|
||||||
|
if (dSgn === 0n) return 0n
|
||||||
|
const candidate = n/d
|
||||||
|
const rem = n - d*candidate
|
||||||
|
const absd = d*dSgn
|
||||||
|
if (2n * rem > absd) return candidate + dSgn
|
||||||
|
if (-2n * rem >= absd) return candidate - dSgn
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
@ -12,9 +12,19 @@ const Complex = new PocomathInstance('Complex')
|
|||||||
Complex.installType('Complex', {
|
Complex.installType('Complex', {
|
||||||
test: isComplex,
|
test: isComplex,
|
||||||
from: {
|
from: {
|
||||||
number: x => ({re: x, im: 0}),
|
number: x => ({re: x, im: 0})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Complex.installType('GaussianInteger', {
|
||||||
|
test: z => typeof z.re == 'bigint' && typeof z.im == 'bigint',
|
||||||
|
refines: 'Complex',
|
||||||
|
from: {
|
||||||
bigint: x => ({re: x, im: 0n})
|
bigint: x => ({re: x, im: 0n})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Complex.promoteUnary = {
|
||||||
|
Complex: ({self,complex}) => z => complex(self(z.re), self(z.im))
|
||||||
|
}
|
||||||
|
|
||||||
export {Complex}
|
export {Complex}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const abs = {Complex: ({sqrt, add, multiply}) => z => {
|
export const abs = {
|
||||||
return sqrt(add(multiply(z.re, z.re), multiply(z.im, z.im)))
|
Complex: ({sqrt, 'absquare(Complex)': absq}) => z => sqrt(absq(z))
|
||||||
}}
|
}
|
||||||
|
5
src/complex/absquare.mjs
Normal file
5
src/complex/absquare.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const absquare = {
|
||||||
|
Complex: ({add, square}) => z => add(square(z.re), square(z.im))
|
||||||
|
}
|
6
src/complex/conjugate.mjs
Normal file
6
src/complex/conjugate.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const conjugate = {
|
||||||
|
Complex: ({negate, complex}) => z => complex(z.re, negate(z.im))
|
||||||
|
}
|
||||||
|
|
17
src/complex/gcd.mjs
Normal file
17
src/complex/gcd.mjs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import PocomathInstance from '../core/PocomathInstance.mjs'
|
||||||
|
import * as Complex from './Types/Complex.mjs'
|
||||||
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
|
|
||||||
|
const imps = {
|
||||||
|
gcdComplexRaw: gcdType('Complex'),
|
||||||
|
gcd: { // Only return gcds with positive real part
|
||||||
|
'Complex, Complex': ({gcdComplexRaw, sign, one, negate}) => (z,m) => {
|
||||||
|
const raw = gcdComplexRaw(z, m)
|
||||||
|
if (sign(raw.re) === one(raw.re)) return raw
|
||||||
|
return negate(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gcd = PocomathInstance.merge(Complex, imps)
|
||||||
|
|
5
src/complex/isZero.mjs
Normal file
5
src/complex/isZero.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const isZero = {
|
||||||
|
Complex: ({self}) => z => self(z.re) && self(z.im)
|
||||||
|
}
|
14
src/complex/multiply.mjs
Normal file
14
src/complex/multiply.mjs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const multiply = {
|
||||||
|
'Complex,Complex': ({
|
||||||
|
'complex(any,any)': cplx,
|
||||||
|
add,
|
||||||
|
subtract,
|
||||||
|
self
|
||||||
|
}) => (w,z) => {
|
||||||
|
return cplx(
|
||||||
|
subtract(self(w.re, z.re), self(w.im, z.im)),
|
||||||
|
add(self(w.re, z.im), self(w.im, z.re)))
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,18 @@
|
|||||||
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
|
|
||||||
export * from './Types/Complex.mjs'
|
export * from './Types/Complex.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 {conjugate} from './conjugate.mjs'
|
||||||
export {complex} from './complex.mjs'
|
export {complex} from './complex.mjs'
|
||||||
|
export {gcd} from './gcd.mjs'
|
||||||
|
export {isZero} from './isZero.mjs'
|
||||||
|
export {multiply} from './multiply.mjs'
|
||||||
export {negate} from './negate.mjs'
|
export {negate} from './negate.mjs'
|
||||||
|
export {quotient} from './quotient.mjs'
|
||||||
|
export {roundquotient} from './roundquotient.mjs'
|
||||||
export {sqrt} from './sqrt.mjs'
|
export {sqrt} from './sqrt.mjs'
|
||||||
|
export {zero} from './zero.mjs'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export * from './Types/Complex.mjs'
|
import {Complex} from './Types/Complex.mjs'
|
||||||
|
|
||||||
export const negate = {
|
const negate = Complex.promoteUnary
|
||||||
Complex: ({self}) => z => ({re: self(z.re), im: self(z.im)})
|
|
||||||
}
|
export {Complex, negate}
|
||||||
|
5
src/complex/quotient.mjs
Normal file
5
src/complex/quotient.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from './roundquotient.mjs'
|
||||||
|
|
||||||
|
export const quotient = {
|
||||||
|
'Complex,Complex': ({roundquotient}) => (w,z) => roundquotient(w,z)
|
||||||
|
}
|
17
src/complex/roundquotient.mjs
Normal file
17
src/complex/roundquotient.mjs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export * from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
export const roundquotient = {
|
||||||
|
'Complex,Complex': ({
|
||||||
|
'isZero(Complex)': isZ,
|
||||||
|
conjugate,
|
||||||
|
'multiply(Complex,Complex)': mult,
|
||||||
|
absquare,
|
||||||
|
self,
|
||||||
|
complex
|
||||||
|
}) => (n,d) => {
|
||||||
|
if (isZ(d)) return d
|
||||||
|
const cnum = mult(n, conjugate(d))
|
||||||
|
const dreal = absquare(d)
|
||||||
|
return complex(self(cnum.re, dreal), self(cnum.im, dreal))
|
||||||
|
}
|
||||||
|
}
|
5
src/complex/zero.mjs
Normal file
5
src/complex/zero.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {Complex} from './Types/Complex.mjs'
|
||||||
|
|
||||||
|
const zero = Complex.promoteUnary
|
||||||
|
|
||||||
|
export {Complex, zero}
|
@ -27,6 +27,7 @@ export default class PocomathInstance {
|
|||||||
this._typed = typed.create()
|
this._typed = typed.create()
|
||||||
this._typed.clear()
|
this._typed.clear()
|
||||||
this.Types = {any: anySpec} // dummy entry to track the default 'any' type
|
this.Types = {any: anySpec} // dummy entry to track the default 'any' type
|
||||||
|
this._subtypes = {} // For each type, gives all of its (in)direct subtypes
|
||||||
this._usedTypes = new Set() // all types that have occurred in a signature
|
this._usedTypes = new Set() // all types that have occurred in a signature
|
||||||
this._doomed = new Set() // for detecting circular reference
|
this._doomed = new Set() // for detecting circular reference
|
||||||
this._config = {predictable: false}
|
this._config = {predictable: false}
|
||||||
@ -190,6 +191,9 @@ export default class PocomathInstance {
|
|||||||
* **to** this type to the corresponding conversion functions
|
* **to** this type to the corresponding conversion functions
|
||||||
* - before: [optional] a list of types this should be added
|
* - before: [optional] a list of types this should be added
|
||||||
* before, in priority order
|
* before, in priority order
|
||||||
|
* - refines: [optional] the name of a type that this is a subtype
|
||||||
|
* of. This means the test is the conjunction of the given test and
|
||||||
|
* the supertype test, and that it must come before the supertype.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Implementation note: unlike _installFunctions below, we can make
|
* Implementation note: unlike _installFunctions below, we can make
|
||||||
@ -202,29 +206,68 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let beforeType = 'any'
|
if (spec.refines && !(spec.refines in this.Types)) {
|
||||||
|
throw new SyntaxError(
|
||||||
|
`Cannot install ${type} before its supertype ${spec.refines}`)
|
||||||
|
}
|
||||||
|
let beforeType = spec.refines
|
||||||
|
if (!beforeType) {
|
||||||
|
beforeType = 'any'
|
||||||
for (const other of spec.before || []) {
|
for (const other of spec.before || []) {
|
||||||
if (other in this.Types) {
|
if (other in this.Types) {
|
||||||
beforeType = other
|
beforeType = other
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._typed.addTypes([{name: type, test: spec.test}], beforeType)
|
}
|
||||||
|
let testFn = spec.test
|
||||||
|
if (spec.refines) {
|
||||||
|
const supertypeTest = this.Types[spec.refines].test
|
||||||
|
testFn = entity => supertypeTest(entity) && spec.test(entity)
|
||||||
|
}
|
||||||
|
this._typed.addTypes([{name: type, test: testFn}], beforeType)
|
||||||
|
this.Types[type] = spec
|
||||||
/* Now add conversions to this type */
|
/* Now add conversions to this type */
|
||||||
for (const from in (spec.from || {})) {
|
for (const from in (spec.from || {})) {
|
||||||
if (from in this.Types) {
|
if (from in this.Types) {
|
||||||
|
// add conversions from "from" to this one and all its supertypes:
|
||||||
|
let nextSuper = type
|
||||||
|
while (nextSuper) {
|
||||||
this._typed.addConversion(
|
this._typed.addConversion(
|
||||||
{from, to: type, convert: spec.from[from]})
|
{from, to: nextSuper, convert: spec.from[from]})
|
||||||
|
nextSuper = this.Types[nextSuper].refines
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* And add conversions from this type */
|
/* And add conversions from this type */
|
||||||
for (const to in this.Types) {
|
for (const to in this.Types) {
|
||||||
if (type in (this.Types[to].from || {})) {
|
if (type in (this.Types[to].from || {})) {
|
||||||
|
if (spec.refines == to || spec.refines in this._subtypes[to]) {
|
||||||
|
throw new SyntaxError(
|
||||||
|
`Conversion of ${type} to its supertype ${to} disallowed.`)
|
||||||
|
}
|
||||||
|
let nextSuper = to
|
||||||
|
while (nextSuper) {
|
||||||
|
this._typed.addConversion({
|
||||||
|
from: type,
|
||||||
|
to: nextSuper,
|
||||||
|
convert: this.Types[to].from[type]
|
||||||
|
})
|
||||||
|
nextSuper = this.Types[nextSuper].refines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spec.refines) {
|
||||||
this._typed.addConversion(
|
this._typed.addConversion(
|
||||||
{from: type, to, convert: this.Types[to].from[type]})
|
{from: type, to: spec.refines, convert: x => x})
|
||||||
}
|
}
|
||||||
|
this._subtypes[type] = new Set()
|
||||||
|
// Update all the subtype sets of supertypes up the chain:
|
||||||
|
let nextSuper = spec.refines
|
||||||
|
while (nextSuper) {
|
||||||
|
this._subtypes[nextSuper].add(type)
|
||||||
|
nextSuper = this.Types[nextSuper].refines
|
||||||
}
|
}
|
||||||
this.Types[type] = spec
|
|
||||||
// rebundle anything that uses the new type:
|
// rebundle anything that uses the new type:
|
||||||
this._invalidateDependents(':' + type)
|
this._invalidateDependents(':' + type)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
export * from './Types/generic.mjs'
|
export * from './Types/generic.mjs'
|
||||||
|
|
||||||
|
export {lcm} from './lcm.mjs'
|
||||||
|
export {mod} from './mod.mjs'
|
||||||
export {multiply} from './multiply.mjs'
|
export {multiply} from './multiply.mjs'
|
||||||
export {divide} from './divide.mjs'
|
export {divide} from './divide.mjs'
|
||||||
export {sign} from './sign.mjs'
|
export {sign} from './sign.mjs'
|
||||||
export {sqrt} from './sqrt.mjs'
|
export {sqrt} from './sqrt.mjs'
|
||||||
|
export {square} from './square.mjs'
|
||||||
export {subtract} from './subtract.mjs'
|
export {subtract} from './subtract.mjs'
|
||||||
|
18
src/generic/gcdType.mjs
Normal file
18
src/generic/gcdType.mjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* Returns a object that defines the gcd for the given type */
|
||||||
|
export default function(type) {
|
||||||
|
const producer = refs => {
|
||||||
|
const modder = refs[`mod(${type},${type})`]
|
||||||
|
const zeroTester = refs[`isZero(${type})`]
|
||||||
|
return (a,b) => {
|
||||||
|
while (!zeroTester(b)) {
|
||||||
|
const r = modder(a,b)
|
||||||
|
a = b
|
||||||
|
b = r
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const retval = {}
|
||||||
|
retval[`${type},${type}`] = producer
|
||||||
|
return retval
|
||||||
|
}
|
6
src/generic/lcm.mjs
Normal file
6
src/generic/lcm.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const lcm = {
|
||||||
|
'any,any': ({
|
||||||
|
multiply,
|
||||||
|
quotient,
|
||||||
|
gcd}) => (a,b) => multiply(quotient(a, gcd(a,b)), b)
|
||||||
|
}
|
6
src/generic/mod.mjs
Normal file
6
src/generic/mod.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const mod = {
|
||||||
|
'any,any': ({
|
||||||
|
subtract,
|
||||||
|
multiply,
|
||||||
|
quotient}) => (a,m) => subtract(a, multiply(m, quotient(a,m)))
|
||||||
|
}
|
@ -4,9 +4,9 @@ export const multiply = {
|
|||||||
'undefined': () => u => u,
|
'undefined': () => u => u,
|
||||||
'undefined,...any': () => (u, rest) => u,
|
'undefined,...any': () => (u, rest) => u,
|
||||||
'any,undefined': () => (x, u) => u,
|
'any,undefined': () => (x, u) => u,
|
||||||
'any,undefined,...any': () => (x, u, rest) => u,
|
'any,any,...any': ({self}) => (a,b,rest) => {
|
||||||
'any,any,undefined': () => (x, y, u) => u,
|
const later = [b, ...rest]
|
||||||
'any,any,undefined,...any': () => (x, y, u, rest) => u
|
return later.reduce((x,y) => self(x,y), a)
|
||||||
// Bit of a hack since this should go on indefinitely...
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
src/generic/square.mjs
Normal file
3
src/generic/square.mjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const square = {
|
||||||
|
any: ({multiply}) => x => multiply(x,x)
|
||||||
|
}
|
@ -5,4 +5,9 @@ Number.installType('number', {
|
|||||||
test: n => typeof n === 'number',
|
test: n => typeof n === 'number',
|
||||||
from: {string: s => +s}
|
from: {string: s => +s}
|
||||||
})
|
})
|
||||||
|
Number.installType('NumInt', {
|
||||||
|
refines: 'number',
|
||||||
|
test: i => isFinite(i) && i === Math.round(i)
|
||||||
|
})
|
||||||
|
|
||||||
export {Number}
|
export {Number}
|
||||||
|
3
src/number/isZero.mjs
Normal file
3
src/number/isZero.mjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
|
export const isZero = {number: () => n => n === 0}
|
@ -1,5 +1,3 @@
|
|||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export const multiply = {
|
export const multiply = {'number,number': () => (m,n) => m*n}
|
||||||
'...number': () => multiplicands => multiplicands.reduce((x,y) => x*y, 1),
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
|
import gcdType from '../generic/gcdType.mjs'
|
||||||
|
|
||||||
export * from './Types/number.mjs'
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
export {abs} from './abs.mjs'
|
export {abs} from './abs.mjs'
|
||||||
export {add} from './add.mjs'
|
export {add} from './add.mjs'
|
||||||
|
export const gcd = gcdType('NumInt')
|
||||||
export {invert} from './invert.mjs'
|
export {invert} from './invert.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 {one} from './one.mjs'
|
export {one} from './one.mjs'
|
||||||
|
export {quotient} from './quotient.mjs'
|
||||||
|
export {roundquotient} from './roundquotient.mjs'
|
||||||
export {sqrt} from './sqrt.mjs'
|
export {sqrt} from './sqrt.mjs'
|
||||||
export {zero} from './zero.mjs'
|
export {zero} from './zero.mjs'
|
||||||
|
8
src/number/quotient.mjs
Normal file
8
src/number/quotient.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
|
export const quotient = {
|
||||||
|
'number,number': () => (n,d) => {
|
||||||
|
if (d === 0) return d
|
||||||
|
return Math.floor(n/d)
|
||||||
|
}
|
||||||
|
}
|
8
src/number/roundquotient.mjs
Normal file
8
src/number/roundquotient.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from './Types/number.mjs'
|
||||||
|
|
||||||
|
export const roundquotient = {
|
||||||
|
'number,number': () => (n,d) => {
|
||||||
|
if (d === 0) return d
|
||||||
|
return Math.round(n/d)
|
||||||
|
}
|
||||||
|
}
|
@ -52,4 +52,16 @@ describe('bigint', () => {
|
|||||||
assert.deepStrictEqual(bo.sqrt(-3249n), bo.complex(0n, 57n))
|
assert.deepStrictEqual(bo.sqrt(-3249n), bo.complex(0n, 57n))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('computes gcd', () => {
|
||||||
|
assert.strictEqual(math.gcd(105n, 70n), 35n)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('computes lcm', () => {
|
||||||
|
assert.strictEqual(math.lcm(105n, 70n), 210n)
|
||||||
|
assert.strictEqual(math.lcm(15n, 60n), 60n)
|
||||||
|
assert.strictEqual(math.lcm(0n, 17n), 0n)
|
||||||
|
assert.strictEqual(math.lcm(20n, 0n), 0n)
|
||||||
|
assert.strictEqual(math.lcm(0n, 0n), 0n)
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -29,4 +29,10 @@ describe('complex', () => {
|
|||||||
math.complex(ms.negate(ms.sqrt(0.5)), ms.sqrt(0.5)))
|
math.complex(ms.negate(ms.sqrt(0.5)), ms.sqrt(0.5)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('computes gcd', () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.gcd(math.complex(53n, 56n), math.complex(47n, -13n)),
|
||||||
|
math.complex(4n, 5n))
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -39,6 +39,7 @@ describe('A custom instance', () => {
|
|||||||
assert.strictEqual(pm.subtract(5, 10), -5)
|
assert.strictEqual(pm.subtract(5, 10), -5)
|
||||||
pm.install(complexAdd)
|
pm.install(complexAdd)
|
||||||
pm.install(complexNegate)
|
pm.install(complexNegate)
|
||||||
|
pm.install(complexComplex)
|
||||||
// Should be enough to allow complex subtraction, as subtract is generic:
|
// Should be enough to allow complex subtraction, as subtract is generic:
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
pm.subtract({re:5, im:0}, {re:10, im:1}), {re:-5, im: -1})
|
pm.subtract({re:5, im:0}, {re:10, im:1}), {re:-5, im: -1})
|
||||||
|
@ -27,4 +27,7 @@ describe('number', () => {
|
|||||||
assert.deepStrictEqual(no.sqrt(-16), no.complex(0,4))
|
assert.deepStrictEqual(no.sqrt(-16), no.complex(0,4))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('computes gcd', () => {
|
||||||
|
assert.strictEqual(math.gcd(15, 35), 5)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user