% compiler directive to load finite domain library
:- use_module(library(fd)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                       8.1 Domains and Labelling                     %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% simple relation likes p252.
likes(kim, maria). 
likes(kim, nicole). 
likes(kim, erika). 
likes(peter, maria). 
likes(bernd, nicole). 
likes(bernd, maria).
likes(bernd, erika). 
 
% goal for the marriage problem p252.
gp252a(N,M,E) :-
     [N,M,E] :: [kim, peter,bernd], N ## M, N ## E, M ## E, 
             likes(N, nicole), likes(M, maria), likes(E, erika).

% smugglers knapsack goal p252. 
gp252b(W,P,C) :-
     [W,P,C] :: [0..9], 4*W + 3*P + 2*C #<= 9, 
	15*W + 10*P + 7*C #>= 30.

% smugglers knapsack goal with labelling p253. 
gp253a(W,P,C) :-
	[W,P,C] :: [0..9], 4*W + 3*P + 2*C #<= 9, 
	15*W + 10*P + 7*C #>= 30, labeling([W,P,C]).

% minimization version of smugglers knapsack p253.
gp253b(W,P,C) :-
	L #= -15*W - 10*P - 7*C,
	minimize(([W,P,C] :: [0..9], 4*W + 3*P + 2*C #<= 9, 
	15*W + 10*P + 7*C #>= 30, labeling([W,P,C])), L).

% send more money program p254. 
smm(S,E,N,D,M,O,R,Y)  :-  
     [S,E,N,D,M,O,R,Y] :: [0..9],
     constrain([S,E,N,D,M,O,R,Y]),
     labeling([S,E,N,D,M,O,R,Y]).
 
constrain([S,E,N,D,M,O,R,Y])  :-  
     S ## 0,M ## 0, 
     alldifferent_neq([S,E,N,D,M,O,R,Y]), 
         1000*S +     100*E +     10*N +     D 
     +    1000*M +     100*O +     10*R +     E 
     #= 10000*M +     1000*O +     100*N +     10*E +     Y.

% alldifferent program Ex 6.3 p190.
alldifferent_neq([]). 
alldifferent_neq([Y|Ys]) :- not_member(Y, Ys), alldifferent_neq(Ys). 
 
not_member(_X, []). 
not_member(X, [Y|Ys]) :- 	
	X ## Y, %% beware not_member will only work correctly
	        %% when both arguments are finite domain,
	not_member(X, Ys).

% goal for send more money p254.
gp254(S,E,N,D,M,O,R,Y) :- smm(S,E,N,D,M,O,R,Y).

% send more money generate and test version p256. 
smm_gt(S,E,N,D,M,O,R,Y)   :-   
     [S,E,N,D,M,O,R,Y] :: [0..9],
     labeling([S,E,N,D,M,O,R,Y]),
     constrain([S,E,N,D,M,O,R,Y]).

% goal for send more money generate and test version p256.
gp256(S,E,N,D,M,O,R,Y) :- smm_gt(S,E,N,D,M,O,R,Y).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                       8.2 Complex Constraints                       %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% old fashion marriage problem with complex constraint p257.
gp257a(N,M,E) :-
      [N,M,E] :: [kim, peter,bernd], alldifferent([N,M,E]), 
     likes(N, nicole), likes(M, maria), likes(E, erika). 
 
% apart constraint progra p257.
apart(X, Y, N) :- X #>= Y + N. 
apart(X, Y, N) :- Y #>= X + N. 
 
% seesaw problem goal p257.
gp257b(L, F, S) :-
     [L,F,S] :: [-5..5],  9 * L + 8 * F + 4 * S #= 0,
     apart(L,F,3), apart(L,S,3), apart(F,S,3),  
     labeling([L,F,S]).
 
% seesaw problem goal with cumulative constraint p258.
% wont work in eclipse becuase no cumulative constraint
gp258(L, F, S) :-
     [L,F,S] :: [-5..5],  9 * L + 8 * F + 4 * S #= 0,
     cumulative([L,F,S],[3,3,3],[1,1,1],1), 
     labeling([L,F,S]).
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                           8.3 Labelling                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% labelling program defn from p259.
labeling259([]). 
labeling259([V|Vs]) :- 
     indomain259(V), 
     labeling259(Vs).

indomain259(V) :- 
     dom(V,D),
     member(V,D).

% copy of send more money goal using user labeling (not in text). 
gp259(S,E,N,D,M,O,R,Y)  :-  
     [S,E,N,D,M,O,R,Y] :: [0..9],
     constrain([S,E,N,D,M,O,R,Y]),
     labeling259([S,E,N,D,M,O,R,Y]).
 
% labelling using first fail strategy of choosing the min domain size p260. 
labelingff260([]). 
labelingff260(Vs) :- 
     deleteff260(Vs, V, Rest), 
     indomain(V), 
     labelingff260(Rest).

deleteff260([V0|Vs], V, Rest) :- 
     get_domain_size(V0, S0), 
     min_size(Vs, S0, V0, V, Rest). 

min_size([], _, V, V, []). 
min_size([V1|Vs], S0, V0, V, [V0|Rest]) :-
     get_domain_size(V1, S1), 
     S1 #< S0, % could be < since the sizes are fixed 
     min_size(Vs, S1, V1, V, Rest). 
min_size([V1|Vs], S0, V0, V, [V1|Rest]) :-
     get_domain_size(V1, S1), 
     S1 #>= S0, 
     min_size(Vs, S0, V0, V, Rest). 

get_domain_size(V, S) :- dom(V, D), length(D, S). 

length([], 0). 
length([_|L], N) :- N #= N1 + 1, length(L, N1). 

% copy of send more money goal using user labelingff (not in text). 
gp260(S,E,N,D,M,O,R,Y)  :-  
     [S,E,N,D,M,O,R,Y] :: [0..9],
     constrain([S,E,N,D,M,O,R,Y]),
     labelingff260([S,E,N,D,M,O,R,Y]).

% alternate defn of get_domain_size for range domains 
get_domain_size261(V, S) :-  
     mindomain(V, Min), maxdomain(V, Max), S #= Max - Min + 1. 
 
% N-queens program p262.
goal(N, Queens) :-
    	length(Queens, N),
    	Queens :: [1..N],
    	queens(Queens),
    	labeling(Queens). 

queens([]). 
queens([X|Y]) :-
       safe(X,Y,1),
       queens(Y).

safe(_,[],_).
safe(X,[F|T],Nb) :-
       noattack(X,F,Nb),
       Newnb #= Nb + 1,
       safe(X,T,Newnb).

noattack(X,Y,Nb) :- 
     Y + Nb ## X, X + Nb ## Y, X ## Y.

% goal for 8 queens
gp262(Queens) :- goal(8, Queens).

% middle out labelling strategy p262--263. 
indomain_middle(Var) :- 
     make_list_of_values(Var, List), 
     member(Var, List).
 
make_list_of_values(Var, List) :- 
     dom(Var, Domain), 
     length(Domain, Size), 
     B :: [0,1], 2*N + B #= Size, % translation of N = Size div 2.
	                          % since Size is fixed a better translation
				  % is N is Size // 2.
     halve_list(N, Domain, [], RFirst, Second),
     merge(Second, RFirst, List). 

halve_list(0, L2, L1, L1, L2).
halve_list(N, [H|R], Li, L1, L2) :-
     	N #>= 1,
        N1 #= N-1,
    	halve_list(N1, R, [H|Li], L1, L2).

merge([], L, L). 
merge([X|L1], L2, [X|L3]) :- merge(L2, L1, L3). 

% goal illustrating part of indomain_middle p263. 
gp263a(RFirst,Second) :-
     halve_list(3, [1,2,5,6,8,9,10], [], RFirst, Second).
 
% copy of labeling using indomain_middle (not in text).
labeling_middle([]). 
labeling_middle([V|Vs]) :- 
     indomain_middle(V), 
     labeling_middle(Vs).

% copy of 8-queens goal using middle labelling (not in text).
gp263b(Queens) :-
    	length(Queens, 8),
    	Queens :: [1..8],
    	queens(Queens),
    	labeling_middle(Queens). 

% domain splitting approach to labelling program p264.
labelingsplit([]). 
labelingsplit([V|Vs]) :- 
    	mindomain(V, Min), 
    	maxdomain(V, Max), 
    	(Min #= Max -> 
        		labelingsplit(Vs) 
    	; 
        		B :: [0,1], 2*Mid + B = Min+Max, % translation of
				% Mid = (Min + Max) div 2 but because
				% Min and Max are fixed better is
			 	% Mid is (Min + Max) // 2.
        		labelsplit(V, Mid, Vs)
    	).
 
labelsplit(V, M, Vs):- 
     	V #<= M, 
    	append(Vs, [V], NVs), 
    	labelingsplit(NVs).
labelsplit(V, M, Vs):- 
     	V >= M+1, 
    	append(Vs, [V], NVs), 
    	labelingsplit(NVs).
 
% domain splitting version of seesaw goal p265.
% no cumulative available in eclipse
gp265(L, F, S) :-
     [L,F,S] :: [-5..5],  9 * L + 8 * F + 4 * S = 0, 
      cumulative([L,F,S],[3,3,3],[1,1,1],1), 
      labelingsplit([L,F,S]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                   8.4 Different Problem Modellings                  %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% simple assigment problem Boolean model Ex 8.5 p267.  
gp267([B11,B12,B13,B14,B21,B22,B23,B24,B31,B32,
          B33,B34,B41,B42,B43,B44]) :-
     [B11,B12,B13,B14,B21,B22,B23,B24,B31,B32,
      B33,B34,B41,B42,B43,B44] :: [0,1], 
    	B11 + B12 + B13 + B14 #= 1,
    	B21 + B22 + B23 + B24 #= 1,
    	B31 + B32 + B33 + B34 #= 1,
    	B41 + B42 + B43 + B44 #= 1,
    	B11 + B21 + B31 + B41 #= 1,
    	B12 + B22 + B32 + B42 #= 1,
    	B13 + B23 + B33 + B43 #= 1,
    	B14 + B24 + B34 + B44 #= 1,
    	P #=          7 * B11 + B12 + 3 * B13 + 4 * B14
        		+ 8 * B21 + 2 * B22 + 5 * B23 +     B24
        		+ 4 * B31 + 3 * B32 + 7 * B33 + 2 * B34
        		+ 3 * B41 +     B42 + 6 * B43 + 3 * B44,
    	P #>= 19,
        labeling([B11,B12,B13,B14,B21,B22,B23,B24,B31,B32,
          B33,B34,B41,B42,B43,B44]).

% simple assigment problem element model Ex 8.6 p268.  
gp268([W1,W2,W3,W4]) :-
     	[W1,W2,W3,W4] :: [1..4], 
     	alldifferent([W1,W2,W3,W4]), 
     	element(W1,[7,1,3,4],WP1), 
     	element(W2,[8,2,5,1],WP2), 
     	element(W3,[4,3,7,2],WP3), 
     	element(W4,[3,1,6,3],WP4), 
     	P #= WP1 + WP2 + WP3 + WP4, 
     	P #>= 19, 
     	labeling([W1,W2,W3,W4]).
 
% simple assigment problem alternate element model Ex 8.7 p269.  
gp269([T1,T2,T3,T4]) :-
      	[T1,T2,T3,T4] :: [1..4], 
     	alldifferent([T1,T2,T3,T4]), 
     	element(T1,[7,8,4,3],TP1), 
     	element(T2,[1,2,3,1],TP2), 
     	element(T3,[3,5,7,6],TP3), 
     	element(T4,[4,1,2,3],TP4), 
     	P #= TP1 + TP2 + TP3 + TP4, 
     	P #>= 19, 
     	labeling([T1,T2,T3,T4]).
 
% simple assignment problem element model with additional constraint p270.
gp270([W1,W2,W3,W4]) :-
      	[B11,B44] :: [0,1], 
     	element(W1,[1,0,0,0],B11), 
     	element(W4,[0,0,0,1],B44), 
    	B11 + B44 #<= 1,
	gp268([W1,W2,W3,W4]).
 
% simple assignment problem combined goal p271.
gp271([W1,W2,W3,W4],[T1,T2,T3,T4]) :-
     	[W1,W2,W3,W4] :: [1..4], 
     	alldifferent([W1,W2,W3,W4]), 
     	element(W1,[7,1,3,4],WP1), 
     	element(W2,[8,2,5,1],WP2), 
     	element(W3,[4,3,7,2],WP3), 
     	element(W4,[3,1,6,3],WP4), 
     	P #= WP1 + WP2 + WP3 + WP4, 
      	[T1,T2,T3,T4] :: [1..4], 
     	alldifferent([T1,T2,T3,T4]), 
     	element(T1,[7,8,4,3],TP1), 
     	element(T2,[1,2,3,1],TP2), 
     	element(T3,[3,5,7,6],TP3), 
     	element(T4,[4,1,2,3],TP4), 
     	P #= TP1 + TP2 + TP3 + TP4, 
     	P #>= 19, 
    	iff(W1,1,T1,1), iff(W1,2,T2,1), iff(W1,3,T3,1), iff(W1,4,T4,1), 
    	iff(W2,1,T1,2), iff(W2,2,T2,2), iff(W2,3,T3,2), iff(W2,4,T4,2), 
    	iff(W3,1,T1,3), iff(W3,2,T2,3), iff(W3,3,T3,3), iff(W3,4,T4,3), 
    	iff(W4,1,T1,4), iff(W4,2,T2,4), iff(W4,3,T3,4), iff(W4,4,T4,4), 
     	labeling([W1,W2,W3,W4]).
 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% iff programmed using the extensible solver
iff(Var1, Val1, Var2, Val2) :-
	dvar_domain(Var1,Dom1),
	dvar_domain(Var2,Dom2),
	(dom_size(Dom1,1), Var1 = Val1 ->
		Var2 = Val2 
	; (dom_size(Dom2,1), Var2 = Val2 ->
		Var1 = Val1 
	; (\+dom_check_in(Val1,Dom1) ->
		dvar_remove_element(Var2,Val2)
	; (\+dom_check_in(Val2,Dom2) ->
		dvar_remove_element(Var1,Val1)
	;
		make_suspension(iff(Var1, Val1, Var2, Val2),3,Susp),
		insert_suspension((Var1,Var2), Susp, any of fd, fd)
	)))),
	wake.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                 8.5 An Extended Example Scheduling                  %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% problem data as predicate p273.
problem([task(j1,3,[],m1), task(j2,8,[],m1), task(j3,8,[j4,j5],m1),
	 task(j4,6,[],m2), task(j5,3,[j1],m2), task(j6,4,[j1],m2)]).


% schedule program p273--274.
schedule(Data, End, Joblist)   :-    
         makejoblist(Data, Joblist, End), 
         precedences(Data, Joblist),
         machines(Data, Joblist),
         labeltasks(Joblist).	
% 
makejoblist([],[],_). 
makejoblist([task(N,D,_,_)|Ts], [job(N,D,TS)|Js], End)  :-  
    	TS :: [0..1000], 
        TS + D #<= End,
	makejoblist(Ts, Js, End).	

getjob(JL, N, D, TS)  :- once(member(job(N,D,TS), JL)).	
 
precedences([],_). 
precedences([task(N,_,Pre,_)|Ts], Joblist)  :- 
    	getjob(Joblist, N, _, TS),
	    prectask(Pre, TS, Joblist),
	    precedences(Ts, Joblist).	
 
prectask([],  _, _).
prectask([Name|Names], PostStart, Joblist)  :-
    	getjob(Joblist, Name, D, TS),
	    TS + D #<= PostStart,
	    prectask(Names, PostStart, Joblist).	
 
machines([], _).
machines([task(N,_,_,M)|Ts], Joblist)  :-
    	getjob(Joblist, N, D, TS),
	    machtask(Ts, M, D, TS, Joblist),
	    machines(Ts, Joblist).	
 
machtask([], _, _, _, _).
machtask([task(SN,_,_,M0)|Ts], M, D, TS, Joblist)  :- 
	     (M #= M0 -> 
	         getjob(Joblist, SN, SD, STS),
	         exclude(D, TS, SD, STS)
	     ; true ),
	    machtask(Ts, M, D, TS, Joblist).	
 
exclude(_D, TS, SD, STS)  :-  STS + SD #<= TS. 
exclude(D, TS, _SD, STS)  :-  TS + D #<= STS. 
 
labeltasks([]). 
labeltasks([job(_,_,TS)|Js])   :-   
     indomain(TS), 
     labeltasks(Js).
 
% goal for finding a schedule p274.
gp274(Joblist) :-
     End = 20, problem(Problem), schedule(Problem, End, Joblist).
 
 
% minimization goal for finding a minimum schedule p276.
gp276(End, Joblist) :-
   End :: [0..1000], problem(Problem),  
   minimize((schedule(Problem, End, Joblist), indomain(End)), End).
 
% schedule program with redundant constraints p277.
schedule277(Data, End, Machinelist, Joblist)  :-  
          makejoblist(Data, Joblist, End), 
          precedences(Data, Joblist),
    	  makeemptylists(Machinelist, Emptys),
          extrainfo(Data,Joblist,Machinelist,Emptys,Joblist), 
          machines(Data, Joblist),
          labeltasks(Joblist).
 
makeemptylists([],[]). 
makeemptylists([_|L],[[]|R]) :- makeemptylists(L,R).
 
extrainfo(_, [], _, _, _).
extrainfo(Data, [job(N,_,TS)|Js], Machinelist, Emptys, Joblist) :-
     	predecessors(N, Data, [], Pre),	
     	splittasks(Pre, Data, Machinelist, Emptys, Split), 
     	list_redundant(Split, Joblist, TS), 
     	extrainfo(Data, Js, Machinelist, Emptys, Joblist).
 
splittasks([], _, _, Split, Split). 
splittasks([N|Ns], Data, Machines, Split0, Split) :-
     gettask(Data, N, task(_,_,_,M)), 
     insert(Machines, M, N, Split0, Split1), 
     splittasks(Ns, Data, Machines, Split1, Split).
 
insert([M|_], M, T, [L|Ls], [[T|L]|Ls]).
insert([M0|Ms], M, T, [L|Ls0], [L|Ls]) :- 
     M0 ## M,
     insert(Ms, M, T, Ls0, Ls).
 
list_redundant([], _, _). 
list_redundant([L|Ls], Joblist, TS) :- 
     redundant(L, Joblist, TS, 0, []), 
     list_redundant(Ls, Joblist, TS).
 
redundant([], _, TS, TotalD, ListTS) :- 
     TS #>= MinTs + TotalD, % translation of TS #>= minimum(ListTS) + TotalD.
     minimum(MinTs,ListTS).     % using new constraint minimum 
redundant([N|Ns], Joblist, TS, TotalD0, ListTS) :- 
     getjob(Joblist, N, DN, TSN), 
     TotalD #= TotalD0 + DN, 
     redundant(Ns, Joblist, TS, TotalD, [TSN|ListTS]).
 
gettask(Data, N, T)  :- 
     T = task(N, _, _, _), 
     once(member(T, Data)).
 
lookup(Data, N, P) :- member(task(N,_,P,_), Data).

% Improved predecessors program from p197.
predecessors(N, List, Pre0, Pre) :- 
     lookup(List, N, NImPre), 
     cumul_predecessors(NImPre, List, Pre0, Pre).
 
cumul_predecessors([], _, Pre, Pre). 
cumul_predecessors([N|Ns], List, Pre0, Pre) :- 
     member(N, Pre0),  
     cumul_predecessors(Ns, List, Pre0, Pre). 
cumul_predecessors([N|Ns], List, Pre0, Pre) :- 
     not_member(N, Pre0), 
     predecessors(N, List, [N|Pre0], Pre1), 
     cumul_predecessors(Ns, List, Pre1, Pre). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%% minimum(X, Ys) X is the minimum of the Ys 
minimum(X, Ys) :-
	(Ys = [] -> X = 0 %% Note specialized for this use
	; (Ys = [Y] -> X = Y
        ;
	     mindomain(X,XMin),
	     mindomains(Ys,Mins),
	     maxdomains(Ys,Maxs),
	     minlist(Mins,MinMin),
	     minlist(Maxs,MinMax),
	     (MinMin = MinMax ->
		 X = MinMin
	     ;
	         dvar_remove_greater(X,MinMax),
		 dvar_remove_smaller(X,MinMin),
		 dvar_remove_smallers(Ys,XMin),
		 remove_non_min_vars(Ys, MinMax, RYs), 
		 (RYs = [Y] -> X = Y
	         ;
		     make_suspension(minimum(X,RYs), 3, Susp),
		     insert_suspension((X,RYs),Susp, min of fd, fd),
		     insert_suspension(RYs,Susp, max of fd, fd)
		 )
	     )
	 )),
	 wake.	

mindomains([],[]).
mindomains([V|Vs],[D|Ds]) :-
	mindomain(V,D),
	mindomains(Vs,Ds).

maxdomains([],[]).
maxdomains([V|Vs],[D|Ds]) :-
	maxdomain(V,D),
	maxdomains(Vs,Ds).

minlist([M0|Ms], Min) :-
	minlist(Ms,M0,Min).
minlist([],Min,Min).
minlist([M|Ms],M0,Min) :-
	(M < M0 -> minlist(Ms, M, Min) ; minlist(Ms,M0,Min)).
	
dvar_remove_smallers([],_).
dvar_remove_smallers([V|Vs],XMin) :-
	dvar_remove_smaller(V,XMin),
	dvar_remove_smallers(Vs,XMin).

remove_non_min_vars([],_,[]).
remove_non_min_vars([V|Vs], MinMax, RVs) :-
	mindomain(V, MV),
	(MV >= MinMax -> RVs = RVs1 ; RVs = [V|RVs1]),
	remove_non_min_vars(Vs, MinMax, RVs1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
% minimization goal with redundancy p278.
gp278(End, Joblist) :-
   End :: [0..1000], problem(Problem), 
   minimize((schedule277(Problem, End, [m1,m2], Joblist), indomain(End)), End).


% scheduling program using cumulative constraints p279. 
schedule279(Data, End, Machinelist, Joblist)  :-  
         makejoblist(Data, Joblist, End), 
          precedences(Data, Joblist),
    	  makeemptylists(Machinelist, Emptys),
          extrainfo(Data,Joblist,Machinelist,Emptys,Joblist), 
          cumulative_machines(Data,Joblist,Machinelist,Emptys),
          labeltasks(Joblist).
 
cumulative_machines(Data, Joblist, Machinelist, Emptys) :- 
     datatolist(Data, Names), 
     splittasks(Names, Data, Machinelist, Emptys, Split), 
     makecumulatives(Split, Joblist).
 
datatolist([],[]). 
datatolist([task(N,_,_,_)|Ts],[N|Ns]) :- 
     datatolist(Ts,Ns).
 
makecumulatives([], _). 
makecumulatives([L|Ls], Joblist) :- 
     makecumulative(L, Joblist, [], [], []), 
     makecumulatives(Ls, Joblist). 
 
makecumulative([], _, Ts, Ds, Rs) :- 
     cumulative(Ts, Ds, Rs, 1). 
makecumulative([N|Ns], Joblist, Ts, Ds, Rs) :- 
     getjob(Joblist, N, DN, TSN), 
     makecumulative(Ns, Joblist, [TSN|Ts], [DN|Ds], [1|Rs]). 

% minimization goal with cumulative p279.
% wont work because there is no cumulative constraint
gp279(End, Joblist) :-
   End :: [0..1000], problem(Problem), 
   minimize((schedule279(Problem, End, [m1,m2], Joblist), indomain(End)), End).

% labelling predicate that labels the earliest job first p280--281. 
labeltasks280([]). 
labeltasks280(Js)   :-   
     gettimes(Js, Ts), 
     label_earliest(Ts).
 
gettimes([],[]). 
gettimes([job(_,_,TS)|Js], [TS|Ts]) :- gettimes(Js, Ts). 
 
label_earliest([]).  
label_earliest([TS0|Ts]) :- 
     mindomain(TS0, M0),
     find_min_start(Ts, TS0, M0, TS, M, RTs), 
     rest_earliest(TS, M, [TS0|Ts], RTs). 
 
rest_earliest(TS, M, _Ts, RTs) :- 
     TS #= M, 
     label_earliest(RTs). 
rest_earliest(TS, M, Ts, _RTs) :- 
     TS ## M, 
     label_earliest(Ts).  
 
find_min_start([], TS, M, TS, M, []). 
find_min_start([TS1|Ts], TS0, M0, TS, M, RTs) :- 
     mindomain(TS1, M1), 
     (M1 #< M0 -> 
          RTs = [TS0|RTs1], 
          find_min_start(Ts, TS1, M1, TS, M, RTs1) 
     ; 
          RTs = [TS1|RTs1], 
          find_min_start(Ts, TS0, M0, TS, M, RTs1) 
     ). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                         8.6 Arc Consistency                         %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% declaration to load generalized propagation library 
:- use_module(library(propia)).
% op declaration for consistent and translation to propia
:- op(100, xf, consistent). 
consistent(A) :- A infers most. 
  
% goal using arc consistency p283.
% doesnt work because of a bug in Eclipse it says
% calling an undefined procedure domain_msg(nicole, nicole, _383) 
% in module propia
gp283(N,M,E) :-
     [N,M,E] :: [kim, peter,bernd], N ## M,N ## E,M ## E, 
             likes(N, nicole) consistent, 
             likes(M, maria) consistent, 
             likes(E, erika) consistent, 
             labeling([N,M,E]).
 
% profit program p284. 
profit_1(1,7).
profit_1(2,1).
profit_1(3,3).
profit_1(4,4).

% goal for profit p284. %% same problem as above
gp284(W1,WP1) :- profit_1(W1,WP1) consistent.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                       8.6 Reified Constraints                       %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% either program either X1 = X or X2 = X p285
% reified constraints are not supported in Eclipse
either(X,X1,X2) :- 
     [B1,B2] :: [0..1], 
     (X=X1 #<=> B1), (X=X2 #<=> B2),
     1 #<= B1+B2.

% program for iff in terms of reified constraints p295. 
iff295(X1,V1,X2,V2) :- 
     B :: [0..1],
     (X1=V1 #<=> B),
     (X2=V2 #<=> B).
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% just for fun heres a simple
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% reified version of X =  Y #<=> B
#<=>(X=Y,B) :-
	mindomain(X,Xm), maxdomain(X,XM),
	mindomain(Y,Ym), maxdomain(Y,YM),
	Dmin is Xm - YM, DMax is XM - Ym,
	[B1,B2] :: [0,1],
	B #<= B1, B #<= B2, B1 + B2 #<= 1 + B,
	X - Y #>= B1 * (-Dmin) + Dmin,
	X - Y #<= (DMax + 1)*B1 - 1,
        Y - X #>= B2 * (DMax) - DMax,
	Y - X #<= (1 - Dmin)*B2 - 1.
	

