Derivative.java
package com.acme.symbolic;

import static church.lang.operators.Arithmetic.*;
import static church.lang.operators.Relational.$$equal;
import static com.acme.symbolic.Expression.Visitor;

@SuppressWarnings("unchecked")
public class Derivative {
    private static interface $I0<T> {
        public Expression<T> pd(Expression<T> exp);
    }

    public static interface $Derivative<T> {
        public Expression<T> derivative(church.lang.Functions.Function1<String, church.lang.Functions.Function1<Expression<T>, Expression<T>>> funNameToDerivative, Expression<T> exp0, Expression<T> x);
    }

    public static <T> $Derivative<T> derivative($Additive_identity<Expression<T>> $L0, $$equal<Expression<T>> $L1, $Multiplicative_identity<Expression<T>> $L2, $$prd<Expression<T>, Expression<T>> $L3, $$sum<Expression<T>> $L4, $$neg<Expression<T>> $L5, $$sub<Expression<T>> $L6, $$div<Expression<T>, Expression<T>> $L7, $$pow<Expression<T>, T> $L8, $$sub<T> $L9, $Multiplicative_identity<T> $L10) {
        return (funNameToDerivative, exp0, x) -> {
            $I0<T> pd = new $I0<T>() {
                public Expression<T> pd(Expression<T> exp) {
                    return new Visitor<T, Expression<T>>() {
                        public Expression<T> $const(T value) {
                            return $L0.additive_identity();
                        }

                        public Expression<T> var(String name) {
                            return $L1.$equal(x, exp) ? $L2.multiplicative_identity() : $L0.additive_identity();
                        }

                        public Expression<T> app(String funName, Expression<T> a) {
                            return $L3.$prd(pd(a), funNameToDerivative.of(funName).of(a));
                        }

                        public Expression<T> sum(Expression<T> f, Expression<T> g) {
                            return $L4.$sum(pd(f), pd(g));
                        }

                        public Expression<T> neg(Expression<T> f) {
                            return $L5.$neg(pd(f));
                        }

                        public Expression<T> sub(Expression<T> f, Expression<T> g) {
                            return $L6.$sub(pd(f), pd(g));
                        }

                        public Expression<T> prd(Expression<T> f, Expression<T> g) {
                            return $L4.$sum($L3.$prd(pd(f), g), $L3.$prd(pd(g), f));
                        }

                        public Expression<T> div(Expression<T> f, Expression<T> g) {
                            return $L7.$div($L6.$sub($L3.$prd(pd(f), g), $L3.$prd(pd(g), f)), $L3.$prd(g, g));
                        }

                        public Expression<T> pow(Expression<T> f, T n) {
                            return $L3.$prd($L3.$prd(pd(f), Expression.$const(n)), $L8.$pow(f, $L9.$sub(n, $L10.multiplicative_identity())));
                        }

                    }
                            .get(exp);
                }
            };
            return pd.pd(exp0);
        };
    }

}