:-    op(740,fx,~).          % not (no parens needed, lowest precedence)
:-    op(750,yfx,and).       % (a and b and c) is left associated.
:-    op(760,yfx,or).        % (a or b or c) is left associated.
:-    op(770,xfx,[imp,iff]). % (a iff b iff c) is illegal.

   %  a and ~b and c or ~d or e iff (~f imp g)               means
   %  iff(or(or(and(and(a,~(b)),c),~(d)),e),imp(~(f),g))     i.e.,
   %  ((((a and ~b) and c) or ~d) or e) iff (~f imp g)

varstart([u,v,w,x,y,z]).  
skolemstart([f,g]).

/*
   gensym(X, Y) - X is a symbol, and Y is a brand new symbol obtained
   from X by appending an integer.  "New" means not returned from a
   previous call to this routine.
*/

gensym(X, Y) :-
   getnum(X, Z),
   name(X, W),
   integername(Z, [], V),
   append(W, V, Y1),
   name(Y, Y1),
   !.

getnum(X, Y) :-
   retract(currentnum(X, Y1)),
   !,
   Y is Y1 + 1,
   asserta(currentnum(X, Y)).
getnum(X, 1) :-
   !,
   asserta(currentnum(X, 1)).

integername(I, Sofar, [C|Sofar]) :-
    I < 10 ,
    !,
    C is I + 48.
integername(I, Sofar, List) :-
    !,
    Top is I // 10,
    Bottom is I mod 10,
    C is Bottom + 48,
    integername(Top, [C|Sofar], List).

/*
    subst(T1, Fin, T2, Fout) - Replace T1 with T2 in Fin, resulting
    in Fout.
*/

subst(T1, Fin, T2, T2) :-
    T1 == Fin,
    !.
subst(_, Fin, _, Fin) :-
    atomic(Fin),
    !.
subst(_, Fin, _, Fin) :-
    var(Fin),
    !.
subst(T1, Fin, T2, Fout) :-
    Fin =.. Lin,
    substrec(T1, Lin, T2, Lout),
    Fout =.. Lout,
    !.

substrec(T1, [Hin|Tin], T2, [Hout|Tout]) :-
    subst(T1, Hin, T2, Hout),
    substrec(T1, Tin, T2, Tout).
substrec(_, [], _, []).

/*
   simsub(L1, Fin, L2, Fout) - simlutaneous substitution of members
   of L1 in F1 with corresponding members of L2.  Example:
   simsub([1, 2, 3], f(1, 2), [a, b, c], f(a, b)).
   Members of L1 must be atomic.
*/

simsub(L1, F, L2, X) :-
    atomic(F),
    !,
    map(L1, F, L2, X),
    !.
simsub(L1, F, L2, X) :-
    F =.. F2,
    simsubrec(L1, F2, L2, X2),
    X =.. X2,
    !.

simsubrec(L1, [X1|X2], L2, [Y1|Y2]) :-
    simsub(L1, X1, L2, Y1),
    simsubrec(L1, X2, L2, Y2),
    !.
simsubrec(_, [], _, []).

/*
   simsub2(L1, Fin, L2, Fout) - simlutaneous substitution of members
   of L1 in F1 with corresponding members of L2.  Example:
   simsub([1, 2, 3], f(1, 2), [a, b, c], f(a, b)).
   Members of L1   NEED NOT BE   be atomic.
*/

simsub2(L1, F, L2, X) :-
    map(L1, F, L2, X),
    X \== F,
    !.
simsub2(_, F, _, F) :-
    atomic(F),
    !.
simsub2(L1, F, L2, X) :-
    F =.. F2,
    simsubrec2(L1, F2, L2, X2),
    X =.. X2,
    !.

simsubrec2(L1, [X1|X2], L2, [Y1|Y2]) :-
    simsub2(L1, X1, L2, Y1),
    simsubrec2(L1, X2, L2, Y2),
    !.
simsubrec2(_, [], _, []).

/*
    map(L1, X, L2, Y) - If X is in L1, then Y is corresponding member
    of L2.  Otherwise, Y = X.
*/

map([X|_], X, [Y|_], Y) :-
    !.
map([_|X], Zin, [_|Y], Zout) :-
    map(X, Zin, Y, Zout),
    !.
map([], Z, [], Z) :-
    !.
map(_, Y, _, Y) :-
    write('map: lists different sizes.'), nl.

/*
    member - deterministic
*/

member(X, [X|_]) :-
    !.
member(X, [_|Z]) :-
    member(X, Z).

/*
    append - the standard append procedure
*/

append([], X, X).
append([X|Y], Z, [X|W]) :-
    append(Y, Z, W).

/*
    tpvar(F) - Succeeds iff F is an atom starting with a letter
    in varstart(VS).
*/

tpvar(F) :-
    atomic(F),
    name(F, [H|_]),
    name(V, [H]),
    varstart(VS),
    member(V, VS),
    !.

increment(_) :- !.
increment(X) :-
    retract(count(X, N)),
    M is N + 1,
    asserta(count(X, M)),
    !.
increment(X) :-
    asserta(count(X, 1)),
    !.

occur(T, T) :-
    !.
occur(T, F) :-
    \+ atomic(F),
    F =.. L,
    occurrec(T, L),
    !.

occurrec(T, [T|_]) :-
    !.
occurrec(T, [X|_]) :-
    occur(T, X),
    !.
occurrec(T, [_|Y]) :-
    occurrec(T, Y),
    !.

bell :-
    name(X, [7]),
    write(X).

seteq([], []).
seteq([X|Y], Z) :-
    !,
    delete(X, Z, W),
    seteq(Y, W).

delete(X, [X|Y], Y) :-
    !.
delete(X, [Y|Z], [Y|W]) :-
    !,
    delete(X, Z, W).

/*
    intersect(L1, L2, L3) - L3 is the intersection of lists L1 and L2.
    The lists need not be ordered.
*/

intersect([H|T1], L, [H|T2]) :-
    member(H, L),
    !,
    intersect(T1, L, T2).
intersect([_|T1], L, T2) :-
    !,
    intersect(T1, L, T2).
intersect([], _, []).

%%%%%%%%%%%%%
%
%  reverse a list
%
%%%%%%%%%%%%%

reverse([H|T], Rin, Rout) :-
    !,
    reverse(T, [H|Rin], Rout).
reverse([], L, L).

%%%%%%%%%%%%%
%
%   oron and andon
%
%%%%%%%%%%%%%

oron(false, X, X) :- !.
oron(X, Y, X or Y).

andon(true, X, X) :- !.
andon(X, Y, X and Y).

%%%%%%%%%%%%%%%%
%
%    append_and(L1, L2, Lout) - It is assumes that L1 and L2 are
%    left associated trees of and's.  Result is left associated.
%
%%%%%%%%%%%%%%%%

append_and(_, false, false) :-
    !.
append_and(false, _, false) :-
    !.
append_and(X, true, X) :-
    !.
append_and(true, X, X) :-
    !.
append_and(X, and(Y, Z), and(W, Z)) :-
    !,
    append_and(X, Y, W).
append_and(X, Y, and(X, Y)).

%%%%%%%%%%%%%%%%
%
%    append_or(L1, L2, Lout) - It is assumes that L1 and L2 are
%    left associated trees of or's.  Result is left associated.
%
%%%%%%%%%%%%%%%%

append_or(_, true, true) :-
    !.
append_or(true, _, true) :-
    !.
append_or(X, false, X) :-
    !.
append_or(false, X, X) :-
    !.
append_or(X, or(Y, Z), or(W, Z)) :-
    !,
    append_or(X, Y, W).
append_or(X, Y, or(X, Y)).


/*
    pcllist(L) - Output a conjunctive formula 
		 without the top level ands.
*/

pcllist(and(X,Y)) :-
    pcllist(X),
    pcllist(Y),
    !.

pcllist(X) :-
    write(X),
    nl,
    !.

/*
    pdisj(L) - Output a disjunctive formula without the top level ors.
*/

pdisj(or(X,Y)) :-
    pdisj(X),
    pdisj(Y),
    !.

pdisj(X) :-
    write(X),
    nl,
    !.

/*
    printdisj(D) - Output a disjunctive formula, printing the
    top level or's.
*/

printdis(X or Y) :-
    !,
    printdis(X), write(' or'), nl,
    printdis(Y).
printdis(X) :-
    write(X).

/* 
    assor - left associate a binary tree of 'or' 
*/

assor(or(X1, Y1), F1, F3) :-
    !, 
    assor(X1, F1, F2), 
    assor(Y1, F2, F3).
assor(X, F1, X) :-
    F1 == [], 
    !.
assor(X, F1, or(F1, X)).

/* 
    assand - left associate a binary tree of 'and' 
*/

assand(and(X1, Y1), F1, F3) :-
    !, 
    assand(X1, F1, F2), 
    assand(Y1, F2, F3).
assand(X, F1, X) :-
    F1 == [], 
    !.
assand(X, F1, and(F1, X)).

