%%%%%%%%%%%%%%
%
%   demodulate(Demods, Term_in, Term_out) 
%   
%   Standard demodulation (inside-out, revisiting demodulated subterms).  
%   option(demod_limit, N) tells the maximum number of rewrites that will 
%   be applied to a term.
%      
%       Demods - the demodulators
%       Term_in - an object to be demodulated
%       Term_out - the result
%       
%%%%%%%%%%%%%%

demodulate(Dlist, Fin, Fout) :-
    demodul(Fin, Dlist, Fout).

demodul(X and Y, Dlist, X1 and Y1) :-
    !,
    demodul(X, Dlist, X1),
    demodul(Y, Dlist, Y1).
demodul(Term_in, Demods, Term_out) :-
    option(demod_limit, Limit),
    demod(Term_in, Demods, Term_out, Limit, _).

demod(Term, _, _, _, _) :-
    var(Term),
    !,
    write('demod error: variable in term'), nl,
    fail.
demod(Term_in, Demods, Term_out, Nin, Nout) :-
    Term_in =.. [H|Args_in],
    demod_args(Args_in, Demods, Args_out, Nin, N1),
    Term    =.. [H|Args_out],
    demod_top_level(Term, Demods, Term_out, N1, Nout),
    !.

demod_args([], _, [], N, N).
demod_args([Hin|Rest_in], Demods, [Hout|Rest_out], Nin, Nout) :-
    demod(Hin, Demods, Hout, Nin, N1),
    demod_args(Rest_in, Demods, Rest_out, N1, Nout).

demod_top_level(Term_in, Demods, Term_out, Nin, Nout) :-
    Nin > 0,
    conj_member(A=B, Demods),
    halfmatch(A, Term_in, [[], []], [V, T]),
    !,
    simsub(V, B, T, T1),
    M is Nin - 1,
    revisit(T1, Demods, B, Term_out, M, Nout).
demod_top_level(Term, _, Term, N, N).

%%%%%%%%%%%%
%
%   revisit - redemodulate a demodulated term.  Do not redemodulate
%   the subterms that correspond to the variables in Beta, because
%   we know that they are already fully demodulated.
%
%%%%%%%%%%%%

revisit(Term_in, _, Beta, Term_in, N, N) :-
     tpvar(Beta),
     !.
revisit(Term_in, Demods, Beta, Term_out, Nin, Nout) :-
     Term_in =.. [H|T_tail],
     Beta    =.. [H|B_tail],
     !,
     revisit_rest(T_tail, Demods, B_tail, Term_1_tail, Nin, N1),
     Term_1 =.. [H|Term_1_tail],
     demod_top_level(Term_1, Demods, Term_out, N1, Nout).
revisit(Term_in, _, Beta, Term_in, N, N) :-
     write(['revisit: Term_in and Beta dont match ',Term_in,Beta]), nl.

revisit_rest([], _, [], [], N, N).
revisit_rest([TH|TT], Demods, [BH|BT], [TH_out|TT_out], Nin, Nout) :-
     revisit(TH, Demods, BH, TH_out, Nin, N1),
     revisit_rest(TT, Demods, BT, TT_out, N1, Nout).

/*
    newdemods - Separate a conjunction of clauses into nondemodulators
    and demodulators.  Special purpose for constraints.  A clause
    is a demodulator iff one of the arguments is a skolem expression.
    If only the right argument is a skolem expression, then the
    equality is flipped.

    Example:
    newdemods(p(a) and (f=b) and (c=g), [], [], p(a), (f=b) and (g=c)).
*/

newdemods(X and Y, Lin, LDin, Lout, LDout) :-
    !,
    newdemods(X, Lin, LDin, L1, LD1),
    newdemods(Y, L1, LD1, Lout, LDout),
    !.
newdemods(A = B, Lin, LDin, Lin, LDin and (A = B)) :-
    tpskolem(A),
    !.
newdemods(A = B, Lin, LDin, Lin, LDin and (B = A)) :-
    tpskolem(B),
    !.
newdemods(X, Lin, LDin, Lin and X, LDin) :-
    !.

/*
    deld - Rewrite to true any equality units whose right arguments
    are atomic skolem expressions.  (They are used to back 
    demodulate, then they are thrown away.)
*/

deld(X and Y, X1 and Y1) :-
    !,
    deld(X, X1),
    deld(Y, Y1),
    !.
deld(A = _, true) :-
    atomic(A),
    tpskolem(A),
    !.
deld(X, X) :-
    !.

/*
    bd1 - Back demodulation.  Clauses are demodulated by new
    demodulators only;  they will be fully demodulated when
    they are processed in the normal way.
	
    arguments:
	1. new demodulators
        2. current demodulators
	3. conjunction of clauses
	4. list to which modifies clauses are added
	5. current + new demodulators
	6. (conjunction of clauses) - (modified claues)
	7. updated list of modified clauses
*/

bd1(true, X, Y, Z, X, Y, Z) :-
    !.
bd1(DMD, DEMODin, Lin, MODin, DEMOD1 and DMD, Lout, MODout) :-
    backdemod(DMD, Lin and DEMODin, Lout and DEMOD1, MODin, MODout).

backdemod(Dlist, X and Y, X1 and Y1, Min, Mout) :-
    !,
    backdemod(Dlist, X, X1, Min, M1),
    backdemod(Dlist, Y, Y1, M1, Mout),
    !.
backdemod(Dlist, X, true, Min, Min and Y) :-
    demodulate(Dlist, X, Y),
    X \== Y,
    !.
backdemod(_, X, X, Min, Min).
    
conj_member(A, B and _) :-
    conj_member(A, B).
conj_member(A, _ and B) :-
    conj_member(A, B).
conj_member(A, A) :-
    \+ (A = (_ and _)).
