ExpressionTest.java
package com.acme.symbolic;

import church.lang.ByteStream;
import church.lang.Hash.$HashCode;
import church.lang.operators.Arithmetic.*;
import church.lang.operators.Relational.$$equal;
import church.lang.operators.Streams.$$encode;
import church.math.operators.Transcendental;
import church.math.operators.Transcendental.*;
import church.primitives.Doubles;
import church.primitives.Objects;
import church.util.HashMap;
import church.util.HashMap.$Map;
import com.acme.symbolic.Expression.*;

import static church.lang.Error.error;
import static church.lang.operators.Streams.output;
import static church.util.Entry.entry;
import static church.util.HashMap.get;
import static com.acme.symbolic.Expression.*;

@SuppressWarnings("unchecked")
public class ExpressionTest {
    private static final $Cos<Expression<Double>>                                      $S0  = ExpressionTest::cos;
    private static final $$equal<Double>                                               $S1  = Doubles::$equal;
    private static final $ExprEqual<Double>                                            $S2  = Expression.exprEqual($S1);
    private static final $$equal<Expression<Double>>                                   $S3  = Expression.$equal($S2);
    private static final $Additive_identity<Double>                                    $S4  = Doubles::additive_identity;
    private static final $Additive_identity<Expression<Double>>                        $S5  = Expression.additive_identity($S4);
    private static final $$sum<Expression<Double>>                                     $S6  = Expression.$sum($S3, $S5);
    private static final $Multiplicative_identity<Double>                              $S7  = Doubles::multiplicative_identity;
    private static final $Multiplicative_identity<Expression<Double>>                  $S8  = Expression.multiplicative_identity($S7);
    private static final $$pow<Expression<Double>, Double>                             $S9  = Expression.$pow($S3, $S5, $S1, $S4, $S8, $S7);
    private static final $$equal<String>                                               $S10 = Objects::$equal;
    private static final $$encode<ByteStream, String>                                  $S11 = Objects::$encode;
    private static final $$encode<ByteStream, Double>                                  $S12 = Doubles::$encode;
    private static final $$encode<ByteStream, Expression<Double>>                      $S13 = Expression.$encode($S12, $S11);
    private static final $$sum<Double>                                                 $S14 = Doubles::$sum;
    private static final $$neg<Double>                                                 $S15 = Doubles::$neg;
    private static final $$sub<Double>                                                 $S16 = Doubles::$sub;
    private static final $$prd<Double, Double>                                         $S17 = Doubles::$prd;
    private static final $$div<Double, Double>                                         $S18 = Doubles::$div;
    private static final $$pow<Double, Double>                                         $S19 = Doubles::$pow;
    private static final $Evaluate<Double>                                             $S20 = Expression.evaluate($S14, $S15, $S16, $S17, $S18, $S19);
    private static final $Test<Double, Double, Double>                                 $S21 = ExpressionTest.test($S13, $S20, $S12);
    private static final $HashCode<String>                                             $S22 = Objects::hashCode;
    private static final $Map<String, church.lang.Functions.Function1<Double, Double>> $S23 = HashMap.map($S10, $S22);
    private static final $Log<Double>                                                  $S24 = Transcendental::log;
    private static final $Exp<Double>                                                  $S25 = Transcendental::exp;
    private static final $Sqrt<Double>                                                 $S26 = Transcendental::sqrt;
    private static final $Sin<Double>                                                  $S27 = Transcendental::sin;
    private static final $Cos<Double>                                                  $S28 = Transcendental::cos;
    private static final $Tan<Double>                                                  $S29 = Transcendental::tan;
    private static final $Asin<Double>                                                 $S30 = Transcendental::asin;
    private static final $Acos<Double>                                                 $S31 = Transcendental::acos;
    private static final $Atan<Double>                                                 $S32 = Transcendental::atan;
    private static final $FunNameToFunction<Double>                                    $S33 = ExpressionTest.funNameToFunction($S23, $S24, $S25, $S26, $S27, $S28, $S29, $S30, $S31, $S32);

    private static interface $I0<T> {
        public T varNameToValue(String name);
    }

    public static <T> Expression<T> log(Expression<T> a) {
        return app("log", a);
    }

    public static <T> Expression<T> exp(Expression<T> a) {
        return app("exp", a);
    }

    public static <T> Expression<T> sqrt(Expression<T> a) {
        return app("sqrt", a);
    }

    public static <T> Expression<T> sin(Expression<T> a) {
        return app("sin", a);
    }

    public static <T> Expression<T> cos(Expression<T> a) {
        return app("cos", a);
    }

    public static <T> Expression<T> tan(Expression<T> a) {
        return app("tan", a);
    }

    public static <T> Expression<T> asin(Expression<T> a) {
        return app("asin", a);
    }

    public static <T> Expression<T> acos(Expression<T> a) {
        return app("acos", a);
    }

    public static <T> Expression<T> atan(Expression<T> a) {
        return app("atan", a);
    }

    public static interface $FunNameToFunction<T> {
        public church.lang.Functions.Function1<T, T> funNameToFunction(String name);
    }

    public static <T> $FunNameToFunction<T> funNameToFunction($Map<String, church.lang.Functions.Function1<T, T>> $L0, $Log<T> $L1, $Exp<T> $L2, $Sqrt<T> $L3, $Sin<T> $L4, $Cos<T> $L5, $Tan<T> $L6, $Asin<T> $L7, $Acos<T> $L8, $Atan<T> $L9) {
        return name -> get($L0.map(entry("log", $L1::log), entry("exp", $L2::exp), entry("sqrt", $L3::sqrt), entry("sin", $L4::sin), entry("cos", $L5::cos), entry("tan", $L6::tan), entry("asin", $L7::asin), entry("acos", $L8::acos), entry("atan", $L9::atan)), name);
    }

    public static Expression<Double> foo(Expression<Double> x) {
        return $S0.cos($S6.$sum($S9.$pow(x, 2.0), $const(1.0)));
    }

    public static interface $Test<T, U, V> {
        public ByteStream test(church.lang.Functions.Function1<String, church.lang.Functions.Function1<T, T>> funNameToFunction, church.lang.Functions.Function1<Expression<U>, Expression<T>> fn, T x0, V expectedFp0);
    }

    public static <T, U, V> $Test<T, U, V> test($$encode<ByteStream, Expression<T>> $L0, $Evaluate<T> $L1, $$encode<ByteStream, T> $L2) {
        return (funNameToFunction, fn, x0, expectedFp0) -> {
            String varName = "x";
            $I0<T> varNameToValue = new $I0<T>() {
                public T varNameToValue(String name) {
                    return $S10.$equal(name, varName) ? x0 : error("Undefined variable");
                }
            };
            Expression<U> x = var(varName);
            Expression<T> f = fn.of(x);
            $S11.$encode($L0.$encode($S11.$encode(output, "f(x)  = "), f), "\n");
            T f0 = $L1.evaluate(funNameToFunction, f, varNameToValue::varNameToValue);
            $S11.$encode($L2.$encode($S11.$encode(output, "f(1)  = "), f0), "\n");
            return $S11.$encode(output, "\n");
        };
    }

    public static void main(String[] args) {
        $S21.test($S33::funNameToFunction, ExpressionTest::foo, 1.0, -1.8185948536513634);
    }

}