%     -------------------------------------------------------------------
%           Symbolische Mathematik  -  Demonstration fr TOY Prolog
%
%                              (c) 1987 JJK
%
%      df(Funktion, Var, Ableitung) berechnet die Ableitung der Funktion
%           nach der Variablen - Beispiel : df(x^2 + 4*x - 5, x, D)
%
%      simplify(Term1, Term2) versucht, Term1 mglichst zu vereinfachen
%     -------------------------------------------------------------------

:- op(200, xfx, '^').

df(Func, Var, DiffF) :- stored_df(Func, Var, DiffF), !.
df(Func, Var, DiffF) :- diff(Func, Var, DiffF1), simplify(DiffF1, DiffF),
      store_df(Func, Var, DiffF).

store_df(F, V, D) :- not stored_df(F, V, D), !,
      assert(stored_df(F, V, D)).
store_df(_, _, _).

diff(Var, Var, 1) :- !.
diff(Atom, Var, 0) :- atomic(Atom), !.

diff(Atom * Var, Var, Atom) :- atomic(Atom), not Var = Atom, !.
diff(Var * Atom, Var, Atom) :- atomic(Atom), not Var = Atom, !.
diff(Var / Atom, Var, 1 / Atom) :- atomic(Atom), not Var = Atom, !.

diff(- X, Var, - Dx) :- !, df(X, Var, Dx).

diff(X + Y, Var, Dx + Dy) :- !, df(X, Var, Dx), df(Y, Var, Dy).
diff(X - Y, Var, Dx - Dy) :- !, df(X, Var, Dx), df(Y, Var, Dy).
diff(X * Y, Var, Dx*Y + X*Dy) :- !, df(X, Var, Dx), df(Y, Var, Dy).
diff(X / Y, Var,(Dx*Y - X*Dy)/(Y*Y)) :- !, df(X, Var, Dx), df(Y, Var, Dy).

diff(sin(X), Var, Dx*cos(X)) :- !, df(X, Var, Dx).
diff(cos(X), Var, -(Dx * sin(X))) :- !, df(X, Var, Dx).
diff(ln(X), Var, Dx / X) :- !, df(X, Var, Dx).
diff(exp(X), Var, Dx * exp(X)) :- !, df(X, Var, Dx).
diff(X ^ Atom, Var, Atom * Dx * X^(Atom - 1)) :- atomic(Atom),
      not Var = Atom, !, df(X, Var, Dx).

diff(Atom^Var, Var, ln(Atom) * Atom^Var) :- atomic(Atom), not Var = Atom, !.

simplify(UglyTerm, NiceTerm) :- stored_simp(UglyTerm, NiceTerm), !.
simplify(UglyTerm, NiceTerm) :- simp(UglyTerm, NotQuiteSoBad),
      try_again(UglyTerm, NotQuiteSoBad, NiceTerm),
      store_simp(UglyTerm, NiceTerm).

store_simp(U, N) :- not (atomic(U), U = N), not stored_simp(U, N), !,
      assert(stored_simp(U, N)).
store_simp(_, _).

try_again(NoChance, NoChance, NoChance) :- !.
try_again(_, StillUgly, Nice) :- simplify(StillUgly, Nice).

simp(X, X) :- atomic(X), !.

simp(- 0, 0) :- !.
simp(-(-X), Sx) :- !, simplify(X, Sx).
simp(X + 0, Sx) :- !, simplify(X, Sx).
simp(0 + X, Sx) :- !, simplify(X, Sx).
simp(X - 0, Sx) :- !, simplify(X, Sx).
simp(0 - X, - Sx) :- !, simplify(X, Sx).

simp(X + X, 2 * Sx) :- !, simplify(X, Sx).
simp(X - X, 0) :- !.

simp(X * 0, 0) :- !.
simp(0 * X, 0) :- !.
simp(X * 1, Sx) :- !, simplify(X, Sx).
simp(1 * X, Sx) :- !, simplify(X, Sx).

simp(X * X, Sx ^ 2) :- !, simplify(X, Sx).

simp(0 / X, 0) :- !.
simp(X / 1, Sx) :- !, simplify(X, Sx).
simp(X / (-1), - Sx) :- !, simplify(X, Sx).
simp(X / X, 1) :- !.

simp(X ^ 0, 1) :- !.
simp(0 ^ X, 0) :- !.
simp(X ^ 1, Sx) :- !, simplify(X, Sx).
simp(1 ^ X, 1) :- !.
simp(X ^ (-1), 1 / Sx) :- !, simplify(X, Sx).
simp(X ^ (-Y), 1 / Sx^Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp((-1) * X, - Sx) :- !, simplify(X, Sx).
simp((-Y) * X, - (Sy * Sx)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X * (-1), - Sx) :- !, simplify(X, Sx).
simp(X * (-Y), - (Sx * Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(- X, - Sx) :- !, simplify(X, Sx).

simp(X + Y, R) :- integer(X), integer(Y), !, R is X + Y.
simp(X - Y, R) :- integer(X), integer(Y), !, R is X - Y.
simp(X * Y, R) :- integer(X), integer(Y), !, R is X * Y.
simp(X / Y, R) :- integer(X), integer(Y), !, R is X / Y.

simp(A*X + B*X, (Sa+Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(A*X + X*B, (Sa+Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(X*A + B*X, (Sa+Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(X*A + X*B, (Sa+Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).

simp(A*X - B*X, (Sa-Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(A*X - X*B, (Sa-Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(X*A - B*X, (Sa-Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(X*A - X*B, (Sa-Sb) * Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).

simp(A/X + B/X, (Sa+Sb) / Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(A/X - B/X, (Sa-Sb) / Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).

simp(X^A * X^B, Sx ^ (Sa+Sb)) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(A^X * B^X, (Sa*Sb) ^ Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(X^A / X^B, Sx ^ (Sa-Sb)) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).
simp(A^X / B^X, (Sa/Sb) ^ Sx) :- !, simplify(A, Sa), simplify(B, Sb),
      simplify(X, Sx).

simp((-X)+Y, Sy - Sx) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X+(-Y), Sx - Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X + Y, Sx + Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp((-X)-Y, -(Sy + Sx)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X-(-Y), Sx + Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X - Y, Sx - Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp((-X) * (-Y), Sx * Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((-X) * Y, -(Sx * Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X * (-Y), -(Sx * Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((1/X) * (1/Y), 1/(Sx * Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((1/X) * Y, Sy / Sx) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X * (1/Y), Sx / Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X * Y, Sx * Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp((-X) / (-Y), Sx / Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((-X) / Y, -(Sx / Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X / (-Y), -(Sx / Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((1/X) / (1/Y), Sy / Sx) :- !, simplify(X, Sx), simplify(Y, Sy).
simp((1/X) / Y, 1/(Sx * Sy)) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X / (1/Y), Sx * Sy) :- !, simplify(X, Sx), simplify(Y, Sy).
simp(X / Y, Sx / Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp(X ^ Y, Sx ^ Sy) :- !, simplify(X, Sx), simplify(Y, Sy).

simp(In, Out) :- In =.. [Fun | Args], !, simpall(Args, SimpArgs),
      Out =.. [Fun | SimpArgs].

simp(X, X).

simpall([], []) :- !.
simpall([H | T], [SH | ST]) :- simplify(H, SH), simpall(T, ST).

atomic(X) :- atom(X); integer(X).

