% data of a variable in active,
% currently Data = [Method,Fd,Needs,Reqred]
% Needs = [(Var,Constr)|T]
% Method = (Method,{Fd,(Fd,Var,Constr,Prob)}

data_choices_for_label_n( [], [], [], [] ).
data_choices_for_label_n( [_H-Datah|T], [Mt|Mts],[Fd|Fds],[Nd|Nds] ) :-
	data_choices( [mtd(Mt),dmn(Fd),nds(Nd)], Datah ),
	data_choices_for_label_n( T, Mts, Fds, Nds ).

data_choices( [H|T], Data ) :-
	data_choose( H, Data ),
	data_choices( T, Data ).
data_choices( [], _Data ).

data_choose( mtd(Method), [Method,_Fd,_Needs,_ReqBy] ).
% see data_update( dmn(), ... )
data_choose( dmn(Fd), [_Method,Fd,_Needs,_ReqBy] ).
data_choose( nds(Needs), [_Method,_Fd,Needs,_ReqBy] ).
data_choose( rqr(ReqBy), [_Method,_Fd,_Needs,ReqBy] ).

data_update( mtd(Method), [_OldMtd,Fd,Needs,ReqBy], [Method,Fd,Needs,ReqBy] ).
data_update( dmn(Fd), [Method,_OldFd,Needs,ReqBy], [Method,Fd,Needs,ReqBy] ).
% alternatively change the auxil.pl aux_map_variables/3. 2000/02/24
% data_update( dmn(Fd), [_Method,_OldFd,_Needs,_ReqBy], Fd ).
data_update( nds(Needs), [Method,Fd,_OldNds,ReqBy], [Method,Fd,Needs,ReqBy] ).
data_update( rqr(ReqBy), [Method,Fd,Needs,_OldRqr], [Method,Fd,Needs,ReqBy] ).

data_swap( mtd(OldM), [OldM,Fd,Needs,ReqBy], NewM, [NewM,Fd,Needs,ReqBy] ).
data_swap( dmn(OldFd), [Method,OldFd,Needs,ReqBy], NewFd, [Method,NewFd,Needs,ReqBy] ).
data_swap( nds(OldN), [Method,Fd,OldN,ReqBy], NewN, [Method,Fd,NewN,ReqBy] ).
data_swap( rqr(OldR), [Method,Fd,Needs,OldR], NewR, [Method,Fd,Needs,NewR] ).

data_needs_is_empty( [] ).

data_update_dmns( [], [], [] ).
data_update_dmns( [H|T], [HVr-HDat|TDat], [HVr-HUp|TUp] ) :-
	ensure_list( H, HList ),
	data_update( dmn(HList), HDat, HUp ),
	data_update_dmns( T, TDat, TUp ).

% data_needs_satisfy( Var, Val, Fd, Needs, NewFd, RemNeeds ) :-
% Var has value Val and is required by the variable to which
% Fd and Needs, belong, NewFd and RemNeeds are the results of
% applying the relavant constraint in Needs that involves
% the two variables and the remaining dependant constraints
% for the variable left in RemNeeds.
%
data_needs_satisfy( Var, Val, MethodStr, Fd, Needs, NewFd, RemNeeds ) :-
	% select( Var-NdCnstr, Needs, RemNeeds ), 
	select( Var-NdCnstr, Needs, RemNeeds ),  % swi
	!,
	( MethodStr=(_Method,Var,_Constr,_Prob) ->
		NewFd = Fd
		;
		apply_conditional( NdCnstr, Val, Fd, NewFd )
	).

apply_conditional( cond_diff, Elem, Set, RedSet ) :-
	ord_del_element( Set, Elem, RedSet ).

% 19990721

apply_conditional( (RfFn-RfVl,DpFn-DpVl), Elem, Set, RedSet ) :-
	% ( apply_conditional_1( RfFn, RfVl, Elem ) -> 
	( unconditional_satisfied_1(RfFn,RfVl,Elem) -> 
		apply_conditional_rev( DpFn, DpVl, Set, RedSet )
		;
		RedSet = Set
	).

% apply_conditional_rev( CnFn, CnVal, OthVal,  ).
apply_conditional_rev( diff, CnVal, Set, RedSet ) :-
	!,
	( ord_del_element( Set, CnVal, RedSet ) ->
		true
		;
		RedSet = Set
	).
apply_conditional_rev( eq, CnVal, Set, RedSet ) :-
	memberchk( CnVal, Set ),
	RedSet = [CnVal].

% end addition

% data_add_needs( DepVar, Constr, Var, DpVDt, NewDpVDt ) :-
% add the information, that DepVar needs Constr-Var, in its data 
% part, DpVDt. Yielding NewDpVDt data part.
%
data_add_needs( Constr, Var, DpVDt, NewDpVDt ) :-
	data_swap( nds(Needs), DpVDt, NewNeeds, NewDpVDt ),
	ord_add_element( Needs, Var-Constr, NewNeeds ).

% data_add_required( Var, DepVar, Constr, VarDt, Roots, NewVDt, NewRoots ) :-
% DepVar is added in the required data part (of Var), VarDt to NewVDt.
% DepVar is eliminated, if it exists, from Roots, yielding NewRoots.
% Constr is not used currently.
%
data_add_required( DepVar, _Constr, VarDt, Roots, NewVDt, NewRoots ) :-
	data_swap( rqr(ReqBy), VarDt, NewReqBy, NewVDt ),
	ord_add_element( ReqBy, DepVar, NewReqBy ),
	( ord_del_element(Roots,DepVar,NewRoots) -> 
		true
		;
		NewRoots = Roots
	).

probe_parts( V-[Fd,Prs,ReqBy], V, Fd, Prs, ReqBy ).
	
probed_find( Var, Probed, Val ) :-
	memberchk( Var-Val, Probed ).

% bi_unconditional_constraint( +Constr, -Constr, Var, Val ) :-
% this is targeted to (each of the two) sides of the condiotional,
% at putting in the store point. (as oppose to enforce time.)
%
bi_unconditional_constraint(  diff(Var1,Var2), diff, Vars, Vals ) :-
	args_to_pfd_vars_and_not( Var1, Var2, diff, Vars, Vals ).
bi_unconditional_constraint(  eq(Var1,Var2), eq, Vars, Vals ) :-
	args_to_pfd_vars_and_not( Var1, Var2, eq, Vars, Vals ).

% args_to_pfd_vars_and_not( ?Item1, ?Item2, -Var, -Val ).
% Succeeds iff one of Items is a pfd variable and 
args_to_pfd_vars_and_not( Item1, Item2, Fnctr, Var, Val ) :- 
	( pfd_var(Item1) -> 
		( pfd_var(Item2) -> 
			Goal =.. [Fnctr,Item1,Item2],
			print_message( error, pfd(8,consistency_error(
				Goal, Item1, Item2, 'Only one of operands should be a Pfd variable.')) )
			;
			Var = Item1,
			Val = Item2
		)
		;
		( pfd_var(Item2) -> 
			Var = Item2, 
			Val = Item1
			;
			Goal =.. [Fnctr,Item1,Item2],
			print_message( error, pfd(8,consistency_error(
				Goal, Item1, Item2, 'At least one of operands should be a Pfd variable.')) )
		)
	).

pfd_predicate( PfdPred, PrologPred, PfdVars, PlaceVars ) :-
	pfd_predicate_1( PfdPred, PrologPred, [], [], PfdVars, PlaceVars ),
	( var(PrologPred) -> 
		write_message( error, instantiation_error(p(PfdPred)), 1 )
		;
		true
	).

pfd_predicate_1( Var, Prolog, AccPfds, AccPrlgs, Pfds, Prlgs ) :-
	var(Var),
	!,
	Prolog = Var,
	Prlgs = AccPrlgs,
	Pfds = AccPfds.
pfd_predicate_1( PfdVar, Prolog, AccPfds, AccPrlgs, Pfds, Prlgs ) :-
	pfd_var( PfdVar ),
	!,
	Pfds = [PfdVar|AccPfds],
	Prlgs= [Var|AccPrlgs],
	Prolog=Var.
pfd_predicate_1( [], [], Pfds, Prlgs, Pfds, Prlgs ) :-
	!.
pfd_predicate_1( [H|T], [HPg|TPg], AccPfds, AccPrlgs, Pfds, Prlgs ) :-
	!,
	pfd_predicate_1( H, HPg, AccPfds, AccPrlgs, NxPfds, NxPrlgs ),
	pfd_predicate_1( T, TPg,  NxPfds,  NxPrlgs,   Pfds,   Prlgs ).
pfd_predicate_1( PfdTerm, Prolog, AccPfds, AccPrlgs, Pfds, Prlgs ) :-
	PfdTerm =.. [Functr|Args],
	pfd_predicate_1( Args, PrologArgs,  AccPfds, AccPrlgs, Pfds, Prlgs ),
	Prolog =.. [Functr|PrologArgs].

unconditional_constraint( Cnstr, RealCnstr, PfdVars, PlaceVars ) :-
	Cnstr =.. [Op|Oprds],
	unconditional_pfd_to_sicstus_op( Op, RealOp ),
	copy_pfd_uncond_operands( Oprds, Cnstr, 1, OprdsCopy, PfdVars, PlaceVars ),
	RealCnstr =.. [RealOp|OprdsCopy].

copy_pfd_uncond_operands( [], _Gl, _N, [], [], [] ).
copy_pfd_uncond_operands( [H|T], Gl, N, [TrH|TrT], PfdVs, PlaceVs ) :-
	( pfd_var(H) -> 
		TrH = FreshVar,
		PlaceVs = [FreshVar|MrPlaceVs],
		PfdVs = [H|MrPfdVs]
		;
		( var(H) ->
			print_message( error, instantiation_error(Gl,N) )
			;
			TrH = H,
			PlaceVs = MrPlaceVs,
			PfdVs = MrPfdVs
		)
	),
	NxtN is N + 1,
	copy_pfd_uncond_operands( T, Gl, NxtN, TrT, MrPfdVs, MrPlaceVs ).

unconditional_pfd_to_sicstus_op( Op, RealOp ) :-
	( Op == '='	-> 
		RealOp = '=='
		;
		( Op == '#'	-> 
			RealOp = '\\=='
			;
			RealOp = Op
		)
	).

%
conditional_constraint( cond_diff(Var1,Var2), [cond_diff,Var1,Var2] ).

% ok this is the minimum check
pfd_var( PfdVar  ) :-
	atom( PfdVar ),
	atom_codes( PfdVar, PfdVarCs ),
	append( _Some, "_", PfdVarCs ).

is_val( Val ) :-
	atomic( Val ).

mustbe_var( Var ) :-
	( var( Var ) ->
		true
		;
		% this is an internal error. The whole predicate should not be used (maybe)
		% once the system is stable.
		print_message( error, type_error(must_var(Var),1,prolog_variable,Var) )
	).
