From f9b723b8824a1d6073c95aa2eae399d6e5591998 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 25 Apr 2025 00:08:29 -0700 Subject: [PATCH] feat: Add isReal and isZero, and fix specializing a generic type --- src/complex/all.js | 1 + src/complex/utils.js | 9 +++++++++ src/core/Type.js | 5 ++--- src/generic/__test__/utils.spec.js | 12 ++++++++++++ src/generic/utils.js | 5 ++++- 5 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 src/complex/utils.js diff --git a/src/complex/all.js b/src/complex/all.js index 8ccff17..023c144 100644 --- a/src/complex/all.js +++ b/src/complex/all.js @@ -2,3 +2,4 @@ export * as typeDefinition from './Complex.js' export * as arithmetic from './arithmetic.js' export * as relational from './relational.js' export * as type from './type.js' +export * as utilities from './utils.js' diff --git a/src/complex/utils.js b/src/complex/utils.js new file mode 100644 index 0000000..2fef03a --- /dev/null +++ b/src/complex/utils.js @@ -0,0 +1,9 @@ +import {Complex} from './Complex.js' +import {match} from '#core/TypePatterns.js' +import {ReturnsAs} from '#generic/helpers.js' + +export const isReal = match(Complex, (math, C) => { + const eq = math.equal.resolve([C.Component, C.Component]) + const add = math.add.resolve([C.Component, C.Component]) + return ReturnsAs(eq, z => eq(z.re, add(z.re, z.im))) +}) diff --git a/src/core/Type.js b/src/core/Type.js index 078f12d..555b547 100644 --- a/src/core/Type.js +++ b/src/core/Type.js @@ -10,9 +10,8 @@ export class Type extends Function { // let the proxy out of this function, never `this`. const rewired = new Proxy(this, { apply: (target, thisForCall, args) => { - const callThrough = thisForCall ?? target - if (callThrough.specialize) return callThrough.specialize(...args) - throw new TypeError(`Type ${callThrough} is not generic`) + if (target.specialize) return target.specialize(...args) + throw new TypeError(`Type ${target} is not generic`) }, get: (target, prop, receiver) => { if (prop === 'isAproxy') return true diff --git a/src/generic/__test__/utils.spec.js b/src/generic/__test__/utils.spec.js index e178a2d..f8b6d53 100644 --- a/src/generic/__test__/utils.spec.js +++ b/src/generic/__test__/utils.spec.js @@ -1,5 +1,6 @@ import assert from 'assert' import math from '#nanomath' +import {NumberT} from '#number/NumberT.js' describe('generic utility functions', () => { it('tests whether an element is zero', () => { @@ -9,5 +10,16 @@ describe('generic utility functions', () => { assert(isZero(false)) assert(!isZero(true)) assert(isZero(undefined)) + assert(isZero(math.types.Complex(NumberT).zero)) + assert(isZero(math.complex(-2e-16, 4e-17))) + assert(!isZero(math.complex(true))) + }) + it('tests whether an element is real', () => { + const {isReal} = math + assert(isReal(Infinity)) + assert(isReal(false)) + assert(isReal(math.types.Complex(NumberT).one)) + assert(isReal(math.complex(-3.25, 4e-16))) + assert(!isReal(math.complex(3, 4))) }) }) diff --git a/src/generic/utils.js b/src/generic/utils.js index b3766c4..d696d6c 100644 --- a/src/generic/utils.js +++ b/src/generic/utils.js @@ -1,7 +1,10 @@ import {ReturnsAs} from './helpers.js' import {ResolutionError} from '#core/helpers.js' -import {Passthru, match} from "#core/TypePatterns.js" +import {Passthru, match} from '#core/TypePatterns.js' +import {boolnum} from '#number/helpers.js' +// Most types are real. Have to make sure to redefine on all non-real types +export const isReal = match(Passthru, boolnum(() => true)) export const isZero = match(Passthru, (math, [T]) => { if (!T) { // called with no arguments throw new ResolutionError('isZero() requires one argument')