% -*- prolog -*-

noah([],[]).
noah([A,B|X], [[A,B]|Y]) :- noah(X,Y).

noah_n(_, [], []).
noah_n(N, X, [A|Z]) :- length(A, N), append(A, Y, X), noah_n(N, Y, Z).

subst_list(X, Y, X, Y) :- !.
subst_list([A|X], [B|Y], A, B) :- !, subst_list(X, Y, A, B).
subst_list([A|X], [B|Y], C, D) :-
	!,
	subst_list(A, B, C, D),
	subst_list(X, Y, C, D).
subst_list(X, X, _, _).

ev(X,X) :- number(X).
ev([+,A,B],N) :- ev(A,X), ev(B,Y), N is X+Y.
ev([*,A,B],N) :- ev(A,X), ev(B,Y), N is X*Y.
ev([exp,A],N) :- ev(A,X), N is exp(X).
ev([sin,A],N) :- ev(A,X), N is sin(X).
ev([cos,A],N) :- ev(A,X), N is cos(X).

evx(E, X, N) :- subst_list(E, F, x, X), ev(F, N).

diff(N, 0) :- number(N).
diff(x, 1).
diff([+,A,B], [+,DA,DB]) :- diff(A,DA), diff(B,DB).
diff([*,A,B], [+,[*,A,DB],[*,DA,B]]) :- diff(A,DA), diff(B,DB).
diff([exp,A], [*,DA,[exp,A]]) :- diff(A,DA).
diff([sin,A], [*,DA,[cos,A]]) :- diff(A,DA).
diff([cos,A], [*,DA,[*,-1,[sin,A]]]) :- diff(A,DA).

%%% Extra credit

ev2(X,N) :- ev2a(X,Y), N is Y.

ev2a(X,X) :- number(X).
ev2a([+,A,B],+(A0,B0)) :- ev2a(A,A0), ev2a(B,B0).
ev2a([*,A,B],*(A0,B0)) :- ev2a(A,A0), ev2a(B,B0).
ev2a([exp,A],exp(A0)) :- ev2a(A,A0).
ev2a([sin,A],sin(A0)) :- ev2a(A,A0).
ev2a([cos,A],cos(A0)) :- ev2a(A,A0).


ev3(X,N) :- ev3a(X,Y), N is Y.

ev3a([F|L], Y) :- !, ev3map(L, M),  ev3b(A), member(a([F|M], Y), A).

ev3a(X, X).

ev3b([a([+,A,B], +(A,B)),
      a([*,A,B], *(A,B)),
      a([exp,A], exp(A)),
      a([sin,A], sin(A)),
      a([cos,A], cos(A))]).

ev3map([], []).
ev3map([E|X], [F|Y]) :- ev3a(E, F), ev3map(X, Y).


%%% simp/2 is just working notes, I never tried to run it.

diffs(E, F) :- diff(E,DE), simp(DE,F).

simp([+,0,E], SE) :- !, simp(E,SE).
simp([+,E,0], SE) :- !, simp(E,SE).
simp([*,0,_], 0) :- !.
simp([*,_,0], 0) :- !.
simp([*,1,E], SE) :- !, simp(E,SE).
simp([*,E,1], SE) :- !, simp(E,SE).

simp([+,x,x], [*,2,x]) :- !.

simp([+,N,[+,M,E]],S) :-
	number(N), number(M), !, NM is N+M, simp([+,NM,E],S).

simp([*,N,[*,M,E]],S) :-
	number(N), number(M), !, NM is N*M, simp([*,NM,E],S).

simp([F,X], N) :- number(X), !, ev([F,X],N).
simp([F,X,Y], N) :- number(X), number(Y), !, ev([F,X,Y],N).

simp([F,E], [F,SE]) :- !, simp(E,SE).
simp([F,X,Y], [F,SX,SY]) :- !, simp(X,SX), simp(Y,SY).

simp(E,E).
