Complex.java
package church.math;

import static church.lang.operators.Arithmetic.*;
import static church.lang.operators.Relational.$$equal;
import static church.lang.operators.Streams.$$encode;

@SuppressWarnings("unchecked")
public class Complex<T> {
    public final T a;
    public final T b;

    public Complex(T a, T b) {
        this.a = a;
        this.b = b;
    }

    public static <T> Complex<T> complex(T a, T b) {
        return new Complex<>(a, b);
    }

    public static <T> T re(Complex<T> $0) {
        return $0.a;
    }

    public static <T> T im(Complex<T> $0) {
        return $0.b;
    }

    public static interface $Conjugate<T> {
        public Complex<T> conjugate(Complex<T> $0);
    }

    public static <T> $Conjugate<T> conjugate($$neg<T> $L0) {
        return $0 -> complex($0.a, $L0.$neg($0.b));
    }

    public static <T, U> $$encode<T, Complex<U>> $encode($$encode<T, String> $L0, $$encode<T, U> $L1) {
        return (stream, $0) -> $L0.$encode($L1.$encode($L0.$encode($L1.$encode(stream, $0.a), " + "), $0.b), "i");
    }

    public static <T> $$equal<Complex<T>> $equal($$equal<T> $L0) {
        return ($0, $1) -> ($L0.$equal($0.a, $1.a) && $L0.$equal($0.b, $1.b));
    }

    public static <T> $$sum<Complex<T>> $sum($$sum<T> $L0) {
        return ($0, $1) -> complex($L0.$sum($0.a, $1.a), $L0.$sum($0.b, $1.b));
    }

    public static <T> $$neg<Complex<T>> $neg($$neg<T> $L0) {
        return $0 -> complex($L0.$neg($0.a), $L0.$neg($0.b));
    }

    public static <T, U> $$prd<Complex<T>, Complex<U>> $prd($$sub<U> $L0, $$prd<T, U> $L1, $$sum<U> $L2) {
        return ($0, $1) -> complex($L0.$sub($L1.$prd($0.a, $1.a), $L1.$prd($0.b, $1.b)), $L2.$sum($L1.$prd($0.a, $1.b), $L1.$prd($0.b, $1.a)));
    }

    public static <T> $$rcp<Complex<T>> $rcp($$sum<T> $L0, $$prd<T, T> $L1, $$div<T, T> $L2, $$neg<T> $L3) {
        return $0 -> {
            T d = $L0.$sum($L1.$prd($0.a, $0.a), $L1.$prd($0.b, $0.b));
            return complex($L2.$div($0.a, d), $L2.$div($L3.$neg($0.b), d));
        };
    }

    public static <T> $Additive_identity<Complex<T>> additive_identity($Additive_identity<T> $L0) {
        return () -> complex($L0.additive_identity(), $L0.additive_identity());
    }

    public static <T> $Multiplicative_identity<Complex<T>> multiplicative_identity($Multiplicative_identity<T> $L0, $Additive_identity<T> $L1) {
        return () -> complex($L0.multiplicative_identity(), $L1.additive_identity());
    }

}