% predicates auxiliary to demo.
%
aux_body_and_goals( Body, RestGoals, NewGoals ) :-
	append( Body, RestGoals, NewGoals ).

aux_goal_select( [Goal|Goals], Goal, Goals ).

aux_map_variables( InList, Active, OutList ) :-
	aux_map_vars( InList, Active, RecList ),
	flatten( RecList, FlatList ),
	keysort( FlatList, OutList ).

aux_map_vars( [PrlVAtom=PfdVOr|Vs], Active, [PrlVAtom-MapVal,Subs|PfdVs] ) :-
	( pfd_var(PfdVOr) -> 
		aux_pfd_var_to_vals( PfdVOr, Active, MapVal )
		;
		% then this wasnt instantiated to a pfd var,
		( compound( PfdVOr ) -> 
			% prolog variable has become a structure
			% which may include pfdvars that didnot appear
			% in the original query
			( is_list(PfdVOr) -> 
				aux_map_args( PfdVOr, Active, Subs )
				;
				PfdVOr =.. [_Name|Args],
				aux_map_args( Args, Active, Subs )
			),
			% MapVal =.. [Name|ActArgs]
			MapVal = PfdVOr
			;
			% generic, ie Var remains a var or is atomic
			MapVal = PfdVOr, 
			Subs = []
		)
	),
	aux_map_vars( Vs, Active, PfdVs ).
aux_map_vars( [], _Active, [] ).

aux_map_args( [], _Active, [] ).
aux_map_args( [H|T], Active, Subs ) :-
	( pfd_var(H) -> 
		aux_pfd_var_to_vals( H, Active, HDom ),
		Subs = [H-HDom|TSubs]
		;
		( compound(H) -> 
			( is_list(H) -> 
				aux_map_args( H, Active, HSubs )
				;
				H =.. [_Name|Args],
				aux_map_args( Args, Active, HSubs )
			),
			Subs = [HSubs|TSubs]
			;
			TSubs = Subs
		)
	),
	aux_map_args( T, Active, TSubs ).

aux_pfd_var_to_vals( PfdVar, Active, ItsPossVals ) :-
	key_member( PfdVar, ItsActData, Active ), 
	!,
	( atomic(ItsActData) -> 
		ItsPossVals = ItsActData
		;
		data_choose( dmn(ItsPossVals), ItsActData )
	).
aux_pfd_var_to_vals( PfdVar, Active, PVals ) :-
	throw( pfd(internal(no_data_for_var(PfdVar,Active,PVals))) ).

key_member( Key, El, [HKey-HEl|T] ) :-
	( Key @< HKey ->
		fail
		;
		( Key == HKey -> 
			El = HEl
			;
			key_member( Key, El, T )
		)
	).


aux_goal_tuple_to_list( (A,B), [PfdA|T] ) :-
	expand_pfd_goal( A, PfdA ),
	!,
	aux_goal_tuple_to_list( B, T ).
aux_goal_tuple_to_list( A, [PfdA] ) :-
	expand_pfd_goal( A, PfdA ),
	!.

% this should work, but double check due to,
% H @< Elem, and the unification,  H = Elem.
%
% aux_ord_del_element( Set, Elem, Acc, Nth, ResSet ) :-
% 
aux_ord_del_element( Set, Elem, Nth, ResSet ) :-
	aux_ord_del_element( Set, Elem, 1,  Nth, ResSet ).

aux_ord_del_element( [H|T], Elem, Acc, Nth, ResSet ) :-
	( H = Elem -> 
		Nth = Acc,
		ResSet = T
		;
		H @< Elem,
		NewAcc is Acc + 1,
		ResSet = [H|MoreSet],
		aux_ord_del_element( T, Elem, NewAcc, Nth, MoreSet )
	).

aux_add_element_on_nth( [], 1, Elem, [Elem] ).
aux_add_element_on_nth( [H|T], Nth, Elem, List ) :-
	( Nth > 1 ->
		List = [H|More],
		RedNth is Nth - 1,
		aux_add_element_on_nth( T, RedNth, Elem, More )
		;
		Nth =:= 1,
		List = [Elem,H|T]
	).

ensure_list( AtomOrList, List ) :-
	( is_list(AtomOrList) ->
		List = AtomOrList
		;
		List = [AtomOrList]
	).

% aux_solution( PfdVars ) :-
% print solution and ask for what to do next,
% if the predicate fails the system will backtrack,
% whereas on succeeds, no more solutions will be 
% tried for.
%
aux_solution( PfdVars ) :-
	nl, 	% newlinw a day takes the doctor ?
	( PfdVars = [] -> 
		Input is 10
		;
		write_ls_vars( PfdVars ), 
		write( ' ? ' ),
		% get0( Input ) % sicstus prompts if we move this.
        % fixme: next 2 lines for testing only
		( bb_get(pfd_not_interactive,true) ->
			Input is 0';
			;
			on_pl( swi(_Swi), (get_single_char(Input),nl) ), % swi
			on_pl( sicstus(_ASics), get_line([Input|_Tine1]) ),
			on_pl( yap(_Ayap), get_line([Input|_Tine2]) )
		)
	),
	/* 
	( Input =\= 10 -> 
		on_pl( sicstus(_ASics), skip_line )
		;
		true
	),
	*/
	( 0'; = Input ->
		fail
		;
        !,
		true
	).

aux_cartesian( [H|T], [HS|TS], [H-HS|Prs] ) :-
	aux_cartesian( T, TS, Prs ).
aux_cartesian( [], [], [] ).

retractall_but( Head, N ) :-
	findall( Head-Body,
		clause(Head,Body),
			Clauses ),
	is_list_of_n_vars( N, Vars ),
	append( Vars, _Sufix, Clauses ),
	retractall( Head ),
	assert_pairs( Vars ).

is_list_of_n_vars( 0, [] ) :- !.

is_list_of_n_vars( N, [H|T] ) :-
        N > 0,
        var( H ),
        N1 is N - 1,
        is_list_of_n_vars( N1, T ).

assert_pairs( [] ).
assert_pairs( [H-B|T] ) :-
	( B==true ->
		assertz( (H) )
		;
		assertz( (H:-B) )
	),
	assert_pairs( T ).

zip( [], [], [] ).
zip( [H1|T1], [H2|T2], [(H1,H2)|T3] ) :-
	zip( T1, T2, T3 ).

ord_zip( [], [], _Op, Pairs, Pairs ).
ord_zip( [H|T], [H1|T1], Op, Acc, Pairs ) :-
	ord_add_prob_pair( Acc, (H,H1), Op, NxAcc ), 
	ord_zip( T, T1, Op, NxAcc, Pairs ).

ord_add_prob_pair( [(Prh,ValH)|T], (Pr,Val), Op, OrdPairs ) :-
	Compare =.. [Op,Pr,Prh],
	call(Compare),
	!,
	OrdPairs = [(Pr,Val),(Prh,ValH)|T].
ord_add_prob_pair( [H|T], (Pr,Val), Op, [H|Rest] ) :-
	!,
	ord_add_prob_pair( T, (Pr,Val), Op, Rest ).
ord_add_prob_pair( [], (Pr,Val), _Op, [(Pr,Val)] ).

% best_polling( List, Inserts, Best ) :-
best_polling( [], Inserts, Best, ItsPrb ) :-
	% reverse( Inserts, Streshni ),
	% insert_progressive( Streshni, [], Best ).
	insert_progressive( Inserts, [], 1/1,  Best, ItsPrb ).
best_polling( [[(Prb,El)|PDmn]|T], AccInserts, Best, ItsPrb ) :-
	the_best( T, Prb, El, 2, 1, BPos, BProb ),
	nth1( BPos, [PDmn|T], _DelColumn, NxCand ),
	NxInserts = [BPos-(BProb,El)|AccInserts],
	best_polling( NxCand, NxInserts, Best, ItsPrb ).

the_best( [], Prb, _El, _Count, BPos, BPos, Prb ).
the_best( [H|T], Prb, El, Count, AccPos, BPos, BProb ) :-
	( (H=[(RivPrb,El)|_Dmn],RivPrb>Prb) -> 
		NxPrb = RivPrb,
		NxAccPos = Count
		;
		NxPrb = Prb,
		NxAccPos = AccPos
	),
	NxCount is Count + 1,
	the_best( T, NxPrb, El, NxCount, NxAccPos, BPos, BProb ).

insert_progressive( [], Whole, Prb, Whole, Prb ).
insert_progressive( [N-H|T], Acc, AccPrb, Whole, Prb ) :-
	H = (HPrb,_HPrb),
	rationals_multiplication( AccPrb, HPrb, NxPrb ),
	nth1( N, NxAcc, H, Acc ), 
	insert_progressive( T, NxAcc, NxPrb, Whole, Prb ).

print_probs_n_vals( pr_order, Probs, Set ) :-
	write( '[ /1000' ), nl,
	print_pfd_pr_order( Probs, Set ),
	write( ']' ), nl, nl.

print_pfd_pr_order( Probs, Set ) :-
	dbg( in_print(Probs,Set) ),
	% min_list( Prob, MaxProb ),
	rationals_sort( Probs, Sort ),
	reverse( Sort, Rever ),
	print_pfd_pr_order( Rever, Probs, Set ).
% print_pfd_pr_order( _Prob, _Set ).

print_pfd_pr_order( [H|T], Probs, Set ) :-
	nth1( Nth1, Probs, H, RemProb ),
	!, % only find first
	nth1( Nth1, Set, Val, RemSet ),
	dbg( after_nths(Nth1) ),
	% MaxNorm is integer( 1000 * MaxProb ),
	% write( '(' ), write( MaxNorm ), 
	N is H * 1000,
	write( '(' ), write( N ), write( ',' ),
	write( H ), write( ',' ),
	atom_chars( Atom, [Val] ),
	write( Atom ), write( '),' ),
	print_pfd_pr_order( T, RemProb, RemSet ).
print_pfd_pr_order( [], _Empty, Set ) :-
	nl,
	( Set == [] -> 
		true
		;
		write( spurious_set(Set) ), nl,
		abort
	).

write_ls_vars( [Fr-Fv,S|T] ) :-
	!,
	write( Fr ), write(=), write( Fv ),
	write( ',' ), nl,
	write_ls_vars( [S|T] ).
write_ls_vars( [Singleton-Val] ) :-
	write( Singleton ), write(=), write(Val).

singleton( [_H] ).
singleton( [H], H ).

must_be( TypePred, Goal, ArgNo ) :-
	( call(TypePred) -> 
		true
		;
		TypePred =.. [TypeName|Args],
		( singleton(Args,Culprit)-> 
			true
			;
			Culprit = Args
		),
		throw( pfd(std(
			type_error(Goal,ArgNo,TypeName,Culprit) 
				)) )
	).

% assume sorted.
collate_el_prb_pairs( [], [] ).
collate_el_prb_pairs( [El-Prb|T], [El-SumPrb|TPairs] ) :-
	collate_el_prb_pairs_1( T, El, Prb, NxTail, SumPrb ),
	collate_el_prb_pairs( NxTail, TPairs ).

% collate_el_prb_pairs_1( [], _El, Prb,  Prb ).
collate_el_prb_pairs_1( [El-ElPrb|T], El, AccPrb, RemT, Prb ) :-
	!,
	rationals_addition( AccPrb, ElPrb, NxAccPrb ),
	collate_el_prb_pairs_1( T, El, NxAccPrb, RemT, Prb ).
collate_el_prb_pairs_1( List, _El, Prb, List, Prb ).

memberchk_identical_list( [], _List ).
memberchk_identical_list( [H|T], List ) :-
	memberchk_identical( List, H ),
	memberchk_identical_list( T, List ).

memberchk_identical( [], _El ).
memberchk_identical( [H|T], El ) :-
	( H == El ->
		true
		;
		memberchk_identical( T, El )
	).

sieve_n_mark_pvars( [], Marks, Lvs, Lvs, Marks, _FdIs, [] ).
	% kv_decompose( Marks, Pvars, Marks ).
sieve_n_mark_pvars( [H|T], AcPMarks, AcLvs, Lvs, PMarks, FDVs, Marked ) :-
	( pfd_var(H, FDVs) -> 
		Marked  = [Mark|Tmrked],
		NxLvs = AcLvs,
		( id_memberchk_kv(AcPMarks,H-Mark) ->
			NxPMarks = AcPMarks
			;
			NxPMarks = [H-Mark|AcPMarks]
		)
		;
		( var(H) ->
			NxLvs = [H|AcLvs],
			NxPMarks = AcPMarks,
			Marked = [H|Tmrked]
			;
			( atomic(H) ->
				NxPMarks = AcPMarks,
				Marked  = [H|Tmrked],
				NxLvs = AcLvs
				;
				H =.. [Name|Args],
				sieve_n_mark_pvars( Args, AcPMarks, AcLvs, NxLvs, FDVs, NxPMarks, MarkedArgs ),
				Hmrked =.. [Name|MarkedArgs],
				Marked = [Hmrked|Tmrked]
			)
		)
	),
	sieve_n_mark_pvars( T, NxPMarks, NxLvs, Lvs, PMarks, FDVs, Tmrked ).

sieve_vars( [], [], [] ).
sieve_vars( [H|T], Vars, NoVars ) :-
	( var(H) ->
		Vars = [H|Tvars],
		NoVars = TnoVars
		;
		Vars = Tvars,
		NoVars = [H|TnoVars]
	),
	sieve_vars( T, Tvars, TnoVars ).

% 2003/02/08, this is being moved from compile time
% to introduction time. Variables are not allowed.
in_num_to_fraction( In, Caller, Fract ) :-
	in_num_to_fraction_1( In, Caller, Fract ),
	( Fract > 1 -> 
		write( 'Error: pi more than 1, in: ' ), 
		write( Caller ), nl,
		abort
		;
		( Fract < 0 -> 
			write( 'Error: pi less than 0, in: ' ), 
			write( Caller ), nl,
			abort
			;
			true
		)
	).
			
in_num_to_fraction_1( In, Caller, Fract ) :-
	( var(In) -> 
		write( 'Error: variable in pi, for constraint: ' ), 
		write( Caller ), nl,
		abort
		;
		( integer(In) ->
			Fract = In / 1
			;
			( arithmetic_to_rational( In, Fract, Lf, Rg, Rat, Rl, Rr ) -> 
				in_num_to_fraction_1( Lf, Caller, Rl ),
				in_num_to_fraction_1( Rg, Caller, Rr ),
				call( Rat )
				;
				write( 'Error: unrecognised expression, ' ), 
				write( In ), write( ' in pi of, ' ),
				write( Caller ), nl,
				abort
			)
		)
	).

				
in_num_to_fraction_old( In, Caller, Fract ) :-
	( var(In) -> 
		Fract = In
		;
		( In =:= 1 ->
			Fract = 1/1
			;
			( In =:= 0 ->
				Fract = 0/1
				;
				Fract = In
			)
		),
		( Fract = _Nm/_Dn ->
			( Fract =< 1 -> true ; 
				write( 'Error: pi more than 1, in: ' ), 
				write( Caller ), nl,
				abort
				;
				true
			)
			;
			write( 'Error: pi should be of the form Nom/Den, in: ' ), 
			write( Caller ), nl,
			abort
		)
	).

select_dichotomy( Elem, List, Selected, Other ) :-
	( select(Elem,List,NxList) -> 
		Selected = [Elem|Ts],
		select_dichotomy( Elem, NxList, Ts, Other )
		;
		Selected = [],
		Other = List
	).

kv_consolidate( [], [] ).
kv_consolidate( [K-V|T], [K-ConsV|R] ) :-
	select_dichotomy( K-_S, T, Sel, Other ),
	kv_decompose( [K-V|Sel], _SingletonKey, ConsV ),
	kv_consolidate( Other, R ).

kv_rat_additive_consolidation( [], [] ).
kv_rat_additive_consolidation( [K-V|T], Consol ) :-
	( select(K-V1,T,ResT) ->
		rationals_addition( V1, V, V2 ),
		Consol = Tconsol,
		Tail = [K-V2|ResT]
		;
		Consol = [K-V|Tconsol],
		Tail = T
	),
	kv_rat_additive_consolidation( Tail, Tconsol ).

kv_additive_consolidation( [], [] ).
kv_additive_consolidation( [K-V|T], Consol ) :-
	( select(K-V1,T,ResT) ->
		V2 is V1 + V,
		Consol = Tconsol,
		Tail = [K-V2|ResT]
		;
		Consol = [K-V|Tconsol],
		Tail = T
	),
	kv_additive_consolidation( Tail, Tconsol ).

kv_decompose( [], [], [] ).
kv_decompose( [K-V|T], [K|Tk], [V|Tv] ) :-
	kv_decompose( T, Tk, Tv ).

% 
kv_compose( [], [], [] ).
kv_compose( [Hk|Tks], [Hv|Tvs], [Hk-Hv|Tkvs] ) :-
	kv_compose( Tks, Tvs, Tkvs ).

true( _X ).

arithmetic_to_rational( (A + B), F, A, B, Rat, L, R ) :-
	Rat = rationals_addition( L, R, F ).
arithmetic_to_rational( (A * B), F, A, B, Rat, L, R ) :-
	Rat = rationals_multiplication( L, R, F ).
arithmetic_to_rational( (A - B), F, A, B, Rat, L, R ) :-
	Rat = rationals_subtraction( L, R, F ).
arithmetic_to_rational( (A / B), F, A, B, Rat, L, R ) :-
	Rat = rationals_division( L, R, F ).

register_mass_loss_function_for( Var ) :-	
	bb_get( vars_with_mass_loss_fun, Vars ),
	bb_put( vars_with_mass_loss_fun, [Var|Vars] ).

has_mass_lossing_function( Var ) :-
	bb_get( vars_with_mass_loss_fun, Vars ),
	id_memberchk( Vars, Var ).

kv_split_triple( [], [], [] ).
kv_split_triple( [A-B-C|T], [A-B|R], [A-C|M] ) :-
	kv_split_triple( T, R, M ).

domain_distros_consolidation( [], _DDistros, [] ).
domain_distros_consolidation( [H|T], DDistros, Conso ) :-
	single_element_probabilities_in_domains( DDistros, H, NxDDs, Prbs ),
	( Prbs = [] -> 
		Conso = TConso
		;
		rationals_add_list( Prbs, HPrb ),
		Conso = [H-HPrb|TConso]
	),
	domain_distros_consolidation( T, NxDDs, TConso ).

single_element_probabilities_in_domains( [], _El, [], [] ).
single_element_probabilities_in_domains( [H|T], El, [Hrm|Trms], Prbs ) :-
	( select(El-HPrb,H,Hrm) ->
		Prbs = [HPrb|TPrbs]
		;
		Hrm = [],
		Prbs = TPrbs
	),
	single_element_probabilities_in_domains( T, El, Trms, TPrbs ).

sieve_positions( [], _, _FullFd, [] ).
sieve_positions( [H|T], Idx, [HEl|TEls], SFd ) :-
	NxIdx is Idx + 1,
	( H =< Idx -> 
		NxPos = T,
		SFd = [HEl|TSFd]
		;
		NxPos = [H|T],
		SFd = TSFd
	),
	sieve_positions( NxPos, NxIdx, TEls, TSFd ).
