Another cubic solver version
parent
fd590b201d
commit
1f5375ef37
@ -0,0 +1,133 @@
|
||||
import type { Complex } from "./types";
|
||||
|
||||
export function complex(real: number, imag?: number): Complex {
|
||||
return [real, imag ?? 0] as Complex;
|
||||
}
|
||||
|
||||
export function add(a: Complex, b: Complex) {
|
||||
return complex(a[0] + b[0], a[1] + b[1]);
|
||||
}
|
||||
|
||||
export function sub(a: Complex, b: Complex): Complex {
|
||||
return complex(a[0] - b[0], a[1] - b[1]);
|
||||
}
|
||||
|
||||
export function mul(a: Complex, b: Complex) {
|
||||
return complex(a[0] * b[0] - a[1] * b[1], a[0] * b[1] + a[1] * b[0]);
|
||||
}
|
||||
|
||||
export function conjugate(a: Complex): Complex {
|
||||
return complex(a[0], -a[1]);
|
||||
}
|
||||
|
||||
export function pow(a: Complex, b: Complex): Complex {
|
||||
const tIsZero = a[0] === 0 && a[1] === 0;
|
||||
const zIsZero = b[0] === 0 && b[1] === 0;
|
||||
|
||||
if (zIsZero) {
|
||||
return complex(1);
|
||||
}
|
||||
|
||||
// If the exponent is real
|
||||
if (b[1] === 0) {
|
||||
if (b[1] === 0 && a[0] > 0) {
|
||||
return complex(Math.pow(a[0], b[0]), 0);
|
||||
} else if (a[0] === 0) {
|
||||
// If base is fully imaginary
|
||||
|
||||
switch (((b[0] % 4) + 4) % 4) {
|
||||
case 0:
|
||||
return complex(Math.pow(a[1], b[0]), 0);
|
||||
case 1:
|
||||
return complex(0, Math.pow(a[1], b[0]));
|
||||
case 2:
|
||||
return complex(-Math.pow(a[1], b[0]), 0);
|
||||
case 3:
|
||||
return complex(0, -Math.pow(b[1], a[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tIsZero && b[0] > 0) {
|
||||
// Same behavior as Wolframalpha, Zero if real part is zero
|
||||
return complex(0);
|
||||
}
|
||||
|
||||
const arg = Math.atan2(a[1], a[0]);
|
||||
const loh = logHypot(a[0], a[1]);
|
||||
|
||||
const re = Math.exp(b[0] * loh - b[1] * arg);
|
||||
const im = b[1] * loh + b[0] * arg;
|
||||
|
||||
return complex(re * Math.cos(im), re * Math.sin(im));
|
||||
}
|
||||
|
||||
export function sqrt([a, b]: Complex): Complex {
|
||||
if (b === 0) {
|
||||
// Real number case
|
||||
if (a >= 0) {
|
||||
return complex(Math.sqrt(a), 0);
|
||||
}
|
||||
|
||||
return complex(0, Math.sqrt(-a));
|
||||
}
|
||||
|
||||
const r = hypot(a, b);
|
||||
|
||||
const re = Math.sqrt(0.5 * (r + Math.abs(a))); // sqrt(2x) / 2 = sqrt(x / 2)
|
||||
const im = Math.abs(b) / (2 * re);
|
||||
|
||||
if (a >= 0) {
|
||||
return complex(re, b < 0 ? -im : im);
|
||||
}
|
||||
|
||||
return complex(im, b < 0 ? -re : re);
|
||||
}
|
||||
|
||||
const hypot = function (x: number, y: number) {
|
||||
x = Math.abs(x);
|
||||
y = Math.abs(y);
|
||||
|
||||
// Ensure `x` is the larger value
|
||||
if (x < y) {
|
||||
[x, y] = [y, x];
|
||||
}
|
||||
|
||||
// If both are below the threshold, use straightforward Pythagoras
|
||||
if (x < 1e8) {
|
||||
return Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
// For larger values, scale to avoid overflow
|
||||
y /= x;
|
||||
return x * Math.sqrt(1 + y * y);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
|
||||
*
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function logHypot(a: number, b: number) {
|
||||
const _a = Math.abs(a);
|
||||
const _b = Math.abs(b);
|
||||
|
||||
if (a === 0) {
|
||||
return Math.log(_b);
|
||||
}
|
||||
|
||||
if (b === 0) {
|
||||
return Math.log(_a);
|
||||
}
|
||||
|
||||
if (_a < 3000 && _b < 3000) {
|
||||
return Math.log(a * a + b * b) * 0.5;
|
||||
}
|
||||
|
||||
a = a * 0.5;
|
||||
b = b * 0.5;
|
||||
|
||||
return 0.5 * Math.log(a * a + b * b) + Math.LN2;
|
||||
}
|
Loading…
Reference in New Issue