import {Complex, ComplexOp} from './type.js' import type { AbsquareOp, AddOp, AddRealOp, ConjOp, ConservativeSqrtOp, DivideOp, DivideByRealOp, MultiplyOp, ReciprocalOp, SqrtOp, SubtractOp, UnaryMinusOp } from '../interfaces/arithmetic.js' import type { NanOp, ReOp, ZeroOp, Depends, RealType, WithConstants, NaNType } from '../interfaces/type.js' import type {IsSquareOp, IsRealOp} from '../interfaces/predicate.js' export const add = (dep: Depends> & Depends>): AddOp> => (w, z) => dep.complex(dep.add(w.re, z.re), dep.add(w.im, z.im)) export const addReal = (dep: Depends> & Depends>): AddRealOp> => (z, r) => dep.complex(dep.addReal(z.re, r), z.im) export const unaryMinus = (dep: Depends> & Depends>): UnaryMinusOp> => z => dep.complex(dep.unaryMinus(z.re), dep.unaryMinus(z.im)) export const conj = (dep: Depends> & Depends> & Depends>): ConjOp> => z => dep.complex(dep.conj(z.re), dep.unaryMinus(z.im)) export const subtract = (dep: Depends> & Depends>): SubtractOp> => (w, z) => dep.complex(dep.subtract(w.re, z.re), dep.subtract(w.im, z.im)) export const multiply = (dep: Depends> & Depends> & Depends> & Depends> & Depends>): MultiplyOp> => (w, z) => { const mult = dep.multiply const realpart = dep.subtract( mult( w.re, z.re), mult(dep.conj(w.im), z.im)) const imagpart = dep.add( mult(dep.conj(w.re), z.im), mult( w.im, z.re)) return dep.complex(realpart, imagpart) } export const absquare = (dep: Depends>> & Depends>): AbsquareOp> => z => dep.add(dep.absquare(z.re), dep.absquare(z.im)) export const divideByReal = (dep: Depends> & Depends>): DivideByRealOp> => (z, r) => dep.complex(dep.divideByReal(z.re, r), dep.divideByReal(z.im, r)) export const reciprocal = (dep: Depends>> & Depends>> & Depends>>): ReciprocalOp> => z => dep.divideByReal(dep.conj(z), dep.absquare(z)) export const divide = (dep: Depends>> & Depends>>): DivideOp> => (w, z) => dep.multiply(w, dep.reciprocal(z)) export type ComplexSqrtOp = { op?: 'complexSqrt', (a: T): Complex | NaNType>> } // Complex square root of a real type T export const complexSqrt = (dep: Depends> & Depends> & Depends> & Depends>> & Depends> & Depends>>>): ComplexSqrtOp => r => { if (dep.isSquare(r)) return dep.complex(dep.conservativeSqrt(r)) const negative = dep.unaryMinus(r) if (dep.isSquare(negative)) { return dep.complex(dep.zero(r), dep.conservativeSqrt(negative)) } // neither the real number or its negative is a square; could happen // for example with bigint. So there is no square root. So we have to // return the NaN of the type. return dep.nan(dep.complex(r)) } export const sqrt = (dep: Depends>> & Depends> & Depends>> & Depends>> & Depends>> & Depends>> & Depends>> & Depends>>): SqrtOp> => z => { if (dep.isReal(z)) return dep.complexSqrt(z.re) const myabs = dep.conservativeSqrt(dep.absquare(z)) const num = dep.addReal(z, myabs) const r = dep.re(z) const denomsq = dep.add(dep.add(myabs, myabs), dep.add(r, r)) const denom = dep.conservativeSqrt(denomsq) return dep.divideByReal(num, denom) } export const conservativeSqrt = sqrt