%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  NAME :
%      ctr_proba_v0.pl
%
% VERSION :
%     0
%
% AUTHOR : Matthieu Petit and Arnaud Gotlieb
%
% FUNCTION :
%      Module of probabilistic choice operators as constraint combinators
% 
% HISTORIC :
%  August 05 : vbeta                               MP
%  January 06 : v0 new filtering algorithm         MP
%
% REMARKS : any problem, contact petit@(without advertissement)irisa.fr
%
% NOTE TO THE USER : 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Module used

:- use_module(library(clpfd)).
:- use_module(library(lists)).
:- use_module(library(terms)).
:- use_module(library(random)).
:- use_module(library(charsio)).
:- use_module(library(atts)).




:- multifile clpfd:dispatch_global/4.
:- attribute numerotation/1, k_flag/2.



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Probabilistic Constraint Combinators                      %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  choose                                      %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Choose need to be encapsuled between init_env and fin_env
% Options :
%       - Default : Domain instantiated and not sorted
%       - sorted : when Domain is sorted
%       - domain_unbound : Domain not instantiated
%       - inconsistency_check : a more costly method which increases the combinator deduction
%       - lvar(L) : add variable of L to the awakening mechanism
%       - no_filtering : no using of the filtering algorithm
%       - rv(U) : U is the random value of the random variable which permits to simulate value for X


% Add of awaken variables

choose(X,Domain-Distribution,Process,Options,Env) :-
        lists:member(lvar(L),Options),
        lists:member(domain_unbound,Options),
        !,
        get_num(Env, Num),
        domain_to_fdset(Domain,Set),
        X in_set Set,
        terms:term_variables(Domain,Lvar_Domain),
        terms:term_variables(Distribution,Lvar_Choice),
        lists:append(Lvar_Domain,Lvar_Choice,Lvar1),
        lists:append(L,Lvar1,Lvar2),
        lists:remove_duplicates([X|Lvar2], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Lvar_Dom),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose(Num,X,Domain,Distribution,Process,U,Lvar_Choice,Options,Env), state(Lvar,Lvar_Name),Lvar_Dom).
       
 
choose(X,Domain-Distribution,Process,Options,Env) :-
        lists:member(lvar(L),Options),
        lists:non_member(domain_unbound,Options),
        !,
        get_num(Env, Num),
        terms:term_variables(Distribution,Lvar_Choice),
        (lists:non_member(sorted,Options) ->
            quicksort_distribution(Domain,Distribution,Domain1,Distribution1)
        ;
            Domain=Domain1,
            Distribution=Distribution1
        ),
        list_to_fdset(Domain1,Set),
        X in_set Set,
        lists:append(L,Lvar_Choice,Lvar1),
        lists:remove_duplicates([X|Lvar1], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Lvar_Dom),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose(Num,X,Domain1,Distribution1,Process,U,Lvar_Choice,Options,Env), state(Lvar,Lvar_Name),Lvar_Dom).

        
choose(X,Domain-Distribution,Process,Options,Env) :-
        lists:member(domain_unbound,Options),
        !,
        get_num(Env, Num),
        domain_to_fdset(Domain,Set),
        X in_set Set,
        terms:term_variables(Domain,Lvar_Domain),
        terms:term_variables(Distribution,Lvar_Choice),
        lists:append(Lvar_Domain,Lvar_Choice,Lvar1),
        lists:remove_duplicates([X|Lvar1], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Lvar_Dom),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose(Num,X,Domain,Distribution,Process,U,Lvar_Choice,Options,Env), state(Lvar,Lvar_Name),Lvar_Dom).




choose(X,Domain-Distribution,Process,Options,Env) :-
        lists:non_member(domain_unbound,Options),
        !,
        get_num(Env, Num),
        terms:term_variables(Distribution,Lvar_Choice),
        (lists:non_member(sorted,Options) ->
            quicksort_distribution(Domain,Distribution,Domain1,Distribution1)
        ;
            Domain=Domain1,
            Distribution=Distribution1
        ),
        list_to_fdset(Domain1,Set),
        X in_set Set,
        lists:remove_duplicates([X|Lvar_Choice], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Lvar_Dom),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose(Num,X,Domain1,Distribution1,Process,U,Lvar_Choice,Options,Env), state(Lvar,Lvar_Name),Lvar_Dom).



%% Solver Call

clpfd:dispatch_global(choose(Num, X, Domain, Distribution, Processus, U, Lvar_distrib, Options, Env),
                      state(Lvar_i, Lvar_name_i), state(Lvar_f, Lvar_name_f), Actions) :-
        choose_solver(Num, X, Domain, Distribution, Processus, U, Lvar_distrib, Lvar_i, Lvar_name_i, Lvar_f, Lvar_name_f, Options, Env,Actions).




%% Stop of consistency check

choose_solver(_Num, _X, _Domain, _Distribution, _Process,_U, _Lvar_distrib, _Lvar_i, _Lvar_name_i, _Lvar_i, _Lvar_name_i, _Options, Env,Actions) :-
        get_atts(Env,+(k_flag(K_flag, _Max))),
        K_flag == 0,
        !,
        Actions =[exit].


% Probabilistic choice completely known

choose_solver(_Num, X, Domain,Distribution,Process, U,Lvar_Choice,_Lvar_i,_Lvar_name_i,_Lvar_i,_Lvar_name_i,Options, _Env,Actions) :-
        terms:term_variables(Lvar_Choice, []),
        !,
        random_draw(Options,U,Domain,Distribution,Value),
            Actions = [exit, call(user:exec([X = Value|Process]))].

% Options : no filtering

choose_solver(_Num, _X, _Domain, _Distribution, _Processus, _U, _Lvar_Choice, _Lvar_i, _Lvar_name_i, _Lvar_f, _Lvar_name_f, Options, _Env, Actions) :-
        lists:member(no_filtering,Options),
        !,
        Actions = [].



% Options = - Domain_Bound
%           - Non inconsistency check


choose_solver(_Num, X, Domain, Distribution, Processus, U, _Lvar_Choice, Lvar_i, Lvar_name_i, Lvar_i, Lvar_name_i, Options,_Env, Actions) :-
        lists:non_member(domain_unbound,Options),
        lists:non_member(inconsistency_check,Options),
        !,
        (narrow_non_uniform([domain_bound],X,Domain,Distribution,U) ->
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Processus))] 
            ;
                Actions = []
            )
        ;
            Actions = [fail]
        ).

% Options = - Domain_unBound
%           - Non inconsistency check


choose_solver(_Num, X, Domain, Distribution, Processus, U, _Lvar_Choice, Lvar_i, Lvar_name_i, Lvar_i, Lvar_name_i, Options,_Env, Actions) :-
        lists:member(domain_unbound,Options),
        lists:non_member(inconsistency_check,Options),
        !,
        (narrow_non_uniform([],X,Domain,Distribution,U) ->
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Processus))] 
            ;
                Actions = []
            )
        ;
            Actions = [fail]
        ).

% Options = - Default + Inconsistency check


choose_solver(Num, X, Domain, Distribution, Processus, U, _Lvar_Choice, Lvar_i, Lvar_name_i, Lvar_f, Lvar_name_f, Options, Env, Actions) :-
        lists:non_member(domain_unbound,Options),
        lists:member(inconsistency_check,Options),
        !,
        (fix_point_choose([domain_bound],Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Env) ->            
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Processus))] 
            ;
                evolution_list(Lvar_i,Lvar_name_i,Lvar_f,Lvar_name_f),
                Actions = []
            )
        ;
            Actions = [fail]
        ).


% Options = - Inconsistency check
%           - Non Domain_bound
% To do : take account the fact that domain becomes bound

choose_solver(Num, X, Domain, Distribution, Processus, U, _Lvar_Choice, _Lvar_i, _Lvar_name_i, Lvar_f, Lvar_name_f, Options, Env, Actions) :-
        lists:member(domain_unbound,Options),
        lists:member(inconsistency_check,Options),
        !,
        (fix_point_choose([],Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Env) ->            
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Processus))] 
            ;
                evolution_list(Lvar_i,Lvar_name_i,Lvar_f,Lvar_name_f),
                Actions = []
            )
        ;
            Actions = [fail]
        ).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Utils choose/5
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Filtering algorithm
% Default behavior : call when domain_bound option is used.
narrow_non_uniform(_Options,_X,[],[],_U) :-
        !.

narrow_non_uniform(Options,X,Domain,Distribution,U) :-
        interval_probability(Distribution,Probability_Inf,Probability_Sup),
        narrow_non_uniform_rec(Options,X,Domain,Probability_Inf,Probability_Sup,U).
        

% Recursive call of narrow_no_uniform

narrow_non_uniform_rec(_Options,_X,[],[],[],_U) :-
        !.


% Using of the fact that Domain is sorted in the increasing order = [domain_bound]

narrow_non_uniform_rec([domain_bound],X,[Val|_Reste_Domaine],[],[(Num_Max,Den_Max)|_Reste_Proba_Sup],U) :-
        Proba_Max is Num_Max/Den_Max,
        U =< Proba_Max,
        !,
        leq(Val,X).


narrow_non_uniform_rec([domain_bound],X,[_Val|Reste_Domaine],[],[(_Num_Max,_Den_Max)|Reste_Proba_Sup],U) :-
        !,
        narrow_non_uniform_rec([domain_bound],X,Reste_Domaine,[],Reste_Proba_Sup,U).

narrow_non_uniform_rec([domain_bound],X,[Val|_Reste_Domaine],[(Num_Min,Den_Min)|_Reste_Proba_Inf],[],U) :-
        Proba_Min is Num_Min/Den_Min,
         U<Proba_Min,
        !,
        le(X,Val).

narrow_non_uniform_rec([domain_bound],X,[_Val|Reste_Domaine],[(_Num_Min,_Den_Min)|Reste_Proba_Inf],[],U) :-
        !,
        narrow_non_uniform_rec([domain_bound],X,Reste_Domaine,Reste_Proba_Inf,[],U).




narrow_non_uniform_rec([domain_bound],X,[Val|Reste_Domaine],[(Num_Min,Den_Min)|Reste_Proba_Inf],[(Num_Max,Den_Max)|Reste_Proba_Sup],U) :-
        Proba_Min is Num_Min/Den_Min,
        Proba_Max is Num_Max/Den_Max,
        (U < Proba_Min ->
            le(X,Val),
            ( U=<Proba_Max ->
                leq(Val,X)
            ;
                narrow_non_uniform_rec([domain_bound],X,Reste_Domaine,[],Reste_Proba_Sup,U)
            )
        ;
            (U =< Proba_Max ->
                leq(Val,X),
                narrow_non_uniform_rec([domain_bound],X,Reste_Domaine,Reste_Proba_Inf,[],U)
            ;
                narrow_non_uniform_rec([domain_bound],X,Reste_Domaine,Reste_Proba_Inf,Reste_Proba_Sup,U)
            )
        ).


% Less deductive approach

narrow_non_uniform_rec([],X,[_V|Reste_Domaine],[(Num_Min,Den_Min)|Reste_Proba_Inf],[(Num_Max,Den_Max)|Reste_Proba_Sup],U) :-
        P_Min is Num_Min/Den_Min,
        P_Max is Num_Max/Den_Max,
        P_Min=<U,
        U=<P_Max,
        !,
        narrow_non_uniform_rec([],X,Reste_Domaine,Reste_Proba_Inf,Reste_Proba_Sup,U).


narrow_non_uniform_rec([],X,[V|Reste_Domaine],[(_Num_Min,_Den_Min)|Reste_Proba_Inf],[(_Num_Max,_Den_Max)|Reste_Proba_Sup],U) :-
        X#\=V,
        narrow_non_uniform_rec([],X,Reste_Domaine,Reste_Proba_Inf,Reste_Proba_Sup,U).





% Interval_probability(Distribution,Probability) is true iff  

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

interval_probability([_P],[(0,1)],[(1,1)]) :-
        !.

interval_probability([P|Distribution],[(0,1)|Reste_Proba_Inf],[(Num_Max,Denom_Max)|Reste_Proba_Sup]) :-
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,Num_Max),
        Denom_Max#=Num_Max-P_Min+Sum_Min,
        interval_probability_rec(Distribution,Reste_Proba_Inf,Reste_Proba_Sup,P_Min,Num_Max,Sum_Min,_Sum_Max).


interval_probability_rec([],[],[],Sum_Min,Sum_Max,Sum_Min,Sum_Max) :-
        !.

interval_probability_rec([P|Distribution],[(S_Min,Den_Min)|Reste_Proba_Inf],[(S_Max2,Den_Max)|Reste_Proba_Sup],S_Min,S_Max,Sum_Min,Sum_Max) :-
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        Den_Min #= S_Min+Sum_Max-S_Max,
        S_Min2 is P_Min+S_Min, 
        S_Max2 is P_Max+S_Max,
        Den_Max #= S_Max2+Sum_Min-S_Min2,
        interval_probability_rec(Distribution,Reste_Proba_Inf,Reste_Proba_Sup,S_Min2,S_Max2,Sum_Min,Sum_Max).





% Domain pruning by an iterative computation composed of insconsistency checking and a domain filtering

fix_point_choose(Options,Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Env) :-
        domaine(Lvar_i,Lvar_Dom),
        fix_point_choose_rec(Options,Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Lvar_Dom,Env).

fix_point_choose_rec(Options,Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Lvar_Dom,Env) :-
       narrow_non_uniform(Options,X,Domain,Distribution,U),
       \+ (\+ (decr_K_flag(Env),exec(Processus),assert_rec(Num,Lvar_i,Lvar_name_i))),
       single_domaine(Num,Lvar_i,Lvar_name_i),
       domaine(Lvar_i,Lvar_Dom2),
       (
         test_domaine(Lvar_Dom,Lvar_Dom2)
         ;
         fix_point_choose_rec(Options,Num,X,Domain,Distribution,Processus,U,Lvar_i,Lvar_name_i,Lvar_Dom2,Env)
       ).
              
       

% domain_to_fd_set(++,--)
% domain_to_fd_set(Domain,Fd_set) is true iff Fd_set is the fd _set associated to Domain


domain_to_fdset([],Set) :-
        !,
        empty_fdset(Set).


domain_to_fdset(Domain,Set) :-
        empty_fdset(Set2),
        domain_to_fdset_rec(Domain,Set2,Set).


domain_to_fdset_rec([X],Set2,Set) :-
        !,
        fd_set(X,Set_X),
        fdset_union(Set_X,Set2,Set).
        

domain_to_fdset_rec([X|Reste_Domaine],Set1,Set) :-
        !,
        fd_set(X,Set_X),
        fdset_union(Set_X,Set1,Set2),
        domain_to_fdset_rec(Reste_Domaine,Set2,Set).




                   

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  choose_range                                %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% choose_range
%       - Default : Range unbound and uniform probability distribution
%       - inconsistency_check : a more costly method which increases the combinator deduction
%       - lvar(L) : add variable of L to the awakening mechanism
%       - no_filtering : no using of the filtering algorithm
%       - rv(U) : U is the random value of the random variable which permits to simulate value for X





% Option : Add awakenning variables
%        : uniform probability distribution

choose_range(X,[Xmin,Xmax]-'uniform',Process,Options,Env) :-       
        lists:member(lvar(L),Options),
        !,
        get_num(Env,Num),
        fd_min(Xmin,Min_Xmin),
        fd_max(Xmax,Max_Xmax),
        terms:term_variables([Xmin,Xmax],Lvar_Domain),
        lists:append(Lvar_Domain,L,Lvar1),
        lists:remove_duplicates([X|Lvar1], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Ldom),
          random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose_range(Num,X,[Xmin,Xmax],Process,U,Options,Env),state(Lvar,Lvar_Name,(Min_Xmin,Max_Xmax),['uniform']),Ldom).

% Option  : uniform probability distribution

choose_range(X,[Xmin,Xmax]-'uniform',Process,Options,Env) :-       
        !,
        get_num(Env,Num),
        fd_min(Xmin,Min_Xmin),
        fd_max(Xmax,Max_Xmax),
        terms:term_variables([Xmin,Xmax],Lvar1),
        lists:remove_duplicates([X|Lvar1], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Ldom),
          random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose_range(Num,X,[Xmin,Xmax],Process,U,Options,Env),state(Lvar,Lvar_Name,(Min_Xmin,Max_Xmax),['uniform']),Ldom).


% Option : Add awakenning variables

choose_range(X,[Xmin,Xmax]-Distribution,Process,Options,Env) :-       
        lists:member(lvar(L),Options),
        !,
        get_num(Env,Num),
        fd_min(Xmin,Min_Xmin),
        fd_max(Xmax,Max_Xmax),
        terms:term_variables([Xmin,Xmax],Lvar1),
        (var(Distribution) ->
            distribution_building(Min_Xmin,Max_Xmax,Distribution),
            lists:append(Lvar1,L,Lvar2),
            lists:append(Lvar2,Distribution,Lvar3)
            ;
            terms:term_variables(Distribution,Distribution2),
            lists:append(Lvar1,L,Lvar2),
            lists:append(Lvar2,Distribution2,Lvar3)
            ),
        lists:remove_duplicates([X|Lvar3], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Ldom),
          random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose_range(Num,X,[Xmin,Xmax],Process,U,Options,Env),state(Lvar,Lvar_Name,(Min_Xmin,Max_Xmax),Distribution),Ldom).


% Default

choose_range(X,[Xmin,Xmax]-Distribution,Process,Options,Env) :-       
        get_num(Env,Num),
        fd_min(Xmin,Min_Xmin),
        fd_max(Xmax,Max_Xmax),
        terms:term_variables([Xmin,Xmax],Lvar1),
        (var(Distribution) ->
            distribution_building(Min_Xmin,Max_Xmax,Distribution),
            lists:append(Lvar1,Distribution,Lvar2)
            ;
            terms:term_variables(Distribution,Distribution2),
            lists:append(Lvar1,Distribution2,Lvar2)
            ),
        lists:remove_duplicates([X|Lvar2], Lvar),
        lvar_to_dom(Lvar,Lvar_Name,Ldom),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
            ;
            true
        ),
        clpfd:fd_global(choose_range(Num,X,[Xmin,Xmax],Process,U,Options,Env),state(Lvar,Lvar_Name,(Min_Xmin,Max_Xmax),Distribution),Ldom).





clpfd:dispatch_global(choose_range(Num, X, Range, Process, U, Options, Env),
                      state(Lvar_i,Lvar_Name_i,Bound_i,Distribution),state(Lvar_f, Lvar_name_f,Bound_f,Distribution_f), Actions) :-
        choose_range_solver(Num, X, Range, Distribution, Process, Lvar_i, Lvar_Name_i, Bound_i,
                            Lvar_f, Lvar_name_f,Bound_f,Distribution_f,U, Options, Env, Actions).




choose_range_solver(_Num, _X, _Range, _Distribution, _Process, _Lvar_i, _Lvar_name_i, _Bound_i,
                    _Lvar_i, _Lvar_name_i, _Bound_i, _Distribution, _U, _Options, Env, Actions) :-
        get_atts(Env,+(k_flag(K_flag, _Max))),
        K_flag == 0,
        !,
        Actions =[].


choose_range_solver(_Num, X,[Xmin,Xmax], Distribution, Process, _Lvar_i, _Lvar_name_i, _Bound_i, _Lvar_i, _Lvar_name_i, _Bound_i, Distribution,
                    U, _Options, _Env, Actions) :-
        terms:term_variables([Xmin,Xmax|Distribution],[]),
        !,
        random_draw(['range'],U,[Xmin,Xmax],Distribution,Value),
        Actions =[exit, call(user:exec([X =Value|Process]))].


choose_range_solver(_Num, _X,[_Xmin,_Xmax], _Distribution, _Process, _Lvar_i, _Lvar_name_i,_Bound_i, _Lvar_i, _Lvar_name_i,_Bound_i, _Distribution,
                    _U, Options, _Env, Actions) :-
         lists:member(no_filtering,Options),
         !,
         Actions = [].


choose_range_solver(_Num, X,[Xmin,Xmax], Distribution, Process, _Lvar_i, _Lvar_name_i,(Min_Xmin,Max_Xmax), _Lvar_i, _Lvar_name_i,
                    (Min_Xmin_f,Max_Xmax_f), Distribution_f, U, Options, _Env, Actions) :-
        lists:non_member(inconsistency_check,Options),
        !,
        intervalle(X,Xmin,Xmax),
        fd_min(Xmin,Min_Xmin_f),
        fd_max(Xmax,Max_Xmax_f),
        Diff_Inf is Min_Xmin_f-Min_Xmin,
        Diff_Sup is Max_Xmax-Max_Xmax_f,
        distribution_update(Diff_Inf,Diff_Sup,Distribution,Distribution_f),
        (narrow_range(X,[Xmin,Xmax],Distribution,U) ->
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Process))] 
            ;
                Actions = []
            )
        ;
            Actions = [fail]
        ).



choose_range_solver(Num, X,[Xmin,Xmax], Distribution, Process, Lvar_i, Lvar_name_i,Bound_i,
                    Lvar_f, Lvar_name_f,Bound_f, Distribution_f, U, Options, Env, Actions) :-
        lists:member(inconsistency_check,Options),
        !,
        (fix_point_choose_range(Num,X,[Xmin,Xmax],Distribution,Process,U,Lvar_i,Lvar_name_i,Bound_i,Distribution_f,Env) ->            
            (nonvar(X) ->
                Actions = [exit,call(user:exec(Process))] 
            ;
                fd_min(Xmin,Min_Xmin),
                fd_max(Xmax,Max_Xmax),
                Bound_f=(Min_Xmin,Max_Xmax),
                evolution_list(Lvar_i,Lvar_name_i,Lvar_f,Lvar_name_f),
                Actions = []
            )
        ;
            Actions = [fail]
        ).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Utils choose_range/5
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Filtering algorithm


% Case : uniform prpobability distribution
narrow_range(_X,[],[],_U) :-
        !.

narrow_range(X,[Xmin,Xmax],['uniform'],U) :-
        !,
        clpfd:fd_min(Xmin,Min_Xmin),
        clpfd:fd_max(Xmin,Max_Xmin),
        clpfd:fd_min(Xmax,Min_Xmax),
        clpfd:fd_max(Xmax,Max_Xmax),
        Value_Min is integer(ceiling(U*(Max_Xmax-Max_Xmin+1)+Max_Xmin)),
        le(X,Value_Min),
        Value_Max is integer(ceiling(U*(Min_Xmax-Min_Xmin+1))+Min_Xmin-1),
        leq(Value_Max,X).
     %   Den_Inf is Max_Xmax-Max_Xmin+1,
     %   Den_Sup is Min_Xmax-Min_Xmin+1,
     %   narrow_range_rec(['uniform'],X,Min_Xmin,(Min_Xmin,Max_Xmin),Den_Inf,Den_Sup,U).


narrow_range(X,[Xmin,Xmax],Distribution,U) :-
        clpfd:fd_min(Xmin,Min_Xmin),
        clpfd:fd_max(Xmin,Max_Xmin),
        clpfd:fd_min(Xmax,Min_Xmax),
        clpfd:fd_max(Xmax,Max_Xmax),
        interval_probability_range([(Min_Xmin,Max_Xmin),(Min_Xmax,Max_Xmax)],Distribution,Probability_Inf,Probability_Sup),
        narrow_range_rec([],X,Min_Xmin,Probability_Inf,Probability_Sup,U).


% Uniform Probability distribution 

%%%narrow_range_rec(['uniform'],X,Value,(Min_Xmin,_Max_Xmin),0,Den_Sup,U) :-
%%%        Proba_Sup is (Value-Min_Xmin+1)/Den_Sup,
%%%        U=<Proba_Sup,
%%%        !,
%%%        leq(Value,X).

%%%narrow_range_rec(['uniform'],X,Value,(Min_Xmin,Max_Xmin),0,Den_Sup,U) :-
%%%        !,
%%%        Value1 is Value+1,
%%%        narrow_range_rec(['uniform'],X,Value1,(Min_Xmin,Max_Xmin),0,Den_Sup,U).

%%%narrow_range_rec(['uniform'],X,Value,(_Min_Xmin,Max_Xmin),Den_Inf,0,U) :-
%%%        Proba_Inf is (Value-Max_Xmin)/Den_Inf,
%%%        U<Proba_Inf,
%%%        !,
%%%        le(X,Value).

%%%narrow_range_rec(['uniform'],X,Value,(Min_Xmin,Max_Xmin),Den_Inf,0,U) :-
%%%        !,
%%%        Value1 is Value+1,
%%%        narrow_range_rec(['uniform'],X,Value1,(Min_Xmin,Max_Xmin),Den_Inf,0,U).


%%%narrow_range_rec(['uniform'],X,Value,(Min_Xmin,Max_Xmin),Den_Inf,Den_Sup,U) :-
%%%        Proba_Inf is (Value-Max_Xmin)/Den_Inf,
%%%        Proba_Sup is (Value-Min_Xmin+1)/Den_Sup,
%%%        (U<Proba_Inf ->
%%%            le(X,Value),
%%%            (  U=<Proba_Sup ->
%%%                leq(Value,X)
%%%            ;
%%%                Value1 is Value+1,
%%%                narrow_range_rec(['uniform'],X,Value1,(Min_Xmin,Max_Xmin),0,Den_Sup,U)
%%%            )
%%%        ;
%%%            (  U=<Proba_Sup ->
%%%                leq(Value,X),
%%%                Value1 is Value+1,
%%%                narrow_range_rec(['uniform'],X,Value1,(Min_Xmin,Max_Xmin),Den_Inf,0,U)
%%%            ;
%%%                Value1 is Value+1,
%%%                narrow_range_rec(['uniform'],X,Value1,(Min_Xmin,Max_Xmin),Den_Inf,Den_Sup,U)
%%%            )
%%%        ).


% Non_Uniform probability distribution

narrow_range_rec([],_X,_Value,[],[],_U).

narrow_range_rec([],X,Value,[],[(Num_Sup,Den_Sup)|_Reste_Proba_Sup],U) :-
        Proba_Sup is Num_Sup/Den_Sup,
        U=<Proba_Sup,
        !,
        leq(Value,X).

narrow_range_rec([],X,Value,[],[(_Num_Sup,_Den_Sup)|Reste_Proba_Sup],U) :-
        !,
        Value1 is Value+1,
        narrow_range_rec([],X,Value1,[],Reste_Proba_Sup,U).

narrow_range_rec([],X,Value,[(Num_Inf,Den_Inf)|_Reste_Proba_Inf],[],U) :-
        Proba_Inf is Num_Inf/Den_Inf,
        U<Proba_Inf,
        !,
        le(X,Value).

narrow_range_rec([],X,Value,[(_Num_Inf,_Den_Inf)|Reste_Proba_Inf],[],U) :-
        !,
        Value1 is Value+1,
        narrow_range_rec([],X,Value1,Reste_Proba_Inf,[],U).


narrow_range_rec([],X,Value,[(Num_Inf,Den_Inf)|Reste_Proba_Inf],[(Num_Sup,Den_Sup)|Reste_Proba_Sup],U) :-
        Proba_Inf is Num_Inf/Den_Inf,
        Proba_Sup is Num_Sup/Den_Sup,
        (U<Proba_Inf ->
            le(X,Value),
            (  U=<Proba_Sup ->
                leq(Value,X)
            ;
                Value1 is Value+1,
                narrow_range_rec([],X,Value1,[],Reste_Proba_Sup,U)
            )
        ;
            (  U=<Proba_Sup ->
                leq(Value,X),
                Value1 is Value+1,
                narrow_range_rec([],X,Value1,Reste_Proba_Inf,[],U)
            ;
                Value1 is Value+1,
                narrow_range_rec([],X,Value1,Reste_Proba_Inf,Reste_Proba_Sup,U)
            )
        ).



% Interval probability when Domain = [Xmin,Xmax]

interval_probability_range(_Range,[],[],[]) :-
        !.

interval_probability_range(_Range,[_P],[(0,1)],[(1,1)]) :-
        !.


interval_probability_range([(Min_Xmin,Min_Xmin),(Min_Xmin,_Max_Xmax)],[P|Distribution],[(0,1)|Reste_Proba_Inf],[(1,1)|Reste_Proba_Sup]) :-
        !,
        Value is Min_Xmin+1,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        interval_probability_range_rec(Value,(Min_Xmin,Min_Xmin),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,P_Min,P_Min,P_Max,
                                       _Sum_Min,_Sum_Min2,_Sum_Max).


interval_probability_range([(Min_Xmin,Max_Xmin),(Min_Xmin,_Max_Xmax)],[P|Distribution],[(0,1)|Reste_Proba_Inf],[(1,1)|Reste_Proba_Sup]) :-
        !,
        Value is Min_Xmin+1,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        interval_probability_range_rec(Value,(Max_Xmin,Min_Xmin),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,P_Min,0,P_Max,
                                       _Sum_Min,_Sum_Min2,_Sum_Max).

interval_probability_range([(Min_Xmin,Min_Xmin),(Min_Xmax,_Max_Xmax)],[P|Distribution],[(0,1)|Reste_Proba_Inf],[(P_Max,Den_Sup)|Reste_Proba_Sup]) :-
        !,
        Value is Min_Xmin+1,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        Den_Sup #= P_Max+Sum_Min-P_Min,
        interval_probability_range_rec(Value,(Min_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,P_Min,P_Min,P_Max,
                                       Sum_Min,_Sum_Min2,_Sum_Max).

interval_probability_range([(Min_Xmin,Max_Xmin),(Min_Xmax,_Max_Xmax)],[P|Distribution],[(0,1)|Reste_Proba_Inf],[(P_Max,Den_Sup)|Reste_Proba_Sup]) :-
        Value is Min_Xmin+1,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        Den_Sup #= P_Max+Sum_Min-P_Min,
        interval_probability_range_rec(Value,(Max_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,P_Min,0,P_Max,Sum_Min,_Sum_Min2,_Sum_Max).


% Recursive call

interval_probability_range_rec(_Value,(_Max_Xmin,_Min_Xmax),[],[],[],Sum_Min,Sum_Min2,Sum_Max,Sum_Min,Sum_Min2,Sum_Max) :-
        !.


interval_probability_range_rec(Value,(Max_Xmin,Min_Xmax),[P|Distribution],[(0,1)|Reste_Proba_Inf],[(1,1)|Reste_Proba_Sup],S_Min,S_Min2,S_Max,
                               Sum_Min,Sum_Min2,Sum_Max) :-
        Value < Max_Xmin,
        Value > Min_Xmax,
        !,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        S_Max2 is S_Max+P_Max,
        Value2 is Value+1,
        S_Min3 is S_Min2+P_Min,
        interval_probability_range_rec(Value2,(Max_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,S_Min,S_Min3,S_Max2,
                                       Sum_Min,Sum_Min2,Sum_Max).


interval_probability_range_rec(Value,(Max_Xmin,Min_Xmax),[P|Distribution],[(0,1)|Reste_Proba_Inf],[(Num_Sup,Den_Sup)|Reste_Proba_Sup],S_Min,S_Min2,S_Max,
                         Sum_Min,Sum_Min2,Sum_Max) :-
        Value < Max_Xmin,
        Value =< Min_Xmax,
        !,
        clpfd:fd_max(P,P_Max),
        clpfd:fd_min(P,P_Min),
        S_Min1 is S_Min+P_Min, 
        Num_Sup is S_Max+P_Max,
        Den_Sup #= Num_Sup+ Sum_Min-S_Min1,
        Value2 is Value+1,
        interval_probability_range_rec(Value2,(Max_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,S_Min1,S_Min2,Num_Sup,
                                       Sum_Min,Sum_Min2,Sum_Max).



interval_probability_range_rec(Value,(Max_Xmin,Min_Xmax),[P|Distribution],[(Num_Inf,Den_Inf)|Reste_Proba_Inf],[(Num_Sup,Den_Sup)|Reste_Proba_Sup],
                               S_Min,S_Min2,S_Max,Sum_Min,Sum_Min2,Sum_Max) :-
        Value >= Max_Xmin,
        Value =< Min_Xmax,
        !,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        Num_Inf is S_Min2,
        Den_Inf #= Sum_Max-S_Max+S_Min2,
        S_Min1 is S_Min+P_Min,
        Num_Sup is S_Max+P_Max,
        Den_Sup #= Num_Sup+ Sum_Min-S_Min1,
        Value2 is Value+1,
        S_Min3 is S_Min2+P_Min,
        interval_probability_range_rec(Value2,(Max_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,S_Min1,S_Min3,Num_Sup,
                                       Sum_Min,Sum_Min2,Sum_Max).

        
interval_probability_range_rec(Value,(Max_Xmin,Min_Xmax),[P|Distribution],[(Num_Inf,Den_Inf)|Reste_Proba_Inf],[(1,1)|Reste_Proba_Sup],
                               S_Min,S_Min2,S_Max,Sum_Min,Sum_Min2,Sum_Max) :-
        Value >= Max_Xmin,
        Value > Min_Xmax,
        !,
        clpfd:fd_min(P,P_Min),
        clpfd:fd_max(P,P_Max),
        Num_Inf is S_Min2,
        Den_Inf #= Sum_Max-S_Max+S_Min2,
        Value2 is Value+1,
        S_Min3 is S_Min2+P_Min,
        S_Max2 is S_Max+P_Max,
        interval_probability_range_rec(Value2,(Max_Xmin,Min_Xmax),Distribution,Reste_Proba_Inf,Reste_Proba_Sup,S_Min,S_Min3,S_Max2,
                                       Sum_Min,Sum_Min2,Sum_Max).



% Some test of interval_probability_range
%  P1 in 1..2, P2 in 1..2, P3 in 3..4, P4 in 1..2, interval_probability_range([(1,3),(4,4)],[P1,P2,P3,P4],Distrib_Inf,Distrib_Sup).
% Answer : Distrib_Inf = [(0,1),(0,1),(0,6),(3,5)],
%          Distrib_Sup = [(2,7),(4,8),(8,9),(10,10)],
% P1 in 1..2, P2 in 1..2, P3 in 3..4, P4 in 2..3,P5 in 1..3, interval_probability_range([(1,2),(4,5)],[P1,P2,P3,P4,P5],Distrib_Inf,Distrib_Sup).
% Answer : Distrib_Inf = [(0,1),(0,12),(1,11),(4,10),(6,9)],
%          Distrib_Sup = [(2,8),(4,9),(8,10),(11,11),(1,1)],
% P1 in 1..2, P2 in 1..2, P3 in 3..4, P4 in 2..3,P5 in 1..3, interval_probability_range([(1,3),(2,5)],[P1,P2,P3,P4,P5],Distrib_Inf,Distrib_Sup).
% Answer Distrib_Inf = [(0,1),(0,1),(0,10),(3,9),(5,8)],
%        Distrib_Sup = [(2,3),(4,4),(1,1),(1,1),(1,1)],





% Domain pruning by an iterative computation composed of insconsistency checking and a domain filtering

fix_point_choose_range(Num,X,Domain,Distribution,Process,U,Lvar_i,Lvar_name_i,Bound_i,Distribution_f,Env) :-
        domaine(Lvar_i,Lvar_Dom),
        fix_point_choose_range_rec(Num,X,Domain,Distribution,Process,U,Lvar_i,Lvar_name_i,Bound_i,Distribution_f,Lvar_Dom,Env).

fix_point_choose_range_rec(Num,X,[Xmin,Xmax],Distribution,Process,U,Lvar_i,Lvar_name_i,(Min_Xmin,Max_Xmax),Distribution_f,Lvar_Dom,Env) :-
        intervalle(X,Xmin,Xmax),
        fd_min(Xmin,Min_Xmin1),
        fd_max(Xmax,Max_Xmax1),
        Diff_Inf is Min_Xmin1-Min_Xmin,
        Diff_Sup is Max_Xmax-Max_Xmax1,
        distribution_update(Diff_Inf,Diff_Sup,Distribution,Distribution1),
        narrow_range(X,[Xmin,Xmax],Distribution1,U),
       \+ (\+ (decr_K_flag(Env),exec(Process),assert_rec(Num,Lvar_i,Lvar_name_i))),
       single_domaine(Num,Lvar_i,Lvar_name_i),
       domaine(Lvar_i,Lvar_Dom2),
       (
         test_domaine(Lvar_Dom,Lvar_Dom2),
         Distribution1 = Distribution_f
%%%         fd_min(Xmin,Min_Xmin2),
%%%         fd_max(Xmax,Max_Xmax2),
%%%         Diff_Inf1 is Min_Xmin2-Min_Xmin1,
%%%         Diff_Sup1 is Max_Xmax1-Max_Xmax2,
%%%         distribution_update(Diff_Inf1,Diff_Sup1,Distribution1,Distribution_f)
       ;
         fix_point_choose_range_rec(Num,X,[Xmin,Xmax],Distribution1,Process,U,Lvar_i,Lvar_name_i,(Min_Xmin1,Max_Xmax1),Distribution1,Lvar_Dom2,Env)
       ).





% Distribution building

distribution_building(Min_Xmin,Min_Xmin,[_P]) :-
        !.


distribution_building(Min_Xmin,Max_Xmax,[_P|Reste_Distrib]) :-
        Min_Xmin2 is Min_Xmin+1,
        distribution_building(Min_Xmin2,Max_Xmax,Reste_Distrib).
        
% Distribution update

distribution_update(0,0,Distribution,Distribution) :-
        !.

distribution_update(_Diff_Inf,_Diff_Sup,[],[]) :-
        !.

distribution_update(_Diff_Inf,_Diff_Sup,['uniform'],['uniform']) :-
        !.

distribution_update(Diff_Inf,Diff_Sup,Distribution,Distribution_f) :-
        length(Distribution,N),
        Nb is N-Diff_Sup,
        distribution_update_rec(Diff_Inf,Nb,Distribution,Distribution_f).

distribution_update_rec(_Diff_Inf,0,_Distribution,[]) :-
        !.

distribution_update_rec(_Diff_Inf,_Nb,[],[]) :-
        !.

distribution_update_rec(Diff_Inf,Nb,[_P|Reste_Distrib],Distribution_f) :-
        Diff_Inf > 0,
        !,
        Diff_Inf1 is Diff_Inf-1,
        Nb1 is Nb-1,
        distribution_update_rec(Diff_Inf1,Nb1,Reste_Distrib,Distribution_f).


distribution_update_rec(0,Nb,[P|Reste_Distrib],[P|Reste_Distrib_f]) :-
        Nb1 is Nb-1,
        distribution_update_rec(0,Nb1,Reste_Distrib,Reste_Distrib_f).


        




%%%choose_range(X,[Xmin,Xmax],Distribution-Q,Processus,Options,Env) :-       
%%%        terms:term_variables([Xmin,Xmax],Lvar1),
%%%        get_num(Env,Num),
%%%        diff_list_variables(Distribution-Q,Lvar2),
%%%        lists:append(Lvar1,Lvar2,L_bound),
%%%        lvar_to_lvarname([X|L_bound], Lvar_name),
%%%        lvar_to_ldom([X|L_bound], Ldom),
%%%        min_inf(Xmax,Min_Xmax),
%%%        max_sup(Xmin,Max_Xmin),
%%%        clpfd:fd_global(choose_range(Num,X,[Xmin,Xmax],Processus,Options,Env),
%%%                        state(L_bound,[X|L_bound],Lvar_name,Distribution-Q,(Max_Xmin,Min_Xmax)),Ldom).



%%%%% Solver Call

%%%clpfd:dispatch_global(choose_range(Num, X, Range, Processus, Options, Env),state(L_bound_i,Lvar_i, Lvar_name_i, Distribution_i,Borne_min_max_i),
%%%                      state(L_bound_f,Lvar_f, Lvar_name_f, Distribution_f,Borne_min_max_f), Actions) :-
%%%        choose_range_solver(Num, X, Range, Processus, L_bound_i, Lvar_i, Lvar_name_i, Distribution_i, Borne_min_max_i, L_bound_f,
%%%                            Lvar_f, Lvar_name_f, Distribution_f, Borne_min_max_f, Options, Env, Actions).



%%%% Stop of the inconstency checking

%%%choose_range_solver(_Num, _X, _Range,  _Processus, _L_bound_i, _Lvar_i, _Lvar_name_i, _Distribution_i, _Borne_min_max_i,
%%%                    _L_bound_i,_Lvar_i, _Lvar_name_i, _Distribution_i,_Borne_min_max_i, _Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag, _Max))),
%%%        K_flag == 0,
%%%        !,
%%%        Actions =[].


%%%% Not in a inconsistency checking and probabilic choice known

%%%choose_range_solver(_Num, X, Range,  Processus, L_bound_i, _Lvar_i, _Lvar_name_i, Distribution_i-Q, _Borne_min_max_i,
%%%                    _L_bound_f, _Lvar_f, _Lvar_name_f, _Distribution_f, _Borne_min_max_f, Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag == Max,
%%%        terms:term_variables(L_bound_i,[]),
%%%        nonvar(Q),
%%%        !,
%%%        random_draw(['range'|Options],Range,Distribution_i,Elt),
%%%        clpfd:fd_set(X,Set_courant),
%%%        (clpfd:fdset_member(Elt, Set_courant)->
%%%            Actions =[exit, call(user:exec([X#=Elt|Processus]))]
%%%        ;
%%%            Actions = [fail]
%%%        ).


%%%% Not in a inconsistency checking but uniform Distribution partially known 

%%%choose_range_solver(_Num, X, Range,  Processus, L_bound_i,_Lvar_i, _Lvar_name_i, Distribution_i-Q, Borne_min_max_i,
%%%                    _L_bound_f, _Lvar_f, _Lvar_name_f, Distribution_f, _Borne_min_max_f, Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag == Max,
%%%        terms:term_variables(L_bound_i,[]),
%%%        var(Q),
%%%        lists:member('uniform',Options),
%%%        !,
%%%        construction_distribution(['uniform'],Range,Borne_min_max_i,Distribution_i-Q,Distribution_f,_Lvar_new),
%%%        arg(1,Distribution_f,Distribution),
%%%        random_draw(['range'|Options],Range,Distribution,Elt),
%%%        clpfd:fd_set(X,Set_courant),
%%%        (clpfd:fdset_member(Elt, Set_courant)->
%%%            Actions =[exit, call(user:exec([X#=Elt|Processus]))]
%%%        ;
%%%            Actions = [fail]
%%%        ).
%%%% Not in a inconsistency checking but not a uniform Distribution partially known 

%%%choose_range_solver(Num, X, Range,  Processus, L_bound_i, Lvar_i, Lvar_name_i, Distribution_i-Q, Borne_min_max_i,
%%%                    L_bound_f, Lvar_f, Lvar_name_f, Distribution_f-P, Borne_min_max_i, Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag == Max,
%%%        terms:term_variables(L_bound_i,[]),
%%%        var(Q),
%%%        !,
%%%        construction_distribution([],Range,Borne_min_max_i,Distribution_i-Q,Distribution_f-P,L_bound_f),      % invaraiant P =[] comme Xmax instanciée
%%%        lists:append(L_bound_f,Lvar_i,Lvar),
%%%        lvar_to_lvarname(L_bound_f,L_bound_name),
%%%        lists:append(L_bound_name,Lvar_name_i,Lvar_name),
%%%        (narrow_choose_range(Num, X, Range,Distribution_f-P, Processus, Lvar, Lvar_name, Env) ->
%%%            evolution_list(Lvar,Lvar_name,Lvar_f,Lvar_name_f),  
%%%            (terms:term_variables(L_bound_f,[]) ->                                               % Cas où tirage aléa possible
%%%                random_draw(['range'|Options],Range,Distribution_f,Elt),
%%%                clpfd:fd_set(X,Set_courant),
%%%                (clpfd:fdset_member(Elt, Set_courant) ->
%%%                    Actions = [exit, call(user:exec([X=Elt|Processus]))] 
%%%                ;
%%%                    Actions =[fail]
%%%                )
%%%            ;
%%%                Actions = []                                   % Cas où il n'est pas possible (ttes les var de distribution ne sont pas instanciées)
%%%            )
%%%        ;
%%%            Actions = [fail]                            % Echec du calcul de point fixe
%%%        ).


%%%% Probabilictic Choice unknown

%%%choose_range_solver(Num, X, [Xmin,Xmax],  Processus, L_bound_i, Lvar_i, Lvar_name_i, Distribution, Borne_min_max_i,
%%%                    L_bound_f, Lvar_f, Lvar_name_f, Distribution_f, Borne_min_max_f, Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag == Max,
%%%        narrow_choose_range(Num,X,[Xmin,Xmax],Distribution, Processus, Lvar_i, Lvar_name_i, Env),
%%%        !,
%%%        (lists:member('uniform',Options) ->
%%%            construction_distribution(['uniform'],[Xmin,Xmax],Borne_min_max_i,Distribution,Distribution_f,Lvar_new)
%%%        ;
%%%            construction_distribution([],[Xmin,Xmax],Borne_min_max_i,Distribution,Distribution_f,Lvar_new)
%%%        ),
%%%        lists:append(Lvar_new,L_bound_i,L_bound),
%%%        terms:term_variables(L_bound,L_bound_f),
%%%        arg(2,Distribution_f,Q),
%%%        arg(1,Distribution_f,Distribution_new),
%%%        (L_bound_f=[], nonvar(Q) -> % le calcul de point fixe et la construction de la distribution ont tous instanciées
%%%            random_draw(['range'|Options],[Xmin,Xmax],Distribution_new,Elt), % = tirage aléa possible
%%%            clpfd:fd_set(X,Set_courant),
%%%            (clpfd:fdset_member(Elt, Set_courant) ->
%%%                Actions = [exit, call(user:exec([X=Elt|Processus]))] 
%%%            ;
%%%                Actions =[fail]
%%%            )
%%%        ;
%%%            lists:append(Lvar_new,Lvar_i,Lvar),                 % mise à jour des éléments de state et suspension
%%%            lvar_to_lvarname(Lvar_new,Lvar_name_new),
%%%            lists:append(Lvar_name_new,Lvar_name_i,Lvar_name),
%%%            evolution_list(Lvar,Lvar_name,Lvar_f,Lvar_name_f),
%%%            min_inf(Xmax,Min_Xmax_f),
%%%            max_sup(Xmin,Max_Xmin_f),
%%%            Borne_min_max_f=(Max_Xmin_f,Min_Xmax_f),
%%%            Actions =[]
%%%          ).

%%%% Cas Calcul du point fixe à échoué = la contrainte échoue

%%%choose_range_solver(_Num, _X, _Range,  _Processus, _L_bound_i, _Lvar_i, _Lvar_name_i, _Distribution, _Borne_min_max_i,
%%%                    _L_bound_f, _Lvar_f, _Lvar_name_f, _Distribution_f, _Borne_min_max_f, _Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag == Max,
%%%        !,
%%%        Actions=[fail].



%%%% cas où le choose est réveillé du à un test de consistance = pour l'instant on fait rien

%%%choose_range_solver(_Num, _X, _Range,  _Processus, _L_bound_i,_Lvar_i, _Lvar_name_i, _Distribution_i, _Borne_min_max_i,
%%%                    _L_bound_f, _Lvar_f, _Lvar_name_f, _Distribution_f, _Borne_min_max_f, _Options, Env, Actions) :-
%%%        get_atts(Env,+(k_flag(K_flag,Max))),
%%%        K_flag < Max,
%%%        !,
%%%        Actions = [].







%%%%---------------
%%%% Utils choose_range/6
%%%%---------------

%%%% narrow

%%%% Invariant : L_dom_i = L_dom_f

%%%narrow_choose_range(Num,X,Range,Distribution, Processus, Lvar_i, Lvar_name_i, Env) :-
%%%        domaine(Lvar_i,Lvar_dom),
%%%        narrow_choose_range_rec(Num,X,Range,Distribution,Processus, Lvar_i, Lvar_name_i, Lvar_dom, Env).




%%%narrow_choose_range_rec(Num,X,[Xmin,Xmax],Distribution, Processus, Lvar_i, Lvar_name_i, Lvar_dom, Env) :-
%%%        intervalle(X,Xmin,Xmax),
%%%%       distribution(X,Xmin,Distribution),
%%%        \+ (\+ (decr_K_flag(Env), exec(Processus), assert_rec(Num, Lvar_i, Lvar_name_i))),
%%%        single_domaine(Num,Lvar_i,Lvar_name_i),
%%%        domaine(Lvar_i,Lvar_dom2),    
%%%        (
%%%          test_domaine(Lvar_dom,Lvar_dom2)
%%%          ;
%%%          narrow_choose_range_rec(Num,X,[Xmin,Xmax],Distribution, Processus, Lvar_i, Lvar_name_i, Lvar_dom2, Env)
%%%        ).
        


%%%% construction_distribution : evolution of Distribution according to the knowegde of range

%%%construction_distribution(Option,[_Xmin,Xmax],(_Max_Xmin,Min_Xmax),Distribution1,Distribution2,Lvar) :-
%%%                    clpfd:fd_min(Xmax,Min),
%%%                    Min>Min_Xmax,
%%%                    !,
%%%                    Decalage is Min-Min_Xmax,
%%%                    construction_distribution_rec(Option,Xmax,Decalage,Distribution1,Distribution2,Lvar).


%%%% Case range does not change

%%%construction_distribution(_Option,[_Xmin,_Xmax],(_Max_Xmin,_Min_Xmax),Distribution1,Distribution1,_Lvar).


%%%% Recursive call

%%%construction_distribution_rec(_,Xmax,0,Distribution1-[],Distribution1-[],[]) :-
%%%        nonvar(Xmax),
%%%        !.

%%%construction_distribution_rec(_,_Xmax,0,Distribution1-P,Distribution1-P,[]) :-
%%%        !.

%%%construction_distribution_rec(['uniform'],Xmax,Decalage,Distribution1-Q,Distribution2,Lvar) :-
%%%        !,
%%%        Decalage1 is Decalage-1,
%%%         Q = [1|P],
%%%        construction_distribution_rec(['uniform'],Xmax,Decalage1,Distribution1-P,Distribution2,Lvar).


%%%construction_distribution_rec([],Xmax,Decalage,Distribution1-Q,Distribution2,[X|Rest_Lvar]) :-
%%%        !,
%%%        Decalage1 is Decalage-1,
%%%        Q = [X|P],
%%%        construction_distribution_rec([],Xmax,Decalage1,Distribution1-P,Distribution2,Rest_Lvar).
                






%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  choose_decision                             %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Note :Choose_decision need to be encapsuled between init_env and fin_env      
% Options :
%       - inconsistency_check : a more costly method which increases the combinator deduction
%       - lvar(L) : add variable of L to the awajkening mechanism
%       - no_filtering : no using of the filtering algorithm
%       - rv(U) : U is the random value of the random variable which permits to simulate value for X
%       - Default :


choose_decision(Contrainte, Distribution, Processus1, Processus2, Options, Env) :-
        lists:member(lvar(L), Options),
        !,
        get_num(Env, Num),
        terms:term_variables(Contrainte, Lvar1),
        terms:term_variables(Distribution, Lvar2),
        lists:append(Lvar1, L, Lvar3),
        lists:append(Lvar2, Lvar3, Lvar4),
        lists:remove_duplicates(Lvar4, Lvar),
        lvar_to_lvarname(Lvar, Lvar_name),
        lvar_to_ldom(Lvar, Ldom),
        var(Consist),
        random:random(U),
        (lists:member(rv(U),Options) ->
            true
        ;
            true
        ),
        clpfd:fd_global(choose_decision(Num, Contrainte,Distribution, Processus1, Processus2, U, Options, Env),
                            state(Consist, Lvar, Lvar_name), Ldom).
      

% Default Behaviour


choose_decision(Contrainte, Distribution, Processus1, Processus2, Options, Env) :-
            get_num(Env, Num),
            terms:term_variables(Contrainte, Lvar1),
            terms:term_variables(Distribution, Lvar2),
            lists:append(Lvar2,Lvar1,Lvar3),
            lists:remove_duplicates(Lvar3, Lvar),
            lvar_to_lvarname(Lvar, Lvar_name),
            lvar_to_ldom(Lvar, Ldom),
            var(Consist),
            random:random(U),
            (lists:member(rv(U),Options) ->
                true
            ;
                true
            ),
            clpfd:fd_global(choose_decision(Num, Contrainte, Distribution, Processus1, Processus2, U, Options, Env),
                            state(Consist, Lvar, Lvar_name), Ldom).

%% Solver
%% Note : var(Consist) true <=> not inconstency detection
%%        Consist = 0 <=>  C^P1 false
%%        Consist = 1 <=> not-C^P2 false

clpfd:dispatch_global(choose_decision(Num, Contrainte, Distribution, Processus1, Processus2, U, Options, Env),
                      state(Consist_i, Lvar_i, Lvar_name_i), state(Consist_f, Lvar_f, Lvar_name_f), Actions) :-
        choose_decision_solver(Num, Contrainte, Distribution, Processus1, Processus2, U, Consist_i, Lvar_i, Lvar_name_i, Consist_f, Lvar_f, Lvar_name_f,
                          Options, Env, Actions).



%% Stop of consistency check

choose_decision_solver(_Num,_Contrainte, _Distribution, _Processus1, _Processus2, _U, _Consist_i, _Lvar_i, _Lvar_name_i, _Consist_i, _Lvar_i, _Lvar_name_i,
                 _Options, Env, Actions) :-
        get_atts(Env, +(k_flag(K_flag, _Max))),
        K_flag == 0,
        !,
        Actions =[exit].


% Probabilistic choice completely known


choose_decision_solver(_Num, Contrainte, [W1,W2], Processus1, Processus2, U,  Consist_i, _Lvar_i, _Lvar_name_i, _Consist_i, _Lvar_i, _Lvar_name_i,
                  Options, _Env, Actions) :-
        terms:term_variables([W1,W2], []),
        !,
        random_draw(Options,U,[0,1],[W1,W2],Value),
        (Consist_i == Value ->             % inconsistency known
            Actions =[fail]
        ;
            (Value == 0 ->
                Actions = [exit, call(user:exec([Contrainte|Processus1]))]
            ;
                neg(Contrainte,Nc),
                Actions = [exit, call(user:exec([Nc|Processus2]))]
            )   
        ).

% no_filtering Option : on

choose_decision_solver(_Num, _Contrainte, _Distribution, _Processus1, _Processus2, _U,  _Consist_i, _Lvar_i, _Lvar_name_i, _Consist_i, _Lvar_i,
                       _Lvar_name_i,Options, _Env, Actions) :-
        lists:member(no_filtering,Options),
        !,
        Actions = [].
     
% Options = - Not Inconsistency check 

choose_decision_solver(_Num, Contrainte, [W1,W2], Processus1, Processus2, U,  Consist_i, Lvar_i, Lvar_name_i, Consist_i, Lvar_i, Lvar_name_i,
                  Options, _Env, Actions) :-
        lists:non_member(inconsistency_check,Options),
        !,
        (narrow_non_uniform([domain_bound],Value,[0,1],[W1,W2],U) ->
            (nonvar(Value) ->
                (Consist_i == Value -> % inconsistency known
                    Actions =[fail]
                ;
                    (Value == 0 ->
                        Actions = [exit, call(user:exec([Contrainte|Processus1]))]
                    ;
                        neg(Contrainte,Nc),
                        Actions = [exit, call(user:exec([Nc|Processus2]))]
                    )
                )
            ;
                Actions = []
            )
        ;
            Actions = [fail]
        ).
              

        
% Options = - Inconsistency check 

choose_decision_solver(Num, Contrainte, Distribution, Processus1, Processus2, U, Consist_i, _Lvar_i, _Lvar_name_i, Consist_f, Lvar_f, Lvar_name_f,
                  Options, Env, Actions) :-
        lists:member(inconsistency_check,Options),
        !,
        (fix_point_choose_decision([domain_bound],Num,Contrainte,Distribution,Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,Consist_f,Value,Env) ->
            (nonvar(Value) ->
               (var(Consist_f) ->
                   (Value == 0 ->
                       Actions = [exit, call(user:exec([Contrainte|Processus1]))]
                   ;
                       neg(Contrainte,Nc),
                       Actions = [exit, call(user:exec([Nc|Processus2]))]
                   )
                 ;
                   (Value == Consist_f ->
                       Actions = [fail]
                       ;
                       (Value == 0 ->
                           Actions = [exit, call(user:exec([Contrainte|Processus1]))]
                       ;
                           neg(Contrainte,Nc),
                           Actions = [exit, call(user:exec([Nc|Processus2]))]
                       )
                   )
               )
            ;
                evolution_list(Lvar_i,Lvar_name_i,Lvar_f,Lvar_name_f),
                Actions = []
            )
        ;
            Actions = [fail]
        ).
        



%--------
% Utils choose_decision/6
%--------

fix_point_choose_decision(Options,Num,Contrainte,Distribution,Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,Consist_f,Value,Env) :-
        domaine(Distribution,Lvar_Dom),
        fix_point_choose_decision_rec(Options,Num,Contrainte,Distribution,Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,
                                      Consist_f,Value,Lvar_Dom,Env).


% Recursive Call

% General case
fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,Consist_f,Value,Lvar_Dom,Env) :-
        var(Consist_i),
        !,
        narrow_non_uniform(Options,Value,[0,1],[W1,W2],U),
        (nonvar(Value) ->
            consist_2_branches(Num,Contrainte,Processus1,Processus2,Consist_i,Lvar_i,Lvar_name_i,Env),
            (var(Consist_i) ->
                union_domaine(Num,Lvar_i,Lvar_name_i)
            ;
                single_domaine(Num,Lvar_i,Lvar_name_i)
            ),
            domaine([W1,W2],Lvar_Dom2),
            (
              test_domaine(Lvar_Dom,Lvar_Dom2),
              Consist_i = Consist_f
            ;
              fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,
                                            Consist_f,Value,Lvar_Dom2,Env)
            )
        ;
            Consist_i = Consist_f   
        ).


% Case : Consist valuated (branch C^Process1 known inconsistant

fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,Consist_f,Value,Lvar_Dom,Env) :-
        Consist_i == 0,
        !,
        narrow_non_uniform(Options,Value,[0,1],[W1,W2],U),
        (nonvar(Value) ->
            neg(Contrainte,Nc),
            \+ (\+ (decr_K_flag(Env), Nc, exec(Processus2), assert_rec(Num,Lvar_i,Lvar_name_i))),
            single_domaine(Num,Lvar_i,Lvar_name_i),
            domaine([W1,W2],Lvar_Dom2),
        (
              test_domaine(Lvar_Dom,Lvar_Dom2),
              Consist_i = Consist_f
            ;
              fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,
                                            Consist_f,Value,Lvar_Dom2,Env)
            )
        ;
            Consist_i = Consist_f   
        ).


% Case : Consist valuated (branch Non C^Process2 known inconsistant

fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,Consist_f,Value,Lvar_Dom,Env) :-
        Consist_i == 1,
        !,
        narrow_non_uniform(Options,Value,[0,1],[W1,W2],U),
        (nonvar(Value) ->
            \+ (\+ (decr_K_flag(Env), Contrainte, exec(Processus1), assert_rec(Num,Lvar_i,Lvar_name_i))),
            single_domaine(Num,Lvar_i,Lvar_name_i),
            domaine([W1,W2],Lvar_Dom2),
        (
              test_domaine(Lvar_Dom,Lvar_Dom2),
              Consist_i = Consist_f
            ;
              fix_point_choose_decision_rec(Options,Num,Contrainte,[W1,W2],Processus1,Processus2,U,Consist_i,Lvar_i,Lvar_name_i,
                                            Consist_f,Value,Lvar_Dom2,Env)
            )
        ;
            Consist_i = Consist_f   
        ).


% consistency checking of the 2 branches (C^Processus1 or Not_C^Processus2)


consist_2_branches(Num, Contrainte, Processus1, Processus2, Consist_i, Lvar_i, Lvar_name_i, Env) :-
        \+ (\+ (decr_K_flag(Env), Contrainte, exec(Processus1), assert_rec(Num, Lvar_i, Lvar_name_i) )),
        !,
        neg(Contrainte, Nc),
        ( \+( \+ (decr_K_flag(Env), Nc, exec(Processus2), assert_rec(Num, Lvar_i, Lvar_name_i) ) ) 
        ;
            Consist_i = 1
        ).

consist_2_branches(Num, Contrainte, _Processus1, Processus2, Consist_i, Lvar_i, Lvar_name_i, Env) :-
        neg(Contrainte, Nc),
        \+ (\+ (decr_K_flag(Env), Nc, exec(Processus2), assert_rec(Num, Lvar_i, Lvar_name_i) ) ),
        Consist_i = 0.





%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Probabilistic Terminal Configuration                      %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Note : the nmuber of the iteration is fixed to 5000

nb_iteration(5000).

ptc([], _, []) :-
        !.

ptc(_Process, [], []) :-
        !.

ptc(Process, Lvar, Result) :-
        nb_iteration(Nb_Iteration),
        lvar_to_lvarname(Lvar, Lvar_name),
        ptc_rec(Process, Lvar, Lvar_name, Nb_Iteration, [], [], Result).

% recursive call

% Computation of the probabilities each contraint store

ptc_rec(_Process, _Lvar, _Lvar_name, 0 , L_Config_term, L_Denombrement, Result) :-
        !,
        nb_iteration(Nb_Iteration),
        normalize_proba(L_Config_term, L_Denombrement, Nb_Iteration, Result).
        


% Case : Process run is true

ptc_rec(Process, Lvar, Lvar_name, Nb_Iteration, L_Config_term, L_Denombrement, Result) :-
        \+ (\+ (exec(Process), assert_rec(Nb_Iteration, Lvar, Lvar_name))),
        !,
        config_term(Nb_Iteration, Lvar_name, L_Set),
        construct_result(L_Config_term, L_Denombrement, L_Set, L_Config_term2, L_Denombrement2),
        Nb_Iteration2 is Nb_Iteration-1,
        ptc_rec(Process, Lvar, Lvar_name, Nb_Iteration2, L_Config_term2, L_Denombrement2,Result).


% Case : Process run fails

ptc_rec(Process, Lvar, Lvar_name, Nb_Iteration, L_Config_term, L_Denombrement, Result) :-
        construct_result(L_Config_term, L_Denombrement, [no], L_Config_term2, L_Denombrement2),
        Nb_Iteration2 is Nb_Iteration-1,
        ptc_rec(Process, Lvar, Lvar_name, Nb_Iteration2, L_Config_term2, L_Denombrement2, Result).





%-------------
% Utils ptc/3
%-------------

% Normalization of the probabilities associate to each constraint stores
        
normalize_proba([], [], _, []) :-
        !.
 

normalize_proba([Config_term|Rest_config], [Den|Rest_den], N_iteration, [[Config_term, Proba]|Rest_result]) :-
        Proba is Den/N_iteration,
        normalize_proba(Rest_config,Rest_den, N_iteration, Rest_result).


% Buiding of L_Set 

config_term(_, [],[]) :-
        !.

config_term(N, [Name|Rest_name], [Set|Rest_set]) :-
        retract(dom(N, Name, Set)),
        !,
        retractall(dom(N, Name, _)),
        config_term(N, Rest_name, Rest_set).



% construct_result : add new L_set to L_config_term else update to one L_Denombrement

construct_result([], [], L_set, [L_set],[1]) :-
        !.

construct_result(L_config_term, L_denombrement, L_set, L_config_term, L_denombrement_new) :-
       lists:member(L_set, L_config_term),
        !,
       lists:nth(N, L_config_term, L_set),
       incr_N(N, L_denombrement, L_denombrement_new).
        
construct_result(L_config_term, L_denombrement, L_set, [L_set|L_config_term], [1|L_denombrement]).




% Increment the Nth element of z lst

incr_N(1, [T|Rest], [T1|Rest]) :-
        !,
        T1 is T+1.

incr_N(N, [T|Rest], [T|Result]) :-
        N1 is N-1,
        incr_N(N1, Rest, Result).



% normalized : building of the probability to attempt a consistent store



normalized(List,Result) :-
        delete_unif(List,[[no],Proba],List2),
        (Proba ==1 ->
            normalized_rec(List2,1,Result)
        ;
            Proba1 is 1-Proba, 
            normalized_rec(List2,Proba1,Result)
        ).

normalized_rec(Result,1,Result) :-
        !.

normalized_rec([],_Proba,[]) :-
        !.

normalized_rec([[Domain,Prob]|Rest_List],Proba,[[Domain,Prob2]|Rest]) :-
        !,
        Prob2 is Prob/Proba,
        normalized_rec(Rest_List,Proba,Rest).



delete_unif([],_Elt,[]) :-
        !.

delete_unif([Elt|Rest],Elt,Rest_Result) :-
        !,
         delete_unif(Rest,Elt,Rest_Result).

delete_unif([T|Rest],Elt, [T|Rest_Result]) :-
        delete_unif(Rest,Elt, Rest_Result).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Experimental Validation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

nb_it(5000).

ev(Domain-Distribution,Result) :-
        nb_it(Nb),
        asserta(user:counter(0)),
        (
          labeling([],Distribution),
          ev_rec(Domain-Distribution,1,Nb,[],R),
          retract(counter(N)),  
          asserta(user:exp_val(N,R)),
          N1 is N+1,
          asserta(user:counter(N1)),
          fail
        ;
          retract(user:counter(N2)),
          N3 is N2-1,
          construction_validation_f(N3,Result)
        ).
        
ev_rec(_Domain-_Distribution,Nb,Nb,R,R) :-
        !.

ev_rec(Domain-Distribution,Nb1,Nb,R_Inter,R) :-
        init_env(Env),
        choose(X,Domain-Distribution,[],[domain_bound,rv(U)],Env),
        fin_env(Env),
        construct_validation(X,U,R_Inter,R_Inter2),
        Nb2 is Nb1+1,
        ev_rec(Domain-Distribution,Nb2,Nb,R_Inter2,R).


%-------
% Utils ev/3
%------

construct_validation(X,U,[],[(X,[U,U])]) :-
        !.

construct_validation(X,U,[(X,[Min,Max])],[(X,[Min2,Max2])]) :-
        !,
        Min2 is min(Min,U),
        Max2 is max(Max,U).

construct_validation(X,U,[T],[T,(X,[U,U])]) :-
        !.


construct_validation(X,U,[(X,[Min,Max])|Q],[(X,[Min2,Max2])|Q]) :-
        !,
        Min2 is min(Min,U),
        Max2 is max(Max,U).

construct_validation(X,U,[T|Q],[T|Q2]) :-
        construct_validation(X,U,Q,Q2).

                    
  

construction_validation_f(N,Result) :-
        construction_validation_f(N,[],Result).

construction_validation_f(0,Result,Result) :-
        !.

construction_validation_f(N,[],Result) :-
        !,
        retract(user:exp_val(N,R)),
        N1 is N-1,
        construction_validation_f(N1,R,Result).


construction_validation_f(N,R,Result) :-
        retract(user:exp_val(N,R1)),
        compare_validation(R,R1,R2),
        N1 is N-1,
        construction_validation_f(N1,R2,Result).


compare_validation([],_R,[]) :-
        !.


compare_validation([(X,[Min1,Max1])|Q],R2,[(X,[Min3,Max3])|Suite]) :-
        lists:member((X,[Min2,Max2]),R2),
        Min3 is min(Min1,Min2),
        Max3 is max(Max1,Max2),
        compare_validation(Q,R2,Suite).


        
        





%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%   Blocking Ask                                             %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



blocking_ask(Constraint,Process) :-
	terms:term_variables(Constraint, Lvar),
	lvar_to_ldom(Lvar, Ldom),
	fd_global(blocking_ask(Constraint,Process), [], Ldom).


clpfd:dispatch_global(blocking_ask(Constraint, Process),[], [], Actions) :-
	blocking_ask_solver(Constraint, Process, Actions).


% appel du solver de blocking ask

blocking_ask_solver(Constraint, Process, Actions) :-
	ask(Constraint),
        !,
        Actions = [exit,call(user:exec(Process))].


blocking_ask_solver(Constraint, _Process, Actions) :-
        neg(Constraint,NC),
        ask(NC),
        !,
        Actions = [exit].

blocking_ask_solver(_ , _, []).








%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Utils                                         %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%-----------
% Common Predicate
%-----------




% exec(++)
% process run

exec([]) :-
        !.


exec([X|Rest]) :-
	X,
	exec(Rest).


% ask(++)
% ask(C) is true iff  not C is not entailled by the constraint store

ask(C) :-
	neg(C, C2),
	C2,
	!,
	fail.

ask(_C).


% arithmetic constraint negation

neg((A) #/\ (B), (NA) #\/ (NB)) :-
        neg(A,NA),
        neg(B,NB).
neg((A) #\/ (B), (NA) #/\ (NB)) :-
        neg(A,NA),
        neg(B,NB).

neg(A #= B, A #\= B).
neg(A #\= B, A #= B).
neg(A #> B, A #=< B).
neg(A #=< B, A #> B).
neg(A #< B, A #>= B).
neg(A #>= B, A #< B).


%-----------
% Env Managing                                 
%-----------

% Deep of consitency checking

max_flag(1).


%% init_env(--)
%% initialisation de l'environnement

init_env(Env) :-
        nonvar(Env),
        !,
        write('erreur :l environnement doit être une variable'),
        fail.


init_env(Env) :-
        put_atts(Env,[+(numerotation(0)), +(k_flag(Max_K_flag, Max_K_flag))]),
        max_flag(Max_K_flag).

% Note : not used 
fin_env(_Env).


%% gestion de la numérotation des choose

get_num(Env,Num) :-
        get_atts(Env, +(numerotation(Num))),
        Num1 is Num + 1,
        put_atts(Env, +(numerotation(Num1))).
                



%% decr(++)
%% decr(Env) est vrai ssi on a décrémenté le k_flag

decr_K_flag(Env) :-
        get_atts(Env,+(k_flag(K, Max_K_Flag))),
        K1 is K-1,
        put_atts(Env, +(k_flag(K1, Max_K_Flag))).



%----------
% Awakenning List
%----------




% lvar_to_dom(++,--)
% lvar_to_dom(L,Lvar_Name,Lvar_Dom) is true iff lvar_to_lvarname(l,Lvar_Name) is true and lvar_to_ldom(L,Lvar_Dom) is true 

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

lvar_to_dom([Var|Rest_var],[Var_name|Rest_name],[dom(Var)|Rest_dom]) :-
        charsio:write_to_chars(Var,L),
	name(Var_name,L),
        lvar_to_dom(Rest_var,Rest_name,Rest_dom).
        

% lvar_to_val(++,--)
% lvar_to_val(L,Lvar_Name,Lvar_Dom) is true iff lvar_to_lvarname(l,Lvar_Name) is true and lvar_to_lval(L,Lvar_Dom) is true 

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

lvar_to_val([Var|Rest_var],[Var_name|Rest_name],[val(Var)|Rest_dom]) :-
        charsio:write_to_chars(Var,L),
	name(Var_name,L),
        lvar_to_val(Rest_var,Rest_name,Rest_dom).


% lvar_to_ldom(++, --)
% lvar_to_ldom([X,Y,...], L) est vrai ssi L = [dom(X), dom(Y), ...] 

lvar_to_ldom([],[]).

lvar_to_ldom([Var|Rest_var],[dom(Var)|Rest_dom]) :-
	lvar_to_ldom(Rest_var, Rest_dom).




% lvar_lval : idem lvar_to_ldom sauf que dom(X) devient val(X)

lvar_to_lval([],[]).

lvar_to_lval([Var|Rest_var],[val(Var)|Rest_dom]) :-
	lvar_to_lval(Rest_var, Rest_dom).




% lvar_to_lvarname(++, --)
% lvar_to_lvarname([X,Y, ..] Lvarname est vrai ssi Lvarname = [ 'X','Y', ...]


lvar_to_lvarname([], []).

lvar_to_lvarname([Var|Rest],[Var_name|Rest_name]) :-
	charsio:write_to_chars(Var,L),
	name(Var_name,L),
	lvar_to_lvarname(Rest,Rest_name).


% diff_list_variables((List,Q),Lvar) is true iff Lvar is the set of the variaviable ofList private to Q

diff_list_variables(Q-Q,[]) :-
        var(Q),
        !.

diff_list_variables(Distribution-Q,[]) :-
        var(Distribution),
        var(Q),
        !.


diff_list_variables([X|Reste]-Q,[X|Rest_Lvar]) :-
        var(X),
        !,
        diff_list_variables(Reste-Q,Rest_Lvar).


diff_list_variables([_X|Reste]-Q,Lvar) :-
        !,
        diff_list_variables(Reste-Q,Lvar).


diff_list_variables(_Distribution-_Q,[]).

evolution_list([],[],[],[]).


evolution_list([T_init|Rest_init],[T_name_init|Rest_name_init], [T_init|Rest_final], [T_name_init|Rest_name_final]) :-
        var(T_init),
        !,
        evolution_list(Rest_init, Rest_name_init, Rest_final, Rest_name_final).



evolution_list([_T_init|Rest_init],[_T_name_init|Rest_name_init], Rest_final, Rest_name_final) :-
        evolution_list(Rest_init, Rest_name_init, Rest_final, Rest_name_final).
        





%-----------
% Domain recording
%-----------


% assert_rec records the domain of Lvar variables

assert_rec(_, [], []).

assert_rec(Num, [Var|Rest_var], [Var_name|Rest_var_name]) :-
	clpfd:fd_set(Var, Set),
	asserta(user:dom(Num, Var_name, Set)),
	assert_rec(Num, Rest_var, Rest_var_name).




% union_domaine(Num,Lvar,Lvar_Name) : Union of the two asserted domains during consistency checking

union_domaine(_Num, [], []).
union_domaine(Num, [Var|Rest_var], [NomVar|Rest_var_name]):-
        union_domaine_rec(Num, Var, NomVar),
        union_domaine(Num, Rest_var, Rest_var_name).


% Union the two domain

union_domaine_rec(Num, Var, NomVar):-
        retract(dom(Num, NomVar, Set1)),
        retract(dom(Num, NomVar, Set2)),
        !,
        retractall(dom(Num, NomVar,_)),
        clpfd:fdset_union([Set1,Set2],UnionDomaine),
	clpfd:fdset_to_range(UnionDomaine,R),
        Var in R.


% single_domaine(Num,Lvar,Lvar_Name) : constraints the constraint store with an asserted domains of Lvar.

single_domaine(_Num, [], []) :-
        !.
single_domaine(Num, [Var|Rest_var], [Nomvar| Rest_var_name]) :-
        single_domaine_rec(Num, Var , Nomvar),
        single_domaine(Num, Rest_var, Rest_var_name).



%% single_domaine_rec(++, ++, ++).
%% single_domaine_rec(Num, Var, NomVar).

single_domaine_rec(Num, Var, NomVar):-
        retract(dom(Num, NomVar, Set)),
        !,
        retractall(dom(Num, NomVar,_)),
	clpfd:fdset_to_range(Set, R),
        Var in R.


%------
% Domain Comparaison
%------


% domaine_range : idem domain mais avec des couples Min Max

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

domaine([X],[(Xmin,Xmax)]) :-
        !,
        fd_min(X, Xmin),
        fd_max(X, Xmax).

domaine([X|Rest], [(Xmin, Xmax)| Rest_bornes]) :-
        fd_min(X, Xmin),
        fd_max(X, Xmax),
        domaine(Rest, Rest_bornes).




% test_domaine ssi les 2 list de domaines sont égaux

test_domaine([(Min,Max)],[(Min2,Max2)]) :-
        Min == Min2,
        Max == Max2,
        !.


test_domaine([(Min,Max)|Rest],[(Min2,Max2)|Rest2]) :-        
        Min == Min2,
        Max == Max2,
        !,
        test_domaine(Rest,Rest2).

test_domaine(_Lvar_Dom,_Lvar_Dom2) :-
        fail.



%-----------
% Domain Pruning
%-----------

% Information exploites : X is in the range [Xmin,Xmax]

intervalle(X,Xmin, Xmax) :-
        !,
        leq(Xmin,X),
        leq(X, Xmax),
        leq(Xmin, Xmax).



% leq(--, --)

leq(X,Y) :-
        domaine([X,Y], [(Xmin,Xmax), (Ymin,Ymax)]),
        minimum_borne_infini(Xmax, Ymax, Xmax2),
        maximum_borne_infini(Xmin, Ymin, Ymin2),
        X in Xmin..Xmax2,
        Y in Ymin2..Ymax.


le(X,Y) :-
        domaine([X,Y], [(Xmin,Xmax), (Ymin,Ymax)]),
        minimum_borne_infini(Xmax, Ymax, Xmax2),
        borne_sup(Xmax,Ymax,Xmax2,Xmax3),
        maximum_borne_infini(Xmin, Ymin, Ymin2),
        borne_inf(Xmin,Ymin,Ymin2,Ymin3),
        X in Xmin..Xmax3,
        Y in Ymin3..Ymax.


% min_inf(X) = fd_min(X) \/ inf

min_inf(X,Min) :-
        clpfd:fd_min(X,Min),
        !.

min_inf(_X,inf).


% max_inf(X) = fd_max(X) \/ sup

max_sup(X,Max) :-
        clpfd:fd_max(X,Max),
        !.

max_sup(_X,sup).


% maximum avec des bornes inf et sup
% maximum_borne_infini(++, ++, ?)

maximum_borne_infini(_ , sup, sup) :-
        !.

maximum_borne_infini(sup, _, sup) :-
        !.

maximum_borne_infini(inf, X, X) :-
        !.

maximum_borne_infini(X, inf, X) :-
        !.

maximum_borne_infini(X, Y, Y):-
        X=<Y,
        !.

maximum_borne_infini(X, _Y, X).



% minimum avec des bornes inf et sup
% minimum_borne_infini(++, ++, ?)

minimum_borne_infini(inf, _, inf) :-
        !.

minimum_borne_infini(_ , inf, inf) :-
        !.

minimum_borne_infini(sup, X, X) :-
        !.

minimum_borne_infini(X, sup, X) :-
        !.


minimum_borne_infini(X, Y, X) :-
        X=<Y,
        !.

minimum_borne_infini(_X, Y, Y).




% permet de calculer la borne sup de X pour X<Y selon le resultat de min(Xmax,Ymax)

borne_sup(_Xmax,_Ymax,sup,sup) :-
        !.

borne_sup(_Xmax,Ymax,Ymax,Xmax2) :-
        !,
        Xmax2 is Ymax-1.


borne_sup(Xmax,_Ymax,Xmax,Xmax) :-
        !.



% permet de calculer la borne inf de Y pour X<Y selon le resultat de max(Xmin,Ymin)

borne_inf(_Xmin,_Ymin,inf,inf) :-
        !.

borne_inf(Xmin,_Ymin,Xmin,Ymin2) :-
        !,
        Ymin2 is Xmin+1.

borne_inf(_Xmin,Ymin,Ymin,Ymin).





%   Information : X is drawn according to Distribution

distribution(_X,_Min,P-P) :-
        var(P),
        !.
        
distribution(X,Min,[W|Reste_Distribution]-P) :-
        var(P),
        clpfd:fd_set(W,Set_W),
        clpfd:fdset_singleton(Set_W,0),
        !,
        clpfd:fd_set(X,Set_X),
        clpfd:fdset_del_element(Set_X,Min,Set_X_new),
        clpfd:fdset_to_range(Set_X_new,Range_X),
        X in Range_X,
        Min2 is Min+1,
        distribution(X,Min2,Reste_Distribution-P).

distribution(X,Min,[_W|Reste_Distribution]-P) :-
        var(P),
        !,
        Min2 is Min+1,
        distribution(X,Min2,Reste_Distribution-P).

% cas où la liste est instanciée


distribution(_X,_Min,[]-[]) :-
        !.
        
        
distribution(X,Min,[W|Reste_Distribution]-[]) :-
        clpfd:fd_set(W,Set_W),
        clpfd:fdset_singleton(Set_W,0),
        !,
        clpfd:fd_set(X,Set_X),
        clpfd:fdset_del_element(Set_X,Min,Set_X_new),
        clpfd:fdset_to_range(Set_X_new,Range_X),
        X in Range_X,
        Min2 is Min+1,
        distribution(X,Min2,Reste_Distribution-[]).



distribution(X,Min,[_W|Reste_Distribution]-[]) :-
        Min2 is Min+1,
        distribution(X,Min2,Reste_Distribution-[]).




%-----------
% Random draw
%-----------


% Domain is Range 


random_draw(Options,U,[Xmin,Xmax],['uniform'],Value) :-
        lists:member('range',Options),
        !,
        W is Xmax-Xmin+1,
        Value is Xmin+integer(floor(U*W)).
       
        
random_draw(Options,U,[Xmin,Xmax],Distribution,Value) :-
        lists:member('range',Options),
        !,
        lists:sum_list(Distribution,W),
        U1 is U*W,
        random_draw_rec(['range'],U1,[Xmin,Xmax],Distribution,Value).


% Default case

random_draw(Options,U,Domain,Distribution,Value) :-
        lists:member(weight(W),Options),
        !,
        U1 is U*W,
        random_draw_rec([],U1,Domain,Distribution,Value).


random_draw(_Options,U,Domain,Distribution,Value) :-
        lists:sum_list(Distribution,W),
        U1 is U*W,
        random_draw_rec([],U1,Domain,Distribution,Value).


random_draw_rec(['range'],_U,[Value,Value],[_P],Value) :-
        !.

random_draw_rec(['range'],U,[Value|_Domain],[Proba|_Distribution],Value) :-
        U<Proba,
        !.

random_draw_rec(['range'],U,[Xmin,Xmax],[Proba|Distribution],Value) :-
        !,
        U1 is U-Proba,
        Xmin1 is Xmin+1,
        random_draw_rec(['range'],U1,[Xmin1,Xmax],Distribution,Value).



random_draw_rec([],_U,[],[],_Value).

random_draw_rec([],U,[Value|_Domain],[Proba|_Distribution],Value) :-
        U<Proba,
        !.

random_draw_rec([],U,[_V|Domain],[Proba|Distribution],Value) :-
        U1 is U-Proba,
        random_draw_rec(_Options,U1,Domain,Distribution,Value).
        

% Domain = Range

%%%random_draw(Options,[Xmin,Xmax],_Distribution,Value) :-
%%%        lists:member('range',Options),
%%%        Xmin==Xmax,
%%%        !,
%%%        Value = Xmin.

%%%% mirror : first value for Distribution = probability to draw Xmax

%%%random_draw(Options,[_Xmin,Xmax],Distribution,Value) :-
%%%        lists:member('range',Options),
%%%        lists:member('mirror',Options),
%%%        !,
%%%        random:random(Rand),
%%%        lists:sum_list(Distribution,Weights),
%%%        random_draw_range_rec(['mirror'],Xmax,Distribution,Weights,Rand,Value).



%%%random_draw(Options,[Xmin,_Xmax],Distribution,Value) :-
%%%        lists:member('range',Options),
%%%        !,
%%%        random:random(Rand),
%%%        lists:sum_list(Distribution,Weights),
%%%        random_draw_range_rec([],Xmin,Distribution,Weights,Rand,Value).

%%%% Domain = List of Values


%%%random_draw(_Options,Domain,Distribution,Value) :-
%%%        random:random(Rand),
%%%        lists:sum_list(Distribution,Weights),
%%%        random_draw_rec([],Domain,Distribution,Weights,Rand,Value).


%%%% Recursive Call Domain = Range

%%%random_draw_range_rec(_Options,Value,[_Pi],_Weights,_Rand,Value) :-
%%%        !.

%%%random_draw_range_rec(_Options,Value,[Pi|_Q],Weights,Rand,Value) :-
%%%        Rand<Pi/Weights,
%%%        !.


%%%random_draw_range_rec(['mirror'],Xmax,[Pi|Q],Weights,Rand,Value) :-
%%%        !,
%%%        Rand2 is Rand-(Pi/Weights),
%%%        Xmax2 is Xmax-1,
%%%        random_draw_range_rec(['mirror'],Xmax2,Q,Weights,Rand2,Value).


%%%random_draw_range_rec([],Xmin,[Pi|Q],Weights,Rand,Value) :-
%%%        Rand2 is Rand-(Pi/Weights),
%%%        Xmin2 is Xmin+1,
%%%        random_draw_range_rec([],Xmin2,Q,Weights,Rand2,Value).


%%%% Recursive call : Domain = List of Values 

%%%random_draw_rec([],[Value],_Distribution,_Weights,_Rand,Value) :-
%%%        !.


%%%random_draw_rec([],[Value|_Rest_Domain],[W|_Rest_Distribution],Weights,Rand,Value) :-
%%%        Rand <W/Weights,
%%%        !.


%%%random_draw_rec([],[_V|Rest_Domain],[W|Rest_Distribution],Weights,Rand,Value) :-
%%%        Rand2 is Rand - (W/Weights),
%%%        random_draw_rec([],Rest_Domain,Rest_Distribution,Weights,Rand2,Value).


%%%%%%%%%%%%%%%%%%%%%%
% Diverse
%%%%%%%%%%%%%%%%%%%%%


% quicksort_distribution(++,++,--,--)

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

quicksort_distribution([X],[P],[X],[P]) :-
        !.

quicksort_distribution([X|Suite],[P|Suite2],Liste,Distribution) :-
        pivot(X,Suite,Suite2,Liste_Inf,Distrib_Inf,Liste_Sup,Distrib_Sup),
        quicksort_distribution(Liste_Inf,Distrib_Inf,L1,D1),
        quicksort_distribution(Liste_Sup,Distrib_Sup,L2,D2),
        lists:append(L1,[X|L2],Liste),
        lists:append(D1,[P|D2],Distribution).


pivot(_Val,[],[],[],[],[],[]) :-
        !.

pivot(Val,[X|Suite],[P|Suite2],[X|Suite_Inf],[P|Distrib_Inf],Liste_Sup,Distrib_Sup) :-
        X<Val,
        !,
        pivot(Val,Suite,Suite2,Suite_Inf,Distrib_Inf,Liste_Sup,Distrib_Sup).


pivot(Val,[X|Suite],[P|Suite2],Liste_Inf,Distrib_Inf,[X|Suite_Sup],[P|Distrib_Sup]) :-
        pivot(Val,Suite,Suite2,Liste_Inf,Distrib_Inf,Suite_Sup,Distrib_Sup).


        

      
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%       Tests
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



test(R) :-
        P1 in 1..2,
        P2 =2,
        P3 =2,
        P4 =2,
        P5 =2,
        P6 in 2..4,
        ev([1,2,3,4,5,6]-[P1,P2,P3,P4,P5,P6],R).
