Created
June 3, 2022 07:44
-
-
Save rhom6us/8f8f910c97f77c33684b2fd267c5fe14 to your computer and use it in GitHub Desktop.
Complex number data structure & operators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://github.com/RobTillaart/Arduino/blob/master/libraries/Complex/complex.cpp | |
function format(real, imag){ | |
if(this.imag == 0){ | |
return real | |
} | |
if(real == 0){ | |
return 'i' + imag; | |
} | |
return `${real} ${imag < 0 ? `-` : `+`} ${Math.abs(imag)}i`; | |
} | |
class Complex { | |
static from(...args/*: [r: number, i?: number] | [[r: number, i?: number] | Complex]*/) { | |
switch(args.length) { | |
case 1: | |
const p = args[0]; | |
if(p instanceof Complex){ | |
return p; | |
} | |
if(Array.isArray(p)){ | |
return Complex.from(...p); | |
} | |
if(typeof p === 'object'){ | |
if(['ϱ', 'm', 'mag', 'magnitude', 'radius', 'mod', 'modulo', 'absoluteValue', 'ϕ', 'p', 'phase', 'argument'].some(key => key in p)){ | |
const magnitude = p.ϱ ?? p.m ?? p.mag ?? p.magnitude ?? ?? p.radius ?? p.mod ?? p.modulo ?? p.absoluteValue ?? p.r ?? throw new Error('Both values must be provided when using polar coordinates'), | |
phase = p.ϕ ?? p.p ?? p.phase ?? p.argument ?? throw new Error('Both values must be provided when using polar coordinates'); | |
return Complex.from({ | |
x: magnitude * Math.cos(phase), | |
y: magnitude * Math.sin(phase) | |
}); | |
} | |
if(['real', 'x', 'i', 'imag', 'imaginary', 'y'].some(key => key in p)){ | |
return Complex.from( | |
p.real ?? p.x ?? p.r ?? 0, | |
p.i ?? p.imag ?? p.imaginary ?? p.y ?? 0 | |
); | |
} | |
if('r' in p){ | |
return Complex.from(p.r); | |
} | |
} | |
if(!isNaN(p)){ | |
return Real.from(+p); | |
} | |
throw new 'wtf mate'; | |
case 2: | |
return new Complex(...args); | |
default: | |
throw new 'wtf mate'; | |
} | |
} | |
constructor(real, imag){ | |
if(isNaN(real) || isNaN(imag)){ | |
throw new Error(`Values cannot be 'not a number'`); | |
} | |
if(new.target === Complex) { | |
if(imag == 0){ | |
return new Real(real); | |
} | |
else if(real == 0){ | |
return new Imaginary(imag); | |
} | |
} | |
this.#r = +real; | |
this.#i = +imag; | |
} | |
#r; | |
get r() { | |
return this.#r; | |
} | |
get real() { | |
return this.r; | |
} | |
get x() { | |
return this.r; | |
} | |
#i; | |
get i() { | |
return this.#i; | |
} | |
get imag() { | |
return this.i; | |
} | |
get imaginary() { | |
return this.i; | |
} | |
get y() { | |
return this.i; | |
} | |
#ϱ; | |
get ϱ() { | |
return this.#ϱ ??= Math.hypot(this.x, this.y); | |
} | |
get magnitude() { | |
return this.ϱ; | |
} | |
#ϕ; | |
get ϕ() { | |
return this.#ϕ ??= Math.atan2(this.y, this.x); | |
} | |
get phase() { | |
return this.ϕ; | |
} | |
get argument() { | |
return this.ϕ; | |
} | |
static conjugate(value/*: Complex | number*/) { | |
const c = Complex.from(value); | |
return Complex.from(c.real, -c.imaginary); | |
} | |
get conjugate(){ | |
return Complex.conjugate(this); | |
} | |
static reciprocal(value/*: Complex | number*/) { | |
const c = Complex.from(value), | |
f = 1.0 / (c.x**2 + c.y**2); | |
return Complex.from({ | |
x: c.x * f, | |
y: -c.y * f | |
}); | |
} | |
get reciprocal() { | |
return Complex.reciprocal(this); | |
} | |
static equal(term1, term2) { | |
const a = Complex.from(term1), | |
b = Complex.from(term2); | |
return (a.x === b.x) && (a.y === b.y); | |
} | |
equals(term/*: Complex | number*/){ | |
return Complex.equal(this, term); | |
} | |
static negate(value/*: Complex | number*/) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
x: -c.x, | |
y: -c.y | |
}); | |
} | |
get negation(){ | |
return Complex.negate(this); | |
} | |
static sum(augend, addend) { | |
const a = Complex.from(augent), | |
b = Complex.from(addend); | |
return Complex.from({ | |
x: a.x + b.x, | |
y: a.y + b.y | |
}); | |
} | |
plus(term){ | |
return Complex.sum(this, addend); | |
} | |
static difference(minuend, subtrahend){ | |
return Complex.sum(minuend, Complex.negate(subtrahend)); | |
} | |
minus(term){ | |
return Complex.difference(this, term); | |
} | |
static product(multiplicand, multiplier) { | |
const a = Complex.from(multiplicand), | |
b = Complex.from(multiplier); | |
return Complex.from({ | |
x: a.x * b.x - a.y * b.y, | |
y: a.x * b.y + a.y * b.x | |
}); | |
} | |
times(factor){ | |
return Complex.product(this, factor); | |
} | |
static quotient(dividend, divisor) { | |
return Complex.product(dividend, Complex.reciprocal(divisor)); | |
} | |
over(divisor){ | |
return Complex.quotient(this, divisor); | |
} | |
static ln(value/*: Complex | number*/) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
real: Math.log(value.magnitude), | |
image: value.phase - (value.phase > Math.PI) * 2 * Math.PI | |
}); | |
} | |
get ln() { | |
return this._ln ??= Complex.ln(this); | |
} | |
static log(value/*: Complex | number */, base/*: Complex | number */) { | |
return Complex.quotient(Complex.ln(value), Complex.ln(base)); | |
} | |
log(base/*: Complex | number */) { | |
return Complex.log(this, base); | |
} | |
static log10(value/*: Complex | number */){ | |
return Complex.log(value, 10); | |
} | |
get log10() { | |
return this._log10 ??= Complex.log10(this); | |
} | |
static exp(value/*: Complex | number */) { | |
const c = Complex.from(value), | |
e = Math.exp(c.x); | |
return Complex.from({ | |
x: e * Math.cos(c.y), | |
y: e * Math.sin(c.y) | |
}); | |
} | |
get exp() { | |
return this._exp ??= Complex.exp(this); | |
} | |
static power(value/*: Complex | number */, exponent)/*: Complex | number */{ | |
return Complex.exp(Complex.product(Complex.ln(value), exponent)); | |
} | |
raisedToThe(exponent){ | |
return Complex.power(this, exponent); | |
} | |
static root(value, exponent){ | |
return Complex.power(value, Complex.reciprocal(exponent)); | |
} | |
nthRoot(exponent){ | |
return Complex.root(this, exponent); | |
} | |
static sqr(value) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
real: c.real**2 - c.imag**2, | |
imag: 2 * c.real * c.imag | |
}); | |
} | |
get sqr() { | |
return this._sqr ??= Complex.sqr(this); | |
} | |
static sqrt(value) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
real: Math.sqrt((c.magnitude + c.real) / 2), | |
imag: Math.sqrt((c.magnitude - c.real) / 2) * (Math.sign(c.imag) || 1) | |
}); | |
} | |
static sin(value) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
x: Math.sin(c.real) * Math.cosh(c.imag), | |
y: Math.cos(c.real) * Math.sinh(c.imag) | |
}); | |
} | |
static cos(value) { | |
const c = Complex.from(value); | |
return Complex.from({ | |
x: Math.cos(c.real) * Math.cosh(c.imag), | |
y: -Math.sin(c.real) * Math.sinh(c.imag) | |
}); | |
} | |
static tan(value) { | |
// return Complex.quotient(Complex.sin(value), Complex.cos(value)); | |
const c = Complex.from(value); | |
//* faster but 350 bytes longer!! | |
const s = Math.sin(c.real); | |
const c = Math.cos(c.real); | |
const sh = Math.sinh(c.imag); | |
const ch = Math.cosh(c.imag); | |
const r0 = s*ch; | |
const i0 = c*sh; | |
const cre = c*ch; | |
const cim = -s*sh; | |
const f = 1.0/(cre**2 + cim**2); | |
const r = r0 * cre + i0 * cim; | |
const i = r0 * cim - i0 * cre; | |
return Complex.from(r * f, -i * f); | |
//*/ | |
} | |
// gonioHeler1(mode){ | |
// new Complex c = (one - this->c_sqr()).c_sqrt(); | |
// if (mode == 0) | |
// { | |
// c = c + this * new Complex(0,-1); | |
// } | |
// else | |
// { | |
// c = this + c * new Complex(0,-1); | |
// } | |
// c = c.c_log() * new Complex(0,1); | |
// return c; | |
// } | |
static asin(value) { | |
return Real.ONE.minus(Complex.sqr(value)).sqrt | |
.plus(Imaginary.NEG_ONE.times(value)) | |
.log.times(Imaginary.ONE); | |
// return Real.ONE.minus(this.sqr()).sqrt() | |
// .plus(this.times(Imaginary.NEG_ONE)) | |
// .log().times(Imaginary.ONE); | |
} | |
static acos(value) { | |
return Real.ONE.minus(Complex.sqr(value)).sqrt | |
.times(Imaginary.NEG_ONE).plus(value) | |
.log.times(Imaginary.ONE); | |
// return Real.ONE.minus(this.sqr()).sqrt() | |
// .times(Imaginary.NEG_ONE).plus(this) | |
// .log().times(Imaginary.ONE); | |
} | |
static atan(value) { | |
return Imaginary.NEG_ONE.times( | |
Imaginary.NEG_ONE.plus(value).over( | |
Complex.negate(value).plus(Imaginary.NEG_ONE) | |
).log | |
).times(Real.HALF); | |
// return Imaginary.NEG_ONE.times( | |
// this.plus(Imaginary.NEG_ONE).over( | |
// this.negate().plus(Imaginary.NEG_ONE) | |
// ).log() | |
// ).times(Real.HALF); | |
} | |
static csc(value) { | |
return Complex.reciprocal(Complex.sin(value)); | |
} | |
static sec(value) { | |
return Complex.reciprocal(Complex.cos(value)); | |
} | |
static cot(value) { | |
return Complex.reciprocal(Complex.tan(value)); | |
} | |
static acsc(value) { | |
return Complex.reciprocal(Complex.asin(value)); | |
} | |
static asec(value) { | |
return Complex.reciprocal(Complex.acos(value)); | |
} | |
static acot(value) { | |
return Complex.reciprocal(Complex.atan(value)); | |
} | |
static sinh(value) { | |
return Complex.from({ | |
x: Math.cos(c.imag) * Math.sinh(c.real), | |
y: Math.sin(c.imag) * Math.cosh(c.real) | |
}); | |
} | |
static cosh(value) { | |
return Complex.from({ | |
x: Math.cos(c.imag) * Math.cosh(c.real), | |
y: Math.sin(c.imag) * Math.sinh(c.real) | |
}); | |
} | |
static tanh(value) { | |
return Complex.sinh(value).over(Complex.cosh(value)); | |
} | |
static asinh(value) { | |
Complex.sqr(value).plus(Real.ONE).sqrt.plus(value).log; | |
} | |
static acosh(value) { | |
Complex.sqr(value).plus(Real.NEG_ONE).sqrt.plus(value).log; | |
} | |
static atanh(value) { | |
return Real.ONE.plus(value).log.minus( | |
Complex.negate(value).plus(Real.ONE).log | |
).times(Real.HALF); | |
} | |
static csch(value) { | |
return Complex.reciprocal(Complex.sinh(value)); | |
} | |
static sech(value) { | |
return Complex.reciprocal(Complex.cosh(value)); | |
} | |
static coth(value) { | |
return Complex.reciprocal(Complex.tanh(value)); | |
} | |
static acsch(value) { | |
return Complex.asinh(Complex.reciprocal(value)); | |
} | |
static asech(value) { | |
return Complex.acosh(Complex.reciprocal(value)); | |
} | |
static acoth(value) { | |
return Complex.atanh(Complex.reciprocal(value)); | |
} | |
toString(radix){ | |
return format(this.real.toString(radix), this.imag.toString(radix)); | |
} | |
toFixed(digits){ | |
return format(this.real.toFixed(digits), this.imag.toFixed(digits)); | |
} | |
toPrecision(precision){ | |
return format(this.real.toPrecision(precision), this.imag.toPrecision(precision)); | |
} | |
} | |
class Imaginary extends Complex { | |
static HALF = new Imaginary(0.5); | |
static ONE = new Imaginary(1); | |
static TWO = new Imaginary(2); | |
static NEG_ONE = new Imaginary(-1); | |
static NEG_TWO = new Imaginary(-2); | |
static from(value) { | |
return new Imaginary(value); | |
} | |
constructor(value){ | |
super(0, value); | |
} | |
} | |
class Real extends Complex { | |
static HALF = new Real(0.5); | |
static ONE = new Real(1); | |
static TWO = new Real(2); | |
static NEG_ONE = new Real(-1); | |
static NEG_TWO = new Real(-2); | |
static from(value) { | |
return new Real(value); | |
} | |
constructor(value){ | |
super(value, 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment