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