#!/usr/bin/swipl -q -s


% XOR-reduction with optimization based on nonce freshness mentioned 
% in Sect. 4.1 and the one based on XOR-rule described in Sect. 4.5
%
% Xihui Chen, Ton van Deursen, Jun Pang: Improving Automatic
% Verification of Protocols with XOR, submitted to ICFEM 2009
%
% Author: Xihui Chen (based on work and code by Ralf Kuesters and Tomasz
% Truderung)

:- multifile proverif/1.

:- dynamic dominating/1.

:- op( 1180, fx, fun ).
:- op( 1180, fx, rule ).
:- op( 1180, fx, secret).
:- op( 1180, fx, dominating ).
:- op( 1180, fx, query ).
:- op( 1180, fx, nonce ).
:- op( 1180, fx, proverif ).

proverif 'pred begin/1 block'.
proverif 'pred i/1 elimVar,decompData'.
proverif 'nounif i:x'.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                          AUXILIARY PREDICATES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sublist([], []).
sublist([_|Xs], Ys)  :- sublist(Xs, Ys).
sublist([X|Xs], [X|Ys])  :- sublist(Xs, Ys).

nonempty([_|_]).

mapterm( SubList, T, MT) :-
    T =.. [F|Args],
    maplist(mapterm(SubList), Args, MappedArgs),
    ( member( F/SF, SubList )
        -> MF = SF
        ;  MF = F ),
    MT =.. [MF|MappedArgs].

append_lists([], []).
append_lists([L|Ls], Res) :-
    append_lists(Ls, Res1),
    append(L, Res1, Res).

% vsubtract  --- like subtract with == instad of unification

vsubtract([], _, []).
vsubtract([X|Xs], L, Res) :-
    vmember(X,L), !,
    vsubtract(Xs, L, Res).
vsubtract([X|Xs], L, [X|Res]) :-
    % \+vmember(X,L)  -- by the cut above
    vsubtract(Xs, L, Res).
    
vmember(X, [Y|_] ) :- X==Y.    
vmember(X, [_|Ys] ) :- vmember(X,Ys).

vlist_to_set([], []).
vlist_to_set([X|Xs], Res) :-
    vmember(X,Xs), !,
    vlist_to_set(Xs,Res).
vlist_to_set([X|Xs], [X|Res]) :-
    % \+ vmember(X,Xs) -- by the cut above
    vlist_to_set(Xs,Res).
    

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%
% ground_rule(R) -- R is a grounded verion of some (user defined)
% rule. This version is obtained by numbervars.
%
ground_rule(R) :- (rule R), make_ground(R).

make_ground(R) :- numbervars(R,23,_).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FUNCTORS AND TERMS
%

% Predefined functors:
%
std_functor( (,)/2 ).
std_functor( (->)/2 ).
std_functor( (+)/2 ).
std_functor( 0/0 ).
std_functor( begin/1 ).

%%
% funct(X) -- X is a functor (standard, as defined above, or defined
% by the user.
%
funct(X) :- std_functor(X) ; (fun X).

%%
% xt_var(T) -- T is a variable (technically, a term substituted for a
% variable by numbervars.
% 
xt_var(T)  :- T =.. ['$VAR'|_].

%%
% xt_ground(T) -- T does not contains subterms S such that xt_var(S).
% 
xt_ground(T) :-
    xt_var(T), !, fail.

xt_ground(T) :-
    T =.. [_|Args],
    maplist(xt_ground, Args).

%%
% check_term(T) -- check, whether T is a proper term. If is not, the
% fact is reported and the program halts.
%
check_term_locally(T) :-
    T =.. [F|Args],
    length(Args,N),
    funct(F/N), !.

check_term_locally(_). 
%:-
 %   format('***  term: ~p\n', [T]),
  %  halt(-1).


check_term(V) :- var(V), !.

check_term(T) :-
    check_term_locally(T),
    T =.. [_|Args],
    maplist(check_term, Args).

check_rule(X) :-
    % format('  >> ~p\n', [X]),
    check_term(X).
    

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Checking if a term is dominated
%

%%
% indom(X) -- X is on the list of dominating atoms (the set of
% dominated atoms closed under + is the dominated set)
%
indom(0).
indom(X) :- (domset(L)), member(X,L).

%%
% standard, nonstandard terms
%
non_standard(_+_).

standard(T) :- var(T) ; \+ non_standard(T).

%%
% xor(X,Y,Z) -- Z == X+Y (Z is somehow simplified)
%
xor( 0, X, X ) :- !.
xor( X, 0, X ) :- !.
xor( X, Y, X+Y).

xorspec( 0, X, X ) :- !.
xorspec( X, 0, X ) :- !.
xorspec( X, Y, xx(X,Y)).

%%
% inxor( X, L ) -- X occurs in L an odd number of times
%
inxor(X, [X|L]) :- !, \+ inxor(X,L).
inxor(X, [_|L]) :- inxor(X,L).

%%
% flat( +T, -Ds, -Ss )
%
%    T is a non-standard term which can be splitted into elements of
%    the dominating set given in Ds and other standard elements given
%    in Ss.
%
flat( T , [T], [] ) :-
    indom(T), !.

flat(xx(X,Y), Ds, Ss) :- !,
    flat(X, XD, XS),
    flat(Y, YD, YS),
    append(XD,YD,Ds),
    append(XS,YS,Ss).

flat( X+Y, Ds, Ss ) :- !,
    flat(X, XD, XS),
    flat(Y, YD, YS),
    append(XD,YD,Ds),
    append(XS,YS,Ss).

flat( T, [], [T] ) :-
    standard(T).

check_dominated(Parent,T) :-
    flat(T, _, Ss),
    check_f_dominated(Parent,Ss,T).

check_f_dominated(_Parent, [], _) :- !.
check_f_dominated(_Parent, [T], _) :- !, T =.. [_|Args], maplist(check_dominated(T), Args).
check_f_dominated(Parent, _, S) :-
    format('*** Term: ~p (in ~p) is not C-dominated\n', [S,Parent]),
    halt(-1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Computing the C set
%


%%
% nonstandard_flat(T,L)
% 
%    T is a nonstandard term and L is the list of its arguments
%
nonstandard_flat(V, [V]) :- var(V), !.

nonstandard_flat(X+Y, Res) :- !,
    nonstandard_flat(X,ResX),
    nonstandard_flat(Y,ResY),
    append(ResX, ResY, Res).

nonstandard_flat(0, []) :- !.
nonstandard_flat(X, [X]).

%% where


%%
% nonstandard_subterms(L, NS) 
%
%       L is a list of terms, NS is the list of nonstandard terms
%       presented as lists (i.e. a+b+c is presented as [a,b,c]).
%

nonstandard_subterms(V, []) :- var(V), !.

nonstandard_subterms([], []) :- !.

nonstandard_subterms([T|Ts], Res) :- !,
    nonstandard_subterms(T, Res1),
    nonstandard_subterms(Ts, Res2),
    append(Res1, Res2, Res).

nonstandard_subterms(T, Res) :-
    standard(T), !,
    nonstandard_rec(T,Res).

nonstandard_subterms(T, Res) :-
    non_standard(T),
    nonstandard_flat(T, TArgs),
    check_linear(TArgs),
    nonstandard_subterms(TArgs, Res1),
    Res = [TArgs | Res1].

%% where
nonstandard_rec(T, Res) :-
    T =.. [_|Args],
    maplist(nonstandard_subterms, Args, ArgsRes),
    append_lists(ArgsRes, Res).

%% where
check_linear(T) :-
    select(X, T, Res),
    \+ground(X),
    \+ground(Res), !,
    format('*** Term ~p is not XOR-linear.\n\n', [T]),
    fail.

check_linear(_). 
    

%%
% build_C0( ListOfNonstandardSubterms, C0, Constraints )
%

% build_C0( [], [], [] ).
% 
% build_C0( [T|_], _, _ ) :-
%     nonlinear(T), !,
%     format('*** Term ~p is not XOR-linear.\n\n', [T]),
%     fail.
% 
% build_C0( [T|Ts], B, C ) :-
%     ground(T), !,
%     build_C0(Ts, B, C1),
%     C = [T|C1].
% 
% build_C0( [T|Ts], B, C ) :-
%     % T has to be linear (by the cut after nonlinear(T))
%     select(X, T, Rest), \+ground(X), !, % the first non-ground term is the only one
%     build_C0(Ts, B1, C),
%     append(Rest, B1, B).
 

sieve_init( Ts, InitBase ) :-
    append_lists(Ts, B ),
    vlist_to_set(B, InitBase).

sieve_step([], B, B).

sieve_step([[]|Ls], Base, NewBase) :-
    sieve_step(Ls, Base, NewBase).

sieve_step([L|Ls], Base, NewBase) :-
    ( sieve_select(L, Base, _, _) 
        -> 
            sieve_select(L, Base, _, Rest),  
            vsubtract(Base, Rest, Base1), 
            sieve_step(Ls, Base1, NewBase)
         ;
            sieve_step(Ls, Base, NewBase)
    ).
    
sieve(Ts, C) :-
    sieve_init(Ts, InitBase),
    setof( Base, sieve_step(Ts, InitBase, Base), Results ),
    find_max(Results,_,Max),
    vsubtract(InitBase,Max,C).


%% where
sieve_select(L, Base, X, Rest) :-
    select(X,L,Rest), ground(Rest), 
    sieve_sel_check(X,Base).

sieve_sel_check(X, Base) :-
    (\+ground(X) ; vmember(X,Base)), !.

%% where
find_max([], -1, none).

find_max([L|Ls], N, X ) :-
    find_max(Ls, PrevN, PrevX),
    length(L, NL),
    (NL<PrevN ->  N=PrevN, X=PrevX
               ;  N=NL, X=L).

%%

comp_dominating_set(ListOfTerms, C) :-
    nonstandard_subterms(ListOfTerms, Ts0),
    vlist_to_set(Ts0,Ts1),
    predsort(mycomp, Ts1, Ts2),
    sublist(filterpred, Ts2, Ts),
    %format('\n *** ~p ***\n', [Ts]),
    sieve(Ts,C).

%%

mycomp('<', X, Y ) :- \+ground(X), ground(Y), !.
mycomp(Op, X, Y ) :- compare(Op,X,Y).

filterpred(X) :- 
    X \= [_].

domset(C) :- (dominating C), !.

domset(_) :- 
    format('\n *** Dominating set not defined. ***\n'),
    halt(-1).

%%

copute_dominating_set_if_necessary :-
    (dominating C), !,
    format('    + Dominating set (~p) defined by the user.\n', [C]).

copute_dominating_set_if_necessary :-
    % not given by the user
    bagof( R, (rule R), Rules),
    % format('    + Rules  =  ~p\n', [Rules]),
    comp_dominating_set(Rules, C),
    format('    + Dominating set =  ~p\n', [C]),
    assert(dominating C).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% normal form of a term
%
normal_form(T, NFT) :-
    standard(T),
    T =.. [F|Args],
    maplist(normal_form, Args, NFArgs),
    NFT =.. [F|NFArgs].

normal_form(T, NFT) :-
    non_standard(T),
    flat(T, Ds, Ss),
    % format('   >>> ~p, ~p, ~p\n',  [T,Ds,Ss] ),
    maplist(normal_form, Ds, Ds1),
    dom_normal_form(Ds1, NFDs),
    maplist(normal_form, Ss, NFSs),
    nf_join(NFDs, NFSs, NFT).


nf_join(X, [], X) :- !.
nf_join(X, [S], Res) :- !, xor(X,S,Res).
nf_join(_, Z, _) :-
    format('*** Term not C-dominated: ~p\n', [Z]),
    halt(-1).

dom_normal_form( Ds , Res ) :-
    (domset(DomList) ),
    dnf_loop(DomList, Ds, Res). % !!!

% enc_dnf_loop(DomList, Ds, Res) :-
%     dnf_loop(DomList, Ds, Res1),
%     ( Res1 = _+_  ->  Res = xx(Res1) ; Res = Res1  ).

dnf_loop( [], _, 0 ).    

dnf_loop( [X|Xs], Ds, Res ) :-
    dnf_loop( Xs, Ds, Res1 ),
    ( inxor(X,Ds) 
        ->  xorspec(Res1,X,Res)
        ;   Res = Res1 ).

inC(Dom,X) :- sublist(Dom, S), dom_normal_form(S,X).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% matching
%

%%
% matching(S, T, Sigma) 
%
%   Sigma is the result of matching S against (ground) T. We assume
%   that both S and T are in normal form. We assume also that
%   these terms are C-dominated.

matching(S, T, Sigma) :-        % 1. S is a variable
    xt_var(S), !,
    Sigma = [T/S].

matching(S, T, Sigma) :-        % 2. S is ground
    % it holds: \+ xt_var(S),
    xt_ground(S), !,
    % format(' [Case2: ~p ~p ]\n', [S, T]),
    S=T,
    Sigma = [].

matching(S, T, Sigma) :-        % 3. S is non-ground, non-standard
    S = C+SP, !,
    normal_form(C+T, CT),
    % format(' [Case3 ~p ~p -- ~p ]\n', [S, T, CT]),
    matching(SP, CT, Sigma).

matching(S, T, Sigma) :-        % 4. S is non-ground, non-variable, standard
    % format(' [Case4 ~p ~p ]\n', [S, T]),
    S =.. [_|SS],
    T =.. [_|TT],
    maplist(matching, SS, TT, Sigmas),
    merge_subst(Sigmas, Sigma).

% where

merge_subst(Sigmas, Sigma) :-
    append_lists(Sigmas, L),
    list_to_set(L,Sigma),
    % format('  .(~p)  ', [Sigma]),
    \+ (select(_/X, Sigma, Rest),  member(_/X,Rest)). % no duplicates.

%%

matchnf(S,T,Sigma) :-
    format('\n'),
    make_ground(S),
    make_ground(T),
    normal_form(S, SNF),
    normal_form(T, TNF),
    matching(SNF,TNF,Sigma).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% XOR-RULES
%
gen_xarg :-
    (domset(DomList)),
    sublist(DomList,A), 
    dom_normal_form(A,AA),
    write('xarg: '), out_term(AA), write(';\n'),
    fail ; true.

% gen_notxarg :-
%     (dominating DomList),
%     sublist(DomList,A), 
%     dom_normal_form(A,AA),
%     write('X <> '), out_term(AA), write(' & '),
%     fail ; write('o<>(o,o) -> notxarg:X;\n').

out_xtab(A,B):-
     A\==[o],B\==[o],A\==B,
    top_de(A,Ta),
    top_de(B,Tb),
    (compare('<',Ta,Tb);Ta==Tb),!,
    append(A,B,C),
    dom_normal_form(A,AA),
    dom_normal_form(B,BB),
    dom_normal_form(C,CC),
    write('xtab: '), 
    out_term(AA), write(', '),
    out_term(BB), write(', '),
    out_term(CC), write(';\n').
out_xtab(_,_).

top_de([X],X):-!.
top_de([X|Y],T):-
     top_de(Y,T1),
     compare('<',X,T1),!,
     T=X.
top_de([_|Y],T):-
     top_de(Y,T).

gen_xtab :-
    (domset(DomList)),
    sublist(DomList,A), 
    sublist(DomList,B),
    out_xtab(A,B), 
    fail ; true.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% variant2 is added since some xtab rules are 
%%% removed for optimization mensioned in Sect. 4.5
%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_xor_rules :-
    write_ln('xarg:X  &  i:X  &  i:Y  ->  i:XOR(X,Y);             (* comp *)'),
    write_ln('xarg:X  &  i:XOR(X,Y)  &  i:X  ->   i:Y;            (* decomp *)'),
    write_ln('xtab:X,Y,Z  &   i:XOR(X,T)  &  i:Y  ->  i:XOR(Z,T); (* variant1 *)'),
    write_ln('xtab:X,Y,Z  &   i:XOR(Y,T)  &  i:X  ->  i:XOR(Z,T); (* variant2*)'),
    write_ln('xtab:X,Y,Z  &   i:XOR(X,T)  &  i:XOR(Y,T)  ->  i:Z; (* gen *)'),
    nl,
    gen_xarg,
    gen_xtab,
    write('i:o.\n').


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PRINTING RULES AND TERMS
% We don't print the term th() because it is of no use for ProVerif

out_rule( L -> R ) :- !,
    out_tuple(L), write('  ->  '), out_fact(R), write(';\n').

out_rule( R ) :- out_tuple(R),write(';\n').

 
out_tuple((X,Y)) :- X=..[th|_],out_tuple(Y),!.

out_tuple((X,Y)) :- !,out_tuple(X), write(' & '), out_tuple(Y).

out_tuple(X) :- out_fact(X).

out_fact(Begin) :-
    nonvar(Begin), Begin=begin(Ev), !,
    write('begin:'), out_term(Ev).

out_fact(F) :-
    write('i:'), out_term(F).

out_term(T) :-
    mapterm( [(+)/'XOR', 0/o], T, MT),
    ( MT = (_,_) -> write('('), print(MT), write(')')
                ; print(MT) ).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INSTANTIATING THE PROTOCOL RULES 
%

%%
% fragile_terms(T, L) 
%   L is the list of fragile variables of T (the set F(t))
%
fragile_terms(T+S, FF) :- !,
    ( standard(T), \+ xt_ground(T) 
      -> AT = [T]
      ;  AT = [] ),
    ( standard(S), \+ xt_ground(S) 
      -> AS = [S]
      ;  AS = [] ),
    fragile_terms(T, FT),
    fragile_terms(S, FS),
    append_lists([AT, AS, FT, FS], FF).

fragile_terms(T, FT) :- 
    T =.. [_|Args],
    fvlist(Args,FT).

% where
fvlist( [], [] ).
fvlist( [A|As], FV) :-
    fragile_terms(A, FA),
    fvlist(As,FAs),
    append(FA,FAs,FV).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  big sigma is to compute the seet of substitutions for a term.
%  We add a parameter 'Dom' to denote the new domset after optimization mensioned in Sect. 4.1

bigSigma(_,[], []).
bigSigma(Dom,[T|Ts], Sigma) :-
    sigmaForFT(Dom, T,Sigma1),
    bigSigma(Dom,Ts,Sigma2),
    merge_subst([Sigma1, Sigma2], Sigma).

sigmaForFT(_,_, []).

sigmaForFT(Dom, T, Sigma) :-
    xt_var(T), !,
    (inC(Dom,C), Sigma=[C/T]  ;  inC(Dom,C), C\=0, Sigma=[(C+T)/T]).
    
sigmaForFT(Dom,T, Sigma) :-
    % we have: \+ xt_var(T), 
    inC(Dom,C), normal_form(C,CN),
    matching(T, CN, Sigma),
    format('    (* !!! *)\n', [Sigma]).
    
%%
substitute(Sigma, Term, Instance) :-
    % format('    (* substitution: ~p *)\n', [Sigma]),
    subst_tmp(Sigma, Term, Instance).

subst_tmp([], Term, Term).
subst_tmp([(T/X)|Rest], Term, Instance) :-
    subst_var(X, T, Term, Term1),
    subst_tmp(Rest, Term1, Instance).

%%
% subst_var(X,S,T,-Res) 
%   -- Res is the result of substituting S for X in term T
%

subst_var(X,S,X,S) :- !.
subst_var(X,S,T,Res) :-
    T =.. [F|Args],
    maplist(subst_var(X,S), Args, SArgs),
    Res =.. [F|SArgs].

%%
% sigma_subst( VarList, Rule, Instance )
%   Instance is an instance of Rule obtained by a substitution
%   from BigSigma(t).
%
sigma_subst( Dom, FT, Term, Instance ) :-
    bigSigma(Dom,FT, Sigma),
    substitute(Sigma, Term, Instance).

%%
% gen_instances(Rule)
%   outputs instances of Rule.
%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%The Following parts are functions used 
%% for opitmizations decribed in Sect. 4.1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%gen_nonce_list(T,Res) generate the set Res of nonces occuring in a term T

gen_nonce_list(T,Res) :-
     nonce_set(Ns),select(X,Ns,_),X=.. [F|_], T=.. [F|_],!,
     Res=[T].
gen_nonce_list(T, Res) :-
     T=.. [_|Args],!,
     %format('T=~p\n',[T]), 
     maplist(gen_nonce_list,Args,SArgs),
     append(SArgs,Res).

gen_nonce_list(_,[]).  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%nonce_set(Ns) compute the set of nonces Ns occuring in this model

nonce_set(Ns) :-
   bagof(N, (nonce N), Ns).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%check if there are some new nonces generated in a rule.

new_nonces(L->R,Res) :-
    !,
    gen_nonce_list(L,Llist),
    gen_nonce_list(R,Rlist),
    vsubtract(Rlist,Llist,Res).
    
new_nonces(R,Res):-
    gen_nonce_list(R,Res),!.

new_nonces(_,[]).

    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%new domset(Rule, Dom) substract the new nonces of Rule and 
%%the new domset is Dom.

new_domset(Rule,Dom):-
    new_nonces(Rule,Re),
    domset(L),
    vsubtract(L,Re,Dom).
    
gen_instances( ORule, Rule ) :-
    fragile_terms(Rule, FT),
    list_to_set(FT,FTSet),
    format('\n(* Instances of  ~p ', [ORule] ),
    new_domset(Rule,Dom),
    format('whose new domset is ~p. *)\n',[Dom]),
    sigma_subst(Dom,FTSet, Rule, Instance),
    normal_form(Instance, NFInstance),
    %format('Instance is ~p\n.',[Instance]),
    out_rule(NFInstance).
gen_instances(_,_).

gen_instances :-
    ground_rule(R), % for old version
    normal_form(R, R1), 
    gen_instances(R,R1),
      fail.

gen_instances.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% OUTPUTING

gen_header(InFile) :-
    format('(* This file is generated automatically by xlpt from ~p *)\n\n', 
            [InFile]).

out_proverif_directives :-
    forall( (proverif D), format('~p.\n', [D]) ).

gen_fun :-    
    format('fun o/0.\n'),
    format('fun XOR/2.\n'),
    format('fun xx/2.\n'),
    forall( (fun F), format('fun ~p.\n', [F]) ).

out_query(Q):-
    write('query i:'),
    out_term(Q),
    write('.\n').
    
    
gen_query :-
    forall((query Q),out_query(Q)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



gen_header_m(InFile) :-
    format('%% This file is generated automaticall by mst form ~p \n\n%% Functors\n',     [InFile]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%TEST AREA




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MAIN

main :- 
    say_hello, !,
    get_args(InFile, OutFile), !,
    [InFile],
   
    % computing the dominating set (if not given by the user)
    copute_dominating_set_if_necessary,

    % checking the rules given by the user:    
    check_rules,

    tell(OutFile),
    % output the header
    gen_header(InFile),
    gen_fun, nl,
    out_proverif_directives, nl,
    gen_query, nl,
    
    format('reduc\n\n'),
  
    format('\n(***** User rules: *****)\n'),
    gen_instances, nl,

    format('(***** XOR-rules: *****)\n'),
    gen_xor_rules,
    told,
    format('    + output written to ~p.\n\n', [OutFile]).


say_hello :- !,
    format('\nXLPT -- XOR-Linear Protocol Transformer.\n\n').

get_args(InFile, OutFile) :-
    unix(argv(Args)), 
    append( _, [--, InFile,OutFile],  Args), !.
get_args(_,_) :-
    format('*** Exactly two commandline arguments needed:\n*** input filename and output filename.\n\n'),
    halt(-1).

check_rules :-
    forall( (rule R), check_rule(R) ),
    write_ln('    + rules are valid'),
    forall( ground_rule(R), check_dominated(R,R) ),
    write_ln('    + rules are C-dominated').
    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- main.
:- halt.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



