Initializing help system before first use

Goal Programming


Type: Programming
Rating: 4 (medium-difficult)
Description: Implementation of preemptive and Archimedian Goal Programming algorithms working on objective functions or constraints. The Goal Programming directives are input from the configuration files *.gol.
File(s): goal_example.c
Data file(s): goalcon3.mat.gz, goalobj1.mat.gz, ga3.gol, gb3.gol, gc1.gol, gd1.gol

goal_example.c
/***********************************************************************
   Xpress Optimizer Examples
   =========================

   file goal_example.c
   ```````````````````
   Demonstrates how to implement traditional goal programming 
   using Xpress.    

   (c) 2017 Fair Isaac Corporation       
***********************************************************************/

#include "stdlib.h"
#include "stdio.h"
#include "xprs.h"

#include "math.h" 
#include "memory.h"
#include "string.h"
#include "ctype.h"

#if defined(WIN32) || defined(_WIN32)
#else
#define strcmpi stricmp
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif

#define ERROR_EXIT(msg)                                                      \
{                                                                            \
  printf("ERROR:%s(%i):%s\n", __FILE__, __LINE__, msg);                      \
  abort(); /*__asm{ int 3 };;*/                                                                   \
}                                                                            \

#define CHECKED_RETURN(function, args)                                       \
{                                                                            \
  int _nReturn;                                                              \
  if ( ((_nReturn = function args)  !=0 ) )  {                               \
    ERROR_EXIT(#function #args)                                              \
  }                                                                          \
}

static void XPRS_CC Message(XPRSprob prob, void* my_object, const char *msg, int len, int msgtype)
{
  switch(msgtype)
  {
  case 4: /* error */
  case 3: /* warning */
  case 2: /* dialogue */
  case 1: /* information */
    printf("%s\n", msg);
    break;
  default: /* exiting - buffers need flushing */
    fflush(stdout);
    break;
  }

}

struct GoalConstraint_s {
  double dCost;
  int iRow;
};
typedef struct GoalConstraint_s * GoalConstraint;

struct GoalObjective_s {
  double dTolerance;
  int iRow;
  int iOptimizationSense;
  int bFractionIsPercentage;
};
typedef struct GoalObjective_s * GoalObjective;

struct SlackPair_s {
  int iSlack_Pos;
  int iSlack_Neg;
};
typedef struct SlackPair_s * SlackPair;

struct Goal_s {
  int bArchimedianOtherwisePreemptive;
  int bConstraintsOtherwiseObjective;
  GoalConstraint gc;
  int gc_count;
  GoalObjective go;
  int go_count;
};
typedef struct Goal_s * Goal;

static void AddSlack(XPRSprob prob, const int iRow, const int iSlackSign, const double dSlackCost, int * const iNewColIndex)
{ 
  double obj = dSlackCost, dmatval = (iSlackSign > 0 ? 1.0 : -1.0);
  double bdl = 0.0, bdu = XPRS_PLUSINFINITY;
  int mrwind = iRow, mstart[] = {0, 1};

  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_COLS, iNewColIndex) ) /* Slack will go on the end of the column set */
  CHECKED_RETURN( XPRSaddcols, (prob, 1 /*newcol*/, 1 /*newnz*/, &obj, mstart, &mrwind, &dmatval, &bdl, &bdu) )
}

static int GcArray_AddRecord(GoalConstraint *gc, int * const gc_count)
{
  if(!*gc) {
    if(!(*gc = (GoalConstraint) malloc(sizeof(**gc) * (*gc_count + 1)))) {
      goto exit_with_failure;
    }
  } else {
    GoalConstraint gc_temp;
    if(!(gc_temp = (GoalConstraint) realloc(*gc, sizeof(**gc) * (*gc_count + 1)))) {
      goto exit_with_failure;
    }
    *gc = gc_temp;
  }
  (*gc_count)++;
  return 0;
exit_with_failure:;
  return 1;
}

static int GoArray_AddRecord(GoalObjective *go, int * const go_count)
{
  if(!*go) {
    if(!(*go = (GoalObjective) malloc(sizeof(**go) * (*go_count + 1)))) {
      goto exit_with_failure;
    }
  } else {
    GoalObjective go_temp;
    if(!(go_temp = (GoalObjective) realloc(*go, sizeof(**go) * (*go_count + 1)))) {
      goto exit_with_failure;
    }
    *go = go_temp;
  }
  (*go_count)++;
  return 0;
exit_with_failure:;
  return 1;
}

static int SpArray_AddRecord(SlackPair *sp, int * const sp_count)
{
  if(!*sp) {
    if(!(*sp = (SlackPair) malloc(sizeof(**sp) * (*sp_count + 1)))) {
      goto exit_with_failure;
    }
  } else {
    SlackPair sp_temp;
    if(!(sp_temp = (SlackPair) realloc(*sp, sizeof(**sp) * (*sp_count + 1)))) {
      goto exit_with_failure;
    }
    *sp = sp_temp;
  }
  (*sp_count)++;
  return 0;
exit_with_failure:;
  return 1;
}

static void RelaxConstraint(XPRSprob prob, const int iRow, int bArchimedianOtherwisePreemptive, const double dArchimedianWeight, SlackPair *sp, int * const sp_count)
{
  char qrtype;
  double dCost = (bArchimedianOtherwisePreemptive ? dArchimedianWeight : 0.0);
  SlackPair sp_;
  CHECKED_RETURN( XPRSgetrowtype, (prob, &qrtype, iRow, iRow) )
  CHECKED_RETURN( SpArray_AddRecord, (sp, sp_count) )
  sp_ = &(*sp)[*sp_count - 1];
  sp_->iSlack_Pos = -1;
  sp_->iSlack_Neg = -1;
  switch(qrtype) {
  case('N') :
    break;
  case('L') :
  case('P') :
    AddSlack(prob, iRow, -1 /*iSlackSign*/, dCost, &sp_->iSlack_Neg);
    break;
  case('G') :
  case('Q') :
    AddSlack(prob, iRow,  1 /*iSlackSign*/, dCost, &sp_->iSlack_Pos);
    break;
  case('E') :
  case('R') :
    AddSlack(prob, iRow,  1 /*iSlackSign*/, dCost, &sp_->iSlack_Pos);
    AddSlack(prob, iRow, -1 /*iSlackSign*/, dCost, &sp_->iSlack_Neg);
    break;
  default :
    ERROR_EXIT("Unexpected value")
  }
}

static void RunGoal_Preemptive_Constraint(XPRSprob prob, const int bRunMip, GoalConstraint GoalRows, const int iGoalRows_Count, int * const bIsInfeasible)
{
  int i, j, nRows, nCols, iStatus, bHaveBasis;
  double dObjVal, dZero = 0.0, dOne = 1.0;
  SlackPair sp = NULL;
  int sp_count = 0;
  char cBoth = 'B';
  int *rstatus = NULL, *cstatus = NULL;
  int bWriteGoalProbs = 0;

  /* Ensure the problem is in the original state */
  CHECKED_RETURN( XPRSpostsolve, (prob) ) 

  /* Add the appropriate slacks for the goals */
  for(i = 0; i < iGoalRows_Count; i++) {
    RelaxConstraint(prob, GoalRows[i].iRow, 0 /*bArchimedianOtherwisePreemptive*/, 0.0 /*dArchimedianWeight*/, &sp, &sp_count);
  }

  /* Setup arrays to store basis and zero all costs */
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_ROWS, &nRows) ) 
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_COLS, &nCols) ) 

  if(!(rstatus = (int *)malloc(sizeof(int) * nRows))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(cstatus = (int *)malloc(sizeof(int) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  for(j = 0; j < nCols; j++) {
    CHECKED_RETURN( XPRSchgobj, (prob, 1, &j, &dZero) )
  }

  if(bWriteGoalProbs) {
    CHECKED_RETURN( XPRSwriteprob, (prob, "goal\\pc_goal0", "") )
  }

  /* Loop through the goal constraints */
  bHaveBasis = 0;
  for(i = 0; i < sp_count; i++) {

    /* Skip non-binding goal rows */
    if(sp[i].iSlack_Neg < 0 && sp[i].iSlack_Pos < 0) continue;

    /* Set the slack cost(s) on the goal row to 1.0 */
    if(sp[i].iSlack_Neg >= 0) CHECKED_RETURN( XPRSchgobj, (prob, 1, &sp[i].iSlack_Neg, &dOne) )
    if(sp[i].iSlack_Pos >= 0) CHECKED_RETURN( XPRSchgobj, (prob, 1, &sp[i].iSlack_Pos, &dOne) )

    if(bHaveBasis) {
      /* We have a basis from the last goal solve use this to hot start this solve */
      CHECKED_RETURN( XPRSloadbasis, (prob, rstatus, cstatus) )
    }

    if(bRunMip) {
      /* Ensure that we restart the MIP solve each time */
      CHECKED_RETURN( XPRSinitglobal, (prob) )
    }

    if(bWriteGoalProbs) {
      char buff[256];
      sprintf(buff, "goal\\pc_goal%i", i + 1);
      CHECKED_RETURN( XPRSwriteprob, (prob, buff, "") )
    }

    /* Solve the LP and get the solve status */
    CHECKED_RETURN( XPRSminim, (prob, "") )
    CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_LPSTATUS, &iStatus) )

    /* Exit if the problem is infeasible even with the slacks we have added to the rows */
    if(iStatus != XPRS_LP_OPTIMAL) goto exit_with_infeasible;

    CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_LPOBJVAL, &dObjVal) )
    if(dObjVal != 0.0) {  
      /* Exit if we can't satisfy the current goal constraint */
      goto exit_with_infeasible;
    }

    /* Update the hot start basis */
    if(!bHaveBasis) {
      CHECKED_RETURN( XPRSgetbasis, (prob, rstatus, cstatus) )
      bHaveBasis = 1;
    }

    /* Run the MIP search if required */
    if(bRunMip) {
      /* Solve the MIP search and get the solve status */
      CHECKED_RETURN( XPRSglobal, (prob) )
      CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_MIPSTATUS, &iStatus) )

      /* Exit if the problem is infeasible even with the slacks we have added to the rows */
      if(iStatus != XPRS_MIP_OPTIMAL) goto exit_with_infeasible;

      CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_MIPOBJVAL, &dObjVal) )
      if(dObjVal != 0.0) {  
        /* Exit if we can't satisfy the current goal constraint */
        goto exit_with_infeasible;
      }
    }

    /* 
    Restore the problem to the original state and fix the 
    slacks on the current goal row to zero ready to process
    the next goal row.
    */
    CHECKED_RETURN( XPRSpostsolve, (prob) ) 
    if(sp[i].iSlack_Neg >= 0) CHECKED_RETURN( XPRSchgbounds, (prob, 1, &sp[i].iSlack_Neg, &cBoth, &dZero) )
    if(sp[i].iSlack_Pos >= 0) CHECKED_RETURN( XPRSchgbounds, (prob, 1, &sp[i].iSlack_Pos, &cBoth, &dZero) )

  }

  *bIsInfeasible = 0;
  if(sp) free(sp); sp = NULL;
  if(rstatus) free(rstatus); rstatus = NULL;
  if(cstatus) free(cstatus); cstatus = NULL;
  return;
exit_with_infeasible:;
  *bIsInfeasible = 1;
  if(sp) free(sp); sp = NULL;
  if(rstatus) free(rstatus); rstatus = NULL;
  if(cstatus) free(cstatus); cstatus = NULL;
  return;
}

static void RunGoal_Preemptive_Objective(XPRSprob prob, const int bRunMip, GoalObjective GoalObjs, const int iGoalGoalObjs_Count, int * const bIsInfeasible, double * const dObjRhs_)
{
  int i, j, nRows, nCols, iStatus, bHaveBasis, ncoeffs;
  double dObjVal, dZero = 0.0, dOne = 1.0;
  int *rstatus = NULL, *cstatus = NULL;
  int bWriteGoalProbs = 0;
  int mstart[2];
  int *mclind = NULL;
  double *dcmatval = NULL;
  double dRhs, dMatRhs, dMatObRhs;

  /* Ensure the problem is in the original state */
  CHECKED_RETURN( XPRSpostsolve, (prob) ) 

  /* Setup arrays to store basis and zero all costs */
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_ROWS, &nRows) ) 
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_COLS, &nCols) ) 

  if(!(rstatus = (int *)malloc(sizeof(int) * nRows))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(cstatus = (int *)malloc(sizeof(int) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(mclind = (int *)malloc(sizeof(int) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(dcmatval = (double *)malloc(sizeof(double) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  if(bWriteGoalProbs) {
    CHECKED_RETURN( XPRSwriteprob, (prob, "goal\\po_goal0", "") )
  }

  /* Loop through the goal objectives */
  bHaveBasis = 0;
  for(i = 0; i < iGoalGoalObjs_Count; i++) {

    CHECKED_RETURN( XPRSgetrhs, (prob, &dMatRhs, GoalObjs[i].iRow, GoalObjs[i].iRow) )
    CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_OBJRHS, &dMatObRhs) )

    /* Copy the row coefficients into the objective */
    CHECKED_RETURN( XPRSgetrows, (prob, mstart, mclind, dcmatval, nCols, &ncoeffs, GoalObjs[i].iRow, GoalObjs[i].iRow) );
    for(j = 0; j < nCols; j++) {
      CHECKED_RETURN( XPRSchgobj, (prob, 1, &j, &dZero) )
    }
    CHECKED_RETURN( XPRSchgobj, (prob, ncoeffs, mclind, dcmatval) )

    if(bHaveBasis) {
      /* We have a basis from the last goal solve use this to hot start this solve */
      CHECKED_RETURN( XPRSloadbasis, (prob, rstatus, cstatus) )
    }

    if(bRunMip) {
      /* Ensure that we restart the MIP solve each time */
      CHECKED_RETURN( XPRSinitglobal, (prob) )
    }

    if(bWriteGoalProbs) {
      char buff[256];
      sprintf(buff, "goal\\po_goal%i", i + 1);
      CHECKED_RETURN( XPRSwriteprob, (prob, buff, "") )
    }

    /* Solve the LP and get the solve status */
    if(GoalObjs[i].iOptimizationSense >= 0) {
      CHECKED_RETURN( XPRSminim, (prob, "") )
    } else {
      CHECKED_RETURN( XPRSmaxim, (prob, "") )
    }
    CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_LPSTATUS, &iStatus) )

    /* Exit if the problem is infeasible even with the slacks we have added to the rows */
    if(iStatus != XPRS_LP_OPTIMAL) goto exit_with_infeasible;

    CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_LPOBJVAL, &dObjVal) )

    /* Update the hot start basis */
    if(!bHaveBasis) {
      CHECKED_RETURN( XPRSgetbasis, (prob, rstatus, cstatus) )
      bHaveBasis = 1;
    }

    /* Run the MIP search if required */
    if(bRunMip) {
      /* Solve the MIP search and get the solve status */
      CHECKED_RETURN( XPRSglobal, (prob) )
      CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_MIPSTATUS, &iStatus) )

      /* Exit if the problem is infeasible even with the slacks we have added to the rows */
      if(iStatus != XPRS_MIP_OPTIMAL) goto exit_with_infeasible;

      CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_MIPOBJVAL, &dObjVal) )
    }

    /* Constraint the row to the objective we have calculated */
    dObjVal -= dMatRhs + dMatObRhs;
    if(GoalObjs[i].iOptimizationSense >= 0) {
      char qLE = 'L';
      CHECKED_RETURN( XPRSchgrowtype, (prob, 1, &GoalObjs[i].iRow, &qLE) )
      if(GoalObjs[i].bFractionIsPercentage) {
        dRhs = dObjVal + fabs(dObjVal) * GoalObjs[i].dTolerance * 0.01 + dMatRhs; 
      } else {
        dRhs = dObjVal + GoalObjs[i].dTolerance + dMatRhs; 
      }
    } else {
      char qGE = 'G';
      CHECKED_RETURN( XPRSchgrowtype, (prob, 1, &GoalObjs[i].iRow, &qGE) )
      if(GoalObjs[i].bFractionIsPercentage) {
        dRhs = dObjVal - fabs(dObjVal) * GoalObjs[i].dTolerance * 0.01 + dMatRhs;   
      } else {
        dRhs = dObjVal - GoalObjs[i].dTolerance + dMatRhs;   
      }
    }
    if(dObjRhs_) *dObjRhs_ = dMatRhs + dMatObRhs;
    CHECKED_RETURN( XPRSchgrhs, (prob, 1, &GoalObjs[i].iRow, &dRhs) )
    
  }

  *bIsInfeasible = 0;
  if(rstatus) free(rstatus); rstatus = NULL;
  if(cstatus) free(cstatus); cstatus = NULL;
  if(mclind) free(mclind); mclind = NULL;
  if(dcmatval) free(dcmatval); dcmatval = NULL;
  return;
exit_with_infeasible:;
  *bIsInfeasible = 1;
  if(rstatus) free(rstatus); rstatus = NULL;
  if(cstatus) free(cstatus); cstatus = NULL;
  if(mclind) free(mclind); mclind = NULL;
  if(dcmatval) free(dcmatval); dcmatval = NULL;
  return;
}

static void RunGoal_Archimedian_Constraint(XPRSprob prob, const int bRunMip, GoalConstraint GoalRows, const int iGoalRows_Count, int * const bIsInfeasible)
{
  int i, j, iStatus, nCols;
  double dObjVal, dZero = 0.0;
  SlackPair sp = NULL;
  int sp_count = 0;
  int bWriteGoalProbs = 0;

  /* Ensure the problem is in the original state */
  CHECKED_RETURN( XPRSpostsolve, (prob) ) 

  /* Zero all costs */
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_COLS, &nCols) ) 
  for(j = 0; j < nCols; j++) {
    CHECKED_RETURN( XPRSchgobj, (prob, 1, &j, &dZero) )
  }

  /* Add the appropriate slacks for the goals (with associated costs for satisfying those goals) */
  for(i = 0; i < iGoalRows_Count; i++) {
    RelaxConstraint(prob, GoalRows[i].iRow, 1 /*bArchimedianOtherwisePreemptive*/, GoalRows[i].dCost /*dArchimedianWeight*/, &sp, &sp_count);
  }

  if(bWriteGoalProbs) {
    CHECKED_RETURN( XPRSwriteprob, (prob, "goal\\ac_goal", "") )
  }

  if(bRunMip) {
    /* Ensure that we restart the MIP solve each time */
    CHECKED_RETURN( XPRSinitglobal, (prob) )
  }

  /* Solve the LP and get the solve status */
  CHECKED_RETURN( XPRSminim, (prob, "") )
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_LPSTATUS, &iStatus) )

  /* Exit if the problem is infeasible even with the slacks we have added to the rows */
  if(iStatus != XPRS_LP_OPTIMAL) goto exit_with_infeasible;

  /* Run the MIP search if required */
  if(bRunMip) {
    /* Solve the MIP search and get the solve status */
    CHECKED_RETURN( XPRSglobal, (prob) )
    CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_MIPSTATUS, &iStatus) )

    /* Exit if the problem is infeasible even with the slacks we have added to the rows */
    if(iStatus != XPRS_MIP_OPTIMAL) goto exit_with_infeasible;
  }

  *bIsInfeasible = 0;
  if(sp) free(sp); sp = NULL;
  return;
exit_with_infeasible:;
  *bIsInfeasible = 1;
  if(sp) free(sp); sp = NULL;
  return;
}

static void RunGoal_Archimedian_Objective(XPRSprob prob, const int bRunMip, GoalObjective GoalObjs, const int iGoalGoalObjs_Count, int * const bIsInfeasible, double * const dObjRhs_)
{
  int i, j, nRows, nCols, iStatus, ncoeffs;
  double dObjVal;
  int bWriteGoalProbs = 0;
  int mstart[2];
  int *mclind = NULL;
  double *dcmatval = NULL;
  double *dcost = NULL;
  double dObjRhs, dMatRhs;

  /* Ensure the problem is in the original state */
  CHECKED_RETURN( XPRSpostsolve, (prob) ) 

  /* Setup arrays to store basis and zero all costs */
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_ROWS, &nRows) ) 
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_COLS, &nCols) ) 

  if(!(mclind = (int *)malloc(sizeof(int) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(dcmatval = (double *)malloc(sizeof(double) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  if(!(dcost = (double *)malloc(sizeof(double) * nCols))) {
    ERROR_EXIT("Malloc failure")
  }

  memset(dcost, 0, sizeof(double) * nCols);

  /* Loop through the goal objectives and sum up the row coefficients into the cost function */
  CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_OBJRHS, &dObjRhs) )
  for(i = 0; i < iGoalGoalObjs_Count; i++) {

    CHECKED_RETURN( XPRSgetrows, (prob, mstart, mclind, dcmatval, nCols, &ncoeffs, GoalObjs[i].iRow, GoalObjs[i].iRow) );

    for(j = 0; j < ncoeffs; j++) {
      dcost[mclind[j]] += dcmatval[j] * GoalObjs[i].dTolerance * GoalObjs[i].iOptimizationSense;
    }

    CHECKED_RETURN( XPRSgetrhs, (prob, &dMatRhs, GoalObjs[i].iRow, GoalObjs[i].iRow) )

    dObjRhs += dMatRhs * GoalObjs[i].dTolerance * GoalObjs[i].iOptimizationSense;
    
  }

  /* Add the new cost function to the problem */
  for(j = 0; j < nCols; j++) {
    CHECKED_RETURN( XPRSchgobj, (prob, 1, &j, &dcost[j]) )
  }

  if(bWriteGoalProbs) {
    CHECKED_RETURN( XPRSwriteprob, (prob, "goal\\ao_goal", "") )
  }

  if(bRunMip) {
    /* Ensure that we restart the MIP solve each time */
    CHECKED_RETURN( XPRSinitglobal, (prob) )
  }

  /* Solve the LP and get the solve status */
  CHECKED_RETURN( XPRSminim, (prob, "") )

  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_LPSTATUS, &iStatus) )

  /* Exit if the problem is infeasible even with the slacks we have added to the rows */
  if(iStatus != XPRS_LP_OPTIMAL) goto exit_with_infeasible;

  CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_LPOBJVAL, &dObjVal) )

  /* Run the MIP search if required */
  if(bRunMip) {
    /* Solve the MIP search and get the solve status */
    CHECKED_RETURN( XPRSglobal, (prob) )
    CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_MIPSTATUS, &iStatus) )

    /* Exit if the problem is infeasible even with the slacks we have added to the rows */
    if(iStatus != XPRS_MIP_OPTIMAL) goto exit_with_infeasible;

    CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_MIPOBJVAL, &dObjVal) )
  }


  if(dObjRhs_) *dObjRhs_ = dObjRhs;
  *bIsInfeasible = 0;
  if(dcost) free(dcost); dcost = NULL;
  if(mclind) free(mclind); mclind = NULL;
  if(dcmatval) free(dcmatval); dcmatval = NULL;
  return;
exit_with_infeasible:;
  if(dObjRhs_) *dObjRhs_ = dObjRhs;
  *bIsInfeasible = 1;
  if(dcost) free(dcost); dcost = NULL;
  if(mclind) free(mclind); mclind = NULL;
  if(dcmatval) free(dcmatval); dcmatval = NULL;
  return;
}

static int Read_Record_GoalObjective(XPRSprob prob, FILE *f, const int bArchimedianOtherwisePreemptive, GoalObjective go, int * const bEof)
{
  char buff[1024], *p;

  *bEof = 0;
  memset(go, -1, sizeof(*go));

  /* Read the row name */
  if(!fgets(buff, sizeof(buff), f)) goto exit_with_eof;
  buff[strlen(buff) - 1] = '\0';
  if(!*buff) goto exit_with_eof;
  CHECKED_RETURN( XPRSgetindex, (prob, 1, buff, &go->iRow) )
  if(go->iRow < 0) {
    goto exit_with_failure;
  }

  if(bArchimedianOtherwisePreemptive) {
    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    go->dTolerance = strtod(buff, &p); if(!isspace(*p)) goto exit_with_failure;

    /* Read sense */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    if(strnicmp(buff, "mi", 2) == 0) {
      go->iOptimizationSense = 1;
    } else if(strnicmp(buff, "ma", 2) == 0) {
      go->iOptimizationSense = -1;
    } else {
      goto exit_with_failure;
    }
  } else {
    /* Read sense */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    if(strnicmp(buff, "mi", 2) == 0) {
      go->iOptimizationSense = 1;
    } else if(strnicmp(buff, "ma", 2) == 0) {
      go->iOptimizationSense = -1;
    } else {
      goto exit_with_failure;
    }   

    /* Read multiplier interpretation */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    if(tolower(*buff) == 'p') {
      go->bFractionIsPercentage = 1;
    } else if(tolower(*buff) == 'd') {
      go->bFractionIsPercentage = 0;
    } else {
      goto exit_with_failure;
    }

    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    go->dTolerance = strtod(buff, &p); if(!isspace(*p)) goto exit_with_failure;
  }
  return 0;
exit_with_failure:;
  return 1;  
exit_with_eof:;
  *bEof = 1;
  return 0;
}

static int Read_Record_GoalConstraint(XPRSprob prob, FILE *f, const int bArchimedianOtherwisePreemptive, GoalConstraint gc, int * const bEof)
{
  char buff[1024], *p;

  *bEof = 0;
  memset(gc, -1, sizeof(*gc));

  /* Read the row name */
  if(!fgets(buff, sizeof(buff), f)) goto exit_with_eof;
  buff[strlen(buff) - 1] = '\0';
  if(!*buff) goto exit_with_eof;
  CHECKED_RETURN( XPRSgetindex, (prob, 1, buff, &gc->iRow) )
  if(gc->iRow < 0) {
    goto exit_with_failure;
  }

  if(bArchimedianOtherwisePreemptive) {
    /* Read multiplier */
    if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
    gc->dCost = strtod(buff, &p); if(!isspace(*p)) goto exit_with_failure;
  }
  return 0;
exit_with_failure:;
  return 1; 
 exit_with_eof:;
  *bEof = 1;
  return 0;
}


static int Read_Goal_Directives(XPRSprob prob, const char *sFileName, Goal g)
{
  FILE *f = NULL;
  char buff[1024];
  int bEof;

  memset(g, 0, sizeof(*g));

  if(strlen(sFileName) + strlen(".gol") >= sizeof(buff)) {
    goto exit_with_failure;
  }

  if(strlen(sFileName) < strlen(".gol")) {
    strcpy(buff, sFileName);
    strcat(buff, ".gol");
  } else {
    strcpy(buff, sFileName);
    if(stricmp(buff + strlen(buff) - strlen(".gol"), ".gol")) {
      strcat(buff, ".gol");
    }
  }

  if(!(f = fopen(buff, "r"))) {
    goto exit_with_failure;
  }

  /* Read Archimedian or Preemptive */
  if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
  switch(tolower(buff[0])) {
  case('a') : g->bArchimedianOtherwisePreemptive = 1; break;
  case('p') : g->bArchimedianOtherwisePreemptive = 0; break;
  default : goto exit_with_failure;
  }

  /* Read Constraints or Objective */
  if(!fgets(buff, sizeof(buff), f)) goto exit_with_failure;
  switch(tolower(buff[0])) {
  case('c') : g->bConstraintsOtherwiseObjective = 1; break;
  case('o') : g->bConstraintsOtherwiseObjective = 0; break;
  default : goto exit_with_failure;
  }

  /* Read records to end-of-file */
  for(;;) {
    if(g->bConstraintsOtherwiseObjective) {
      CHECKED_RETURN( GcArray_AddRecord, (&g->gc, &g->gc_count) )
      CHECKED_RETURN( Read_Record_GoalConstraint, (prob, f, g->bArchimedianOtherwisePreemptive, &g->gc[g->gc_count - 1], &bEof) )
      if(bEof) {g->gc_count--; break;}
    } else {
      CHECKED_RETURN( GoArray_AddRecord, (&g->go, &g->go_count) )
      CHECKED_RETURN( Read_Record_GoalObjective, (prob, f, g->bArchimedianOtherwisePreemptive, &g->go[g->go_count - 1], &bEof) )
      if(bEof) {g->go_count--; break;}
    }
  }

  if(f) fclose(f); f = NULL;
  return 0;
exit_with_failure:;
  if(f) fclose(f); f = NULL;
  return 1;
}


void Goal_Run_One(XPRSprob prob, const char * const sGoalFileBaseName, double * const dResult)
{
  int i, bIsInfeasible, iMipEnts, bRunMip;
  struct Goal_s g;
  double dObjVal, dObjRhs;

  /* Read .gol file directives */
  CHECKED_RETURN( Read_Goal_Directives, (prob,  sGoalFileBaseName, &g) )

  /* Decide if we are solving a MIP */
  CHECKED_RETURN( XPRSgetintattrib, (prob, XPRS_MIPENTS, &iMipEnts) )
  bRunMip = !iMipEnts ? 0 : 1;

  /* Run the goal */
  dObjRhs = 0;
  if(g.bConstraintsOtherwiseObjective) {
    if(g.bArchimedianOtherwisePreemptive) {
      RunGoal_Archimedian_Constraint(prob, bRunMip, g.gc, g.gc_count, &bIsInfeasible);
    } else {
      RunGoal_Preemptive_Constraint(prob, bRunMip, g.gc, g.gc_count, &bIsInfeasible);
    }
  } else {
    if(g.bArchimedianOtherwisePreemptive) {
      RunGoal_Archimedian_Objective(prob, bRunMip, g.go, g.go_count, &bIsInfeasible, &dObjRhs);
    } else {
      RunGoal_Preemptive_Objective(prob, bRunMip, g.go, g.go_count, &bIsInfeasible, &dObjRhs);
    }
  }
  
  /* Free memory allocated in Read_Goal_Directives */
  if(g.go) free(g.go); g.go = NULL;
  if(g.gc) free(g.gc); g.gc = NULL;

  /* Log results */
  CHECKED_RETURN( XPRSgetdblattrib, (prob, XPRS_LPOBJVAL, &dObjVal) )
  dObjVal -= dObjRhs;
  *dResult = dObjVal;
}

int main(void) {
  XPRSprob prob;
  char banner[256];
  double dObjVal;

  CHECKED_RETURN( XPRSinit, ("") );

  CHECKED_RETURN( XPRSgetbanner, (banner) );
  printf("%s\n", banner);

  CHECKED_RETURN( XPRScreateprob, (&prob) );

  CHECKED_RETURN( XPRSsetcbmessage, (prob,Message,NULL) );

  #define DIRECTORY_MATRICES ""
  #define DIRECTORY_GOALFILE ""

  /* Preemptive Objective */
  CHECKED_RETURN( XPRSreadprob, (prob, DIRECTORY_MATRICES "goalobj1","") );
  Goal_Run_One(prob, DIRECTORY_GOALFILE "gc1", &dObjVal);

  /* Archimedian Objective */
  CHECKED_RETURN( XPRSreadprob, (prob, DIRECTORY_MATRICES "goalobj1","") );
  Goal_Run_One(prob, DIRECTORY_GOALFILE "gd1", &dObjVal);

  /* Preemptive Constraint */
  CHECKED_RETURN( XPRSreadprob, (prob, DIRECTORY_MATRICES "goalcon3","") );
  Goal_Run_One(prob, DIRECTORY_GOALFILE "ga3", &dObjVal);

  /* Archimedian Constraint */
  CHECKED_RETURN( XPRSreadprob, (prob, DIRECTORY_MATRICES "goalcon3","") );
  Goal_Run_One(prob, DIRECTORY_GOALFILE "gb3", &dObjVal);
  
  XPRSdestroyprob(prob);
  XPRSfree();

  return 0;
}