Download
language ESSENCE' 1.0

letting daysPerWeek = 7

given numberOfWeeks : int(1..)

letting numberOfDays = numberOfWeeks * daysPerWeek

given s_min : int(1..)
given s_max : int(1..)

$ 3 shifts: early = 1, late = 2 and night shift = 3 + day off (rest day) = 0
letting numberOfShifts = 3

given shiftRequirements : matrix indexed by [int(1..daysPerWeek), int(1..numberOfShifts+1)] of int(0..)


find plan1d : matrix indexed by [int(1..numberOfWeeks * daysPerWeek)] of int(0..numberOfShifts)
find plan2d : matrix indexed by [int(1..numberOfWeeks), int(1..daysPerWeek)] of int(0..numberOfShifts)

find s_min_arrays : matrix indexed by [int(1..s_min), int(1..s_min)] of int(0..numberOfShifts)
find s_max_arrays : matrix indexed by [int(1..s_max), int(1..s_max)] of int(0..numberOfShifts)

branching on [plan1d]

such that 

$ flatten
forAll week : int(1..numberOfWeeks) .
    forAll day : int(1..daysPerWeek) .
        plan2d[week,day] = plan1d[(week-1) * daysPerWeek + day],

$ C_equalDays: constrains that weekend days (Saturday and Sunday) always have the same shift
forAll week : int(1..numberOfWeeks) .
    plan2d[week, daysPerWeek - 1] = plan2d[week, daysPerWeek],

$ create the sub arrays over the array bounds
forAll i : int(1..s_min) .
    forAll j : int(1..s_min) .
        s_min_arrays[i,j] = plan1d[((numberOfDays - s_min - 1 + i +j) % numberOfDays) + 1],

$ C_shiftRepetitions:for every shift type a minimum number of consecutive assignments to this shift is given
forAll day : int(1..numberOfDays-s_min) .
    plan1d[day] != plan1d[day+1] -> 
        forAll i : int(day+1..day+s_min) .
            plan1d[i] = plan1d[i+1],

$ the constraints over the array bounds
forAll d : int(1..s_min) . 
    plan1d[d+numberOfDays - s_min] != plan1d[((d+numberOfDays-s_min) % numberOfDays) + 1] ->
        forAll i : int(2..s_min) .
            s_min_arrays[d,1] = s_min_arrays[d,i],

$ create the sub arrays other the array bounds
forAll i : int(1..s_max) .
    forAll j : int(1..s_max) .
        s_max_arrays[i,j] = plan1d[((numberOfDays - s_max - 2 + i +j) % numberOfDays) + 1],

$ C_shiftRepetitions:for every shift type a maximum number of consecutive assignments to this shift is given 
forAll d : int(1..numberOfWeeks * daysPerWeek - s_max) .
    (forAll i : int(d+1..d+s_max-1) .
        plan1d[d] = plan1d[i]) ->
            plan1d[d] != plan1d[d + s_max],

$ the constraints over the array bounds
forAll d : int(1..s_max) . 
    (forAll i : int(2..s_max) .
        s_max_arrays[d,1] = s_max_arrays[d,i]) ->
            plan1d[d+numberOfDays-s_max] != plan1d[d],

$ C_restDays: at least 2 days must be rest days every 2 weeks
forAll day : int(1..((numberOfWeeks - 2)*daysPerWeek)) .
    sum([plan1d[i] = 0 | i : int(day..(day+daysPerWeek*2))]) >= 2,

forAll i : int(1..(2*daysPerWeek-1)) .
    sum([plan1d[j] = 0 | j : int(numberOfWeeks*daysPerWeek-i..numberOfWeeks * daysPerWeek)]) + sum([plan1d[k]=0 | k : int(1..2*daysPerWeek-i)]) >= 2,

$ C_shiftOrder: restricts the order of shifts. 
$ There is a forward rotating principle. This means, that after an early shift there can only follow a shift with the same or a higher value, or a rest shift.
forAll day : int(2..numberOfWeeks*daysPerWeek-1) . 
    (plan1d[day] <= plan1d[day+1]) 
    \/ (plan1d[day+1] = 0),

$ forward rotating
(plan1d[1] >= plan1d[numberOfDays]) \/ plan1d[1] = 0,

forAll day : int(1..daysPerWeek) .
    gcc(plan2d[..,day], [0,1,2,3], shiftRequirements[day, ..])