fix(Types): Move distinct types into distinct identifiers

This allows types to be collected; prior to this commit they
   were conflicting from different modules.

   Uses this fix to extend sqrt to bigint, with the convention
   that it is undefined for non-perfect squares when 'predictable'
   is false and is the "best" approximation to the square root when
   'predictable' is true. Furthermore, for negative bigints, you might
   get a Gaussian integer when predictable is false; or you will just get
   your argument back when 'predictable' is true because what other
   bigint could you give back for a negative bigint?

   Also had to modify tests on the sign in sqrt(Complex) and add functions
   'zero' and 'one' to get types to match, as expected in #27.

   Adds numerous tests.

   Resolves #26.
   Resolves #27.
This commit is contained in:
Glen Whitney 2022-07-25 11:56:12 -07:00
parent b21d2b59fa
commit f68c7bd1fb
44 changed files with 287 additions and 109 deletions

View file

@ -2,21 +2,15 @@
* can be any type (for this proof-of-concept; in reality we'd want to
* insist on some numeric or scalar supertype).
*/
export function isComplex(z) {
function isComplex(z) {
return z && typeof z === 'object' && 're' in z && 'im' in z
}
export const Types = {
Complex: {
test: isComplex,
from: {
number: x => ({re: x, im: 0}),
bigint: x => ({re: x, im: 0n})
}
export const Type_Complex = {
test: isComplex,
from: {
number: x => ({re: x, im: 0}),
bigint: x => ({re: x, im: 0n})
}
}
/* test if an entity is Complex<number>, so to speak: */
export function numComplex(z) {
return isComplex(z) && typeof z.re === 'number' && typeof z.im === 'number'
}

View file

@ -1,4 +1,4 @@
export {Types} from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export const abs = {Complex: ({sqrt, add, multiply}) => z => {
return sqrt(add(multiply(z.re, z.re), multiply(z.im, z.im)))

View file

@ -1,4 +1,4 @@
export {Types} from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export const add = {
'...Complex': ({self}) => addends => {

View file

@ -1,2 +1,5 @@
export * from './native.mjs'
export * from '../generic/arithmetic.mjs'
export * from './native.mjs'
// resolve the conflicts
export {sqrt} from './sqrt.mjs'

View file

@ -1,11 +1,15 @@
export {Types} from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export * from '../generic/Types/generic.mjs'
export const complex = {
/* Very permissive for sake of proof-of-concept; would be better to
* have a numeric/scalar type, e.g. by implementing subtypes in
* typed-function
*/
'any, any': () => (x, y) => ({re: x, im: y}),
'undefined': () => u => u,
'undefined,any': () => (u, y) => u,
'any,undefined': () => (x, u) => u,
'any,any': () => (x, y) => ({re: x, im: y}),
/* Take advantage of conversions in typed-function */
Complex: () => z => z
}

View file

@ -1,4 +1,4 @@
export {Types} from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export {abs} from './abs.mjs'
export {add} from './add.mjs'

View file

@ -1,4 +1,4 @@
export {Types} from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export const negate = {
Complex: ({self}) => z => ({re: self(z.re), im: self(z.im)})

View file

@ -1,35 +1,44 @@
export { Types } from './Types/Complex.mjs'
export * from './Types/Complex.mjs'
export const sqrt = {
Complex: ({
config,
zero,
sign,
one,
add,
complex,
multiply,
sign,
self,
divide,
add,
'abs(Complex)': abs,
subtract
}) => {
if (config.predictable) {
return z => {
const imZero = zero(z.im)
const imSign = sign(z.im)
const reOne = one(z.re)
const reSign = sign(z.re)
if (imSign === 0 && reSign === 1) return complex(self(z.re))
if (imSign === imZero && reSign === reOne) return complex(self(z.re))
const reTwo = add(reOne, reOne)
return complex(
multiply(sign(z.im), self(divide(add(abs(z),z.re), 2))),
self(divide(subtract(abs(z),z.re), 2))
multiply(sign(z.im), self(divide(add(abs(z),z.re), reTwo))),
self(divide(subtract(abs(z),z.re), reTwo))
)
}
}
return z => {
const imZero = zero(z.im)
const imSign = sign(z.im)
const reOne = one(z.re)
const reSign = sign(z.re)
if (imSign === 0 && reSign === 1) return self(z.re)
if (imSign === imZero && reSign === reOne) return self(z.re)
const reTwo = add(reOne, reOne)
const partial = add(abs(z), z.re)
return complex(
multiply(sign(z.im), self(divide(add(abs(z),z.re), 2))),
self(divide(subtract(abs(z),z.re), 2))
multiply(sign(z.im), self(divide(add(abs(z),z.re), reTwo))),
self(divide(subtract(abs(z),z.re), reTwo))
)
}
}