refactor(Complex): Now a template type!

This means that the real and imaginary parts of a Complex must now be
  the same type. This seems like a real benefit: a Complex with a number real
  part and a bigint imaginary part does not seem sensible.

  Note that this is now straining typed-function in (at least) the following
  ways:
  (1) In this change, it was necessary to remove the logic that the square root
      of a negative number calls complex square root, which then calls back
      to the number square root in its algorithm. (This was creating a circular
      reference in the typed-function which the old implementation of Complex
      was somehow sidestepping.)
  (2) typed-function could not follow conversions that would be allowed by
      uninstantiated templates (e.g. number => Complex<number> if the latter
      template has not been instantiated) and so the facility for
      instantiating a template was surfaced (and for example is called explicitly
      in the demo loader `extendToComplex`. Similarly, this necessitated
      making the unary signature of the `complex` conversion function explicit,
      rather than just via implicit conversion to Complex.
  (3) I find the order of implementations is mattering more in typed-function
      definitions, implying that typed-function's sorting algorithm is having
      trouble distinguishing alternatives.

  But otherwise, the conversion went quite smoothly and I think is a good demo
  of the power of this approach. And I expect that it will work even more
  smoothly if some of the underlying facilities (subtypes, template types) are
  integrated into typed-function.
This commit is contained in:
Glen Whitney 2022-08-06 08:27:44 -07:00
parent 845a2354c9
commit 1444b9828f
25 changed files with 240 additions and 157 deletions

View file

@ -2,14 +2,18 @@ export * from './Types/bigint.mjs'
import isqrt from 'bigint-isqrt'
export const sqrt = {
bigint: ({config, complex, 'self(Complex)': complexSqrt}) => {
bigint: ({
config,
'complex(bigint,bigint)': cplx,
'negate(bigint)': neg
}) => {
if (config.predictable) {
// Don't just return the constant isqrt here because the object
// gets decorated with info that might need to be different
// for different PocomathInstancss
return b => isqrt(b)
}
if (!complexSqrt) {
if (!cplx) {
return b => {
if (b >= 0n) {
const trial = isqrt(b)
@ -19,12 +23,16 @@ export const sqrt = {
}
}
return b => {
if (b >= 0n) {
const trial = isqrt(b)
if (trial * trial === b) return trial
return undefined
if (b === undefined) return undefined
let real = true
if (b < 0n) {
b = neg(b)
real = false
}
return complexSqrt(complex(b))
const trial = isqrt(b)
if (trial * trial !== b) return undefined
if (real) return trial
return cplx(0n, trial)
}
}
}