/*

Car sequencing in Picat.

This model is based on the car sequencing model in
Pascal Van Hentenryck
"The OPL Optimization Programming Language", page 184ff.
(Via other implementations:
http://www.hakank.org/minizinc/car.mzn
http://www.hakank.org/bprolog/car.pl
)

Model created by Hakan Kjellerstrand, hakank@gmail.com

*/

% Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/

import cp.

main => go.

go =>

NbCars = 6,
Cars = 1..NbCars,
NbOptions = 5,
Options = 1..NbOptions,
NbSlots = 10,
Slots = 1..NbSlots,
Demand = [1, 1, 2, 2, 2, 2],
Option = [[1, 0, 0, 0, 1, 1],
[0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 0],
[1, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0]],
Capacity = [[1,2],
[2,3],
[1,3],
[2,5],
[1,5]],

% OptionDemand = [],
% foreach(I in Options)
%    OD #= sum([ Demand[J]*Option[I,J] : J in Cars]),
%    OptionDemand := OptionDemand ++ [OD]
% end,

OptionDemand = [sum([Demand[J]*Option[I,J] : J in Cars]) : I in Options],

%
% decision variables
%
Slot = new_list(NbSlots),
Slot :: 1..NbCars,

Setup = new_array(NbOptions,NbSlots),
Setup :: 0..1,

% To minimize
Z #= sum([S*Slot[S] : S in Cars]),

%
% Constraints
%
foreach(C in Cars)
sum([(Slot[S] #= C) : S in Slots]) #= Demand[C]
end,

foreach(O in Options, S in 1..(NbSlots - Capacity[O,2] + 1))
sum([Setup[O,J] : J in S..S + Capacity[O,2]- 1]) #=< Capacity[O,1]
end,

%% This don't work
% foreach(O in Options, S in Slots)
%    Setup[O,S] #= Option[O,Slot[S]]
% end,
%% Instead one has to use a couple of element/3.
foreach(O in Options, S in Slots)
element(S,Slot,SlotS),
matrix_element(Option,O,SlotS,SS),
Setup[O,S] #= SS
end,

foreach(O in Options, I in 1..OptionDemand[O])
sum([Setup[O,S] : S in 1..(NbSlots - I * Capacity[O,2])]) #>=
(OptionDemand[O] - I * Capacity[O,1])
end,

Vars = Slot ++ Setup,
solve([\$min(Z)], Vars),

writeln(z=Z),
printf("slot:\n%w\n", Slot),
printf("setup:\n"),
foreach(Row in Setup) writeln(Row.to_list()) end,

nl.

% matrix_element(X, I, J, Val) =>
%    element(I, X, Row),
%    element(J, Row, Val).

matrix_element(X, I, J, Val) =>
freeze(I, (element(I, X, Row),freeze(J,element(J,Row,Val)))).