model Lesson3_Insurance
  options noimplicit
  uses "mmxprs", "mmsystem"

  parameters
    numOfWorkers=4000
    workerLimit=1000
  end-parameters

  declarations
    WORKSTATIONS=1..16
    INSURANCE=1..6

    insuranceCosts: array(INSURANCE) of real
    workersMin: array(INSURANCE) of real
    workersMax: array(INSURANCE) of real

    insuranceTypes: array(WORKSTATIONS,INSURANCE) of mpvar
    insurancePaid: array(WORKSTATIONS) of mpvar
    workersAssigned: array(WORKSTATIONS) of mpvar

    OBJ:linctr
    startTime:time
    endTime:time
  end-declarations

  procedure linearization(x: mpvar, y: mpvar, b: mpvar, M: real)

    y <= M*b
    y <= x
    x + M*b - M <= y

  end-procedure

  startTime:=time(SYS_NOW)
  insuranceCosts::[250,600,1250,3150,6250,12500]
  workersMin::[1,20,50,100,250,500]
  workersMax::[19,49,99,249,499,1000]

  forall(w in WORKSTATIONS, i in INSURANCE) insuranceTypes(w,i) is_binary
  forall(w in WORKSTATIONS) workersAssigned(w) is_integer

  forall(w in WORKSTATIONS) do
    sum(i in INSURANCE) insuranceTypes(w,i) = 1
  end-do

  forall(w in WORKSTATIONS) do
    workersAssigned(w) <= workerLimit
    workersAssigned(w) >= 1
  end-do

  forall(w in WORKSTATIONS) do
    insurancePaid(w) = sum(i in INSURANCE) insuranceTypes(w,i)*insuranceCosts(i)
  end-do

  sum(w in WORKSTATIONS) workersAssigned(w) = numOfWorkers

  !
  ! This part crashes the program:
  !

  !forall(w in WORKSTATIONS, i in INSURANCE) do
  !  workersAssigned(w)*insuranceTypes(w,i) <= workersMax(i)*insuranceTypes(w,i)
  !  workersAssigned(w)*insuranceTypes(w,i) >= workersMin(i)*insuranceTypes(w,i)
  !end-do

  ! Problem: This is no longer linear!
  ! Both workersAssigned and insuranceTypes are decision variables, and
  ! we're MULTIPLYING them together. -> LINEARIZATION!

  ! UNCOMMENT FOR THE SOLUTION:

  declarations
    workerTimesInsurance: array(WORKSTATIONS,INSURANCE) of mpvar
  end-declarations

  forall(w in WORKSTATIONS, i in INSURANCE) do
    workerTimesInsurance(w,i) is_integer
  end-do

  forall(w in WORKSTATIONS, i in INSURANCE) do
    linearization(workersAssigned(w), workerTimesInsurance(w,i), insuranceTypes(w,i), workerLimit)
  end-do

  (!
  forall(w in WORKSTATIONS, i in INSURANCE) do
    workerTimesInsurance(w,i) <= workerLimit*insuranceTypes(w,i)
    workerTimesInsurance(w,i) <= workersAssigned(w)
    workersAssigned(w) + workerLimit*insuranceTypes(w,i) - workerLimit <= workerTimesInsurance(w,i)
  end-do
  !)

  forall(w in WORKSTATIONS, i in INSURANCE) do
    workerTimesInsurance(w,i) <= workersMax(i)*insuranceTypes(w,i)
    workerTimesInsurance(w,i) >= workersMin(i)*insuranceTypes(w,i)
  end-do


  OBJ:= sum(w in WORKSTATIONS) insurancePaid(w)
  minimise(OBJ)

  endTime:=time(SYS_NOW)

  writeln("Paid amount: ", getobjval)
  writeln("")

  forall(w in WORKSTATIONS) do
    writeln("Workstation ", w, ": ", getsol(workersAssigned(w)),
    " workers", "; Insurance paid: ", getsol(insurancePaid(w)))
  end-do
  writeln("")

  writeln("Time elapsed: ", time(endTime-startTime))

end-model
