import { BigFloat,bf,zero,one,half } from "./bf";
/**
* High-Precision Complex Number Utility Class.
* Provides arithmetic and transcendental functions for complex numbers using BigFloat.
*
* @class
* @property {BigFloat} re - The real part.
* @property {BigFloat} im - The imaginary part.
*/
export class Complex {
/**
* @param {number|string|BigFloat|Complex} re - Real part or Complex object
* @param {number|string|BigFloat} [im=undefined] - Imaginary part
*/
constructor(re, im) {
if (re instanceof Complex) {
this.re = re.re;
this.im = re.im;
} else {
this.re = bf(re);
this.im = im === undefined ? zero : bf(im);
}
}
// --- Basic Arithmetic ---
/**
* Adds another complex number.
* @param {Complex|number|string|BigFloat} other
* @returns {Complex}
*/
add(other) {
const b = this._wrap(other);
return new Complex(this.re.add(b.re), this.im.add(b.im));
}
/**
* Subtracts another complex number.
* @param {Complex|number|string|BigFloat} other
* @returns {Complex}
*/
sub(other) {
const b = this._wrap(other);
return new Complex(this.re.sub(b.re), this.im.sub(b.im));
}
/**
* Multiplies by another complex number.
* @param {Complex|number|string|BigFloat} other
* @returns {Complex}
*/
mul(other) {
//fast path
if(other instanceof BigFloat || typeof(other)=="number"){
// (a+bi)(c) = (ac) + (bc)i
const b = other instanceof BigFloat ? other:bf(other);
const ac = this.re.mul(b);
const bc = this.im.mul(b);
return new Complex(ac, bc);
}
const b = this._wrap(other);
// (a+bi)(c+di) = (ac-bd) + (ad+bc)i
const ac = this.re.mul(b.re);
const bd = this.im.mul(b.im);
const ad = this.re.mul(b.im);
const bc = this.im.mul(b.re);
return new Complex(ac.sub(bd), ad.add(bc));
}
/**
* Divides by another complex number.
* @param {Complex|number|string|BigFloat} other
* @returns {Complex}
*/
div(other) {
//fast path
if(other instanceof BigFloat || typeof(other)=="number"){
// (a+bi)/(c) = [(ac) + (bc)i] / (c^2)
const b = other instanceof BigFloat ? other:bf(other);
const ac = this.re.mul(b);
const denom=b.mul(b);
ac.setdiv(ac,denom);
const bc = this.im.mul(b);
bc.setdiv(bc,denom);
return new Complex(ac, bc);
}
const b = this._wrap(other);
// (a+bi)/(c+di) = [(ac+bd) + (bc-ad)i] / (c^2+d^2)
let tmpa=bf(undefined, 10, false, false),
tmpb=bf(undefined, 10, false, false);
tmpa.setmul(b.re,b.re);
tmpb.setmul(b.im,b.im);
let denom = bf(undefined, 10, false, false).setadd(tmpa,tmpb);
if (denom.isZero()) {
tmpa.dispose(false);
tmpb.dispose(false);
denom.dispose(false);
throw new Error("Complex division by zero");
}
const ac = tmpa.setmul(this.re,b.re);
const bd = tmpb.setmul(this.im,b.im);
let newRe = ac.add(bd);
newRe.setdiv(newRe,denom);
const bc = tmpa.setmul(this.im,b.re);
const ad = tmpb.setmul(this.re,b.im);
let newIm = bc.sub(ad);
newIm.setdiv(newIm, denom);
tmpa.dispose(false);
tmpb.dispose(false);
denom.dispose(false);
return new Complex(newRe, newIm);
}
/**
* Raises this complex number to the power of another.
* @param {Complex|number|string|BigFloat} other
* @returns {Complex}
*/
pow(other) {
const b = this._wrap(other);
if (this.re.isZero() && this.im.isZero()) {
return new Complex(zero, zero);
}
// w^z = exp(z * ln(w))
return this.log().mul(b).exp();
}
// --- Advanced Math & Transcendental Functions ---
/**
* Magnitude (Absolute value) |z|
* @returns {BigFloat}
*/
abs() {
let a = this.re.mul(this.re);
let b = this.im.mul(this.im);
b.setadd(a,b);
a.setsqrt(b);
return a;
}
/**
* Argument (Angle) arg(z)
* @returns {BigFloat}
*/
arg() {
return this.im.atan2(this.re);
}
/**
* Complex Square Root sqrt(z)
* @returns {Complex}
*/
sqrt() {
const r = this.abs();
let re = r.add(this.re);
re = re.setmul(re,half).sqrt();
let im = r.sub(this.re);
im = im.setmul(im,half).sqrt();
return new Complex(re, this.im.cmp(zero) >= 0 ? im : im.neg());
}
/**
* Complex Exponential e^z
* @returns {Complex}
*/
exp() {
// e^(a+bi) = e^a * (cos(b) + i*sin(b))
const expRe = this.re.exp();
return new Complex(expRe.mul(this.im.cos()), expRe.mul(this.im.sin()));
}
/**
* Complex Natural Logarithm ln(z)
* @returns {Complex}
*/
log() {
// ln(z) = ln|z| + i*arg(z)
const rSq = this.re.mul(this.re).add(this.im.mul(this.im));
return new Complex(rSq.log().mul(half), this.arg());
}
/**
* Trigonometric Sine sin(z)
* sin(x+iy) = sin(x)cosh(y) + i cos(x)sinh(y)
* @returns {Complex}
*/
sin() {
const x = this.re;
const y = this.im;
let re=x.sin();
re=re.setmul(re, y.cosh());
let im=x.cos();
im.setmul(im, y.sinh());
return new Complex(re,im);
}
/**
* Trigonometric Cosine cos(z)
* cos(x+iy) = cos(x)cosh(y) - i sin(x)sinh(y)
* @returns {Complex}
*/
cos() {
const x = this.re;
const y = this.im;
let re=x.cos();
re=re.setmul(re,y.cosh());
let im=x.sin();
im.setmul(im,y.sinh());
im.setneg();
return new Complex(re,im);
}
/**
* Trigonometric Tangent tan(z)
* @returns {Complex}
*/
tan() {
return this.sin().div(this.cos());
}
/**
* Hyperbolic Sine sinh(z)
* sinh(x+iy) = sinh(x)cos(y) + i cosh(x)sin(y)
* @returns {Complex}
*/
sinh() {
const x = this.re;
const y = this.im;
let re=x.sinh();
re.setmul(re,y.cos());
let im=x.cosh();
im.setmul(im,y.sin())
return new Complex(re,im);
}
/**
* Hyperbolic Cosine cosh(z)
* cosh(x+iy) = cosh(x)cos(y) + i sinh(x)sin(y)
* @returns {Complex}
*/
cosh() {
const x = this.re;
const y = this.im;
let re=x.cosh();
re.setmul(re,y.cos());
let im=x.sinh();
im.setmul(im,y.sin())
return new Complex(re,im);
}
/**
* Hyperbolic Tangent tanh(z)
* @returns {Complex}
*/
tanh() {
return this.sinh().div(this.cosh());
}
/**
* Inverse Sine asin(z) = -i * ln(iz + sqrt(1 - z^2))
* @returns {Complex}
*/
asin() {
const i = new Complex(zero, one);
const c_one = new Complex(one, zero);
const iz = i.mul(this);
const sqrtPart = c_one.sub(this.mul(this)).sqrt();
return iz.add(sqrtPart).log().mul(i.neg());
}
/**
* Inverse Cosine acos(z) = -i * ln(z + i*sqrt(1 - z^2))
* @returns {Complex}
*/
acos() {
const i = new Complex(zero, one);
const c_one = new Complex(one, zero);
const sqrtPart = c_one.sub(this.mul(this)).sqrt();
return this.add(i.mul(sqrtPart)).log().mul(i.neg());
}
/**
* Inverse Tangent atan(z) = (i/2) * ln((i+z)/(i-z))
* @returns {Complex}
*/
atan() {
const i = new Complex(zero, one);
const halfI = new Complex(zero, half);
const numerator = i.add(this);
const denominator = i.sub(this);
return numerator.div(denominator).log().mul(halfI.neg());
}
/**
* Inverse Hyperbolic Sine asinh(z) = ln(z + sqrt(z^2 + 1))
* @returns {Complex}
*/
asinh() {
const c_one = new Complex(one, zero);
return this.add(this.mul(this).add(c_one).sqrt()).log();
}
/**
* Inverse Hyperbolic Cosine acosh(z) = ln(z + sqrt(z^2 - 1))
* @returns {Complex}
*/
acosh() {
const c_one = new Complex(one, zero);
return this.add(this.mul(this).sub(c_one).sqrt()).log();
}
/**
* Inverse Hyperbolic Tangent atanh(z) = 0.5 * ln((1+z)/(1-z))
* @returns {Complex}
*/
atanh() {
const c_one = new Complex(one, zero);
const half = new Complex(half, zero);
return c_one.add(this).div(c_one.sub(this)).log().mul(half);
}
// --- Utilities ---
/**
* Returns the complex conjugate.
* @returns {Complex}
*/
conj() {
return new Complex(this.re, this.im.neg());
}
/**
* Negates the complex number.
* @returns {Complex}
*/
neg() {
return new Complex(this.re.neg(), this.im.neg());
}
/**
* Checks for equality with another complex number.
* @param {Complex|number|string|BigFloat} b
* @returns {boolean}
*/
equals(b) {
const other = this._wrap(b);
return this.re.equals(other.re) && this.im.equals(other.im);
}
/**
* Checks if the complex number is almost zero.
* @returns {boolean}
*/
isAlmostZero() {
return this.re.isAlmostZero() && this.im.isAlmostZero();
}
/**
* Checks if the complex number is exactly zero.
* @returns {boolean}
*/
isZero(){
return this.re.isZero() && this.im.isZero();
}
/**
* Converts the complex number to a string.
* @param {number} [base=10]
* @param {number} [precision=-1]
* @param {boolean} [pretty=false] pretty print
* @returns {string}
*/
toString(base = 10, precision = -1, pretty=false) {
let rezero=pretty?this.re.isAlmostZero():this.re.isZero();
let imzero=pretty?this.im.isAlmostZero():this.im.isZero();
if(rezero && imzero){
return "0";
}else if(imzero){
return this.re.toString(base, precision, pretty);
}else {
let imabs=this.im.abs();
let imabsf=imabs.toNumber();
const imStr = imabsf==1?"":imabs.toString(base, precision, pretty);
if(rezero){
const sign = this.im.cmp(zero) < 0 ? '-' : '';
return `${sign}${imStr}i`;
}
const sign = this.im.cmp(zero) < 0 ? '-' : '+';
const reStr = this.re.toString(base, precision, pretty);
return `(${reStr} ${sign} ${imStr}i)`;
}
}
/**
* Wraps a value in a Complex object if it isn't one already.
* @private
* @param {Complex|number|string|BigFloat} v
* @returns {Complex}
*/
_wrap(v) {
if (v === undefined) {
throw new Error("Operand mismatch");
}
if (v instanceof Complex) return v;
return new Complex(v);
}
/**
* Creates a complex number from polar coordinates.
* @param {number|string|BigFloat} r - The radius.
* @param {number|string|BigFloat} theta - The angle.
* @returns {Complex}
*/
static fromPolar(r, theta) {
const R = bf(r);
const T = bf(theta);
return new Complex(R.mul(T.cos()), R.mul(T.sin()));
}
/**
* Creates a complex number from a string.
* @param {string} s
* @returns {Complex}
*/
static fromString(s){
s=s.trim();
s=s.replace(/I/g,"i");
// If it's just "i" or "-i"
if (s === 'i') return new Complex(0, 1);
if (s === '-i') return new Complex(0, -1);
// Use a regex to split real and imaginary parts
// This regex handles: "1+2i", "1-2i", "1.5+i", "-2i"
const match = s.match(/^(.+?)([+-].*|(?=i))i$/);
if (match) {
const re = match[1] === "" ? 0 : match[1];
let im = match[2];
if (im === "+" || im === "") im = 1;
if (im === "-") im = -1;
return new Complex(re, im);
}
// Pure imaginary like "2i"
if (s.endsWith('i')) {
const im = s.slice(0, -1);
return new Complex(0, im === "" ? 1 : (im === "-" ? -1 : im));
}
return new Complex(BigFloat.fromString(s));
}
}
/** @type {function(Complex|number|string|BigFloat): Complex} */
Complex.prototype.operatorAdd=Complex.prototype.add;
/** @type {function(Complex|number|string|BigFloat): Complex} */
Complex.prototype.operatorSub=Complex.prototype.sub;
/** @type {function(Complex|number|string|BigFloat): Complex} */
Complex.prototype.operatorMul=Complex.prototype.mul;
/** @type {function(Complex|number|string|BigFloat): Complex} */
Complex.prototype.operatorDiv=Complex.prototype.div;
/** @type {function(Complex|number|string|BigFloat): Complex} */
Complex.prototype.operatorPow=Complex.prototype.pow;
/** @type {function(): Complex} */
Complex.prototype.operatorNeg=Complex.prototype.neg;
/**
* Creates a new Complex instance.
* @param {number|string|BigFloat|Complex} re - Real part or Complex object
* @param {number|string|BigFloat} [im] - Imaginary part
* @returns {Complex}
*/
export function complex(re,im){
return new Complex(re,im);
}