%%%%%%%%%%%%%%%
%
%   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) :-
    !.

%%%%%%%%%%%%%%%
%
%    nnf2(F, G) - Convert F to negation normal form (move negation
%    signs all the way in).  This version does not tranfform "iff"
%    (It does transform the subformulas if "iff")
%
%%%%%%%%%%%%%%%

nnf2(iff(X1, Y1), iff(X2, Y2)) :-
    !,
    nnf2(X1, X2),
    nnf2(Y1, Y2).
nnf2(~(iff(X1, Y1)), ~(iff(X2, Y2))) :-
    !,
    nnf2(X1, X2),
    nnf2(Y1, Y2).
nnf2(all(X, Y1), all(X, Y2)) :-
    !,
    nnf2(Y1, Y2).
nnf2(exists(X, Y1), exists(X, Y2)) :-
    !,
    nnf2(Y1, Y2).
nnf2(and(X1, Y1), and(X2, Y2)) :-
    !,
    nnf2(X1, X2),
    nnf2(Y1, Y2).
nnf2(or(X1, Y1), or(X2, Y2)) :-
    !,
    nnf2(X1, X2),
    nnf2(Y1, Y2).
nnf2(imp(X, Y), Z) :-
    !,
    nnf2(or(~(X), Y), Z).
nnf2(~(all(X, Y)), exists(X, Z)) :-
    !,
    nnf2(~(Y), Z).
nnf2(~(exists(X, Y)), all(X, Z)) :-
    !,
    nnf2(~(Y), Z).
nnf2(~(and(X, Y)), Z) :-
    !,
    nnf2(or(~(X), ~(Y)), Z).
nnf2(~(or(X, Y)), Z) :-
    !,
    nnf2(and(~(X), ~(Y)), Z).
nnf2(~(imp(X, Y)), Z) :-
    !,
    nnf2(and(X, ~(Y)), Z).
nnf2(~(~(X)), X1) :-
    !,
    nnf2(X, X1).
nnf2(~true, false) :-
    !.
nnf2(~false, true) :-
    !.
nnf2(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(_, [], _, []).

