% CS 415, Fall 2005
%
% This code from 
% http://www.csupomona.edu/~jrfisher/www/prolog_tutorial/2_13.html
%
% This code will print a truth table for a given Boolean expression


% Defines an operator.  'xfy' means it's infix, 'fy' means it's unary
:- op(1000,xfy,'and').
:- op(1000,xfy,'or').
:- op(900,fy,'not').

% Extract the variables from the boolean expression.
find_vars(N,V,V) :- member(N,[0,1]),!.    /* Boolean constants in expression */
find_vars(X,Vin,Vout) :- atom(X), 
                         (member(X,Vin) -> Vout = Vin ;   /* already have  */
                            Vout = [X|Vin]).              /* include       */
find_vars(X and Y,Vin,Vout) :- find_vars(X,Vin,Vtemp),
                               find_vars(Y,Vtemp,Vout).
find_vars(X or Y,Vin,Vout) :-  find_vars(X,Vin,Vtemp),
                               find_vars(Y,Vtemp,Vout).
find_vars(not X,Vin,Vout) :-   find_vars(X,Vin,Vout).

% Find the initial assignments for the variables (all 0's)
initial_assign([],[]).
initial_assign([_X|R],[0|S]) :- initial_assign(R,S).

% Changes the truth assignments by doing a binary addition with 1 (but
% the binary number is _reversed_
next([0|R],[1|R]).
next([1|R],[0|S]) :- next(R,S).

% Reverse a list
reverse([],[]).
reverse([H],[H]).
reverse(A,B) :- reverse(A,[],B).
reverse([],R,R).
reverse([H|T],S,L) :- reverse(T,[H|S],L).

% Find the next set of truth table values
successor(A,S) :- reverse(A,R),
                  next(R,N),
                  reverse(N,S).

% The Boolean definitions for and, or, and not; for use in truth_value
boole_and(0,0,0).
boole_and(0,1,0).
boole_and(1,0,0).
boole_and(1,1,1).
boole_or(0,0,0).
boole_or(0,1,1).
boole_or(1,0,1).
boole_or(1,1,1).
boole_not(0,1).
boole_not(1,0).

% Returns the truth value for the passed expression
truth_value(N,_,_,N) :- member(N,[0,1]).
truth_value(X,Vars,A,Val) :- atom(X),
                             lookup(X,Vars,A,Val).
truth_value(X and Y,Vars,A,Val) :- truth_value(X,Vars,A,VX),
                                   truth_value(Y,Vars,A,VY),
                                   boole_and(VX,VY,Val).
truth_value(X or Y,Vars,A,Val) :-  truth_value(X,Vars,A,VX),
                                   truth_value(Y,Vars,A,VY),
                                   boole_or(VX,VY,Val).
truth_value(not X,Vars,A,Val) :-   truth_value(X,Vars,A,VX),
                                   boole_not(VX,Val).

% Given a key and two lists (one which is the list of keys, the other
% which is the list of values), will return the appropriate value.
% The last parameter is the output parameter.
lookup(X,[X|_],[V|_],V).
lookup(X,[_|Vars],[_|A],V) :- lookup(X,Vars,A,V).

% Generates the truth table
% Profile: find_vars(+The_Expression,+Previously_Found_Variables,-Answer)
tt(E) :- find_vars(E,[],V),
         reverse(V,Vars),
         initial_assign(Vars,A),
         write('  '), write(Vars), write('    '), write(E), nl,
         write('-----------------------------------------'), nl,
         write_row(E,Vars,A),
         write('-----------------------------------------'), nl.

% Prints out the given row, and the recursively prints out all the
% remaining rows
write_row(E,Vars,A) :- write('  '), write(A), write('        '), 
                       truth_value(E,Vars,A,V), write(V), nl,
                       (successor(A,N) -> write_row(E,Vars,N) ; true).

% Usage:
% tt(x or (not y  and z)).
