// Complex.fls - Complex number arithmetic for Flaris.
//
// A Complex instance holds a real and imaginary part (float or int).
// All arithmetic methods return a new Complex - instances are immutable by convention.
//
// Usage:
//   import { Complex } from library("Complex", "1.0");
//
//   let a = new Complex(3, 4);                  // 3+4i
//   let b = Complex.FromPolar(5.0, Math.PI/4);  // from polar (r, θ)
//   let c = a.Add(b).Mul(new Complex(0, 1));    // chain operations
//   Console.WriteLine(a.ToString());            // "3+4i"
//   Console.WriteLine(a.Magnitude());           // 5.0
//
// Constructor:
//   new Complex(real, imag)
//
// Static factories:
//   Complex.FromPolar(r, theta)   →  Complex
//   Complex.Zero()                →  0+0i
//   Complex.One()                 →  1+0i
//   Complex.I()                   →  0+1i
//   Complex.NaN()                 →  NaN+NaNi
//
// Arithmetic (all return new Complex):
//   Add(other)  Sub(other)  Mul(other)  Div(other)
//   Neg()       - unary negation
//   Conj()      - conjugate (a-bi)
//   Scale(f)    - multiply real and imag by scalar f
//   Reciprocal()
//
// Powers & logarithms:
//   Exp()       - e^z
//   Log()       - natural log
//   Sqrt()
//   Pow(n:int)  - integer power
//   PowF(n:float)
//   PowC(other) - complex power z^w
//
// Trigonometry:
//   Sin()  Cos()  Tan()
//
// Measurement:
//   Magnitude()  →  float   - |z| = sqrt(r²+i²)
//   Norm()       →  number  - |z|² = r²+i²
//   Arg()        →  float   - angle in radians
//
// Comparison:
//   Equals(other)                    →  bool  - exact
//   ApproxEquals(other, epsilon)     →  bool  - |a-b| < epsilon
//   ApproxEqualsMag(other, epsilon)  →  bool  - magnitudes within epsilon
//   IsReal()  IsImaginary()  IsNaN()  IsInfinite()
//
// Formatting:
//   ToString()   →  string   - "3+4i", "2-1i", "5i", "3"
//   Clone()      →  Complex

class Complex {

    fn Constructor(real:number, imag:number) {
        this.real = real;
        this.imag = imag;
    }

    // --------------------------------------------------------
    //  Arithmetic
    // --------------------------------------------------------

    fn Add(other:instance) : instance {
        return new Complex(this.real + other.real, this.imag + other.imag);
    }

    fn Sub(other:instance) : instance {
        return new Complex(this.real - other.real, this.imag - other.imag);
    }

    fn Mul(other:instance) : instance {
        let r = this.real * other.real - this.imag * other.imag;
        let i = this.real * other.imag + this.imag * other.real;
        return new Complex(r, i);
    }

    fn Div(other:instance) : instance {
        let denom = float(other.real * other.real + other.imag * other.imag);
        if (denom == 0) throw(1, "Divide by zero in complex division");
        let r = (this.real * other.real + this.imag * other.imag) / denom;
        let i = (this.imag * other.real - this.real * other.imag) / denom;
        return new Complex(r, i);
    }

    fn Neg() : instance {
        return new Complex(-this.real, -this.imag);
    }

    // Complex conjugate: a - bi
    fn Conj() : instance {
        return new Complex(this.real, -this.imag);
    }

    // Scale both components by a real factor.
    fn Scale(factor:number) : instance {
        return new Complex(this.real * factor, this.imag * factor);
    }

    // --------------------------------------------------------
    //  Powers and exponentials
    // --------------------------------------------------------

    // e^(a+bi) = e^a * (cos(b) + i*sin(b))
    fn Exp() : instance {
        let ea = Math.Exp(this.real);
        return new Complex(ea * Math.Cos(this.imag), ea * Math.Sin(this.imag));
    }

    // Integer power via repeated squaring. n may be negative.
    fn Pow(n:int) : instance {
        if (n == 0) 
            return Complex.One();
        if (n < 0){
            return Complex.One().Div(this.Pow(-n));
        }

        var result:instance = Complex.One();
        var base:instance   = this.Clone();
        var exp:int         = n;
        while (exp > 0) {
            if (exp & 1 == 1) result = result.Mul(base);
            base = base.Mul(base);
            exp >>= 1;
        }
        return result;
    }

    // Principal square root: sqrt(r) * e^(i*arg/2)
    fn Sqrt() : instance {
        let r = this.Magnitude();
        let theta = this.Arg();
        return Complex.FromPolar(Math.Sqrt(r), theta / 2.0);
    }

    // Natural logarithm: ln(r) + i*arg
    fn Log() : instance {
        return new Complex(Math.Log(this.Magnitude()), this.Arg());
    }

    // --------------------------------------------------------
    //  Descriptors
    // --------------------------------------------------------

    // Absolute value (modulus): sqrt(a² + b²)
    fn Magnitude() : float {
        return Math.Sqrt(this.real * this.real + this.imag * this.imag);
    }

    // Squared magnitude - avoids sqrt, useful for comparisons.
    fn Norm() : number {
        return this.real * this.real + this.imag * this.imag;
    }

    // Argument (angle) in radians: atan2(imag, real)
    fn Arg() : float {
        return Math.Atan2(this.imag, this.real);
    }

    // --------------------------------------------------------
    //  Comparison and utility
    // --------------------------------------------------------

    fn Equals(other:instance|nil) : bool {
        if (other == nil) return false;
        return this.real == other.real && this.imag == other.imag;
    }

    fn Clone() : instance {
        return new Complex(this.real, this.imag);
    }

    // Human-readable string: "a+bi", "a-bi", "a", "bi"
    fn ToString() : string {
        if (this.imag == 0) return str(this.real);
        if (this.real == 0) return str(this.imag) + "i";
        if (this.imag < 0)  return str(this.real) + str(this.imag) + "i";
        return str(this.real) + "+" + str(this.imag) + "i";
    }

    // --------------------------------------------------------
    //  Trig (complex domain)
    // --------------------------------------------------------

    // sin(a+bi) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
    fn Sin() : instance {
        return new Complex(
            Math.Sin(this.real) * Math.Cosh(this.imag),
            Math.Cos(this.real) * Math.Sinh(this.imag)
        );
    }

    // cos(a+bi) = cos(a)*cosh(b) - i*sin(a)*sinh(b)
    fn Cos() : instance {
        return new Complex(
            Math.Cos(this.real) * Math.Cosh(this.imag),
            -Math.Sin(this.real) * Math.Sinh(this.imag)
        );
    }

    // tan(z) = sin(z) / cos(z)
    fn Tan() : instance {
        return this.Sin().Div(this.Cos());
    }

    // --------------------------------------------------------
    //  Extra utility
    // --------------------------------------------------------

    // Float exponent: z^n = e^(n * ln(z))
    fn PowF(n:float) : instance {
        return this.Log().Scale(n).Exp();
    }

    // 1 / z
    fn Reciprocal() : instance {
        return Complex.One().Div(this);
    }

    fn IsReal()      : bool { return this.imag == 0; }
    fn IsImaginary() : bool { return this.real == 0; }

    // Equality within a tolerance (use for float comparisons)
    fn ApproxEquals(other:instance, epsilon:float) : bool {
        return Math.AbsF(this.real - other.real) <= epsilon &&
               Math.AbsF(this.imag - other.imag) <= epsilon;
    }

    // Equality by magnitude of difference
    fn ApproxEqualsMag(other:instance, epsilon:float) : bool {
        return this.Sub(other).Magnitude() <= epsilon;
    }

    // True if either component is NaN
    fn IsNaN() : bool {
        return Math.IsNaN(this.real) || Math.IsNaN(this.imag);
    }

    // True if either component is infinite
    fn IsInfinite() : bool {
        return !Math.IsFinite(this.real) || !Math.IsFinite(this.imag);
    }

    // Complex exponent: this^other = e^(other * ln(this))
    fn PowC(other:instance) : instance {
        return this.Log().Mul(other).Exp();
    }

    // --------------------------------------------------------
    //  Static constructors
    // --------------------------------------------------------

    // From polar form: r * e^(i*theta)
    fn static FromPolar(r:number, theta:number) : instance {
        return new Complex(r * Math.Cos(theta), r * Math.Sin(theta));
    }

    fn static Zero() : instance { return new Complex(0, 0); }
    fn static One()  : instance { return new Complex(1, 0); }
    fn static I()    : instance { return new Complex(0, 1); }
    fn static NaN()  : instance { return new Complex(Math.NaN, Math.NaN); }
}

export { Complex };
