/* lpglpk30.c */

/*----------------------------------------------------------------------
-- This file is a part of the GLPK package.
--
-- Copyright (C) 2000, 2001 Andrew Makhorin <mao@mai2.rcnet.ru>,
--                          Department for Applied Informatics,
--                          Moscow Aviation Institute, Moscow, Russia.
--                          All rights reserved.
--
-- This code is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This software is distributed "as is" in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
----------------------------------------------------------------------*/

#include <float.h>
#include <stddef.h>
#include <glpk.h>
#include <glplib.h>
#include "lp.h"

#define error print

/*----------------------------------------------------------------------
-- This is a non-trivial example of using GLPK as a base LP solver for
-- Concorde, a program for solving Traveling Salesman Problem (TSP).
--
-- If you wish to try GLPK and Concorde together, do the following:
--
-- 1. Install GLPK (see the file 'INSTALL' in GLPK distribution).
--
-- 2. Download Concorde from
--    <http://www.caam.rice.edu/keck/concorde.html>.
--
--    Note that these GLPK interface routines were tested with Concorde
--    version 991215.
--
-- 3. Unpack and unarchive Concorde tgz-file.
--
-- 4. Copy the file you are reading into the subdirectory 'concorde/LP/'
--    keeping its original name 'lpglpk30.c'.
--
-- 5. Configure Concorde in the usual way and then build Concorde using
--    the following command:
--
--       make LPSOLVER_INTERFACE=lpglpk30.c LPSOLVER_LIB=-lglpk
--
--    (Be sure that GLPK was successfully installed before.)
--
-- 6. Run Concorde to solve your TSP instances. See also for TSPLIB,
--    a library of standard TSP instances; this library is available at
--    <http://www.iwr.uni-heidelberg.de/iwr/comopt/software/TSPLIB95/>.
--
-- Note that these interface routines doesn't include many important
-- features (for example, efficient strong branching), therefore hard
-- TSP instances can't be solved in reasonable time. This is fault of
-- GLPK implementation, not of Concorde.
----------------------------------------------------------------------*/

static int MSGLEV = 0;
/* 0  no output (error messages only)
   1  output one line per one call to GLPK solver
   2  detailed output */

static char *SOLVER = "GLPK";

#define AT_LOWER 0
#define IS_BASIC 1
#define AT_UPPER 2

struct CClp { LPI *lp; };

struct CClp_warmstart { int nrows, ncols, *rstat, *cstat; };

struct CClp_info { int nrows, ncols, *rstat, *cstat; };

/*--------------------------------------------------------------------*/

int CClp_init(CClp **lp)
{     /* INITIALIZES the LP. */
      CClp_free(lp);
      (*lp) = umalloc(sizeof(CClp));
      (*lp)->lp = NULL;
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_force_perturb(CClp *lp)
{     /* FORCES a perturbation in the LP simplex solves. */
      insist(lp == lp);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_tune_small(CClp *lp)
{     /* SETS solver options for tiny problems. */
      insist(lp == lp);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_disable_presolve(CClp *lp)
{     /* DISABLES the solvers presolve. */
      insist(lp == lp);
      return 0;
}

/*--------------------------------------------------------------------*/

void CClp_free(CClp **lp)
{     /* FREES the LP, both the allocation in CClp_init () and the
         allocation in CClp_loadlp. */
      if ((*lp) != NULL)
      {  CClp_freelp(lp);
         ufree((*lp)), (*lp) = NULL;
      }
      return;
}

/*--------------------------------------------------------------------*/

void CClp_freelp(CClp **lp)
{     /* FREES the LP loaded in CClp_loadlp (or CClp_create), but does
         not free the data allocated by CClp_init. */
      if ((*lp) != NULL && (*lp)->lp != NULL)
         glp_delete_prob((*lp)->lp), (*lp)->lp = NULL;
      return;
}

/*--------------------------------------------------------------------*/

int CClp_loadlp(CClp *lp, const char *name, int ncols, int nrows,
      int objsense, double *obj, double *rhs, char *sense, int *matbeg,
      int *matcnt, int *matind, double *matval, double *lb, double *ub)
{     /* LOADS the data into the LP.
         - name attaches a name to the LP (it can be used by the LP
           solver in io routines)
         - ncols and nrows give the number of columns and rows in the LP
         - objsense should be 1 for minimize and -1 for maximize
         - obj and rhs are arrays giving the objective function and rhs
         - sense is an array specifying 'L', 'E', or 'G' for each of the
           rows
         - matbeg, matcnt, matind, and matval give the coefficients of
           the constraint matrix in column by column order.
           matbeg gives the index of the start of each column;
           matcnt gives the number of coefficients in each column;
           matind gives the indices of the rows where the coefficients
           are located in the constraint matrix (so for column j, the
           indices are given in matcnt[j] locations starting at
           matind[matbeg[j]]; and matval gives the actual coefficients
           (organized like matind).
         - lb and ub are arrays giving the upper and lower bounds of the
           variables. */
      int i, j;
      /* create empty problem object */
      insist(lp->lp == NULL);
      lp->lp = glp_create_prob((char *)name);
      /* set objective sense */
      switch (objsense)
      {  case +1:
            /* minimization */
            glp_set_obj_sense(lp->lp, '-'); break;
         case -1:
            /* maximization */
            glp_set_obj_sense(lp->lp, '+'); break;
         default:
            insist(objsense != objsense);
      }
      /* add rows */
      for (i = 0; i < nrows; i++)
      {  int seqn, type;
         double lo, up;
         glp_new_row(lp->lp, NULL);
         seqn = i+1;
         switch (sense[i])
         {  case 'L':
               type = 'U', lo = 0.0, up = rhs[i];
               break;
            case 'E':
               type = 'S', lo = up = rhs[i];
               break;
            case 'G':
               type = 'L', lo = rhs[i], up = 0.0;
               break;
            default:
               insist(sense[i] != sense[i]);
         }
         glp_set_row_bnds(lp->lp, seqn, type, lo, up);
      }
      /* add columns and constraint coefficients */
      for (j = 0; j < ncols; j++)
      {  int seqn, type, k;
         double lo, up;
         glp_new_col(lp->lp, NULL);
         seqn = j+1;
         glp_set_obj_coef(lp->lp, seqn, obj == NULL ? 0.0 : obj[j]);
         lo = lb[j], up = ub[j];
         /* check for finite bounds */
         insist(-1e12 <= lo && lo <= up && up <= +1e12);
         type = (lo == up ? 'S' : 'D');
         glp_set_col_bnds(lp->lp, seqn, type, lo, up);
         for (k = matbeg[j]; k < matbeg[j] + matcnt[j]; k++)
            glp_new_aij(lp->lp, matind[k]+1, seqn, matval[k]);
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_create(CClp *lp, const char *name)
{     /* CREATES an empty lp. This supports an alternative to
         CClp_loadlp for loading a problem.
         - name attaches a name to the LP (it can be used by the LP
           solver in io routines). */
      insist(lp->lp == NULL);
      lp->lp = glp_create_prob((char *)name);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_new_row(CClp *lp, char sense, double rhs)
{     /* ADDS a new empty row to the lp.
         - sense is 'L', 'E', or 'G' for a <=, =, or >= constraint;
         - rhs is the right-hand side of the row. */
      int seqn;
      glp_new_row(lp->lp, NULL);
      seqn = glp_get_num_rows(lp->lp);
      switch (sense)
      {  case 'L':
            glp_set_row_bnds(lp->lp, seqn, 'U', 0.0, rhs);
            break;
         case 'E':
            glp_set_row_bnds(lp->lp, seqn, 'S', rhs, rhs);
            break;
         case 'G':
            glp_set_row_bnds(lp->lp, seqn, 'L', rhs, 0.0);
            break;
         default:
            insist(sense != sense);
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_change_sense(CClp *lp, int row, char sense)
{     /* CHANGES the sense of a row.
         - row is the row number to change;
         - sense is 'L', 'E', or 'G' to change to <=, =, or >=. */
      int type;
      double lo, up, rhs;
      glp_get_row_bnds(lp->lp, row+1, &type, &lo, &up);
      switch (type)
      {  case 'L':
            rhs = lo; break;
         case 'U':
            rhs = up; break;
         case 'S':
            rhs = lo; break;
         default:
            insist(type != type);
      }
      switch (sense)
      {  case 'L':
            type = 'U', lo = 0.0, up = rhs; break;
         case 'E':
            type = 'S', lo = up = rhs; break;
         case 'G':
            type = 'L', lo = rhs, up = 0.0; break;
         default:
            insist(sense != sense);
      }
      glp_set_row_bnds(lp->lp, row+1, type, lo, up);
      return 0;
}

/*--------------------------------------------------------------------*/

static void patch_tagx(CClp *lp)
{     /* patch statuses of non-basic variables */
      int m = glp_get_num_rows(lp->lp);
      int n = glp_get_num_cols(lp->lp);
      int k, type, tagx;
      double lo, up;
      for (k = 1; k <= m+n; k++)
      {  if (k <= m)
         {  glp_get_row_bnds(lp->lp, k, &type, &lo, &up);
            glp_get_row_soln(lp->lp, k, &tagx, NULL, NULL);
         }
         else
         {  glp_get_col_bnds(lp->lp, k-m, &type, &lo, &up);
            glp_get_col_soln(lp->lp, k-m, &tagx, NULL, NULL);
         }
         if (tagx == 'B') continue;
         switch (type)
         {  case 'F':
               tagx = 'F'; break;
            case 'L':
               tagx = 'L'; break;
            case 'U':
               tagx = 'U'; break;
            case 'D':
               if (!(tagx == 'L' || tagx == 'U'))
                  tagx = (fabs(lo) <= fabs(up) ? 'L' : 'U');
               break;
            case 'S':
               tagx = 'S'; break;
            default:
               insist(type != type);
         }
         if (k <= m)
            glp_put_row_soln(lp->lp, k, tagx, 0.0, 0.0);
         else
            glp_put_col_soln(lp->lp, k-m, tagx, 0.0, 0.0);
      }
      return;
}

/*--------------------------------------------------------------------*/

int CClp_opt(CClp *lp, int method)
{     /* CALLS designated LP solution method. */
      struct spx1 parm;
      int stat, ret;
      if (MSGLEV >= 1)
      {  int m = glp_get_num_rows(lp->lp);
         int n = glp_get_num_cols(lp->lp);
         int nz = glp_get_num_nz(lp->lp);
         print("CClp_opt: %-11s m = %d; n = %d; nz = %d",
            method == CClp_METHOD_DUAL ? "(dual)" : "(primal)",
            m, n, nz);
      }
      patch_tagx(lp);
      glp_init_spx1(&parm);
      parm.initb = 1;
      parm.dual = CClp_METHOD_DUAL ? 1 : 0;
      switch (MSGLEV)
      {  case 0:
            parm.quiet = 1, parm.delay = 1e6, parm.freq = 1e6;
            break;
         case 1:
            parm.quiet = 1, parm.delay = 5.0, parm.freq = 1.0;
            break;
         case 2:
            parm.quiet = 0, parm.delay = 0.0, parm.freq = 1.0;
            break;
         default:
            insist(MSGLEV != MSGLEV);
      }
      ret = glp_simplex1(lp->lp, &parm);
      if (ret == +2)
      {  print("CClp_opt: restarting from standard initial basis...");
         parm.initb = 0;
         ret = glp_simplex1(lp->lp, &parm);
      }
      if (ret != 0)
      {  error("CClp_opt: glp_simplex1() failed; return code = %d", ret)
            ;
         ret = 1;
         goto done;
      }
      stat = glp_get_status(lp->lp);
      if (stat == GLP_OPT)
         ret = 0;
      else if (stat == GLP_NOFEAS)
         ret = 2;
      else
      {  error("CClp_opt: optimization status = %d", stat);
         ret = 1;
      }
done: return ret;
}

/*--------------------------------------------------------------------*/

int CClp_limited_dualopt(CClp *lp, int iterationlim, int *status,
      double *objupperlim)
{     /* CALLS the dual simplex method with a limit on the number of
         pivots.
         - upperbound it is used to cutoff the dual simplex method (when
           the objective value reaches upperbound); it can be NULL;
         - status returns the status of the optimization (it can be
           NULL). */
      struct spx1 parm;
      int stat, ret;
      if (MSGLEV >= 1)
      {  int m = glp_get_num_rows(lp->lp);
         int n = glp_get_num_cols(lp->lp);
         int nz = glp_get_num_nz(lp->lp);
         print("CClp_limited_dualopt: m = %d; n = %d; nz = %d", m, n,
            nz);
      }
      patch_tagx(lp);
      glp_init_spx1(&parm);
      parm.initb = 1;
      parm.dual = 1;
      parm.max_iter = iterationlim;
      if (objupperlim != NULL) parm.obj_max = *objupperlim;
      switch (MSGLEV)
      {  case 0:
            parm.quiet = 1, parm.delay = 1e6, parm.freq = 1e6;
            break;
         case 1:
            parm.quiet = 1, parm.delay = 5.0, parm.freq = 1.0;
            break;
         case 2:
            parm.quiet = 0, parm.delay = 0.0, parm.freq = 1.0;
            break;
         default:
            insist(MSGLEV != MSGLEV);
      }
      ret = glp_simplex1(lp->lp, &parm);
      if (ret > 0)
      {  error("CClp_limited_dualopt: glp_simplex1() failed; return cod"
            "e = %d", ret);
         if (status != NULL) *status = CClp_FAILURE;
         ret = 1;
         goto done;
      }
      stat = glp_get_status(lp->lp);
      if (ret == 0 && stat == GLP_OPT || ret == -4)
      {  if (status) *status = CClp_SUCCESS;
         ret = 0;
      }
      else if (ret == 0 && stat == GLP_NOFEAS)
      {  if (status) *status = CClp_INFEASIBLE;
         ret = 0;
      }
      else if (ret == -1)
      {  if (status) *status = CClp_UNKNOWN;
         ret = 0;
      }
      else
      {  error("CClp_limited_dualopt: optimization status = %d", stat);
         if (status) *status = CClp_FAILURE;
         ret = 0;
      }
done: return ret;
}

/*--------------------------------------------------------------------*/

int CClp_addrows(CClp *lp, int newrows, int newnz, double *rhs,
      char *sense, int *rmatbeg, int *rmatind, double *rmatval)
{     /* ADDS the rows to the LP.
         - newrows is the number of rows to be added;
         - newnz is the number of nonzero coefficients in the new rows;
         - rhs is an array of the rhs values for the new rows;
         - sense is 'L', 'E', or 'G' for each of the new rows;
         - rmatbeg, rmatind, and rmatval give the coefficients of the
           new rows in sparse format. The arrays can be freed after the
           call. */
      int i;
      for (i = 0; i < newrows; i++)
      {  int seqn, type, k, t;
         double lo, up;
         glp_new_row(lp->lp, NULL);
         seqn = glp_get_num_rows(lp->lp);
         switch (sense[i])
         {  case 'L':
               type = 'U', lo = 0.0, up = rhs[i];
               break;
            case 'E':
               type = 'S', lo = up = rhs[i];
               break;
            case 'G':
               type = 'L', lo = rhs[i], up = 0.0;
               break;
            default:
               insist(sense[i] != sense[i]);
         }
         glp_set_row_bnds(lp->lp, seqn, type, lo, up);
         insist(rmatbeg != NULL);
         insist(rmatind != NULL);
         insist(rmatval != NULL);
         insist(rmatbeg[0] == 0);
         t = (i < newrows-1 ? rmatbeg[i+1] : newnz);
         for (k = rmatbeg[i]; k < t; k++)
            glp_new_aij(lp->lp, seqn, rmatind[k]+1, rmatval[k]);
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_addcols(CClp *lp, int newcols, int newnz, double *obj,
      int *cmatbeg, int *cmatind, double *cmatval, double *lb,
      double *ub)
{     /* ADDS the columns to the LP. */
      int j;
      for (j = 0; j < newcols; j++)
      {  int seqn, type, k, t;
         double lo, up;
         glp_new_col(lp->lp, NULL);
         seqn = glp_get_num_cols(lp->lp);
         glp_set_obj_coef(lp->lp, seqn, obj == NULL ? 0.0 : obj[j]);
         lo = lb[j], up = ub[j];
         /* check for finite bounds */
         insist(-1e10 <= lo && lo <= up && up <= +1e10);
         type = (lo == up ? 'S' : 'D');
         glp_set_col_bnds(lp->lp, seqn, type, lo, up);
         insist(cmatbeg != NULL);
         insist(cmatind != NULL);
         insist(cmatval != NULL);
         insist(cmatbeg[0] == 0);
         t = (j < newcols-1 ? cmatbeg[j+1] : newnz);
         for (k = cmatbeg[j]; k < t; k++)
            glp_new_aij(lp->lp, cmatind[k]+1, seqn, cmatval[k]);
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_delete_row(CClp *lp, int i)
{     /* DELETES row i of the LP. */
      int m = glp_get_num_rows(lp->lp), *flag, t, ret;
      insist(0 <= i && i < m);
      flag = ucalloc(1+m, sizeof(int));
      /* try to push the i-th auxiliary variable into the basis */
      for (t = 1; t <= m; t++) flag[t] = 0;
      flag[i+1] = 1;
      ret = glp_pivot_in(lp->lp, flag, NULL);
      if (ret > 0)
         error("CClp_delete_row: glp_pivot_in() failed; return code %d",
            ret);
      /* delete the i-th row from the problem object */
      glp_delete_rows(lp->lp, flag);
      ufree(flag);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_delete_set_of_rows(CClp *lp, int *delstat)
{     /* DELETES the rows corresponding to 1 entries in delstat.
         - delstat is a 0/1 array having an entry for each row. */
      int ret;
      /* try to push the corresponding auxiliary variables into the
         basis */
      ret = glp_pivot_in(lp->lp, delstat-1, NULL);
      if (ret > 0)
         error("CClp_delete_set_of_rows: glp_pivot_in() failed; return "
            "code %d", ret);
      /* delete required rows from the problem object */
      glp_delete_rows(lp->lp, delstat-1);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_delete_column(CClp *lp, int j)
{     /* DELETES column j from the LP. */
      int n = glp_get_num_cols(lp->lp), *flag, t, ret;
      insist(0 <= j && j < n);
      flag = ucalloc(1+n, sizeof(int));
      /* try to pull the j-th structural variable from the basis */
      for (t = 1; t <= n; t++) flag[t] = 0;
      flag[j+1] = 1;
      ret = glp_pivot_out(lp->lp, flag, NULL);
      if (ret > 0)
         error("CClp_delete_column: glp_pivot_out() failed; return code"
            " %d", ret);
      /* delete the j-th column from the problem object */
      glp_delete_cols(lp->lp, flag);
      ufree(flag);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_delete_set_of_columns(CClp *lp, int *delstat)
{     /* DELETES the columns corresponding to the 1 entries in delstat.
         - delstat is a 0/1 array having an entry for each column. */
      insist(lp == lp);
      insist(delstat == delstat);
      fault("CClp_delete_set_of_columns: not implemented");
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_setbnd(CClp *lp, int j, char lower_or_upper, double bnd)
{     /* SETS the bound on the variable index by j.
         - lower_or_upper should be either 'L' or 'U'. */
      int type;
      double lo, up;
      glp_get_col_bnds(lp->lp, j+1, &type, &lo, &up);
      if (type == 'F' || type == 'U') lo = -DBL_MAX;
      if (type == 'F' || type == 'L') up = +DBL_MAX;
      switch (lower_or_upper)
      {  case 'L':
            lo = bnd; break;
         case 'U':
            up = bnd; break;
         default:
            insist(lower_or_upper != lower_or_upper);
      }
      if (lo == -DBL_MAX && up == +DBL_MAX)
         insist(2 + 2 == 5);
      else if (up == +DBL_MAX)
         type = 'L';
      else if (lo == -DBL_MAX)
         type = 'U';
      else if (lo != up)
         type = 'D';
      else
         type = 'S';
      glp_set_col_bnds(lp->lp, j+1, type, lo, up);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_get_warmstart(CClp *lp, CClp_warmstart **warm)
{     /* SAVES information for efficiently resolving the current lp in
         warm, for example, basis or norm information. */
      int i, j, tagx;
      CClp_free_warmstart(warm);
      (*warm) = umalloc(sizeof(CClp_warmstart));
      (*warm)->ncols = 0;
      (*warm)->nrows = 0;
      (*warm)->cstat = NULL;
      (*warm)->rstat = NULL;
      (*warm)->ncols = glp_get_num_cols(lp->lp);
      if ((*warm)->ncols == 0)
      {  error("CClp_get_warmstart: no columns in LP");
         CClp_free_warmstart(warm);
         return 1;
      }
      (*warm)->nrows = glp_get_num_rows(lp->lp);
      if ((*warm)->nrows == 0)
      {  error("CClp_get_warmstart: no rows in LP");
         CClp_free_warmstart(warm);
         return 1;
      }
      (*warm)->cstat = ucalloc((*warm)->ncols, sizeof(int));
      (*warm)->rstat = ucalloc((*warm)->nrows, sizeof(int));
      for (i = 1; i <= (*warm)->nrows; i++)
      {  glp_get_row_soln(lp->lp, i, &tagx, NULL, NULL);
         switch (tagx)
         {  case 'B':
               (*warm)->rstat[i-1] = IS_BASIC; break;
            case 'L':
               (*warm)->rstat[i-1] = AT_LOWER; break;
            case 'U':
               (*warm)->rstat[i-1] = AT_UPPER; break;
            case 'S':
               (*warm)->rstat[i-1] = AT_LOWER; break;
            default: insist(tagx != tagx);
         }
      }
      for (j = 1; j <= (*warm)->ncols; j++)
      {  glp_get_col_soln(lp->lp, j, &tagx, NULL, NULL);
         switch (tagx)
         {  case 'B':
               (*warm)->cstat[j-1] = IS_BASIC; break;
            case 'L':
               (*warm)->cstat[j-1] = AT_LOWER; break;
            case 'U':
               (*warm)->cstat[j-1] = AT_UPPER; break;
            case 'S':
               (*warm)->cstat[j-1] = AT_LOWER; break;
            default:
               insist(tagx != tagx);
         }
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_load_warmstart(CClp *lp, CClp_warmstart *warm)
{     /* RESTORES the warmstart information in warm. */
      int i, j, m, n, tagx, type, *rstat, *cstat;
      m = glp_get_num_rows(lp->lp);
      n = glp_get_num_cols(lp->lp);
      cstat = warm->cstat;
      rstat = warm->rstat;
      if (cstat == NULL || rstat == NULL)
      {  error("CClp_load_warmstart: no basis information");
         return 0;
      }
      for (j = 0; j < n; j++)
      {  if (cstat[j] == IS_BASIC)
            tagx = 'B';
         else
         {  glp_get_col_bnds(lp->lp, j+1, &type, NULL, NULL);
            switch (type)
            {  case 'F':
                  tagx = 'F'; break;
               case 'L':
                  tagx = 'L'; break;
               case 'U':
                  tagx = 'U'; break;
               case 'D':
                  tagx = (cstat[j] == AT_UPPER ? 'U' : 'L'); break;
               case 'S':
                  tagx = 'S'; break;
               default:
                  insist(type != type);
            }
         }
         glp_put_col_soln(lp->lp, j+1, tagx, 0.0, 0.0);
      }
      for (i = 0; i < m; i++)
      {  if (rstat[i] == IS_BASIC)
            tagx = 'B';
         else
         {  glp_get_row_bnds(lp->lp, i+1, &type, NULL, NULL);
            switch (type)
            {  case 'F':
                  tagx = 'F'; break;
               case 'L':
                  tagx = 'L'; break;
               case 'U':
                  tagx = 'U'; break;
               case 'D':
                  tagx = (rstat[i] == AT_UPPER ? 'U' : 'L'); break;
               case 'S':
                  tagx = 'S'; break;
               default:
                  insist(type != type);
            }
         }
         glp_put_row_soln(lp->lp, i+1, tagx, 0.0, 0.0);
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_build_warmstart(CClp_warmstart **warm, CClp_info *info)
{     /* BUILDS some warmstart information from the row/column
         information in info. */
      int i, j;
      CClp_free_warmstart(warm);
      (*warm) = umalloc(sizeof(CClp_warmstart));
      (*warm)->ncols = 0;
      (*warm)->nrows = 0;
      (*warm)->cstat = NULL;
      (*warm)->rstat = NULL;
      (*warm)->ncols = info->ncols;
      insist((*warm)->ncols > 0);
      (*warm)->nrows = info->nrows;
      insist((*warm)->nrows > 0);
      (*warm)->cstat = ucalloc((*warm)->ncols, sizeof(int));
      (*warm)->rstat = ucalloc((*warm)->nrows, sizeof(int));
      for (j = 0; j < (*warm)->ncols; j++)
         (*warm)->cstat[j] = info->cstat[j];
      for (i = 0; i < (*warm)->nrows; i++)
         (*warm)->rstat[i] = info->rstat[i];
      return 0;
}

/*--------------------------------------------------------------------*/

void CClp_free_warmstart(CClp_warmstart **warm)
{     /* FREES the memory used by warm. */
      if (*warm != NULL)
      {  if ((*warm)->cstat != NULL)
            ufree((*warm)->cstat), (*warm)->cstat = NULL;
         if ((*warm)->rstat != NULL)
            ufree((*warm)->rstat), (*warm)->rstat = NULL;
         ufree((*warm)), (*warm) = NULL;
      }
      return;
}

/*--------------------------------------------------------------------*/

int CClp_sread_warmstart(CC_SFILE *file, CClp_warmstart **warm)
{     /* READS warmstart information from the file. */
      int i, j, ccount, rcount;
      char name[5];
      CClp_free_warmstart(warm);
      for (i = 0; i < 4; i++)
         if (CCutil_sread_char(file, &name[i])) goto fail;
      name[4] = '\0';
      if (strncmp(name, SOLVER, 4))
      {  error("CClp_sread_warmstart: warmstart for another solver (%s)"
            " ignored", name);
         return 0;
      }
      if (CCutil_sread_int(file, &ccount)) goto fail;
      if (CCutil_sread_int(file, &rcount)) goto fail;
      (*warm) = umalloc(sizeof(CClp_warmstart));
      (*warm)->ncols = 0;
      (*warm)->nrows = 0;
      (*warm)->cstat = NULL;
      (*warm)->rstat = NULL;
      (*warm)->cstat = ucalloc(ccount, sizeof(int));
      (*warm)->rstat = ucalloc(rcount, sizeof(int));
      for (j = 0; j < ccount; j++)
         if (CCutil_sread_bits(file, &(((*warm)->cstat)[j]), 2))
            goto fail;
      for (i = 0; i < rcount; i++)
         if (CCutil_sread_bits(file, &(((*warm)->rstat)[i]), 1))
            goto fail;
      (*warm)->ncols = ccount;
      (*warm)->nrows = rcount;
      return 0;
fail: CClp_free_warmstart(warm);
      return 1;
}

/*--------------------------------------------------------------------*/

int CClp_swrite_warmstart(CC_SFILE *file, CClp_warmstart *warm)
{     /* WRITES warmstart information from the f. */
      int i, j;
      const char *name = SOLVER;
      for (i = 0; i < 4; i++)
         if (CCutil_swrite_char(file, name[i])) return 1;
      if (CCutil_swrite_int(file, warm->ncols)) return 1;
      if (CCutil_swrite_int(file, warm->nrows)) return 1;
      for (j = 0; j < warm->ncols; j++)
         if (CCutil_swrite_bits (file, warm->cstat[j], 2)) return 1;
      for (i = 0; i < warm->nrows; i++)
         if (CCutil_swrite_bits (file, warm->rstat[i], 1)) return 1;
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_get_info(CClp *lp, CClp_info **info)
{     /* BUILDS information useful for efficiently answering questions
         about the status of rows and columns. */
      int i, j, tagx;
      CClp_free_info(info);
      (*info) = umalloc(sizeof(CClp_info));
      (*info)->ncols = 0;
      (*info)->nrows = 0;
      (*info)->cstat = NULL;
      (*info)->rstat = NULL;
      (*info)->ncols = glp_get_num_cols(lp->lp);
      insist((*info)->ncols > 0);
      (*info)->nrows = glp_get_num_rows(lp->lp);
      insist((*info)->nrows > 0);
      (*info)->cstat = ucalloc((*info)->ncols, sizeof(int));
      (*info)->rstat = ucalloc((*info)->nrows, sizeof(int));
      for (i = 1; i <= (*info)->nrows; i++)
      {  glp_get_row_soln(lp->lp, i, &tagx, NULL, NULL);
         (*info)->rstat[i-1] = tagx == 'B' ? 1 : 0;
      }
      for (j = 1; j <= (*info)->ncols; j++)
      {  glp_get_col_soln(lp->lp, j, &tagx, NULL, NULL);
         switch (tagx)
         {  case 'B':
               (*info)->cstat[j-1] = 0; break;
            case 'L':
               (*info)->cstat[j-1] = 1; break;
            case 'U':
               (*info)->cstat[j-1] = 2; break;
            case 'S':
               (*info)->cstat[j-1] = 1; break;
            default:
               insist(tagx != tagx);
         }
      }
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_create_info(CClp_info **info, int nrows, int ncols)
{     /* CREATES a structure for storing information about the status
         of rows and columns. */
      int i, j;
      CClp_free_info(info);
      (*info) = umalloc(sizeof(CClp_info));
      (*info)->ncols = 0;
      (*info)->nrows = 0;
      (*info)->cstat = NULL;
      (*info)->rstat = NULL;
      (*info)->ncols = ncols;
      insist(ncols > 0);
      (*info)->nrows = nrows;
      insist(nrows > 0);
      (*info)->cstat = ucalloc((*info)->ncols, sizeof(int));
      (*info)->rstat = ucalloc((*info)->nrows, sizeof(int));
      for (j = 0; j < ncols; j++) (*info)->cstat[j] = 0;
      for (i = 0; i < nrows; i++) (*info)->rstat[i] = 0;
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_is_col_active(CClp_info *info, int j)
{     /* returns 1 if column j is active, 0 otherwise.
         "active" means participating in the current solution (for
         example, it could mean basic or nonbasic at upper bound) */
      if (j < 0 || j >= info->ncols) return 0;
      return info->cstat[j] == 1 || info->cstat[j] == 2;
}

/*--------------------------------------------------------------------*/

int CClp_is_row_active(CClp_info *info, int i)
{     /* returns 1 if row i is active, 0 otherwise.
         "active" means participating in the current solution (for
         example, it could mean the row's slack is non-basic) */
      if (i < 0 || i >= info->nrows) return 0;
      return info->rstat[i] == 0;
}

/*--------------------------------------------------------------------*/

void CClp_set_col_active(CClp_info *info, int j)
{     /* marks column j as active */
      if (j >= 0 && j < info->ncols) info->cstat[j] = 1;
}

/*--------------------------------------------------------------------*/

void CClp_set_col_inactive(CClp_info *info, int j)
{     /*  marks column j as inactive */
      if (j >= 0 && j < info->ncols) info->cstat[j] = 0;
}

/*--------------------------------------------------------------------*/

void CClp_set_col_upper(CClp_info *info, int j)
{     /* marks column j as active at upper bound */
      if (j >= 0 && j < info->ncols) info->cstat[j] = 2;
}

/*--------------------------------------------------------------------*/

void CClp_set_row_active(CClp_info *info, int i)
{     /* marks row i as active */
      if (i >= 0 && i < info->nrows) info->rstat[i] = 0;
}

/*--------------------------------------------------------------------*/

void CClp_set_row_inactive(CClp_info *info, int i)
{     /* marks row i as inactive */
      if (i >= 0 && i < info->nrows) info->rstat[i] = 1;
}

/*--------------------------------------------------------------------*/

void CClp_free_info(CClp_info **info)
{     /* FREES the memory used by info. */
      if ((*info) != NULL)
      {  if ((*info)->cstat != NULL)
            ufree((*info)->cstat), (*info)->cstat = NULL;
         if ((*info)->rstat != NULL)
            ufree((*info)->rstat), (*info)->rstat = NULL;
         ufree((*info)), (*info) = NULL;
      }
      return;
}

/*--------------------------------------------------------------------*/

int CClp_x(CClp *lp, double *x)
{     /* RETURNS the current LP solution.
         - x should be an array of length at least ncols. */
      int ncols, j;
      ncols = glp_get_num_cols(lp->lp);
      insist(ncols > 0);
      for (j = 0; j < ncols; j++)
         glp_get_col_soln(lp->lp, j+1, NULL, &x[j], NULL);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_rc(CClp *lp, double *rc)
{     /* RETURNS the current reduced costs.
         - rc should be an array of length at least ncols. */
      int ncols, j;
      ncols = glp_get_num_cols(lp->lp);
      insist(ncols > 0);
      for (j = 0; j < ncols; j++)
         glp_get_col_soln(lp->lp, j+1, NULL, NULL, &rc[j]);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_pi(CClp *lp, double *pi)
{     /* RETURNS the dual values on the constraints.
         - pi should be an array of length at least nrows. */
      int nrows, i;
      nrows = glp_get_num_rows(lp->lp);
      insist(nrows > 0);
      for (i = 0; i < nrows; i++)
         glp_get_row_soln(lp->lp, i+1, NULL, NULL, &pi[i]);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_objval(CClp *lp, double *obj)
{     /* RETURNS the objective value of the lp. */
      if (obj != NULL) *obj = glp_get_obj_val(lp->lp);
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_nrows(CClp *lp)
{     /* RETURNS the number of rows in the LP. */
      return glp_get_num_rows(lp->lp);
}

/*--------------------------------------------------------------------*/

int CClp_ncols(CClp *lp)
{     /* RETURNS the number of columns in the LP. */
      return glp_get_num_cols(lp->lp);
}

/*--------------------------------------------------------------------*/

int CClp_nnonzeros(CClp *lp)
{     /* RETURNS the number of nonzeros in the LP. */
      return glp_get_num_nz(lp->lp);
}

/*--------------------------------------------------------------------*/

int CClp_status(CClp *lp, int *status)
{     /* CHECKS whether the current lp is infeasible or whether an
         optimal solution has been found. It returns an error if the LP
         has not not been optimized.
         - lp is the lp;
         - status returns 0 if the lp has an optimal solution and 1 if
           it is infeasible. */
      insist(lp == lp);
      insist(status == status);
      fault("CClp_status: not implemented");
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_getweight(CClp *lp, int nrows, int *rmatbeg, int *rmatind,
      double *rmatval, double *weight)
{     /* COMPUTES the duals of the steepest edge norms for the n rows
         specified in rmatbeg, rmatind, and rmatval.
         - weight returns the array of weights; the array should be at
           least nrows long. */
      insist(lp == lp);
      insist(nrows == nrows);
      insist(rmatbeg == rmatbeg);
      insist(rmatind == rmatind);
      insist(rmatval == rmatval);
      insist(weight == weight);
      fault("CClp_getweight: not implemented");
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_dump_lp(CClp *lp, const char *fname)
{     /* WRITES the LP to file fname. */
      insist(lp == lp);
      insist(fname == fname);
      fault("CClp_dump_lp: not implemented");
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_getgoodlist(CClp *lp, int *goodlist, int *goodlen_p,
      double *downpen, double *uppen)
{     /* RETURNS an array of the column indices corresponding to
         variables that move in both the up and down directions. This
         is a useful list of candidates for strong branching.
         - goodlist, downpen and uppen should be arrays of length at
           least ncols. */
      insist(lp == lp);
      insist(goodlist == goodlist);
      insist(downpen == downpen);
      insist(uppen == uppen);
      /* not implemented */
      *goodlen_p = 0;
      return 0;
}

/*--------------------------------------------------------------------*/

int CClp_strongbranch(CClp *lp, int *candidatelist, int ncand,
      double *downpen, double *uppen, int iterations, double upperbound)
{     /* RETURNS estimates of the lp values obtained by setting each of
         the ncand variables listed in candidatelist to 0 and 1. The
         estimates are obtained by performing iterations pivots of dual
         simplex method. upperbound is used to cutoff the dual simplex
         method. downpen and uppen should never be > upperbound.
         -downpen and uppen should be arrays of length at least ncand */
      int i;
      insist(lp == lp);
      insist(candidatelist == candidatelist);
      insist(iterations == iterations);
      /* not implemented */
      for (i = 0; i < ncand; i++)
      {  downpen[i] = upperbound;
         uppen[i] = upperbound;
      }
      return 0;
}

/* eof */
