# Copyright (c) 2017: Miles Lubin and contributors
# Copyright (c) 2017: Google Inc.
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

# JuMP requires first and second-order symbolic derivatives for univariate
# functions. Calculus.jl is a package for computing them, and JuMP 1.0.0 used to
# dynamically compute them during precompilation. The benefit of this approach
# is that if the rules in Calculus are updated, then hey will automatically flow
# into JuMP. However, the Calculus package has not seen any development for two
# years, and JuMP uses only a very small part of the total package. Therefore,
# this script statically builds the list of univariate functions supported by
# JuMP and writes them to univariate_expressions.jl. This has the benefit of
# removing a dependency, and saves the recalculation of these derivatives by
# every user.
#
# If you want to rebuild this list of derivatives in future, you will need v0.5
# of Calculus: `import Pkg; Pkg.pkg"add Calculus@0.5"`.

import Calculus

function _differentiate(f)
    try
        return Calculus.simplify(Calculus.differentiate(f, :x))
    catch
        # For some functions the derivative is not defined.
        return nothing
    end
end

_to_expr(f) = f isa Real ? :(typeof(x)($f)) : f

open("univariate_expressions.jl", "w") do io
    println(
        io,
        """
#! format:off
#
# This file is machine generated by running univariate_expressions_generator.jl

const SYMBOLIC_UNIVARIATE_EXPRESSIONS = Tuple{Symbol,Expr,Any}[
    (:+, :(one(x)), :(zero(x))),
    (:-, :(-one(x)), :(zero(x))),
    (:abs, :(ifelse(x >= 0, one(x), -one(x))), :(zero(x))),
    (:sign, :(zero(x)), :(zero(x))),""",
    )
    for (op, deriv) in Calculus.symbolic_derivatives_1arg()
        f = Expr(:call, op, :x)
        ∇f = _differentiate(f)
        print(io, "    (:", op, ", :(", _to_expr(∇f), ")")
        println(io, ", :(", _to_expr(_differentiate(∇f)), ")),")
    end
    println(io, "]")
    println(io)
    return
end
