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

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

% == right rules ==
%% topR
prove(_,top).
%% andR
prove(G,and(A,B)) :- prove(G,A),prove(G,B).
%% no rule botR
%% orR1
prove(G,or(A,_)) :- prove(G,A).
%% orR2
prove(G,or(_,B)) :- prove(G,B).
%% impliesR
prove(G,implies(A,B)) :- prove([A|G],B).

% == left rules ==
%% topL
prove(G0,C) :-
	choose(G0,top,G),
	prove(G,C).
%% andL
prove(G0,C) :-
	choose(G0,and(A,B),G),
	prove([A,B|G],C).
%% botL
prove(G0,_) :-
	choose(G0,bot,_).
%% orL
prove(G0,C) :-
	choose(G0,or(A,B),G),
	prove([A|G],C),
	prove([B|G],C).
%% impliesL
prove(G0,C) :-
	choose(G0,implies(A,B),G),
	prove(G0,A),
	prove([B|G],C).


% == tests ==

%% should succeed (yes)
test :- 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)))).

%% should fail (no)
testfalse :- prove([],or(atom(p),implies(atom(p),bot))).
testfalse :- prove([],implies(atom(p),atom(q))).
testfalse :- prove([],implies(and(atom(p),atom(q)),and(atom(w),atom(p)))).

%% should succeed, but loop instead
testloop :-
 	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)).

%% if you run the query 'shouldBeTrue' you should get 'yes'
shouldBeTrue :-
	  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' you should get 'no'
shouldBeFalse :- prove([], implies(implies(implies(atom(a),atom(b)),atom(c)),and(or(atom(a),atom(b)),implies(atom(b),atom(c))))).
shouldBeFalse :- prove([], implies(implies(atom(a),or(atom(b),atom(c))),or(implies(atom(a),atom(b)),implies(atom(a),atom(c))))).
shouldBeFalse :- prove([], implies(implies(implies(atom(a),atom(b)),atom(a)),atom(a))).
shouldBeFalse :- prove([], implies(implies(implies(atom(a),bot),bot),atom(a))).
shouldBeFalse :- prove([], implies(implies(and(atom(a),atom(b)),bot),or(implies(atom(a),bot),implies(atom(b),bot)))).
shouldBeFalse :- prove([], implies(implies(implies(atom(a),atom(b)),bot),and(atom(a),implies(atom(b),bot)))).
