Skip to content

Instantly share code, notes, and snippets.

@rhom6us
Created June 3, 2022 07:44
Show Gist options
  • Save rhom6us/8f8f910c97f77c33684b2fd267c5fe14 to your computer and use it in GitHub Desktop.
Save rhom6us/8f8f910c97f77c33684b2fd267c5fe14 to your computer and use it in GitHub Desktop.
Complex number data structure & operators
// 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