From 80d7f476a40acc90edccd407ed3df673c3a99c1e Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Sat, 2 May 2026 00:21:44 +0200 Subject: [PATCH 1/7] Started on complex numbers --- math/complex.ts | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 math/complex.ts diff --git a/math/complex.ts b/math/complex.ts new file mode 100644 index 000000000000..2e3cc0874f4e --- /dev/null +++ b/math/complex.ts @@ -0,0 +1,75 @@ +// Copyright 2018-2026 the Deno authors. MIT license. + +export class Complex { + real: number; + imag: number; + + constructor(real: number, imag?: number) { + this.real = real; + this.imag = imag ?? 0; + } +} + +export function add(...nums: Complex[]): Complex { + let sum = new Complex(0); + + for (const num of nums) { + sum = new Complex(sum.real + num.real, sum.imag + num.real); + } + + return sum; +} + +export function sub(x: Complex, y: Complex): Complex { + return new Complex(x.real - y.real, x.imag - y.imag); +} + +export function mul(...nums: Complex[]): Complex { + let prod = new Complex(1); + + for (const num of nums) { + prod = new Complex( + prod.real * num.real - prod.imag * num.imag, + prod.real * num.imag + prod.imag * num.real, + ); + } + + return prod; +} + +export function div(x: Complex, y: Complex): Complex { + const absSquaredY = absSquared(y); + + return new Complex( + (x.real * y.real + x.imag * y.imag) / absSquaredY, + (x.imag * y.real - x.real * y.imag) / absSquaredY, + ); +} + +export function recip(z: Complex): Complex { + const absSquaredZ = absSquared(z); + + return new Complex( + z.real / absSquaredZ, + -z.imag / absSquaredZ, + ); +} + +export function absSquared(z: Complex): number { + return z.real * z.real + z.imag * z.imag; +} + +export function abs(z: Complex): number { + return Math.sqrt(absSquared(z)); +} + +export function arg(z: Complex): number { + return z.real && z.imag ? 0 : Math.sign(z.imag) * Math.acos(z.real / abs(z)); +} + +export function sqrt(z: Complex): Complex { + return new Complex( + Math.sqrt((z.real + abs(z)) / 2), + Math.sign(z.imag) * Math.sqrt((-z.real + abs(z)) / 2), + ); +} From 3928d02797b4029a4304cb3722193b2231f9bd78 Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Sat, 2 May 2026 16:49:25 +0200 Subject: [PATCH 2/7] More functions --- math/complex.ts | 50 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/math/complex.ts b/math/complex.ts index 2e3cc0874f4e..2add377bebea 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -10,10 +10,13 @@ export class Complex { } } -export function add(...nums: Complex[]): Complex { +export const I = new Complex(0, 1); + +export function add(...nums: (Complex | number)[]): Complex { let sum = new Complex(0); - for (const num of nums) { + for (let num of nums) { + if (typeof num === "number") num = new Complex(num) sum = new Complex(sum.real + num.real, sum.imag + num.real); } @@ -67,9 +70,52 @@ export function arg(z: Complex): number { return z.real && z.imag ? 0 : Math.sign(z.imag) * Math.acos(z.real / abs(z)); } +export function conj(z: Complex): Complex { + return new Complex(z.real, -z.imag); +} + export function sqrt(z: Complex): Complex { return new Complex( Math.sqrt((z.real + abs(z)) / 2), Math.sign(z.imag) * Math.sqrt((-z.real + abs(z)) / 2), ); } + +export function ln(z: Complex): Complex { + return new Complex(Math.log(absSquared(z)) / 2, arg(z)); +} + +export function exp(z: Complex): Complex { + const expReal = Math.exp(z.real); + return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); +} + +export function pow(z: Complex | number, w: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); + + if (typeof w === "number" && w % 1 === 0) { + // If w is an integer, use exponentiation by squaring + return w === 0 + ? new Complex(1, 0) + : w === 1 + ? z + : w === -1 + ? recip(z) + : w < 0 + ? recip(pow(z, -w)) + : w % 2 === 0 + ? pow(mul(z, z), w / 2) + : mul(z, pow(mul(z, z), (w - 1) / 2)); + } else if (typeof w === "number") { + // If w is a real number, use de Moivres formula + const argZ = arg(z); + const absPow = Math.pow(abs(z), w); + + return new Complex( + absPow * Math.cos(argZ * w), + absPow * Math.sin(argZ * w), + ); + } else { + return mul(pow(z, w.real), exp(mul(ln(z), new Complex(0, w.imag)))); + } +} From ffb91a1d4ec19f560ed731da7831572e3ae2da68 Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Sun, 3 May 2026 00:59:37 +0200 Subject: [PATCH 3/7] 24. trig. functions --- math/complex.ts | 178 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 170 insertions(+), 8 deletions(-) diff --git a/math/complex.ts b/math/complex.ts index 2add377bebea..4086d2755fc9 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -11,26 +11,33 @@ export class Complex { } export const I = new Complex(0, 1); +export const NEGI = new Complex(0, -1); -export function add(...nums: (Complex | number)[]): Complex { +type Number = Complex | number; + +export function add(...nums: (Number)[]): Complex { let sum = new Complex(0); for (let num of nums) { - if (typeof num === "number") num = new Complex(num) + if (typeof num === "number") num = new Complex(num); sum = new Complex(sum.real + num.real, sum.imag + num.real); } return sum; } -export function sub(x: Complex, y: Complex): Complex { +export function sub(x: Number, y: Number): Complex { + if (typeof x === "number") x = new Complex(x); + if (typeof y === "number") y = new Complex(y); + return new Complex(x.real - y.real, x.imag - y.imag); } -export function mul(...nums: Complex[]): Complex { +export function mul(...nums: (Number)[]): Complex { let prod = new Complex(1); - for (const num of nums) { + for (let num of nums) { + if (typeof num === "number") num = new Complex(num); prod = new Complex( prod.real * num.real - prod.imag * num.imag, prod.real * num.imag + prod.imag * num.real, @@ -40,7 +47,10 @@ export function mul(...nums: Complex[]): Complex { return prod; } -export function div(x: Complex, y: Complex): Complex { +export function div(x: Number, y: Number): Complex { + if (typeof x === "number") x = new Complex(x); + if (typeof y === "number") y = new Complex(y); + const absSquaredY = absSquared(y); return new Complex( @@ -49,7 +59,9 @@ export function div(x: Complex, y: Complex): Complex { ); } -export function recip(z: Complex): Complex { +export function recip(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + const absSquaredZ = absSquared(z); return new Complex( @@ -81,16 +93,38 @@ export function sqrt(z: Complex): Complex { ); } +export function cbrt(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + // If w is a real number, use de Moivres formula + const argZ = arg(z); + const absCbrt = Math.cbrt(abs(z)); + + return new Complex( + absCbrt * Math.cos(argZ / 3), + absCbrt * Math.sin(argZ / 3), + ); +} + +// I know in the Math namespace the natural logarithm is log and not ln, but fuck that export function ln(z: Complex): Complex { return new Complex(Math.log(absSquared(z)) / 2, arg(z)); } +export function log(z: Complex): Complex { + return (div(ln(z), Math.LN10)); +} + +export function logn(z: Complex, n: number): Complex { + return (div(ln(z), Math.log(n))); +} + export function exp(z: Complex): Complex { const expReal = Math.exp(z.real); return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); } -export function pow(z: Complex | number, w: Complex | number): Complex { +export function pow(z: Number, w: Number): Complex { if (typeof z === "number") z = new Complex(z); if (typeof w === "number" && w % 1 === 0) { @@ -119,3 +153,131 @@ export function pow(z: Complex | number, w: Complex | number): Complex { return mul(pow(z, w.real), exp(mul(ln(z), new Complex(0, w.imag)))); } } + +// Trig! + +export function sin(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + return new Complex( + Math.sin(z.real) * Math.cosh(z.imag), + Math.cos(z.real) * Math.sinh(z.imag), + ); +} + +export function cos(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + return new Complex( + Math.cos(z.real) * Math.cosh(z.imag), + -Math.sin(z.real) * Math.sinh(z.imag), + ); +} + +export function tan(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + const w = Math.cos(2 * z.imag) + Math.cosh(2 * z.imag); + + return new Complex( + Math.sin(2 * z.real) / w, + Math.sinh(2 * z.real) / w, + ); +} + +export function cot(z: Number): Complex { + return div(1, tan(z)); +} + +export function sec(z: Number): Complex { + return div(1, cos(z)); +} + +export function csc(z: Number): Complex { + return div(1, sin(z)); +} + +// Inverse trig! +export function asin(z: Number): Complex { + return mul(NEGI, ln(add(sqrt(sub(1, pow(z, 2))), mul(I, z)))); +} + +export function acos(z: Number): Complex { + return sub(Math.PI / 2, asin(z)); +} + +export function atan(z: Number): Complex { + return asin(div(z, sqrt(sub(1, pow(z, 2))))); +} + +export function acot(z: Number): Complex { + return atan(div(1, z)); +} + +export function asec(z: Number): Complex { + return acos(div(1, z)); +} + +export function acsc(z: Number): Complex { + return asin(div(1, z)); +} + +// Hypertrig! +export function sinh(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + return new Complex( + Math.sinh(z.real) * Math.cos(z.imag), + Math.cosh(z.real) * Math.sin(z.imag), + ); +} + +export function cosh(z: Number): Complex { + if (typeof z === "number") z = new Complex(z); + + return new Complex( + Math.cosh(z.real) * Math.cos(z.imag), + Math.sinh(z.real) * Math.sin(z.imag), + ); +} + +export function tanh(z: Number): Complex { + return div(sinh(z), cosh(z)); +} + +export function coth(z: Number): Complex { + return div(cosh(z), sinh(z)); +} + +export function sech(z: Number): Complex { + return recip(cosh(z)); +} + +export function csch(z: Number): Complex { + return recip(sinh(z)); +} + +// Inverse hyprtrig! +export function asinh(z: Number): Complex { + return ln(add(sqrt(add(pow(z, 2), 1)), z)); +} + +export function acosh(z: Number): Complex { + return ln(add(sqrt(sub(pow(z, 2), 1)), z)); +} + +export function atanh(z: Number): Complex { + return div(ln(div(add(1, z), sub(1, z))), 2); +} + +export function acoth(z: Number): Complex { + return atanh(recip(z)); +} + +export function asech(z: Number): Complex { + return acosh(recip(z)); +} + +export function acsch(z: Number): Complex { + return asinh(recip(z)); +} From 8434554736c9322d580202639710c0c15d993684 Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Sun, 3 May 2026 22:23:12 +0200 Subject: [PATCH 4/7] A LOT of functions and docs --- math/complex.ts | 489 +++++++++++++++++++++++++++--------------------- 1 file changed, 278 insertions(+), 211 deletions(-) diff --git a/math/complex.ts b/math/complex.ts index 4086d2755fc9..d68bf83591c4 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -4,280 +4,347 @@ export class Complex { real: number; imag: number; + /** + * Create a new complex number + */ constructor(real: number, imag?: number) { this.real = real; this.imag = imag ?? 0; } -} - -export const I = new Complex(0, 1); -export const NEGI = new Complex(0, -1); -type Number = Complex | number; + /** i, the imaginary unit */ + static i = new Complex(0, 1); + /** -i, the negative of the imaginary unit */ + static negI = new Complex(0, -1); + + /** + * Returns the sum of the supplied complex numbers + * + * @param {(Complex | number)[]} nums The complex numbers to sum + * + * @returns {Complex} The sum of the supplied complex numbers + */ + static add(...nums: (Complex | number)[]): Complex { + let sum = new Complex(0); + + for (let num of nums) { + if (typeof num === "number") num = new Complex(num); + sum = new Complex(sum.real + num.real, sum.imag + num.real); + } + + return sum; + } -export function add(...nums: (Number)[]): Complex { - let sum = new Complex(0); + /** + * Subtracts one complex number from another + */ + static sub(x: Complex | number, y: Complex | number): Complex { + if (typeof x === "number") x = new Complex(x); + if (typeof y === "number") y = new Complex(y); - for (let num of nums) { - if (typeof num === "number") num = new Complex(num); - sum = new Complex(sum.real + num.real, sum.imag + num.real); + return new Complex(x.real - y.real, x.imag - y.imag); } - return sum; -} - -export function sub(x: Number, y: Number): Complex { - if (typeof x === "number") x = new Complex(x); - if (typeof y === "number") y = new Complex(y); + /** + * Returns the product of the supplied complex numbers + * + * @param {(Complex | number)[]} nums The complex numbers to sum + * + * @returns {Complex} The product of the supplied complex numbers + */ + static mul(...nums: (Complex | number)[]): Complex { + let prod = new Complex(1); + + for (let num of nums) { + if (typeof num === "number") num = new Complex(num); + prod = new Complex( + prod.real * num.real - prod.imag * num.imag, + prod.real * num.imag + prod.imag * num.real, + ); + } + + return prod; + } - return new Complex(x.real - y.real, x.imag - y.imag); -} + /** Returns the ratio of two complex numbers (divides one complex number by another). */ + static div(x: Complex | number, y: Complex | number): Complex { + if (typeof x === "number") x = new Complex(x); + if (typeof y === "number") y = new Complex(y); -export function mul(...nums: (Number)[]): Complex { - let prod = new Complex(1); + const absSquaredY = Complex.absSquared(y); - for (let num of nums) { - if (typeof num === "number") num = new Complex(num); - prod = new Complex( - prod.real * num.real - prod.imag * num.imag, - prod.real * num.imag + prod.imag * num.real, + return new Complex( + (x.real * y.real + x.imag * y.imag) / absSquaredY, + (x.imag * y.real - x.real * y.imag) / absSquaredY, ); } - return prod; -} - -export function div(x: Number, y: Number): Complex { - if (typeof x === "number") x = new Complex(x); - if (typeof y === "number") y = new Complex(y); + /** Returns the reciprocal (multiplicative inverse) of a complex number. */ + static recip(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); - const absSquaredY = absSquared(y); + const absSquaredZ = this.absSquared(z); - return new Complex( - (x.real * y.real + x.imag * y.imag) / absSquaredY, - (x.imag * y.real - x.real * y.imag) / absSquaredY, - ); -} + return new Complex( + z.real / absSquaredZ, + -z.imag / absSquaredZ, + ); + } -export function recip(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + /** Returns the square of the absolute value of a complex number. */ + static absSquared(z: Complex): number { + return z.real * z.real + z.imag * z.imag; + } - const absSquaredZ = absSquared(z); + /** Returns the absolute value of a complex number (the distance from the complex number to the origin on the complex plane). */ + static abs(z: Complex): number { + return Math.sqrt(this.absSquared(z)); + } - return new Complex( - z.real / absSquaredZ, - -z.imag / absSquaredZ, - ); -} + /** Returns the argument of a complex number with a domain of [pi, -pi). */ + static arg(z: Complex): number { + return z.real === 0 && z.imag === 0 + ? 0 + : Math.sign(z.imag) * Math.acos(z.real / Complex.abs(z)); + } -export function absSquared(z: Complex): number { - return z.real * z.real + z.imag * z.imag; -} + /** Returns the conjugate of a complex number (the complex number with its imaginary part negated). */ + static conj(z: Complex): Complex { + return new Complex(z.real, -z.imag); + } -export function abs(z: Complex): number { - return Math.sqrt(absSquared(z)); -} + /** Returns the square root of a complex number. */ + static sqrt(z: Complex): Complex { + return new Complex( + Math.sqrt((z.real + this.abs(z)) / 2), + Math.sign(z.imag) * Math.sqrt((-z.real + this.abs(z)) / 2), + ); + } -export function arg(z: Complex): number { - return z.real && z.imag ? 0 : Math.sign(z.imag) * Math.acos(z.real / abs(z)); -} + /** Returns the cube root of a complex number. */ + static cbrt(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); -export function conj(z: Complex): Complex { - return new Complex(z.real, -z.imag); -} + const argZ = this.arg(z); + const absCbrt = Math.cbrt(Complex.abs(z)); -export function sqrt(z: Complex): Complex { - return new Complex( - Math.sqrt((z.real + abs(z)) / 2), - Math.sign(z.imag) * Math.sqrt((-z.real + abs(z)) / 2), - ); -} + return new Complex( + absCbrt * Math.cos(argZ / 3), + absCbrt * Math.sin(argZ / 3), + ); + } -export function cbrt(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + // I know in the Math namespace the natural logarithm is log and not ln, but fuck that + /** Returns the natural logarithm of a complex number. */ + static ln(z: Complex): Complex { + return new Complex(Math.log(this.absSquared(z)) / 2, this.arg(z)); + } - // If w is a real number, use de Moivres formula - const argZ = arg(z); - const absCbrt = Math.cbrt(abs(z)); + /** Returns the base-10 logarithm of a complex number. */ + static log(z: Complex): Complex { + return (this.div(this.ln(z), Math.LN10)); + } - return new Complex( - absCbrt * Math.cos(argZ / 3), - absCbrt * Math.sin(argZ / 3), - ); -} + /** Returns the base-n logarithm of a complex number. */ + static logn(z: Complex, n: number): Complex { + return (this.div(this.ln(z), Math.log(n))); + } -// I know in the Math namespace the natural logarithm is log and not ln, but fuck that -export function ln(z: Complex): Complex { - return new Complex(Math.log(absSquared(z)) / 2, arg(z)); -} + /** Returns the e (Euler's number) raised to the power of a complex number. */ + static exp(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); -export function log(z: Complex): Complex { - return (div(ln(z), Math.LN10)); -} + const expReal = Math.exp(z.real); -export function logn(z: Complex, n: number): Complex { - return (div(ln(z), Math.log(n))); -} + return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); + } -export function exp(z: Complex): Complex { - const expReal = Math.exp(z.real); - return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); -} + /** Returns a complex number raised to the power of another complex number. */ + static pow(z: Complex | number, w: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); + + if (typeof w === "number" && w % 1 === 0) { + // If w is an integer, use exponentiation by squaring + return w === 0 + ? new Complex(1, 0) + : w === 1 + ? z + : w === -1 + ? this.recip(z) + : w < 0 + ? this.recip(this.pow(z, -w)) + : w % 2 === 0 + ? this.pow(this.mul(z, z), w / 2) + : this.mul(z, this.pow(this.mul(z, z), (w - 1) / 2)); + } else if (typeof w === "number") { + // If w is a real number, use de Moivres formula + const argZ = Complex.arg(z); + const absPow = Math.pow(Complex.abs(z), w); + + return new Complex( + absPow * Math.cos(argZ * w), + absPow * Math.sin(argZ * w), + ); + } else { + return Complex.mul( + Complex.pow(z, w.real), + Complex.exp(Complex.mul(Complex.ln(z), new Complex(0, w.imag))), + ); + } + } -export function pow(z: Number, w: Number): Complex { - if (typeof z === "number") z = new Complex(z); - - if (typeof w === "number" && w % 1 === 0) { - // If w is an integer, use exponentiation by squaring - return w === 0 - ? new Complex(1, 0) - : w === 1 - ? z - : w === -1 - ? recip(z) - : w < 0 - ? recip(pow(z, -w)) - : w % 2 === 0 - ? pow(mul(z, z), w / 2) - : mul(z, pow(mul(z, z), (w - 1) / 2)); - } else if (typeof w === "number") { - // If w is a real number, use de Moivres formula - const argZ = arg(z); - const absPow = Math.pow(abs(z), w); + /** Returns the sine of a complex number. */ + static sin(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); return new Complex( - absPow * Math.cos(argZ * w), - absPow * Math.sin(argZ * w), + Math.sin(z.real) * Math.cosh(z.imag), + Math.cos(z.real) * Math.sinh(z.imag), ); - } else { - return mul(pow(z, w.real), exp(mul(ln(z), new Complex(0, w.imag)))); } -} - -// Trig! - -export function sin(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); - - return new Complex( - Math.sin(z.real) * Math.cosh(z.imag), - Math.cos(z.real) * Math.sinh(z.imag), - ); -} -export function cos(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + /** Returns the cosine of a complex number. */ + static cos(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); - return new Complex( - Math.cos(z.real) * Math.cosh(z.imag), - -Math.sin(z.real) * Math.sinh(z.imag), - ); -} + return new Complex( + Math.cos(z.real) * Math.cosh(z.imag), + -Math.sin(z.real) * Math.sinh(z.imag), + ); + } -export function tan(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + /** Returns the tangent of a complex number. */ + static tan(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); - const w = Math.cos(2 * z.imag) + Math.cosh(2 * z.imag); + const w = Math.cos(2 * z.imag) + Math.cosh(2 * z.imag); - return new Complex( - Math.sin(2 * z.real) / w, - Math.sinh(2 * z.real) / w, - ); -} + return new Complex( + Math.sin(2 * z.real) / w, + Math.sinh(2 * z.real) / w, + ); + } -export function cot(z: Number): Complex { - return div(1, tan(z)); -} + /** Returns the cotangent of a complex number. */ + static cot(z: Complex | number): Complex { + return this.div(1, this.tan(z)); + } -export function sec(z: Number): Complex { - return div(1, cos(z)); -} + /** Returns the secant of a complex number. */ + static sec(z: Complex | number): Complex { + return this.div(1, this.cos(z)); + } -export function csc(z: Number): Complex { - return div(1, sin(z)); -} + /** Returns the cosecant of a complex number. */ + static csc(z: Complex | number): Complex { + return this.div(1, this.sin(z)); + } -// Inverse trig! -export function asin(z: Number): Complex { - return mul(NEGI, ln(add(sqrt(sub(1, pow(z, 2))), mul(I, z)))); -} + /** Returns the arcsine (inverse sine) of a complex number. */ + static asin(z: Complex | number): Complex { + return Complex.mul( + this.negI, + this.ln( + this.add( + this.sqrt(this.sub(1, this.pow(z, 2))), + this.mul(this.i, z), + ), + ), + ); + } -export function acos(z: Number): Complex { - return sub(Math.PI / 2, asin(z)); -} + /** Returns the arccosine (inverse cosine) of a complex number. */ + static acos(z: Complex | number): Complex { + return this.sub(Math.PI / 2, this.asin(z)); + } -export function atan(z: Number): Complex { - return asin(div(z, sqrt(sub(1, pow(z, 2))))); -} + /** Returns the arctangent (inverse tangent) of a complex number. */ + static atan(z: Complex | number): Complex { + return this.asin(this.div(z, this.sqrt(this.sub(1, this.pow(z, 2))))); + } -export function acot(z: Number): Complex { - return atan(div(1, z)); -} + /** Returns the arccotangent (inverse cotangent) of a complex number. */ + static acot(z: Complex | number): Complex { + return this.atan(this.recip(z)); + } -export function asec(z: Number): Complex { - return acos(div(1, z)); -} + /** Returns the arcsecant (inverse secant) of a complex number. */ + static asec(z: Complex | number): Complex { + return this.acos(this.recip(z)); + } -export function acsc(z: Number): Complex { - return asin(div(1, z)); -} + /** Returns the arccosecant (inverse cosecant) of a complex number. */ + static acsc(z: Complex | number): Complex { + return this.asin(this.recip(z)); + } -// Hypertrig! -export function sinh(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + /** Returns the hyperbolic sine of a complex number. */ + static sinh(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); - return new Complex( - Math.sinh(z.real) * Math.cos(z.imag), - Math.cosh(z.real) * Math.sin(z.imag), - ); -} + return new Complex( + Math.sinh(z.real) * Math.cos(z.imag), + Math.cosh(z.real) * Math.sin(z.imag), + ); + } -export function cosh(z: Number): Complex { - if (typeof z === "number") z = new Complex(z); + /** Returns the hyperbolic cosine of a complex number. */ + static cosh(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); - return new Complex( - Math.cosh(z.real) * Math.cos(z.imag), - Math.sinh(z.real) * Math.sin(z.imag), - ); -} + return new Complex( + Math.cosh(z.real) * Math.cos(z.imag), + Math.sinh(z.real) * Math.sin(z.imag), + ); + } -export function tanh(z: Number): Complex { - return div(sinh(z), cosh(z)); -} + /** Returns the hyperbolic tangent of a complex number. */ + static tanh(z: Complex | number): Complex { + return this.div(this.sinh(z), this.cosh(z)); + } -export function coth(z: Number): Complex { - return div(cosh(z), sinh(z)); -} + /** Returns the hyperbolic cotangent of a complex number. */ + static coth(z: Complex | number): Complex { + return this.div(this.cosh(z), this.sinh(z)); + } -export function sech(z: Number): Complex { - return recip(cosh(z)); -} + /** Returns the hyperbolic secant of a complex number. */ + static sech(z: Complex | number): Complex { + return this.recip(this.cosh(z)); + } -export function csch(z: Number): Complex { - return recip(sinh(z)); -} + /** Returns the hyperbolic cosecant of a complex number. */ + static csch(z: Complex | number): Complex { + return this.recip(this.sinh(z)); + } -// Inverse hyprtrig! -export function asinh(z: Number): Complex { - return ln(add(sqrt(add(pow(z, 2), 1)), z)); -} + /** Returns the hyperbolic arcsine (inverse hyperbolic sine) of a complex number. */ + static asinh(z: Complex | number): Complex { + return this.ln(this.add(this.sqrt(this.add(this.pow(z, 2), 1)), z)); + } -export function acosh(z: Number): Complex { - return ln(add(sqrt(sub(pow(z, 2), 1)), z)); -} + /** Returns the hyperbolic arccosine (inverse hyperbolic cosine) of a complex number. */ + static acosh(z: Complex | number): Complex { + return this.ln(this.add(this.sqrt(this.sub(this.pow(z, 2), 1)), z)); + } -export function atanh(z: Number): Complex { - return div(ln(div(add(1, z), sub(1, z))), 2); -} + /** Returns the hyperbolic arctangent (inverse hyperbolic tangent) of a complex number. */ + static atanh(z: Complex | number): Complex { + return this.div(this.ln(this.div(this.add(1, z), this.sub(1, z))), 2); + } -export function acoth(z: Number): Complex { - return atanh(recip(z)); -} + /** Returns the hyperbolic arccotangent (inverse hyperbolic cotangent) of a complex number. */ + static acoth(z: Complex | number): Complex { + return this.atanh(this.recip(z)); + } -export function asech(z: Number): Complex { - return acosh(recip(z)); -} + /** Returns the hyperbolic arcsecant (inverse hyperbolic secant) of a complex number. */ + static asech(z: Complex | number): Complex { + return this.acosh(this.recip(z)); + } -export function acsch(z: Number): Complex { - return asinh(recip(z)); + /** Returns the hyperbolic arccosecant (inverse hyperbolic cosecant) of a complex number. */ + static acsch(z: Complex | number): Complex { + return this.asinh(this.recip(z)); + } } From a560ee9fb8ceacdcc93b1eab38b7baab05c5725b Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Sun, 3 May 2026 22:36:49 +0200 Subject: [PATCH 5/7] Added a neg function, updated docs, refactored some one-liner functions. --- math/complex.ts | 53 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/math/complex.ts b/math/complex.ts index d68bf83591c4..28db003c5136 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -1,5 +1,6 @@ // Copyright 2018-2026 the Deno authors. MIT license. +// Should this class be parameterized to support bigint? export class Complex { real: number; imag: number; @@ -17,13 +18,7 @@ export class Complex { /** -i, the negative of the imaginary unit */ static negI = new Complex(0, -1); - /** - * Returns the sum of the supplied complex numbers - * - * @param {(Complex | number)[]} nums The complex numbers to sum - * - * @returns {Complex} The sum of the supplied complex numbers - */ + /** Returns the sum of the supplied complex numbers. */ static add(...nums: (Complex | number)[]): Complex { let sum = new Complex(0); @@ -35,9 +30,12 @@ export class Complex { return sum; } - /** - * Subtracts one complex number from another - */ + /** Returns the negative of a complex number. */ + static neg(z: Complex): Complex { + return new Complex(-z.real, -z.imag); + } + + /** Returns the difference of two complex numbers. */ static sub(x: Complex | number, y: Complex | number): Complex { if (typeof x === "number") x = new Complex(x); if (typeof y === "number") y = new Complex(y); @@ -45,13 +43,7 @@ export class Complex { return new Complex(x.real - y.real, x.imag - y.imag); } - /** - * Returns the product of the supplied complex numbers - * - * @param {(Complex | number)[]} nums The complex numbers to sum - * - * @returns {Complex} The product of the supplied complex numbers - */ + /** Returns the product of the supplied complex numbers. */ static mul(...nums: (Complex | number)[]): Complex { let prod = new Complex(1); @@ -114,7 +106,9 @@ export class Complex { } /** Returns the square root of a complex number. */ - static sqrt(z: Complex): Complex { + static sqrt(z: Complex | number): Complex { + if (typeof z === "number") z = new Complex(z); + return new Complex( Math.sqrt((z.real + this.abs(z)) / 2), Math.sign(z.imag) * Math.sqrt((-z.real + this.abs(z)) / 2), @@ -134,7 +128,7 @@ export class Complex { ); } - // I know in the Math namespace the natural logarithm is log and not ln, but fuck that + // Should this be named ln or log? /** Returns the natural logarithm of a complex number. */ static ln(z: Complex): Complex { return new Complex(Math.log(this.absSquared(z)) / 2, this.arg(z)); @@ -150,7 +144,7 @@ export class Complex { return (this.div(this.ln(z), Math.log(n))); } - /** Returns the e (Euler's number) raised to the power of a complex number. */ + /** Returns e (Euler's number) raised to the power of a complex number. */ static exp(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -164,7 +158,7 @@ export class Complex { if (typeof z === "number") z = new Complex(z); if (typeof w === "number" && w % 1 === 0) { - // If w is an integer, use exponentiation by squaring + // If w is an integer, use exponentiation by squaring. return w === 0 ? new Complex(1, 0) : w === 1 @@ -177,7 +171,7 @@ export class Complex { ? this.pow(this.mul(z, z), w / 2) : this.mul(z, this.pow(this.mul(z, z), (w - 1) / 2)); } else if (typeof w === "number") { - // If w is a real number, use de Moivres formula + // If w is a real number, use De Moivre's formula. const argZ = Complex.arg(z); const absPow = Math.pow(Complex.abs(z), w); @@ -227,28 +221,25 @@ export class Complex { /** Returns the cotangent of a complex number. */ static cot(z: Complex | number): Complex { - return this.div(1, this.tan(z)); + return this.recip(this.tan(z)); } /** Returns the secant of a complex number. */ static sec(z: Complex | number): Complex { - return this.div(1, this.cos(z)); + return this.recip(this.cos(z)); } /** Returns the cosecant of a complex number. */ static csc(z: Complex | number): Complex { - return this.div(1, this.sin(z)); + return this.recip(this.sin(z)); } /** Returns the arcsine (inverse sine) of a complex number. */ static asin(z: Complex | number): Complex { - return Complex.mul( + return this.mul( this.negI, this.ln( - this.add( - this.sqrt(this.sub(1, this.pow(z, 2))), - this.mul(this.i, z), - ), + this.add(this.sqrt(this.sub(1, this.pow(z, 2))), this.mul(this.i, z)), ), ); } @@ -305,7 +296,7 @@ export class Complex { /** Returns the hyperbolic cotangent of a complex number. */ static coth(z: Complex | number): Complex { - return this.div(this.cosh(z), this.sinh(z)); + return this.recip(this.tanh(z)); } /** Returns the hyperbolic secant of a complex number. */ From 488886924ae9e3172c8b3db8e93db5ed1bb77f20 Mon Sep 17 00:00:00 2001 From: Derek Verduijn <158269354+Definitely-Not-A-Dolphin@users.noreply.github.com> Date: Mon, 4 May 2026 11:44:29 +0000 Subject: [PATCH 6/7] feat(math) Updated the docs for all symbols in math/complex.ts --- math/complex.ts | 344 +++++++++++++++++++++++++++++++++++++++++------- math/mod.ts | 1 + 2 files changed, 301 insertions(+), 44 deletions(-) diff --git a/math/complex.ts b/math/complex.ts index 28db003c5136..7e164675e47b 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -1,13 +1,17 @@ // Copyright 2018-2026 the Deno authors. MIT license. // Should this class be parameterized to support bigint? + +/** + * A class representing a complex number. Also contains utility functions for complex numbers. + * + * @param {number} real The real part of this complex number. + * @param {number} imag The imaginary part of this complex number. + */ export class Complex { real: number; imag: number; - /** - * Create a new complex number - */ constructor(real: number, imag?: number) { this.real = real; this.imag = imag ?? 0; @@ -18,7 +22,13 @@ export class Complex { /** -i, the negative of the imaginary unit */ static negI = new Complex(0, -1); - /** Returns the sum of the supplied complex numbers. */ + /** + * Returns the sum of the supplied complex numbers. + * + * @param {(Complex | number)[]} nums The complex numbers to sum. + * + * @returns {Complex} The sum of the supplied complex numbers. + */ static add(...nums: (Complex | number)[]): Complex { let sum = new Complex(0); @@ -30,12 +40,25 @@ export class Complex { return sum; } - /** Returns the negative of a complex number. */ + /** + * Returns the negative of a complex number. + * + * @param {Complex} z The complex number to negate. + * + * @returns {Complex} The negative of the complex number. + */ static neg(z: Complex): Complex { return new Complex(-z.real, -z.imag); } - /** Returns the difference of two complex numbers. */ + /** + * Returns the difference of two complex numbers. + * + * @param {Complex} x A complex number. + * @param {Complex} y A complex number. + * + * @returns {Complex} The difference of the two complex numbers. + */ static sub(x: Complex | number, y: Complex | number): Complex { if (typeof x === "number") x = new Complex(x); if (typeof y === "number") y = new Complex(y); @@ -43,7 +66,13 @@ export class Complex { return new Complex(x.real - y.real, x.imag - y.imag); } - /** Returns the product of the supplied complex numbers. */ + /** + * Returns the product of the supplied complex numbers. + * + * @param {(Complex | number)[]} nums The complex numbers to multiply. + * + * @returns {Complex} The product of the supplied complex numbers. + */ static mul(...nums: (Complex | number)[]): Complex { let prod = new Complex(1); @@ -58,7 +87,14 @@ export class Complex { return prod; } - /** Returns the ratio of two complex numbers (divides one complex number by another). */ + /** + * Returns the ratio of two complex numbers. + * + * @param {Complex} x A complex number. + * @param {Complex} y A complex number. + * + * @returns {Complex} The ratio of the two complex numbers. + */ static div(x: Complex | number, y: Complex | number): Complex { if (typeof x === "number") x = new Complex(x); if (typeof y === "number") y = new Complex(y); @@ -71,7 +107,13 @@ export class Complex { ); } - /** Returns the reciprocal (multiplicative inverse) of a complex number. */ + /** + * Returns the reciprocal (multiplicative inverse) of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The reciprocal of the supplied number. + */ static recip(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -83,29 +125,63 @@ export class Complex { ); } - /** Returns the square of the absolute value of a complex number. */ + /** + * Returns the square of the absolute value of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The square of the absolute value of the supplied number. + */ static absSquared(z: Complex): number { return z.real * z.real + z.imag * z.imag; } - /** Returns the absolute value of a complex number (the distance from the complex number to the origin on the complex plane). */ + /** + * Returns the absolute value of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The absolute value of the supplied number. + */ static abs(z: Complex): number { return Math.sqrt(this.absSquared(z)); } - /** Returns the argument of a complex number with a domain of [pi, -pi). */ + /** + * Returns the argument of a complex number. The range of this function is (-pi, pi]. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The square of the absolute value of the supplied number. + */ static arg(z: Complex): number { return z.real === 0 && z.imag === 0 ? 0 + : z.imag === 0 && z.real > 0 + ? 0 + : z.imag === 0 && z.real < 0 + ? Math.PI : Math.sign(z.imag) * Math.acos(z.real / Complex.abs(z)); } - /** Returns the conjugate of a complex number (the complex number with its imaginary part negated). */ + /** + * Returns the conjugate of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The conjugate of the supplied number. + */ static conj(z: Complex): Complex { return new Complex(z.real, -z.imag); } - /** Returns the square root of a complex number. */ + /** + * Returns the square root of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The square root of the supplied number. + */ static sqrt(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -115,7 +191,13 @@ export class Complex { ); } - /** Returns the cube root of a complex number. */ + /** + * Returns the cube root of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The cube root of the supplied number. + */ static cbrt(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -129,22 +211,46 @@ export class Complex { } // Should this be named ln or log? - /** Returns the natural logarithm of a complex number. */ + /** + * Returns the natural logarithm of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The natural logarithm of the supplied number. + */ static ln(z: Complex): Complex { return new Complex(Math.log(this.absSquared(z)) / 2, this.arg(z)); } - /** Returns the base-10 logarithm of a complex number. */ + /** + * Returns the base-10 logarithm of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The base-10 logarithm of the supplied number. + */ static log(z: Complex): Complex { return (this.div(this.ln(z), Math.LN10)); } - /** Returns the base-n logarithm of a complex number. */ + /** + * Returns the base-n logarithm of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The base-n logarithm of the supplied number. + */ static logn(z: Complex, n: number): Complex { return (this.div(this.ln(z), Math.log(n))); } - /** Returns e (Euler's number) raised to the power of a complex number. */ + /** + * Returns e (Euler's number) raised to the power of a complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} E (Euler's number) raised to the power of a complex number. + */ static exp(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -153,7 +259,13 @@ export class Complex { return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); } - /** Returns a complex number raised to the power of another complex number. */ + /** + * Returns a complex number raised to the power of another complex number. + * + * @param {Complex | number} z A complex number + * + * @returns {Complex} The supplied complex number raised to the power the other complex number. + */ static pow(z: Complex | number, w: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -187,7 +299,13 @@ export class Complex { } } - /** Returns the sine of a complex number. */ + /** + * Returns the sine of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The sine of the supplied complex number. + */ static sin(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -197,7 +315,13 @@ export class Complex { ); } - /** Returns the cosine of a complex number. */ + /** + * Returns the cosine of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The cosine of the supplied complex number. + */ static cos(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -207,7 +331,13 @@ export class Complex { ); } - /** Returns the tangent of a complex number. */ + /** + * Returns the tangent of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The tangent of the supplied complex number. + */ static tan(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -219,22 +349,46 @@ export class Complex { ); } - /** Returns the cotangent of a complex number. */ + /** + * Returns the cotangent of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The cotangent of the supplied complex number. + */ static cot(z: Complex | number): Complex { return this.recip(this.tan(z)); } - /** Returns the secant of a complex number. */ + /** + * Returns the secant of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The secant of the supplied complex number. + */ static sec(z: Complex | number): Complex { return this.recip(this.cos(z)); } - /** Returns the cosecant of a complex number. */ + /** + * Returns the cosecant of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The cosecant of the supplied complex number. + */ static csc(z: Complex | number): Complex { return this.recip(this.sin(z)); } - /** Returns the arcsine (inverse sine) of a complex number. */ + /** + * Returns the arcsine (inverse sine) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arcsine of the supplied complex number. + */ static asin(z: Complex | number): Complex { return this.mul( this.negI, @@ -244,32 +398,68 @@ export class Complex { ); } - /** Returns the arccosine (inverse cosine) of a complex number. */ + /** + * Returns the arccosine (inverse cosine) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arccosine of the supplied complex number. + */ static acos(z: Complex | number): Complex { return this.sub(Math.PI / 2, this.asin(z)); } - /** Returns the arctangent (inverse tangent) of a complex number. */ + /** + * Returns the arctangent (inverse tangent) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arctangent of the supplied complex number. + */ static atan(z: Complex | number): Complex { return this.asin(this.div(z, this.sqrt(this.sub(1, this.pow(z, 2))))); } - /** Returns the arccotangent (inverse cotangent) of a complex number. */ + /** + * Returns the arccotangent (inverse cotangent) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arccotangent of the supplied complex number. + */ static acot(z: Complex | number): Complex { return this.atan(this.recip(z)); } - /** Returns the arcsecant (inverse secant) of a complex number. */ + /** + * Returns the arcsecant (inverse secant) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arcsecant of the supplied complex number. + */ static asec(z: Complex | number): Complex { return this.acos(this.recip(z)); } - /** Returns the arccosecant (inverse cosecant) of a complex number. */ + /** + * Returns the arccosecant (inverse cosecant) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The arccosecant of the supplied complex number. + */ static acsc(z: Complex | number): Complex { return this.asin(this.recip(z)); } - /** Returns the hyperbolic sine of a complex number. */ + /** + * Returns the hyperbolic sine of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic sine of the supplied complex number. + */ static sinh(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -279,7 +469,13 @@ export class Complex { ); } - /** Returns the hyperbolic cosine of a complex number. */ + /** + * Returns the hyperbolic cosine of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic cosine of the supplied complex number. + */ static cosh(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); @@ -289,52 +485,112 @@ export class Complex { ); } - /** Returns the hyperbolic tangent of a complex number. */ + /** + * Returns the hyperbolic tangent of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic tangent of the supplied complex number. + */ static tanh(z: Complex | number): Complex { return this.div(this.sinh(z), this.cosh(z)); } - /** Returns the hyperbolic cotangent of a complex number. */ + /** + * Returns the hyperbolic cotangent of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic cotangent of the supplied complex number. + */ static coth(z: Complex | number): Complex { return this.recip(this.tanh(z)); } - /** Returns the hyperbolic secant of a complex number. */ + /** + * Returns the hyperbolic secant of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic secant of the supplied complex number. + */ static sech(z: Complex | number): Complex { return this.recip(this.cosh(z)); } - /** Returns the hyperbolic cosecant of a complex number. */ + /** + * Returns the hyperbolic cosecant of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns The hyperbolic cosecant of the supplied complex number. + */ static csch(z: Complex | number): Complex { return this.recip(this.sinh(z)); } - /** Returns the hyperbolic arcsine (inverse hyperbolic sine) of a complex number. */ + /** + * Returns the hyperbolic arcsine (inverse hyperbolic sine) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arcsine (inverse hyperbolic sine) of the supplied complex number. + */ static asinh(z: Complex | number): Complex { return this.ln(this.add(this.sqrt(this.add(this.pow(z, 2), 1)), z)); } - /** Returns the hyperbolic arccosine (inverse hyperbolic cosine) of a complex number. */ + /** + * Returns the hyperbolic arccosine (inverse hyperbolic cosine) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arccosine (inverse hyperbolic cosine) of the supplied complex number. + */ static acosh(z: Complex | number): Complex { return this.ln(this.add(this.sqrt(this.sub(this.pow(z, 2), 1)), z)); } - /** Returns the hyperbolic arctangent (inverse hyperbolic tangent) of a complex number. */ + /** + * Returns the hyperbolic arctangent (inverse hyperbolic tangent) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arctangent (inverse hyperbolic tangent) of the supplied complex number. + */ static atanh(z: Complex | number): Complex { return this.div(this.ln(this.div(this.add(1, z), this.sub(1, z))), 2); } - /** Returns the hyperbolic arccotangent (inverse hyperbolic cotangent) of a complex number. */ + /** + * Returns the hyperbolic arccotangent (inverse hyperbolic cotangent) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arctangent (inverse hyperbolic cotangent) of the supplied complex number. + */ static acoth(z: Complex | number): Complex { return this.atanh(this.recip(z)); } - /** Returns the hyperbolic arcsecant (inverse hyperbolic secant) of a complex number. */ + /** + * Returns the hyperbolic arcsecant (inverse hyperbolic secant) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arcsecant (inverse hyperbolic secant) of the supplied complex number. + */ static asech(z: Complex | number): Complex { return this.acosh(this.recip(z)); } - /** Returns the hyperbolic arccosecant (inverse hyperbolic cosecant) of a complex number. */ + /** + * Returns the hyperbolic arccosecant (inverse hyperbolic cosecant) of a complex number. + * + * @param {Complex | number} z A complex number. + * + * @returns {Complex | number} The hyperbolic arccosecant (inverse hyperbolic cosecant) of the supplied complex number. + */ static acsch(z: Complex | number): Complex { return this.asinh(this.recip(z)); } diff --git a/math/mod.ts b/math/mod.ts index 4e788dd1656a..08b7bb839caf 100644 --- a/math/mod.ts +++ b/math/mod.ts @@ -19,6 +19,7 @@ * @module */ +export * from "./complex.ts"; export * from "./clamp.ts"; export * from "./modulo.ts"; export * from "./round_to.ts"; From 7bcc9e1559b75bd2bca83991e7b8f962e04ed4f7 Mon Sep 17 00:00:00 2001 From: Derek Verduijn Date: Mon, 4 May 2026 23:46:12 +0200 Subject: [PATCH 7/7] NaN-optimizing functions and researching why the arcfunctions are broken --- math/complex.ts | 105 ++++++++++++++++++++++++++++++++++--------- math/complex_test.ts | 43 ++++++++++++++++++ 2 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 math/complex_test.ts diff --git a/math/complex.ts b/math/complex.ts index 7e164675e47b..a54e2b56353e 100644 --- a/math/complex.ts +++ b/math/complex.ts @@ -2,6 +2,24 @@ // Should this class be parameterized to support bigint? +/* +Currently, the following functions are broken +- asin +- acos +- atan +- acot +- asec +- acsc +- asinh +- acosh +- atanh +- acoth +- asech +- acsch + +I suspect this is because of faulty branch cuts but I really don't know. +*/ + /** * A class representing a complex number. Also contains utility functions for complex numbers. * @@ -9,12 +27,25 @@ * @param {number} imag The imaginary part of this complex number. */ export class Complex { - real: number; - imag: number; + #real: number; + #imag: number; + + get real() { + return this.#real; + } + + get imag() { + return this.#imag; + } constructor(real: number, imag?: number) { - this.real = real; - this.imag = imag ?? 0; + if (Number.isNaN(real) || Number.isNaN(imag)) { + this.#real = NaN; + this.#imag = NaN; + } else { + this.#real = real; + this.#imag = imag ?? 0; + } } /** i, the imaginary unit */ @@ -22,6 +53,12 @@ export class Complex { /** -i, the negative of the imaginary unit */ static negI = new Complex(0, -1); + static #complexNaN = new Complex(NaN, NaN); + + static #NaNChecker(z: Complex) { + return Number.isNaN(z.real) || Number.isNaN(z.real); + } + /** * Returns the sum of the supplied complex numbers. * @@ -30,6 +67,8 @@ export class Complex { * @returns {Complex} The sum of the supplied complex numbers. */ static add(...nums: (Complex | number)[]): Complex { + if (nums.includes(this.#complexNaN)) return this.#complexNaN; + let sum = new Complex(0); for (let num of nums) { @@ -60,6 +99,9 @@ export class Complex { * @returns {Complex} The difference of the two complex numbers. */ static sub(x: Complex | number, y: Complex | number): Complex { + if (x === this.#complexNaN || y === this.#complexNaN) { + return this.#complexNaN; + } if (typeof x === "number") x = new Complex(x); if (typeof y === "number") y = new Complex(y); @@ -74,10 +116,13 @@ export class Complex { * @returns {Complex} The product of the supplied complex numbers. */ static mul(...nums: (Complex | number)[]): Complex { + if (nums.includes(this.#complexNaN)) return this.#complexNaN; + let prod = new Complex(1); for (let num of nums) { if (typeof num === "number") num = new Complex(num); + prod = new Complex( prod.real * num.real - prod.imag * num.imag, prod.real * num.imag + prod.imag * num.real, @@ -96,6 +141,9 @@ export class Complex { * @returns {Complex} The ratio of the two complex numbers. */ static div(x: Complex | number, y: Complex | number): Complex { + if (x === this.#complexNaN || y === this.#complexNaN) { + return this.#complexNaN; + } if (typeof x === "number") x = new Complex(x); if (typeof y === "number") y = new Complex(y); @@ -115,7 +163,7 @@ export class Complex { * @returns {Complex} The reciprocal of the supplied number. */ static recip(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(1 / z); const absSquaredZ = this.absSquared(z); @@ -185,9 +233,11 @@ export class Complex { static sqrt(z: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); + const absZ = this.abs(z); + return new Complex( - Math.sqrt((z.real + this.abs(z)) / 2), - Math.sign(z.imag) * Math.sqrt((-z.real + this.abs(z)) / 2), + Math.sqrt((z.real + absZ) / 2), + Math.sign(z.imag) * Math.sqrt((-z.real + absZ) / 2), ); } @@ -199,14 +249,14 @@ export class Complex { * @returns {Complex} The cube root of the supplied number. */ static cbrt(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(Math.cbrt(z)); - const argZ = this.arg(z); + const argZdiv = this.arg(z) / 3; const absCbrt = Math.cbrt(Complex.abs(z)); return new Complex( - absCbrt * Math.cos(argZ / 3), - absCbrt * Math.sin(argZ / 3), + absCbrt * Math.cos(argZdiv), + absCbrt * Math.sin(argZdiv), ); } @@ -230,7 +280,7 @@ export class Complex { * @returns {Complex} The base-10 logarithm of the supplied number. */ static log(z: Complex): Complex { - return (this.div(this.ln(z), Math.LN10)); + return this.div(this.ln(z), Math.LN10); } /** @@ -252,13 +302,14 @@ export class Complex { * @returns {Complex} E (Euler's number) raised to the power of a complex number. */ static exp(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(Math.exp(z)); const expReal = Math.exp(z.real); return new Complex(expReal * Math.cos(z.imag), expReal * Math.sin(z.imag)); } + // Branch cut conforming! /** * Returns a complex number raised to the power of another complex number. * @@ -268,8 +319,9 @@ export class Complex { */ static pow(z: Complex | number, w: Complex | number): Complex { if (typeof z === "number") z = new Complex(z); + if (typeof w !== "number" && w.imag === 0) w = w.real; - if (typeof w === "number" && w % 1 === 0) { + if (typeof w === "number" && Number.isInteger(w)) { // If w is an integer, use exponentiation by squaring. return w === 0 ? new Complex(1, 0) @@ -284,12 +336,12 @@ export class Complex { : this.mul(z, this.pow(this.mul(z, z), (w - 1) / 2)); } else if (typeof w === "number") { // If w is a real number, use De Moivre's formula. - const argZ = Complex.arg(z); + const argZw = Complex.arg(z) * w; const absPow = Math.pow(Complex.abs(z), w); return new Complex( - absPow * Math.cos(argZ * w), - absPow * Math.sin(argZ * w), + absPow * Math.cos(argZw), + absPow * Math.sin(argZw), ); } else { return Complex.mul( @@ -307,7 +359,7 @@ export class Complex { * @returns The sine of the supplied complex number. */ static sin(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(Math.sin(z)); return new Complex( Math.sin(z.real) * Math.cosh(z.imag), @@ -323,7 +375,7 @@ export class Complex { * @returns The cosine of the supplied complex number. */ static cos(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(Math.cos(z)); return new Complex( Math.cos(z.real) * Math.cosh(z.imag), @@ -339,7 +391,7 @@ export class Complex { * @returns The tangent of the supplied complex number. */ static tan(z: Complex | number): Complex { - if (typeof z === "number") z = new Complex(z); + if (typeof z === "number") return new Complex(Math.tan(z)); const w = Math.cos(2 * z.imag) + Math.cosh(2 * z.imag); @@ -382,6 +434,7 @@ export class Complex { return this.recip(this.sin(z)); } + // This function is broken and I have no clue why /** * Returns the arcsine (inverse sine) of a complex number. * @@ -537,7 +590,17 @@ export class Complex { * @returns {Complex | number} The hyperbolic arcsine (inverse hyperbolic sine) of the supplied complex number. */ static asinh(z: Complex | number): Complex { - return this.ln(this.add(this.sqrt(this.add(this.pow(z, 2), 1)), z)); + return this.ln( + this.add( + this.sqrt( + this.add( + this.pow(z, 2), + 1, + ), + ), + z, + ), + ); } /** diff --git a/math/complex_test.ts b/math/complex_test.ts new file mode 100644 index 000000000000..46d5982617e1 --- /dev/null +++ b/math/complex_test.ts @@ -0,0 +1,43 @@ +// Copyright 2018-2026 the Deno authors. MIT license. +import { Complex } from "./complex.ts"; +import { assertEquals } from "@std/assert"; + +Deno.test("Complex", async (t) => { + const complexZero = new Complex(0); + const complexNaN = new Complex(NaN); + const complexInfinity = new Complex(Infinity, Infinity); + const complexNegInfinity = new Complex(-Infinity, -Infinity); + + await t.step("constructor", () => { + assertEquals(new Complex(5, 1).imag, 1); + assertEquals(new Complex(5, 1).real, 5); + + assertEquals(new Complex(NaN, 1), complexNaN); + assertEquals(new Complex(1, NaN), complexNaN); + assertEquals(new Complex(NaN, NaN), complexNaN); + + assertEquals(new Complex(Infinity, 1), complexInfinity); + assertEquals(new Complex(1, Infinity), complexInfinity); + assertEquals(new Complex(Infinity, Infinity), complexInfinity); + + assertEquals(new Complex(-Infinity, 1), complexNegInfinity); + assertEquals(new Complex(1, -Infinity), complexNegInfinity); + assertEquals(new Complex(-Infinity, -Infinity), complexNegInfinity); + }); + + await t.step("add()", () => { + assertEquals( + Complex.add(new Complex(0, 0), new Complex(1, 2), new Complex(4, 2)), + new Complex(5, 4), + ); + assertEquals( + Complex.add(new Complex(0, 0), new Complex(1, 2), new Complex(4, 2)), + new Complex(5, 4), + ); + + assertEquals( + Complex.add(new Complex(2, 3), new Complex(2, Infinity)), + new Complex(0), + ); + }); +});