int: n_colours;
int: n_orders;
int: n_slabs;
int: n_caps;
array [1..n_orders] of int: weight;
array [1..n_orders] of int: colour;
array [1..n_caps] of int: capacity; % in ascending order
array [0..capacity[n_caps]] of int: loss_table = array1d(0..capacity[n_caps], [0] ++ [
min (j in 1..n_caps where capacity[j] >= i) (capacity[j] - i)
| i in 1..capacity[n_caps]]);
array [1..n_orders] of var 1..n_slabs: x; % which slab to assign order i to
array [1..n_slabs,1..n_colours] of var bool: slab_colour; % whether colour j appears on slab i
array [1..n_slabs] of var 0..2: slab_num_colours; % number of colours on slab i
constraint forall (i in 1..n_slabs, j in 1..n_colours) (
exists (k in 1..n_orders where colour[k] = j) (x[k] = i) <-> slab_colour[i,j]
);
constraint forall (i in 1..n_slabs) (
sum (j in 1..n_colours) (bool2int(slab_colour[i,j])) = slab_num_colours[i]
);
% Dominance breaking constraints
array [1..n_slabs] of var int: load1; % load of first colour appearing on slab
array [1..n_slabs] of var int: load2; % load of second colour appearing on slab
constraint forall (i in 1..n_slabs) (
slab_num_colours[i] <= 1 -> load2[i] = 0 /\
slab_num_colours[i] <= 0 -> load1[i] = 0 /\
slab_num_colours[i] >= 2 -> load2[i] = (
let { var 1..n_colours: c = max (j in 1..n_colours) (j - n_colours * bool2int(not slab_colour[i,j])) } in
sum (j in 1..n_orders) (weight[j] * bool2int(x[j] = i /\ colour[j] = c))
) /\
slab_num_colours[i] >= 1 -> load1[i] = (
let { var 1..n_colours: c = min (j in 1..n_colours) (j + n_colours * bool2int(not slab_colour[i,j])) } in
sum (j in 1..n_orders) (weight[j] * bool2int(x[j] = i /\ colour[j] = c))
)
);
constraint forall (i, j in 1..n_slabs) (
loss_table[load1[i] + load2[i]] + loss_table[load1[j] + load2[j]] <= loss_table[load1[i] + load2[j]] + loss_table[load2[i] + load1[j]] /\
loss_table[load1[i] + load2[i]] + loss_table[load1[j] + load2[j]] <= loss_table[load1[i] + load1[j]] + loss_table[load2[i] + load2[j]]
);
solve minimize sum (i in 1..n_slabs) (loss_table[sum (j in 1..n_orders) (weight[j] * bool2int(x[j] = i))]);