:- cond_load( swi(_V), true, [], ['System/Swi/std'] ).

:- cond_load( sicstus(_V), true, [], ['System/Sicstus/std'] ).

% :- ensure_loaded( 'System/Swi/sicstus_ordsets' ).
:- ensure_loaded( rationals ).
:- ensure_loaded( library(arrays) ).

% :- cond_load( sicstus(_Any), true,
% 	[ compilation_mode(consult) ],
% 	[ library(lists),
% 			% member/2
% 	  'System/Sicstus/std'
% 		] ). 

test_con( Numb-ProdPrb-ValPrbs-Vals ) :-
	Vals = [_A,_B,_C,_D,_E,_F],
	Cont = [[(1/1,99)],[(1/1,111)],[(1/1,104)],[(1/1,101)],[(74190367/1856741825,110),(74030188/1856741825,115),(73845583/1856741825,114),(73491459/1856741825,116),(73287401/1856741825,111),(73259799/1856741825,105),(73132362/1856741825,97),(72932483/1856741825,99),(72783608/1856741825,108),(72417177/1856741825,100),(14275328/371348365,103),(14243568/371348365,98),(71189841/1856741825,101),(71126133/1856741825,117),(14163374/371348365,104),(70791462/1856741825,109),(70431383/1856741825,102),(70349601/1856741825,112),(70222958/1856741825,121),(69731472/1856741825,118),(69553219/1856741825,107),(69430149/1856741825,119),(69388464/1856741825,122),(69387273/1856741825,120),(69194331/1856741825,113),(69163762/1856741825,106)],[(1/1,100)]],
	List = [(1/1,[],[],Cont)],
	new_array( Array0 ),
	aset( 5000, Array0, List, Array ),
	values( Array, 50000, [], 1, 0, [], ProdPrb, ValPrbs, Vals ),
	Numb is ProdPrb.

prom( Numb-ProdPrb-ValPrbs-Vals ) :-
	Vals = [_A,_B,_C,_D,_E,_F],
	% Mtr  = 0/1,
	Prb  = 1/1,
	% List = [], 
	Alts = [],
	Cont = 	[ [(1/1,c)],
			  [(1/1,o)],
			  [(1/1,h)],
			  [(1/1,e)],
			  [ (74190367/1856741825,n),(74030188/1856741825,s),(73845583/1856741825,r),
			    (73491459/1856741825,t),(73287401/1856741825,o),(73259799/1856741825,i),
			    (73132362/1856741825,a),(72932483/1856741825,c),(72783608/1856741825,l),
			    (72417177/1856741825,d),(14275328/371348365,g),(14243568/371348365,b),
			    (71189841/1856741825,e),(71126133/1856741825,u),(14163374/371348365,h),
			    (70791462/1856741825,m),(70431383/1856741825,f),(70349601/1856741825,p),
			    (70222958/1856741825,y),(69731472/1856741825,v),(69553219/1856741825,k),
			    (69430149/1856741825,w),(69388464/1856741825,z),(69387273/1856741825,x),
			    (69194331/1856741825,q),(69163762/1856741825,j)
			   ],
			  [(1/1,d)]],
	% 73491459/1856741825,t, % 73287401/1856741825,o, % 73259799/1856741825,i,
      % 73132362/1856741825,a, % 72932483/1856741825,c, % 72783608/1856741825,l,
      % 72417177/1856741825,d, % 14275328/371348365,g, % 14243568/371348365,b,
      % 71189841/1856741825,e, % 71126133/1856741825,u, % 14163374/371348365,h,
      % 70791462/1856741825,m, % 70431383/1856741825,f, % 70349601/1856741825,p,
      % 70222958/1856741825,y, % 69731472/1856741825,v, % 69553219/1856741825,k,
      % 69430149/1856741825,w, % 69388464/1856741825,z, % 69387273/1856741825,x,
      % 69194331/1856741825,q,
      % 69163762/1856741825,j,
	% Cont = 	[ 1/1-[(3/8,a),(5/16,b),(3/16,c),(2/16,d)],
			  % 1/1-[(5/16,d),(4/16,c),(4/16,a),(3/16,b)],
			  % 1/1-[(3/7,a),(2/7,b),(1/7,d),(1/7,c)],
			  % 1/1-[(9/32,d),(9/32,c),(7/32,b),(7/32,a)]],
	% Tree = [(Mtr,Prb,List,Alts,Cont)],
	List = [(Prb,[],[],Cont)],
	new_array( Array0 ),
	aset( 5000, Array0, List, Array ),
	values( Array, 50000, [], 1, 0, [], ProdPrb, ValPrbs, Vals ),
	Numb is ProdPrb.

values( _A, _B, _I, _Iter, _Seen, Complete, Prb, ValPrbs, Vals ) :-
	member( Prb-_Metr-Pairs, Complete ),
	% write( metric(Metr) ), nl,
	pair_list( Pairs, ValPrbs, Vals ).
values( Array, Best, Indcs, Iter, Seen, OldComplete, Prb, ValPrbs, Values ) :-
	pick_best_branch( Best, Array, Branch, NxArray, NxBest ),
	% length( [H|T], Lgth ),
	length( OldComplete, Plus ),
	NxSeen is Seen + Plus,
	% write( its_sn_nds(Iter,NxSeen) ), nl,
	% write( its_sn_nds(Iter,NxSeen,Lgth) ), nl,
	expand_branch( Branch, NxBest, Branches, Complete ),
	collapse_branches( Branches, Collapsed ),
	insert_branches_arrays( Collapsed, NxArray, NxBest, Brray, FrBest ),
	NxIter is Iter + 1,
	values( Brray, FrBest, Indcs, NxIter, NxSeen, Complete, Prb, ValPrbs, Values ).

pick_best_branch( Best, Array, Branch, NxArray, NxBest ) :-
	arefl( Best, Array, Elements ),
	( Elements = [Branch|T] -> 
		aset( Best, Array, T, NxArray ),
		NxBest is Best
		;
		\+ Best =< 0,
		BestMinus is Best - 1, 
		pick_best_branch( BestMinus, Array, Branch, NxArray, NxBest )
	).
	

insert_branches_arrays( [], Array, Best, Array, Best ).
insert_branches_arrays( [Mtr-Tuples|T], Array, Best, NwArray, NwBest ) :-
	( Mtr > Best -> 
		NxBest is Mtr 
		;
		NxBest is Best
	),
	arefl( Mtr, Array, Elements ),
	append( Tuples, Elements, NwElements ),
	aset( Mtr, Array, NwElements, NxArray ),
	insert_branches_arrays( T, NxArray, NxBest, NwArray, NwBest ).

collapse_branches( Branches, Collapsed ) :-
	list_to_tree( Branches, nil, Tree ),
	tree_to_list( Tree, Collapsed, [] ).

list_to_tree( [], Tree, Tree ).
list_to_tree( [H|T], Tree, NwTree ) :-
	H = Mtr-Tuple,
	insert_to_tree( Tree, Mtr, Tuple, NxTree ),
	list_to_tree( T, NxTree, NwTree ).

insert_to_tree( nil, Idx, Val, t(Idx,[Val],nil,nil) ).
insert_to_tree( t(Cidx,Cvals,Left,Right), Idx, Val, t(Cidx,NwVals,NwL,NwR) ) :-
	( Idx < Cidx -> 
		insert_to_tree( Left, Idx, Val, NwL ),
		NwVals = Cvals, NwR = Right
		;
		( Idx =:= Cidx ->
			NwVals = [Val|Cvals],
			NwR = Right, NwL = Left
			;
			insert_to_tree( Right, Idx, Val, NwR ),
			NwVals =Cvals, NwL = Left
		)
	).

tree_to_list( nil, Collapsed, Collapsed ).
tree_to_list( t(Idx,Vals,Left,Right), List, Tail ) :-
	tree_to_list( Left, List, Tail1 ),
	Tail1 = [Idx-Vals|Tail2],
	tree_to_list( Right, Tail2, Tail ).

expand_branch( (BrPrb,List,Alts,Contin), BrMtr, Branches, Complete ) :-
	( Contin == [] ->
		reverse( List, Solution ),
		Complete = [BrPrb-BrMtr-Solution],
		Branches = []
		;
		Contin = [H|T],
		findall( NxMtr-(NxBrPrb,HList,HAlts,T1),
			(  	select( (HPrb,HEl), H, HRemPairs ),
				( select((MxPrb,HEl),Alts,MidAlts) -> 
					true
					;
					MxPrb = 0/1, MidAlts = Alts
				),
				clear_dmns_from_el_kp( T, HEl, 0/1, T1, RmvdPrb ),
				rat_max( RmvdPrb, MxPrb, BestPrb ),
				branch_probability( BrMtr, BrPrb, BestPrb, HPrb, NxMtr, NxBrPrb ),
				HList = [(HPrb,HEl)|List],
				refresh_alts( HRemPairs, MidAlts, HAlts )
				% write( NxMtr-(NxBrPrb,HList,HAlts,T1) ),nl
			),
			Branches ),
		( Branches = [] -> write( empty_branches(H,T) ), nl, fail ; true ),
		Complete = []
	).
				
refresh_alts( [], Alts, Alts ).
refresh_alts( [(HPrb,HEl)|T], Alts, [(NwHElPrb,HEl)|TNwAlts] ) :-
	( select((AltHElPrb,HEl),Alts,NxAlts) -> 
		( HPrb > AltHElPrb -> 
			NwHElPrb = HPrb
			;
			NwHElPrb = AltHElPrb
		)
		;
		NwHElPrb = HPrb,
		NxAlts = Alts
	),
	refresh_alts( T, NxAlts, TNwAlts ).

clear_dmns_from_el_kp( [], _El, RmPrb, [], RmPrb ).
clear_dmns_from_el_kp( [H|T], El, AccRmPrb, [NoElH|NoElT], RmPrb ) :-
	( select( (PrbEl,El), H, NoElH ) -> 
		rat_max( AccRmPrb, PrbEl, UpdtPrb )
		;
		UpdtPrb = AccRmPrb,
		NoElH = H
	),
	clear_dmns_from_el_kp( T, El, UpdtPrb, NoElT, RmPrb ).

rat_max( RmPrb, PrbEl, UpdtPrb ) :-
	( RmPrb < PrbEl -> 
		UpdtPrb = PrbEl
		;
		UpdtPrb = RmPrb
	).

pair_list( [], [], [] ).
pair_list( [(A,B)|T], [A|T1], [B|T2] ) :-
	pair_list( T, T1, T2 ).

branch_probability( BaseMetr, BasePrb, MxAltPrb, MarginPrb, BranchMetric, BranchPrb ) :-
	% rationals_subtraction( MarginPrb, MxAltPrb, Diff ),
	rationals_multiplication( BasePrb, MarginPrb, BranchPrb ),
	% write( branchprb(BranchPrb) ), nl,
	% rationals_subtraction( BranchPrb, MxAltPrb, Diff ),
	% write( diff(Diff) ), nl,
	% rationals_to_aprox_int( Diff, 10000, Apprx ),
	% rationals_dilute( BranchPrb, Diluted, 100000 ),
	% write( diluted(Diluted) ), nl,
	% rationals_to_aprox_int( Diluted, 10000000, Apprx ),
	% rationals_inflate( BranchPrb, 100000000, DApprx ),
	% BranchMetric is min( max( integer(((BranchPrb * 10000000)-5)*100),0), 10000 ),
	% BranchMetric is min( max( integer(10+(BranchPrb-1/1000000)*1000000000), 1), 10100 ),
	% Apprx is RApprx mod 10000,
	% write( basemetric(BaseMetr) ), nl,
	Diff is MarginPrb - MxAltPrb,
	% write( diff(Diff) ), nl,
	Jump is max( -200, min( 200, integer(Diff*10000))),
	% write( jump(Jump) ),
	BranchMetric is BaseMetr + Jump,
	% write( metric(BranchMetric) ), nl,

	% BranchMetric is 10000 + BaseMetr + Apprx,
	( BranchMetric < 0 -> write(neg_metric(BranchMetric)), nl, abort ; true ).