package com.acme.math; import static church.lang.Error.error; import static church.lang.operators.Arithmetic.*; import static church.lang.operators.Relational.$$equal; import static church.lang.operators.Streams.$$encode; import static com.acme.math.Pair.*; @SuppressWarnings("unchecked") public abstract class Polynomial<T> { public interface Visitor<T, S> { default S get(Polynomial<T> receiver) { S result = receiver.accept(this); return result == null ? defaultImplementation(receiver) : result; } default S getOrDefault(Polynomial<T> receiver, S defaultValue) { S result = receiver.accept(this); return result == null ? defaultValue : result; } default S constant(T a) { return null; } default S term(T a, int n, Polynomial<T> r) { return null; } default S defaultImplementation(Polynomial<T> e) { throw new UnsupportedOperationException(); } } public abstract <S> S accept(Visitor<T, S> visitor); public static <T> Polynomial<T> constant(T a) { return new Polynomial<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.constant(a); } }; } public static <T> Polynomial<T> term(T a, int n, Polynomial<T> r) { return new Polynomial<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.term(a, n, r); } }; } public static interface $Polynomial<T> { public Polynomial<T> polynomial(T a, int n, Polynomial<T> r); } public static <T> $Polynomial<T> polynomial($$equal<T> $L0, $Additive_identity<T> $L1) { return (a, n, r) -> $L0.$equal(a, $L1.additive_identity()) ? r : n == 0 ? constant(a) : term(a, n, r); } public static <T> T lc(Polynomial<T> $0) { return new Visitor<T, T>() { public T constant(T a) { return a; } public T term(T a, int n, Polynomial<T> r) { return a; } } .get($0); } public static <T> int deg(Polynomial<T> $0) { return new Visitor<T, Integer>() { public Integer constant(T a) { return 0; } public Integer term(T a, int n, Polynomial<T> r) { return n; } } .get($0); } public static interface $Red<T> { public Polynomial<T> red(Polynomial<T> $0); } public static <T> $Red<T> red($Additive_identity<Polynomial<T>> $L0) { return $0 -> new Visitor<T, Polynomial<T>>() { public Polynomial<T> constant(T a) { return $L0.additive_identity(); } public Polynomial<T> term(T a, int n, Polynomial<T> r) { return r; } } .get($0); } public static <T, U> $$encode<T, Polynomial<U>> $encode($$encode<T, U> $L0, $$encode<T, String> $L1, $$equal<U> $L2, $Multiplicative_identity<U> $L3, $$encode<T, Integer> $L4, $$equal<Polynomial<U>> $L5, $Additive_identity<Polynomial<U>> $L6) { return new $$encode<T, Polynomial<U>>() { public T $encode(T stream, Polynomial<U> $0) { return new Visitor<U, T>() { public T constant(U a) { return $L0.$encode(stream, a); } public T term(U a, int n, Polynomial<U> r) { T s1 = $L1.$encode($L2.$equal(a, $L3.multiplicative_identity()) ? stream : $L1.$encode($L0.$encode(stream, a), "*"), "x"); T s2 = n == 1 ? s1 : $L4.$encode($L1.$encode(s1, "^"), n); return $L5.$equal(r, $L6.additive_identity()) ? s2 : $encode($L1.$encode(s2, " + "), r); } } .get($0); } }; } public static <T> $$equal<Polynomial<T>> $equal($$equal<T> $L0) { return new $$equal<Polynomial<T>>() { public boolean $equal(Polynomial<T> $0, Polynomial<T> $1) { return new Visitor<T, Boolean>() { public Boolean constant(T a1) { return new Visitor<T, Boolean>() { public Boolean constant(T a2) { return $L0.$equal(a1, a2); } public Boolean term(T a2, int n2, Polynomial<T> r2) { return false; } } .get($1); } public Boolean term(T a1, int n1, Polynomial<T> r1) { return new Visitor<T, Boolean>() { public Boolean constant(T a2) { return false; } public Boolean term(T a2, int n2, Polynomial<T> r2) { return (($L0.$equal(a1, a2) && n1 == n2) && $equal(r1, r2)); } } .get($1); } } .get($0); } }; } public static <T> $Additive_identity<Polynomial<T>> additive_identity($Additive_identity<T> $L0) { return () -> constant($L0.additive_identity()); } public static <T> $$sum<Polynomial<T>> $sum($$sum<T> $L0, $Polynomial<T> $L1) { return new $$sum<Polynomial<T>>() { public Polynomial<T> $sum(Polynomial<T> $0, Polynomial<T> $1) { return new Visitor<T, Polynomial<T>>() { public Polynomial<T> constant(T a) { return new Visitor<T, Polynomial<T>>() { public Polynomial<T> constant(T b) { return Polynomial.constant($L0.$sum(a, b)); } public Polynomial<T> term(T a2, int n2, Polynomial<T> r2) { return Polynomial.term(a2, n2, $sum(Polynomial.constant(a), r2)); } } .get($1); } public Polynomial<T> term(T a1, int n1, Polynomial<T> r1) { return new Visitor<T, Polynomial<T>>() { public Polynomial<T> constant(T a2) { return Polynomial.term(a1, n1, $sum(r1, Polynomial.constant(a2))); } public Polynomial<T> term(T a2, int n2, Polynomial<T> r2) { return (n1 > n2) ? Polynomial.term(a1, n1, $sum(r1, Polynomial.term(a2, n2, r2))) : (n1 < n2) ? Polynomial.term(a2, n2, $sum(Polynomial.term(a1, n1, r1), r2)) : $L1.polynomial($L0.$sum(a1, a2), n1, $sum(r1, r2)); } } .get($1); } } .get($0); } }; } public static <T> $$neg<Polynomial<T>> $neg($$neg<T> $L0) { return new $$neg<Polynomial<T>>() { public Polynomial<T> $neg(Polynomial<T> $0) { return new Visitor<T, Polynomial<T>>() { public Polynomial<T> constant(T a) { return Polynomial.constant($L0.$neg(a)); } public Polynomial<T> term(T a, int n, Polynomial<T> r) { return Polynomial.term($L0.$neg(a), n, $neg(r)); } } .get($0); } }; } public static <T, U> U map2(church.lang.Functions.Function1<T, church.lang.Functions.Function1<Integer, church.lang.Functions.Function1<U, U>>> f, church.lang.Functions.Function1<T, U> g, Polynomial<T> $0) { return new Visitor<T, U>() { public U constant(T c) { return g.of(c); } public U term(T a, int n, Polynomial<T> r) { return f.of(a).of(n).of(Polynomial.map2(f, g, r)); } } .get($0); } public static <T, U> Polynomial<U> map1(church.lang.Functions.Function1<T, U> f, Polynomial<T> p) { return map2(a -> n -> r -> term(f.of(a), n, r), a -> constant(f.of(a)), p); } public static interface $Scale<T, U> { public Polynomial<U> scale(T k, Polynomial<U> p); } public static <T, U> $Scale<T, U> scale($$prd<T, U> $L0) { return (k, p) -> map1(a -> $L0.$prd(k, a), p); } public static interface $ScaleAndShift<T, U> { public Polynomial<U> scaleAndShift(T k, int delta, Polynomial<U> p); } public static <T, U> $ScaleAndShift<T, U> scaleAndShift($Scale<T, U> $L0, $Polynomial<U> $L1, $$prd<T, U> $L2, $Additive_identity<Polynomial<U>> $L3) { return (k, delta, p) -> delta == 0 ? $L0.scale(k, p) : map2(a -> n -> r -> $L1.polynomial($L2.$prd(k, a), (n + delta), r), a -> $L1.polynomial($L2.$prd(k, a), delta, $L3.additive_identity()), p); } public static <T> $Multiplicative_identity<Polynomial<T>> multiplicative_identity($Multiplicative_identity<T> $L0) { return () -> constant($L0.multiplicative_identity()); } public static <T, U> $$prd<Polynomial<T>, Polynomial<U>> $prd($$prd<T, U> $L0, $Polynomial<U> $L1, $$sum<Polynomial<U>> $L2, $ScaleAndShift<T, U> $L3) { return new $$prd<Polynomial<T>, Polynomial<U>>() { public Polynomial<U> $prd(Polynomial<T> $0, Polynomial<U> $1) { return new Visitor<T, Polynomial<U>>() { public Polynomial<U> constant(T a1) { return new Visitor<U, Polynomial<U>>() { public Polynomial<U> constant(U a2) { return Polynomial.constant($L0.$prd(a1, a2)); } public Polynomial<U> term(U a2, int n2, Polynomial<U> r2) { return $L1.polynomial($L0.$prd(a1, a2), n2, $prd(Polynomial.constant(a1), r2)); } } .get($1); } public Polynomial<U> term(T a1, int n1, Polynomial<T> r1) { return new Visitor<U, Polynomial<U>>() { public Polynomial<U> constant(U a2) { return $L1.polynomial($L0.$prd(a1, a2), n1, $prd(r1, Polynomial.constant(a2))); } public Polynomial<U> term(U a2, int n2, Polynomial<U> r2) { return $L2.$sum($L3.scaleAndShift(a1, n1, Polynomial.term(a2, n2, r2)), $prd(r1, Polynomial.term(a2, n2, r2))); } } .get($1); } } .get($0); } }; } public static interface $DivideOrFail2<T> { public Polynomial<T> divideOrFail2(Polynomial<T> p, T v); } public static <T> $DivideOrFail2<T> divideOrFail2($$dof<T> $L0) { return (p, v) -> map1(a -> $L0.$dof(a, v), p); } public static interface $Divide1<T> { public Pair<Polynomial<T>> divide1(Polynomial<T> q, Polynomial<T> r, Polynomial<T> v); } public static <T> $Divide1<T> divide1($$equal<Polynomial<T>> $L0, $Additive_identity<Polynomial<T>> $L1, $$dof<T> $L2, $Polynomial<T> $L3, $$sub<Polynomial<T>> $L4, $Red<T> $L5, $ScaleAndShift<T, T> $L6) { return new $Divide1<T>() { public Pair<Polynomial<T>> divide1(Polynomial<T> q, Polynomial<T> r, Polynomial<T> v) { if ($L0.$equal(v, $L1.additive_identity())) { return error("Polynomial:: divide by zero. "); } int delta = (deg(r) - deg(v)); if (($L0.$equal(r, $L1.additive_identity()) || (delta < 0))) { return pair(q, r); } T k = $L2.$dof(lc(r), lc(v)); return divide1($L3.polynomial(k, delta, q), $L4.$sub($L5.red(r), $L6.scaleAndShift(k, delta, $L5.red(v))), v); } }; } public static interface $Divide2<T> { public Pair<Polynomial<T>> divide2(Polynomial<T> u, Polynomial<T> v); } public static <T> $Divide2<T> divide2($Divide1<T> $L0, $Additive_identity<Polynomial<T>> $L1) { return (u, v) -> $L0.divide1($L1.additive_identity(), u, v); } public static <T> $Quotient<Polynomial<T>> quotient($Divide2<T> $L0) { return (u, v) -> first($L0.divide2(u, v)); } public static <T> $$rem<Polynomial<T>> $rem($Divide2<T> $L0) { return (u, v) -> second($L0.divide2(u, v)); } public static interface $Content<T> { public T content(Polynomial<T> p); } public static <T> $Content<T> content($Gcd<T> $L0) { return p -> map2(a -> n -> r -> $L0.gcd(a, r), c -> c, p); } public static interface $PrinciplePart<T> { public Polynomial<T> principlePart(Polynomial<T> p); } public static <T> $PrinciplePart<T> principlePart($DivideOrFail2<T> $L0, $Content<T> $L1) { return p -> $L0.divideOrFail2(p, $L1.content(p)); } }