test_skolem(F) :-
    see(F),
    repeat,
	read(X),
	clausify(X,Y),
	nl, nl, write('Formula: '), nl,
	write(X), nl,
	nl, write('Clauses: '), nl,
	pcllist(Y), nl,
    X == end_of_file,
    seen.

%%%%%%%%%%%%%%%
%
%    clausify(Fin, Fout) - several different versions
%
%%%%%%%%%%%%%%%

clausify(Fin, Fout) :-
    !,
    unique_vars(Fin, F2),
    skolem(F2, [], f, F3),
    delall(F3, F4),
    cnf(F4, F5),
    normalize_list(F5, Fout).

%%%%%%%%%%%%%%%
%
%   skolem(F, L, S, G) - G is the result of Skolemizing F.  L is a
%   list of varaibles that Skolem functions should include ([] on
%   initial call).
%   S is a symbol used to generate unique Skolem functions.
%   All connectives are handled, iff's are expanded, and the
%   result is in negation normal form WITH universal quantifiers.
%
%%%%%%%%%%%%%%%

skolem(all(W, X), V, Xs, all(W, Z)) :-
    !,
    skolem(X, [W|V], Xs, Z).
skolem(exists(W, X), V, Xs, Z) :-
    !,
    gensym(Xs, Xf),
    reverse(V, [], VR),
    Xk =.. [Xf|VR],
    subst_free(W, X, Xk, Z1),
    skolem(Z1, V, Xs, Z).
skolem(and(X1, Y1), V, Xs, and(X2, Y2)) :-
    !,
    skolem(X1, V, Xs, X2),
    skolem(Y1, V, Xs, Y2).
skolem(or(X1, Y1), V, Xs, or(X2, Y2)) :-
    !,
    skolem(X1, V, Xs, X2),
    skolem(Y1, V, Xs, Y2).
skolem(imp(X, Y), V, Xs, Z) :-
    !,
    skolem(or(~(X), Y), V, Xs, Z).
skolem(iff(X, Y), V, Xs, Z) :-
    !,
    copy_new_vars(X, X1),
    copy_new_vars(Y, Y1),
    skolem(and(or(~(X), Y), or(X1, ~(Y1))), V, Xs, Z).
skolem(~(X), V, Xs, X2) :-
    nnf(~(X), X1),
    ~(X) \== X1, % make sure that nnf produced something different
    !,
    skolem(X1, V, Xs, X2).
skolem(X, _, _, X) :-
    !.

%%%%%%%%%%%%%%%
%
%    nnf(F, G) - Convert F to negation normal form (move negation
%    signs all the way in).  Handles all connectives.
%
%%%%%%%%%%%%%%%

nnf(all(X, Y1), all(X, Y2)) :-
    !,
    nnf(Y1, Y2).
nnf(exists(X, Y1), exists(X, Y2)) :-
    !,
    nnf(Y1, Y2).
nnf(and(X1, Y1), and(X2, Y2)) :-
    !,
    nnf(X1, X2),
    nnf(Y1, Y2).
nnf(or(X1, Y1), or(X2, Y2)) :-
    !,
    nnf(X1, X2),
    nnf(Y1, Y2).
nnf(imp(X, Y), Z) :-
    !,
    nnf(or(~(X), Y), Z).
nnf(iff(X, Y), Z) :-
    !,
    copy_new_vars(X, X1),
    copy_new_vars(Y, Y1),
    nnf(and(or(~(X), Y), or(X1, ~(Y1))), Z).
nnf(~(all(X, Y)), exists(X, Z)) :-
    !,
    nnf(~(Y), Z).
nnf(~(exists(X, Y)), all(X, Z)) :-
    !,
    nnf(~(Y), Z).
nnf(~(and(X, Y)), Z) :-
    !,
    nnf(or(~(X), ~(Y)), Z).
nnf(~(or(X, Y)), Z) :-
    !,
    nnf(and(~(X), ~(Y)), Z).
nnf(~(imp(X, Y)), Z) :-
    !,
    nnf(and(X, ~(Y)), Z).
nnf(~(iff(X, Y)), Z) :-
    !,
    copy_new_vars(X, X1),
    copy_new_vars(Y, Y1),
    nnf(and(or(X, Y), or(~(X1), ~(Y1))), Z).
nnf(~(~(X)), X1) :-
    !,
    nnf(X, X1).
nnf(~true, false) :-
    !.
nnf(~false, true) :-
    !.
nnf(X, X) :-
    !.

%%%%%%%%%%%%%%%
%
%    unique_vars(Fin, Fout) -  make quantified variables unique.
%    Handles all connectives.  
%
%%%%%%%%%%%%%%%

unique_vars(Fin, Fout) :-
    !,
    renamevars(Fin, uu, [], _, Fout).

%%%%%%%%%%%%%%%
%
%   renamevars(F, S, Lin, Lout, G) - Make sure that all
%   quantified variables are all distinct.  If not, rename.
%   Handles all connectives.  
%
%   F - input formula (must be in negation normal form (nnf))
%   S - symbol used to generate new variable names
%   Lin - input list of previous variables
%   Lout - possibly appended Lin
%   G - resulting formula
%
%%%%%%%%%%%%%%%

renamevars(all(X, Y), Xs, L, L2, all(Xf, Z1)) :-
    member(X, L),
    !,
    gensym(Xs, Xf),
    subst_free(X, Y, Xf, Z),
    renamevars(Z, Xs, [Xf|L], L2, Z1).
renamevars(all(X, Y), Xs, L, L2, all(X, Y1)) :-
    !,
    renamevars(Y, Xs, [X|L], L2, Y1).
renamevars(exists(X, Y), Xs, L, L2, exists(Xf, Z1)) :-
    member(X, L),
    !,
    gensym(Xs, Xf),
    subst_free(X, Y, Xf, Z),
    renamevars(Z, Xs, [Xf|L], L2, Z1).
renamevars(exists(X, Y), Xs, L, L2, exists(X, Y1)) :-
    !,
    renamevars(Y, Xs, [X|L], L2, Y1).
renamevars(and(X, Y), Xs, L1, L3, and(X1, Y1)) :-
    !,
    renamevars(X, Xs, L1, L2, X1),
    renamevars(Y, Xs, L2, L3, Y1).
renamevars(or(X, Y), Xs, L1, L3, or(X1, Y1)) :-
    !,
    renamevars(X, Xs, L1, L2, X1),
    renamevars(Y, Xs, L2, L3, Y1).
renamevars(iff(X, Y), Xs, L1, L3, iff(X1, Y1)) :-
    !,
    renamevars(X, Xs, L1, L2, X1),
    renamevars(Y, Xs, L2, L3, Y1).
renamevars(imp(X, Y), Xs, L1, L3, imp(X1, Y1)) :-
    !,
    renamevars(X, Xs, L1, L2, X1),
    renamevars(Y, Xs, L2, L3, Y1).
renamevars(~X, Xs, L1, L2, ~X1) :-
    !,
    renamevars(X, Xs, L1, L2, X1).
renamevars(X, _, L, L, X) :-
    !.

%%%%%%%%%%%%%%%
%
%   copy_new_vars(Fin, Fout)  -  Fout is a copy of Fin in which
%   all of the quantified variables have been renamed with new symbols.
%   Handles all connectives.  
%
%%%%%%%%%%%%%%%

copy_new_vars(all(X, F), all(Y, H)) :-
    !,
    gensym(uu, Y),
    subst_free(X, F, Y, G),
    copy_new_vars(G, H).
copy_new_vars(exists(X, F), exists(Y, H)) :-
    !,
    gensym(uu, Y),
    subst_free(X, F, Y, G),
    copy_new_vars(G, H).
copy_new_vars(A or B, A1 or B1) :-
    !,
    copy_new_vars(A, A1),
    copy_new_vars(B, B1).
copy_new_vars(A and B, A1 and B1) :-
    !,
    copy_new_vars(A, A1),
    copy_new_vars(B, B1).
copy_new_vars(A imp B, A1 imp B1) :-
    !,
    copy_new_vars(A, A1),
    copy_new_vars(B, B1).
copy_new_vars(A iff B, A1 iff B1) :-
    !,
    copy_new_vars(A, A1),
    copy_new_vars(B, B1).
copy_new_vars(~A, ~A1) :-
    !,
    copy_new_vars(A, A1).
copy_new_vars(X, X) :-
    !.

%%%%%%%%%%%%%%%
%
%   delall(F, G) - Delete universal quantifiers.  It is assumed that
%   F is in negation normal form.
%   Warning:  whoever calls this routine must must be able to distinguish
%   variables from non-variables.  The rule used by the clause-handling
%   routines is that variables start with "u" through "z".
%
%%%%%%%%%%%%%%%

delall(all(_, Y), Y1) :-
    !,
    delall(Y, Y1).
delall(and(X, Y), and(X1, Y1)) :-
    !,
    delall(X, X1),
    delall(Y, Y1).
delall(or(X, Y), or(X1, Y1)) :-
    !,
    delall(X, X1),
    delall(Y, Y1).
delall(X, X) :-
    !.

%%%%%%%%%%%%%%%
%
%  subst_free(A, Fin, B, Fout)
%
%  In Fin, replace the free occurrences of A  with B, producing Fout.
%
%%%%%%%%%%%%%%%

subst_free(X, all(X,F), _, all(X,F)) :-
    !.
subst_free(X, exists(X,F), _, exists(X,F)) :-
    !.
subst_free(X, Y, F, F) :-
    X == Y,
    !.
subst_free(_, Fin, _, Fin) :-
    var(Fin),
    !.
subst_free(T1, Fin, T2, Fout) :-
    !,
    Fin =.. [H|Lin],
    subst_free_list(T1, Lin, T2, Lout),
    Fout =.. [H|Lout].

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

%%%%%%%%%%%%%%%
%
%    cnf(Fin, Fout) - naive conversion to conjunctive normal form 
%
%    Fin can be associated any way; Fout is left associated.
%    When formulas are copied, quantified variables are renamed.
%    See procedure `rcnf' for "smart" conversion to cnf.
%
%%%%%%%%%%%%%%%

cnf(and(X, Y), Fout) :-
    !,
    cnf(X, X1), 
    cnf(Y, Y1), 
    append_and(X1, Y1, Fout).
cnf(or(X, Y), Z) :-
    !,
    cnf(X, X1), 
    cnf(Y, Y1), 
    cnf_1(X1, Y1, Z).
cnf(imp(X, Y), Z) :-
    !,
    cnf(or(~(X), Y), Z).
cnf(iff(X, Y), Z) :-
    !,
    copy_new_vars(X, X1),
    copy_new_vars(Y, Y1),
    cnf(and(or(~(X), Y), or(X1, ~(Y1))), Z).
cnf(~(X), X2) :-
    nnf(~(X), X1), 
    ~(X) \== X1, 
    !,
    cnf(X1, X2).
cnf(X, X) :-
    !.

cnf_1(and(X, Y), Z, Fout) :-
    !,
    copy_new_vars(Z, Z1),
    cnf(or(X, Z), W1), 
    cnf(or(Y, Z1), W2),
    append_and(W1, W2, Fout).
cnf_1(Z, and(X, Y), Fout) :-
    !,
    copy_new_vars(Z, Z1),
    cnf(or(Z, X), W1), 
    cnf(or(Z1, Y), W2),
    append_and(W1, W2, Fout).
cnf_1(X, Y, Fout) :-
    !,
    append_or(X, Y, Fout).

%%%%%%%%%%%%%%%
%
%    dnf(Fin, Fout) - naive conversion to disjunctive normal form
%
%    Fin can be associated any way; Fout is left associated.
%    When formulas are copied, quantified variables are renamed.
%    See procedure `rdnf' for "smart" conversion to dnf.
%
%%%%%%%%%%%%%%%

dnf(or(X, Y), Fout) :-
    !,
    dnf(X, X1), 
    dnf(Y, Y1), 
    append_or(X1, Y1, Fout).
dnf(and(X, Y), Z) :-
    !,
    dnf(X, X1), 
    dnf(Y, Y1), 
    dnf_1(X1, Y1, Z).
dnf(imp(X, Y), Z) :-
    !,
    dnf(and(~(X), Y), Z).
dnf(iff(X, Y), Z) :-
    !,
    copy_new_vars(X, X1),
    copy_new_vars(Y, Y1),
    dnf(or(and(~(X), Y), and(X1, ~(Y1))), Z).
dnf(~(X), X2) :-
    nnf(~(X), X1), 
    ~(X) \== X1, 
    !,
    dnf(X1, X2).
dnf(X, X) :-
    !.

dnf_1(or(X, Y), Z, Fout) :-
    !,
    copy_new_vars(Z, Z1),
    dnf(and(X, Z), W1), 
    dnf(and(Y, Z1), W2),
    append_or(W1, W2, Fout).
dnf_1(Z, or(X, Y), Fout) :-
    !,
    copy_new_vars(Z, Z1),
    dnf(and(Z, X), W1), 
    dnf(and(Z1, Y), W2),
    append_or(W1, W2, Fout).
dnf_1(X, Y, Fout) :-
    !,
    append_and(X, Y, Fout).

%%%%%%%%%%%%%%%
%
%    normalize(Fin, Fout) - rename all varaibles in Fin, starting with
%    x1, x2, ... .  The result is Fout.  No assumptions are made about
%    the form of Fin.
%
%%%%%%%%%%%%%%%

normalize(Fin, Fout) :-
    norm_table(Fin, [[], []], [VO, VN], 0, _),
    simsub(VO, Fin, VN, Fout).

norm_table(Fin, Vin, Vout, Nin, Nout) :-
    \+ atomic(Fin),
    !,
    Fin =.. [_|T],
    norm_table_args(T, Vin, Vout, Nin, Nout).
norm_table(Fin, Vin, Vout, Nin, Nout) :-
    tpvar(Fin),
    !,
    norm_tpvar(Fin, Vin, Vout, Nin, Nout).
norm_table(_, Vin, Vin, Nin, Nin).

norm_tpvar(Fin, [VOin, VNin], [VOin, VNin], Nin, Nin) :-
    member(Fin, VOin),
    !.
norm_tpvar(Fin, [VOin, VNin], [[Fin|VOin], [XN|VNin]], Nin, Nout) :-
    Nout is Nin + 1,
    integername(Nout, [], IN),
    append("x", IN, L),
    name(XN, L).

norm_table_args([H|T], Vin, Vout, Nin, Nout) :-
    norm_table(H, Vin, V1, Nin, N1),
    norm_table_args(T, V1, Vout, N1, Nout).
norm_table_args([], Vin, Vin, Nin, Nin).

%%%%%%%%%%%%%%%
%
%    normalize_list(Fin, Fout) - It is assumed that Fin is a conjunction
%    For each conjunct, call "normalize" to rename the variables.
%
%%%%%%%%%%%%%%%

normalize_list(X and Y, X1 and Y1) :-
    !,
    normalize_list(X, X1),
    normalize_list(Y, Y1),
    !.
normalize_list(X, Y) :-
    normalize(X, Y),
    !.

