
%% Task 1.1

%% G , A|B, A ==> C  iff  G, A ==> C
%% The "if" direction is just weakening.  The "only if":
%% Assume G , A|B, A ==> C.
%% By identity, G,A ==> A.
%% By applying the or right rule to this, G,A ==> A|B.
%% By cut with the assumption, G,A ==> C.
%%
%% The other case is symmetric.

%% ----------------------------------------------------------------------

%% Task 2.1

%% Here's the sequence of goals you try to solve (using Tutch notation for the propositions):
%% prove([~(P|~P)],F)  
%% then apply implication-left: prove([~(P|~P)],P|~P)  and   prove([~(P|~P),F],F)
%% then apply or-right-1 to the first subgoal: prove([~(P|~P)],P)
%% then apply implication-left: prove([~(P|~P)],P|~P)  and   prove([~(P|~P),F],P)
%% ...

%% So the goal prove([~(P|~P)],P|~P) reduces to itself.

%% Task 2.2

%% Recall the proof term for LEM: (fn u => u (inr (fn v => u (inl v))))
%% The inner use of u is impossible if the left-rule for implication
%% forces you to drop the implication itself from the context.  

%% ----------------------------------------------------------------------

%% Task 3

choose([X|Xs],X,Xs).
choose([X|Xs],Y,[X|Ys]) :-
	choose(Xs,Y,Ys).

%% init
prove(G0,atom(P)) :- choose(G0,atom(P),_).

%% right rules
prove(_,top).
prove(G,and(A,B)) :- prove(G,A),prove(G,B).
% no right rule for bot
prove(G,or(A,_)) :- prove(G,A).
prove(G,or(_,B)) :- prove(G,B).
prove(G,implies(A,B)) :- prove([A|G],B).

% left rules
prove(G0,C) :-
	choose(G0,top,G),
	prove(G,C).
prove(G0,C) :-
	choose(G0,and(A,B),G),
	prove([A,B|G],C).
prove(G0,_) :-
	choose(G0,bot,_).
prove(G0,C) :-
	choose(G0,or(A,B),G),
	prove([A|G],C),
	prove([B|G],C).

%% the six new rules:
prove(G0,C) :-
	choose(G0,implies(top,B),G),  %% (top => B) = B
	prove([B|G],C).
prove(G0,C) :-
	choose(G0,implies(and(A1,A2),B),G),  %% (A1 & A2 => B) = A1 => A2 => B
	prove([implies(A1,implies(A2,B))|G],C).
prove(G0,C) :-
	choose(G0,implies(or(A1,A2),B),G),  %% (A1 | A2 => B) = A1 => B & A2 => B
	prove([implies(A1,B),implies(A2,B)|G],C).
prove(G0,C) :-
	choose(G0,implies(bot,_),G),  %% (0 => B) = 1
	prove(G,C).
prove(G0,C) :-
	choose(G0,implies(atom(P),B),G),  
	prove(G,atom(P)),
	prove([B|G],C).
prove(G0,C) :-
	choose(G0,implies(implies(A1,A2),B),G),
	prove([implies(A2,B),A1|G],A2),
	prove([B|G],C).

%% ----------------------------------------------------------------------

%% Task 3.1

%% Everything but the last two (P => B and (A1 => A2) => B) are invertible.

%% Task 3.2

choose([X|Xs],X,Xs).
choose([X|Xs],Y,[X|Ys]) :-
	choose(Xs,Y,Ys).

%% init
prove(G,atom(P)) :- choose(G,atom(P),_).

%% right rules
prove(_,top).
prove(G,and(A,B)) :- prove(G,A),prove(G,B).
% no right rule for bot
prove(G,or(A,_)) :- prove(G,A).
prove(G,or(_,B)) :- prove(G,B).
prove(G,implies(A,B)) :- invert(G,[A],B).

%% invertible left rules 
prove(G0,C) :-
	choose(G0,implies(atom(P),B),G),  
	choose(G,atom(P),_),
	invert(G,[B],C).
prove(G0,C) :-
	choose(G0,implies(implies(A1,A2),B),G),
	invert(G,[implies(A2,B),A1],A2),
	invert(G,[B],C).

%% G = stable context
%% D = working to invert

% left rules
invert(G,[],C) :- prove(G,C).  %% nothing left to invert
invert(G,[top|D],C) :-
	invert(G,D,C).
invert(G,[and(A,B)|D],C) :-
	invert(G,[A,B|D],C).
invert(_,[bot|_],_).
invert(G,[or(A,B)|D],C) :-
	invert(G,[A|D],C),
	invert(G,[B|D],C).
invert(G,[implies(top,B)|D],C) :- %% (top => B) = B
	invert(G,[B|D],C).
invert(G,[implies(and(A1,A2),B)|D],C) :- %% (A1 & A2 => B) = A1 => A2 => B
	invert(G,[implies(A1,implies(A2,B))|D],C).
invert(G,[implies(or(A1,A2),B)|D],C) :- %% (A1 | A2 => B) = A1 => B & A2 => B
	invert(G,[implies(A1,B),implies(A2,B)|D],C).
invert(G,[implies(bot,_)|D],C) :- %% (0 => B) = 1
	invert(G,D,C).
%% move the first thing into G if it isn't invertible:
invert(G,[atom(P)|D],C) :-
	invert([atom(P)|G],D,C).
invert(G,[implies(atom(P),B)|D],C) :-
	invert([implies(atom(P),B)|G],D,C).
invert(G,[implies(implies(A1,A2),B)|D],C) :-
	invert([implies(implies(A1,A2),B)|G],D,C).


%% if you run the query 'shouldBeTrue(a)' you should get 'yes'
shouldBeTrue(a) :-
	  prove([],implies(atom(p),atom(p))),
	  prove([],implies(and(atom(p),atom(q)),and(atom(q),atom(p)))),
	  prove([],implies(or(atom(p),atom(q)),or(atom(q),atom(p)))),
	  prove([],implies(implies(or(atom(p),implies(atom(p),bot)),bot),bot)),
	  prove([],implies(implies(or(implies(atom(p),atom(q)),implies(atom(q),atom(p))),bot),bot)).
	  prove([],and(implies(implies(atom(a),implies(atom(b),atom(c))),implies(and(atom(a),atom(b)),atom(c))),implies(implies(and(atom(a),atom(b)),atom(c)),implies(atom(a),implies(atom(b),atom(c)))))),
	  prove([],and(implies(implies(atom(a),and(atom(b),atom(c))),and(implies(atom(a),atom(b)),implies(atom(a),atom(c)))),implies(and(implies(atom(a),atom(b)),implies(atom(a),atom(c))),implies(atom(a),and(atom(b),atom(c)))))),
	  prove([],implies(or(implies(atom(a),atom(b)),implies(atom(a),atom(c))),implies(atom(a),or(atom(b),atom(c))))),
	  prove([],implies(and(or(atom(a),atom(b)),implies(atom(b),atom(c))),implies(implies(atom(a),atom(b)),atom(c)))),
	  prove([],and(implies(implies(or(atom(a),atom(b)),atom(c)),and(implies(atom(a),atom(c)),implies(atom(b),atom(c)))),implies(and(implies(atom(a),atom(c)),implies(atom(b),atom(c))),implies(or(atom(a),atom(b)),atom(c))))),
	  prove([],and(implies(and(atom(a),or(atom(b),atom(c))),or(and(atom(a),atom(b)),and(atom(a),atom(c)))),implies(or(and(atom(a),atom(b)),and(atom(a),atom(c))),and(atom(a),or(atom(b),atom(c)))))),
	  prove([],and(implies(or(atom(a),and(atom(b),atom(c))),and(or(atom(a),atom(b)),or(atom(a),atom(c)))),implies(and(or(atom(a),atom(b)),or(atom(a),atom(c))),or(atom(a),and(atom(b),atom(c)))))),
	  prove([],implies(or(implies(atom(a),bot),implies(atom(b),bot)),implies(and(atom(a),atom(b)),bot))),
	  prove([],and(implies(implies(or(atom(a),atom(b)),bot),and(implies(atom(a),bot),implies(atom(b),bot))),implies(and(implies(atom(a),bot),implies(atom(b),bot)),implies(or(atom(a),atom(b)),bot)))),
	  prove([],implies(and(atom(a),implies(atom(b),bot)),implies(implies(atom(a),atom(b)),bot))),
	  prove([],implies(atom(a),implies(implies(atom(a),bot),bot))),
	  prove([],and(implies(implies(top,bot),bot),implies(bot,implies(top,bot)))),
	  prove([],and(implies(implies(bot,bot),top),implies(top,implies(bot,bot)))),
	  prove([],and(implies(implies(implies(implies(atom(a),bot),bot),bot),implies(atom(a),bot)),implies(implies(atom(a),bot),implies(implies(implies(atom(a),bot),bot),bot)))),
	  prove([],implies(implies(atom(a),atom(b)),implies(implies(atom(b),atom(c)),implies(implies(atom(c),atom(d)),implies(atom(a),atom(d)))))),
	  prove([],implies(atom(a),implies(atom(b),atom(a)))),
	  prove([],implies(implies(atom(a),atom(b)),implies(implies(atom(a),implies(atom(b),atom(c))),implies(atom(a),atom(c))))).


%% if you run the query 'shouldBeFalse(a)' you should get 'no'
shouldBeFalse(a) :- prove([],or(atom(p),implies(atom(p),bot))).
shouldBeFalse(a) :- prove([],implies(atom(p),atom(q))).
shouldBeFalse(a) :- prove([],implies(and(atom(p),atom(q)),and(atom(w),atom(p)))).
shouldBeFalse(a) :- prove([], implies(implies(implies(atom(a),atom(b)),atom(c)),and(or(atom(a),atom(b)),implies(atom(b),atom(c))))).
shouldBeFalse(a) :- prove([], implies(implies(atom(a),or(atom(b),atom(c))),or(implies(atom(a),atom(b)),implies(atom(a),atom(c))))).
shouldBeFalse(a) :- prove([], implies(implies(implies(atom(a),atom(b)),atom(a)),atom(a))).
shouldBeFalse(a) :- prove([], implies(implies(implies(atom(a),bot),bot),atom(a))).
shouldBeFalse(a) :- prove([], implies(implies(and(atom(a),atom(b)),bot),or(implies(atom(a),bot),implies(atom(b),bot)))).
shouldBeFalse(a) :- prove([], implies(implies(implies(atom(a),atom(b)),bot),and(atom(a),implies(atom(b),bot)))).
