COMPLEX NUMBERS

using System;

namespace Freya.Demos;
public

    // An immutable structure.
    Complex = const record
    public
        constructor(Re, Im: Double);

        property Re, Im: Double; readonly;

        property R: Double => (Re.Sqr + Im.Sqr).Sqrt;

        method ToString: String; override;
        method Equals(Other: Object): Boolean; override;
        method GetHashCode: Integer; override;

    public
        // Predefined complex constants.
        static Zero: Complex = new Complex; readonly;
        static One: Complex = new Complex(1, 0); readonly;
        static I: Complex = new Complex(0, 1); readonly;

        method=(c1, c2: Complex): Boolean =>
            c1.Re = c2.Re and c1.Im = c2.Im;
        method<>(c1, c2: Complex): Boolean =>
            c1.Re <> c2.Re or c1.Im <> c2.Im;

        method-(c: Complex) => new Complex(-c.Re, -c.Im);

        method+(c1, c2: Complex) =>
            new Complex(c1.Re + c2.Re, c1.Im + c2.Im);
        method-(c1, c2: Complex) =>
            new Complex(c1.Re - c2.Re, c1.Im - c2.Im);
        method*(c1, c2: Complex) =>
            new Complex(
                c1.Re * c2.Re - c1.Im * c2.Im,
                c1.Re * c2.Im + c1.Im * c2.Re);
        method/(c1, c2: Complex) =>
            using r2 := c2.R.Sqr do
                new Complex(
                    (+c1.Re * c2.Re + c1.Im * c2.Im) / r2,
                    (-c1.Re * c2.Im - c1.Im * c2.Re) / r2);

        operator Explicit(C: Complex): Double => C.Re;
        operator Implicit(Value: Integer) => new Complex(Value, 0);
        operator Implicit(Value: Double) => new Complex(Value, 0);
    end;

implementation for Complex is

    constructor(Re, Im: Double);
    begin
        Self.Re := Re;
        Self.Im := Im;
    end;

    method ToString: String;
    begin
        if Im = 0.0 then
            Result := Re.ToString
        else if Re = 0.0 then
            if Im = 1.0 then
                Result := "i"
            else
                Result := Im.ToString + "i"
        else if Im = 1.0 then
            Result := String.Format("<{0}+i>", Re)
        else
            Result := String.Format('<{0}+{1}i>', Re, Im);
    end;

    method Equals(Other: Object): Boolean;
    // Click here for the compiled IL code!
    begin
        if Other <> nil and Other.GetType = Complex then
        begin
            var c := Complex(Other);
            Result := Re = c.Re and Im = c.Im;
        end
        else
            Result := false;
    end;

    method GetHashCode: Integer =>
        Re.GetHashCode xor Im.GetHashCode;

end.