text: /* Copyright 2021, Gurobi Optimization, LLC */
  
  /* Assign workers to shifts; each worker may or may not be available on a
     particular day. We use Pareto optimization to solve the model:
     first, we minimize the linear sum of the slacks. Then, we constrain
     the sum of the slacks, and we minimize a quadratic objective that
     tries to balance the workload among the workers. */
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <math.h>
  #include <string.h>
  #include "gurobi_c.h"
  
  int solveAndPrint(GRBmodel* model,
                    int nShifts, int nWorkers, char** Workers,
                    int* status);
  
  
  #define xcol(w,s)         nShifts*w+s
  #define slackcol(s)       nShifts*nWorkers+s
  #define totSlackcol       nShifts*(nWorkers+1)
  #define totShiftscol(w)   nShifts*(nWorkers+1)+1+w
  #define avgShiftscol      (nShifts+1)*(nWorkers+1)
  #define diffShiftscol(w)  (nShifts+1)*(nWorkers+1)+1+w
  #define MAXSTR     128
  
  
  int
  main(int   argc,
       char *argv[])
  {
    GRBenv   *env = NULL;
    GRBmodel *model = NULL;
    int       error = 0, status;
    int       s, w, col;
    int      *cbeg = NULL;
    int      *cind = NULL;
    int       idx;
    double   *cval = NULL;
    char     *sense = NULL;
    char      vname[MAXSTR], cname[MAXSTR];
    double    val;
  
    /* Sample data */
    const int nShifts = 14;
    const int nWorkers = 7;
  
    /* Sets of days and workers */
    char* Shifts[] =
      { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
        "Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
        "Sun14" };
    char* Workers[] =
      { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
  
    /* Number of workers required for each shift */
    double shiftRequirements[] =
      { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
  
    /* Worker availability: 0 if the worker is unavailable for a shift */
    double availability[][14] =
      { { 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
        { 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 },
        { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 },
        { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 },
        { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
  
    /* Create environment */
    error = GRBloadenv(&env, "workforce4.log");
    if (error) goto QUIT;
  
    /* Create initial model */
    error = GRBnewmodel(env, &model, "workforce4",
                        (nShifts + 1) * (nWorkers + 1),
                        NULL, NULL, NULL, NULL, NULL);
    if (error) goto QUIT;
  
    /* Initialize assignment decision variables:
       x[w][s] == 1 if worker w is assigned to shift s.
       This is no longer a pure assignment model, so we must
       use binary variables. */
    for (w = 0; w < nWorkers; ++w)
    {
      for (s = 0; s < nShifts; ++s)
      {
        col = xcol(w, s);
        sprintf(vname, "%s.%s", Workers[w], Shifts[s]);
        error = GRBsetcharattrelement(model, "VType", col, GRB_BINARY);
        if (error) goto QUIT;
        error = GRBsetdblattrelement(model, "UB", col, availability[w][s]);
        if (error) goto QUIT;
        error = GRBsetstrattrelement(model, "VarName", col, vname);
        if (error) goto QUIT;
      }
    }
  
    /* Initialize slack decision variables */
    for (s = 0; s < nShifts; ++s)
    {
      sprintf(vname, "%sSlack", Shifts[s]);
      error = GRBsetstrattrelement(model, "VarName", slackcol(s), vname);
      if (error) goto QUIT;
    }
  
    /* Initialize total slack decision variable */
    error = GRBsetstrattrelement(model, "VarName", totSlackcol, "totSlack");
    if (error) goto QUIT;
  
    /* Initialize variables to count the total shifts worked by each worker */
    for (w = 0; w < nWorkers; ++w)
    {
      sprintf(vname, "%sTotShifts", Workers[w]);
      error = GRBsetstrattrelement(model, "VarName", totShiftscol(w), vname);
      if (error) goto QUIT;
    }
  
    /* The objective is to minimize the sum of the slacks */
    error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
    if (error) goto QUIT;
    error = GRBsetdblattrelement(model, "Obj", totSlackcol, 1.0);
    if (error) goto QUIT;
  
    /* Make space for constraint data */
    cbeg = malloc(sizeof(int) * nShifts);
    if (!cbeg) goto QUIT;
    cind = malloc(sizeof(int) * nShifts * (nWorkers + 1));
    if (!cind) goto QUIT;
    cval = malloc(sizeof(double) * nShifts * (nWorkers + 1));
    if (!cval) goto QUIT;
    sense = malloc(sizeof(char) * nShifts);
    if (!sense) goto QUIT;
  
    /* Constraint: assign exactly shiftRequirements[s] workers
       to each shift s, plus the slack */
    idx = 0;
    for (s = 0; s < nShifts; ++s)
    {
      cbeg[s] = idx;
      sense[s] = GRB_EQUAL;
      for (w = 0; w < nWorkers; ++w)
      {
        cind[idx] = xcol(w, s);
        cval[idx++] = 1.0;
      }
      cind[idx] = slackcol(s);
      cval[idx++] = 1.0;
    }
    error = GRBaddconstrs(model, nShifts, idx, cbeg, cind, cval, sense,
                          shiftRequirements, Shifts);
    if (error) goto QUIT;
  
    /* Constraint: set totSlack column equal to the total slack */
    idx = 0;
    for (s = 0; s < nShifts; ++s)
    {
      cind[idx] = slackcol(s);
      cval[idx++] = 1.0;
    }
    cind[idx] = totSlackcol;
    cval[idx++] = -1.0;
    error = GRBaddconstr(model, idx, cind, cval, GRB_EQUAL,
                         0.0, "totSlack");
    if (error) goto QUIT;
  
    /* Constraint: compute the total number of shifts for each worker */
    for (w = 0; w < nWorkers; ++w)
    {
      idx = 0;
      for (s = 0; s < nShifts; ++s)
      {
        cind[idx] = xcol(w,s);
        cval[idx++] = 1.0;
      }
      sprintf(cname, "totShifts%s", Workers[w]);
      cind[idx] = totShiftscol(w);
      cval[idx++] = -1.0;
      error = GRBaddconstr(model, idx, cind, cval, GRB_EQUAL, 0.0, cname);
      if (error) goto QUIT;
    }
  
    /* Optimize */
    error = solveAndPrint(model, nShifts, nWorkers, Workers, &status);
    if (error) goto QUIT;
    if (status != GRB_OPTIMAL) goto QUIT;
  
    /* Constrain the slack by setting its upper and lower bounds */
    error = GRBgetdblattrelement(model, "X", totSlackcol, &val);
    if (error) goto QUIT;
    error = GRBsetdblattrelement(model, "UB", totSlackcol, val);
    if (error) goto QUIT;
    error = GRBsetdblattrelement(model, "LB", totSlackcol, val);
    if (error) goto QUIT;
  
    /* Variable to count the average number of shifts worked */
    error = GRBaddvar(model, 0, NULL, NULL, 0, 0, GRB_INFINITY, GRB_CONTINUOUS,
                      "avgShifts");
    if (error) goto QUIT;
  
    /* Variables to count the difference from average for each worker;
       note that these variables can take negative values. */
    error = GRBaddvars(model, nWorkers, 0, NULL, NULL, NULL, NULL, NULL, NULL,
                       NULL, NULL);
    if (error) goto QUIT;
  
    for (w = 0; w < nWorkers; ++w)
    {
      sprintf(vname, "%sDiff", Workers[w]);
      error = GRBsetstrattrelement(model, "VarName", diffShiftscol(w), vname);
      if (error) goto QUIT;
      error = GRBsetdblattrelement(model, "LB", diffShiftscol(w), -GRB_INFINITY);
      if (error) goto QUIT;
    }
  
    /* Constraint: compute the average number of shifts worked */
    idx = 0;
    for (w = 0; w < nWorkers; ++w)
    {
      cind[idx] = totShiftscol(w);
      cval[idx++] = 1.0;
    }
    cind[idx] = avgShiftscol;
    cval[idx++] = -nWorkers;
    error = GRBaddconstr(model, idx, cind, cval, GRB_EQUAL, 0.0, "avgShifts");
    if (error) goto QUIT;
  
    /* Constraint: compute the difference from the average number of shifts */
    for (w = 0; w < nWorkers; ++w)
    {
      cind[0] = totShiftscol(w);
      cval[0] = 1.0;
      cind[1] = avgShiftscol;
      cval[1] = -1.0;
      cind[2] = diffShiftscol(w);
      cval[2] = -1.0;
      sprintf(cname, "%sDiff", Workers[w]);
      error = GRBaddconstr(model, 3, cind, cval, GRB_EQUAL, 0.0, cname);
      if (error) goto QUIT;
    }
  
    /* Objective: minimize the sum of the square of the difference from the
       average number of shifts worked */
    error = GRBsetdblattrelement(model, "Obj", totSlackcol, 0.0);
    if (error) goto QUIT;
  
    for (w = 0; w < nWorkers; ++w)
    {
      cind[w] = diffShiftscol(w);
      cval[w] = 1.0;
    }
    error = GRBaddqpterms(model, nWorkers, cind, cind, cval);
    if (error) goto QUIT;
  
    /* Optimize */
    error = solveAndPrint(model, nShifts, nWorkers, Workers, &status);
    if (error) goto QUIT;
    if (status != GRB_OPTIMAL) goto QUIT;
  
  
  QUIT:
  
    /* Error reporting */
  
    if (error)
    {
      printf("ERROR: %s\n", GRBgeterrormsg(env));
      exit(1);
    }
  
    /* Free data */
  
    free(cbeg);
    free(cind);
    free(cval);
    free(sense);
  
    /* Free model */
  
    GRBfreemodel(model);
  
    /* Free environment */
  
    GRBfreeenv(env);
  
    return 0;
  }
  
  
  int solveAndPrint(GRBmodel* model,
                    int nShifts, int nWorkers, char** Workers,
                    int* status)
  {
    int error, w;
    double val;
  
    error = GRBoptimize(model);
    if (error) return error;
  
    error = GRBgetintattr(model, "Status", status);
    if (error) return error;
  
    if ((*status == GRB_INF_OR_UNBD) || (*status == GRB_INFEASIBLE) ||
        (*status == GRB_UNBOUNDED))
    {
      printf("The model cannot be solved "
             "because it is infeasible or unbounded\n");
      return 0;
    }
    if (*status != GRB_OPTIMAL)
    {
      printf("Optimization was stopped with status %i\n", *status);
      return 0;
    }
  
    /* Print total slack and the number of shifts worked for each worker */
    error = GRBgetdblattrelement(model, "X", totSlackcol, &val);
    if (error) return error;
  
    printf("\nTotal slack required: %f\n", val);
    for (w = 0; w < nWorkers; ++w)
    {
      error = GRBgetdblattrelement(model, "X", totShiftscol(w), &val);
      if (error) return error;
      printf("%s worked %f shifts\n", Workers[w], val);
    }
    printf("\n");
    return 0;
  }
  