Interval.java
package church.math;

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

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

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

    public static <T> Interval<T> interval(T a, T b) {
        return new Interval<>(a, b);
    }

    public static interface $Contains<T> {
        public boolean contains(Interval<T> $0, T x);
    }

    public static <T> $Contains<T> contains($$lte<T> $L0) {
        return ($0, x) -> ($L0.$lte($0.a, x) && $L0.$lte(x, $0.b));
    }

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

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

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

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

    public static <T, U> $$prd<Interval<T>, Interval<U>> $prd($Min<U> $L0, $$prd<T, U> $L1, $Max<U> $L2) {
        return ($0, $1) -> interval($L0.min($L0.min($L1.$prd($0.a, $1.a), $L1.$prd($0.a, $1.b)), $L0.min($L1.$prd($0.b, $1.a), $L1.$prd($0.b, $1.b))), $L2.max($L2.max($L1.$prd($0.a, $1.a), $L1.$prd($0.a, $1.b)), $L2.max($L1.$prd($0.b, $1.a), $L1.$prd($0.b, $1.b))));
    }

    public static <T> $$rcp<Interval<T>> $rcp($Contains<T> $L0, $Additive_identity<T> $L1, $Min_value<T> $L2, $Max_value<T> $L3, $$rcp<T> $L4) {
        return $0 -> $L0.contains(interval($0.a, $0.b), $L1.additive_identity()) ? interval($L2.min_value(), $L3.max_value()) : interval($L4.$rcp($0.b), $L4.$rcp($0.a));
    }

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

    public static <T> $Multiplicative_identity<Interval<T>> multiplicative_identity($Multiplicative_identity<T> $L0) {
        return () -> interval($L0.multiplicative_identity(), $L0.multiplicative_identity());
    }

}