package funkin.audio.visualize.dsp;

/**
  Complex number representation.
**/
@:forward(real, imag) @:notNull @:pure
abstract Complex({
  final real:Float;
  final imag:Float;
})
{
  public inline function new(real:Float, imag:Float)
    this = {real: real, imag: imag};

  /**
    Makes a Complex number with the given Float as its real part and a zero imag part.
  **/
  @:from
  public static inline function fromReal(r:Float)
    return new Complex(r, 0);

  /**
    Complex argument, in radians.
  **/
  public var angle(get, never):Float;

  inline function get_angle()
    return Math.atan2(this.imag, this.real);

  /**
    Complex module.
  **/
  public var magnitude(get, never):Float;

  inline function get_magnitude()
    return Math.sqrt(this.real * this.real + this.imag * this.imag);

  @:op(A + B)
  public inline function add(rhs:Complex):Complex
    return new Complex(this.real + rhs.real, this.imag + rhs.imag);

  @:op(A - B)
  public inline function sub(rhs:Complex):Complex
    return new Complex(this.real - rhs.real, this.imag - rhs.imag);

  @:op(A * B)
  public inline function mult(rhs:Complex):Complex
    return new Complex(this.real * rhs.real - this.imag * rhs.imag, this.real * rhs.imag + this.imag * rhs.real);

  /**
    Returns the complex conjugate, does not modify this object.
  **/
  public inline function conj():Complex
    return new Complex(this.real, -this.imag);

  /**
    Multiplication by a real factor, does not modify this object.
  **/
  public inline function scale(k:Float):Complex
    return new Complex(this.real * k, this.imag * k);

  public inline function copy():Complex
    return new Complex(this.real, this.imag);

  /**
    The imaginary unit.
  **/
  public static final im = new Complex(0, 1);

  /**
    The complex zero.
  **/
  public static final zero = new Complex(0, 0);

  /**
    Computes the complex exponential `e^(iw)`.
  **/
  public static inline function exp(w:Float)
    return new Complex(Math.cos(w), Math.sin(w));
}