package com.acme.math; import static church.lang.Array.*; import static church.lang.Error.error; import static church.lang.operators.Arithmetic.*; import static church.lang.operators.Relational.*; import static church.lang.operators.Streams.$$encode; @SuppressWarnings("unchecked") public class Modular<T> { public final T modulus; public final T residue; public Modular(T modulus, T residue) { this.modulus = modulus; this.residue = residue; } public static <T> Modular<T> modular(T modulus, T residue) { return new Modular<>(modulus, residue); } public static interface $Canonical<T> { public Modular<T> canonical(T m1, T m2, T residue); } public static <T> $Canonical<T> canonical($$neq<T> $L0, $$rem<T> $L1) { return (m1, m2, residue) -> $L0.$neq(m1, m2) ? error("Modular:: incompatible moduli") : modular(m1, $L1.$rem(residue, m1)); } public static interface $ExtendedEuclid<T> { public T[] extendedEuclid(T[] u, T[] v); } public static <T> $ExtendedEuclid<T> extendedEuclid($$equal<T> $L0, $Additive_identity<T> $L1, $Quotient<T> $L2, $CreateArray<T> $L3, $$sub<T> $L4, $$prd<T, T> $L5) { return new $ExtendedEuclid<T>() { public T[] extendedEuclid(T[] u, T[] v) { if ($L0.$equal(v[2], $L1.additive_identity())) { return u; } T q = $L2.quotient(u[2], v[2]); return extendedEuclid(v, $L3.createArray(3, i -> $L4.$sub(u[i], $L5.$prd(q, v[i])))); } }; } public static <T, U> $$encode<T, Modular<U>> $encode($$encode<T, String> $L0, $$encode<T, U> $L1) { return (stream, $0) -> $L0.$encode($L1.$encode($L0.$encode($L1.$encode(stream, $0.residue), " (modulo "), $0.modulus), ")"); } public static <T> $$equal<Modular<T>> $equal($$equal<T> $L0) { return ($0, $1) -> ($L0.$equal($0.modulus, $1.modulus) && $L0.$equal($0.residue, $1.residue)); } public static <T> $$sum<Modular<T>> $sum($Canonical<T> $L0, $$sum<T> $L1) { return ($0, $1) -> $L0.canonical($0.modulus, $1.modulus, $L1.$sum($0.residue, $1.residue)); } public static <T> $$neg<Modular<T>> $neg($$sub<T> $L0) { return $0 -> modular($0.modulus, $L0.$sub($0.modulus, $0.residue)); } public static <T> $$prd<Modular<T>, Modular<T>> $prd($Canonical<T> $L0, $$prd<T, T> $L1) { return ($0, $1) -> $L0.canonical($0.modulus, $1.modulus, $L1.$prd($0.residue, $1.residue)); } public static <T> $$rcp<Modular<T>> $rcp($Multiplicative_identity<T> $L0, $Additive_identity<T> $L1, $ExtendedEuclid<T> $L2, $$neq<T> $L3) { return $0 -> { T[] u = array($L0.multiplicative_identity(), $L1.additive_identity(), $0.modulus); T[] v = array($L1.additive_identity(), $L0.multiplicative_identity(), $0.residue); T[] lc = $L2.extendedEuclid(u, v); return $L3.$neq(lc[2], $L0.multiplicative_identity()) ? error("Modular: cannot invert residue as it shares a factor with modulus") : modular($0.modulus, lc[1]); }; } }