import { TAU } from './Constants' ; import { hslToHex } from './helpers' ; export type ComplexFormat = 'cartesian' | 'polar' | 'polarTau' ; /**
 * Complex number class
 * https://en.wikipedia.org/wiki/Complex_number
 */ export default class Complex { re : number ; im : number ; param re param im returns constructor ( re , im = 0 ) { this . re = re ; this . im = im ; } returns get r ( ) : number { return this . abs ( ) ; } returns get phi ( ) : number { return this . arg ( ) ; } returns get phiTau ( ) : number { return this . arg ( ) / TAU ; } returns abs2 ( ) { return this . re * this . re + this . im * this . im ; } returns abs ( ) { return Math . sqrt ( this . re * this . re + this . im * this . im ) ; } returns arg ( ) { let arg = Math . atan2 ( this . im , this . re ) ; if ( arg < 0 ) { arg += TAU ; } return arg ; } returns conj ( ) { return new Complex ( this . re , - this . im ) ; } param z2 returns add ( z2 ) { const z1 = this ; return new Complex ( z1 . re + z2 . re , z1 . im + z2 . im ) ; } param z2 returns sub ( z2 ) { const z1 = this ; return new Complex ( z1 . re - z2 . re , z1 . im - z2 . im ) ; } param z2 returns mul ( z2 ) { const z1 = this ; return new Complex ( z1 . re * z2 . re - z1 . im * z2 . im , z1 . re * z2 . im + z1 . im * z2 . re ) ; } param z2 returns mulGauss ( z2 ) { const z1 = this ; const k1 = z2 . re * ( z1 . re + z1 . im ) ; const k2 = z1 . re * ( z2 . im - z2 . re ) ; const k3 = z1 . im * ( z2 . re + z2 . im ) ; return new Complex ( k1 - k3 , k1 + k2 ) ; } param z2 returns div ( z2 ) { const z1 = this ; const denom = z2 . im * z2 . im + z2 . re * z2 . re ; if ( denom === 0 ) { throw new Error ( `Cannot divide by 0. z1: ${ this . toString ( ) } / z2: ${ z2 . toString ( ) }` ) ; } const re = ( z1 . re * z2 . re + z1 . im * z2 . im ) / denom ; const im = ( z2 . re * z1 . im - z1 . re * z2 . im ) / denom ; return new Complex ( re , im ) ; } returns normalize ( ) { const norm = this . r ; if ( norm !== 0 ) { return new Complex ( this . re / norm , this . im / norm ) ; } else { throw new Error ( 'Cannot normalize a 0 length vector...' ) ; } } param z2 param eps returns isCloseTo ( z2 , eps = 1e-6 ) { return this . sub ( z2 ) . r < eps ; } param z2 returns equal ( z2 ) { return this . re === z2 . re && this . im === z2 . im ; } return isZero ( ) { return this . re === 0 && this . im === 0 ; } return isOne ( ) { return this . re === 1 && this . im === 0 ; } return isAlmostZero ( ) { return this . abs2 ( ) < 1e-12 ; } return isNormal ( ) { return this . abs2 ( ) === 1 ; } param complexFormat param precision returns toString ( complexFormat = 'cartesian' , precision = 2 ) { switch ( complexFormat ) { case 'cartesian' : return `(${ this . re . toFixed ( precision ) } ${ this . im >= 0 ? '+' : '' }${ this . im . toFixed ( precision ) }i)` ; case 'polar' : return `${ this . r . toFixed ( precision ) } exp(${ this . phi . toFixed ( precision ) }i)` ; case 'polarTau' : return `${ this . r . toFixed ( precision ) } exp(${ this . phiTau . toFixed ( precision ) }τi)` ; default : throw new Error ( `complexFormat '${ complexFormat }' is not in ['cartesian', 'polar', 'polarTau'].` ) ; } } returns toColor ( ) { const angle = ( ( this . phi * 360 ) / TAU + 360 ) % 360 ; return hslToHex ( angle , 100 , 100 - 50 * this . r ) ; } param r param phi static fromPolar ( r , phi ) { return new Complex ( r * Math . cos ( phi ) , r * Math . sin ( phi ) ) ; } /**
     * Generates a random complex with Gaussian distribution
     * (with zero mean and unit variance)
     * using https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform.
     */ static randomGaussian ( ) { const u = Math . random ( ) ; const v = Math . random ( ) ; return Complex . fromPolar ( Math . sqrt ( - 2 * Math . log ( u ) ) , 2 * Math . PI * v ) ; } } param re param im returns export function Cx ( re , im = 0 ) { return new Complex ( re , im ) ; }