/* glplpx.c */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- GLPK 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, or (at your option)
-- any later version.
--
-- GLPK is distributed 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 GLPK; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#include <ctype.h>
#include <math.h>
#include <string.h>
#include "glpspx.h"

/*----------------------------------------------------------------------
-- lpx_create_prob - create LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- LPX *lpx_create_prob(void);
--
-- *Description*
--
-- The routine lpx_create_prob creates an LP problem object.
--
-- Initially the created object corresponds to an "empty" LP problem,
-- which has no rows and no columns.
--
-- *Returns*
--
-- The routine returns a pointer to the created object. */

LPX *lpx_create_prob(void)
{     LPX *lp;
      int m_max = 50, n_max = 100, sv_size = 500;
      lp = umalloc(sizeof(LPX));
      lp->m_max = m_max;
      lp->n_max = n_max;
      lp->m = lp->n = 0;
      lp->pool = create_str_pool();
      lp->buf = ucalloc(255+1, sizeof(char));
      lp->name = ucalloc(1+m_max+n_max, sizeof(STR *));
      lp->name[0] = NULL;
      lp->typx = ucalloc(1+m_max+n_max, sizeof(int));
      lp->lb = ucalloc(1+m_max+n_max, sizeof(double));
      lp->ub = ucalloc(1+m_max+n_max, sizeof(double));
      lp->rs = ucalloc(1+m_max+n_max, sizeof(double));
      lp->mark = ucalloc(1+m_max+n_max, sizeof(int));
      lp->dir = LPX_MIN;
      lp->coef = ucalloc(1+m_max+n_max, sizeof(double));
      lp->coef[0] = 0.0;
      lp->aa_ptr = ucalloc(1+m_max+n_max, sizeof(int));
      lp->aa_len = ucalloc(1+m_max+n_max, sizeof(int));
      lp->aa_cap = ucalloc(1+m_max+n_max, sizeof(int));
      lp->sv_size = sv_size;
      lp->sv_used = 0;
      lp->sv_ndx = ucalloc(1+sv_size, sizeof(int));
      lp->sv_val = ucalloc(1+sv_size, sizeof(double));
      lp->sv_head = lp->sv_tail = 0;
      lp->sv_prev = ucalloc(1+m_max+n_max, sizeof(int));
      lp->sv_next = ucalloc(1+m_max+n_max, sizeof(int));
      lp->b_stat = LPX_B_UNDEF;
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      lp->tagx = ucalloc(1+m_max+n_max, sizeof(int));
      lp->posx = ucalloc(1+m_max+n_max, sizeof(int));
      lp->indx = ucalloc(1+m_max+n_max, sizeof(int));
      lp->inv = NULL;
      lp->bbar = ucalloc(1+m_max, sizeof(double));
      lp->pi = ucalloc(1+m_max, sizeof(double));
      lp->cbar = ucalloc(1+n_max, sizeof(double));
      lpx_reset_parms(lp);
      return lp;
}

/*----------------------------------------------------------------------
-- lpx_reset_parms - reset control parameters to default values.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_reset_parms(LPX *lp);
--
-- *Description*
--
-- The routine lpx_reset_parms resets all control parameters associated
-- with an LP problem object, which the parameter lp points to, to their
-- default values. */

void lpx_reset_parms(LPX *lp)
{     lp->msg_lev = 2;
      lp->start   = 1;
      lp->dual    = 0;
      lp->price   = 1;
      lp->relax   = 0.07;
      lp->tol_bnd = 1e-7;
      lp->tol_dj  = 1e-7;
      lp->tol_piv = 1e-9;
      lp->it_lim  = -1;
      lp->it_cnt  = 0;
      return;
}

/*----------------------------------------------------------------------
-- lpx_realloc_prob - reallocate LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_realloc_prob(LPX *lp, int m_max, int n_max);
--
-- *Description*
--
-- The routine lpx_realloc_prob reallocates arrays in an LP problem
-- object, which the parameter lp points to.
--
-- The parameter m_max specifies a new maximal number of rows, and the
-- parameter n_max specifies a new maximal number of columns.
--
-- This routine is intended for auxiliary purposes and should not be
-- used directly. */

void lpx_realloc_prob(LPX *lp, int m_max, int n_max)
{     int m = lp->m;
      int n = lp->n;
      void *temp;
      insist(m_max >= m);
      insist(n_max >= n);
#     define reallocate(type, ptr, max_len, len) \
      temp = ucalloc(max_len, sizeof(type)); \
      memcpy(temp, ptr, (len) * sizeof(type)); \
      ufree(ptr), ptr = temp;
      reallocate(STR *, lp->name, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->typx, 1+m_max+n_max, 1+m+n);
      reallocate(double, lp->lb, 1+m_max+n_max, 1+m+n);
      reallocate(double, lp->ub, 1+m_max+n_max, 1+m+n);
      reallocate(double, lp->rs, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->mark, 1+m_max+n_max, 1+m+n);
      reallocate(double, lp->coef, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->aa_ptr, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->aa_len, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->aa_cap, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->sv_prev, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->sv_next, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->tagx, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->posx, 1+m_max+n_max, 1+m+n);
      reallocate(int, lp->indx, 1+m_max+n_max, 1+m+n);
      reallocate(double, lp->bbar, 1+m_max, 1+m);
      reallocate(double, lp->pi, 1+m_max, 1+m);
      reallocate(double, lp->cbar, 1+n_max, 1+n);
#     undef reallocate
      lp->m_max = m_max;
      lp->n_max = n_max;
      return;
}

/*----------------------------------------------------------------------
-- lpx_add_rows - add new rows to LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_add_rows(LPX *lp, int nrs);
--
-- *Description*
--
-- The routine lpx_add_rows adds nrs rows (constraints) to LP problem
-- object, which the parameter lp points to. New rows are always added
-- to the end of the row list, therefore the numbers of existing rows
-- remain unchanged.
--
-- Being added each a new row is free (unbounded) and has no constraint
-- coefficients. */

void lpx_add_rows(LPX *lp, int nrs)
{     int m = lp->m;
      int n = lp->n;
      STR **name = lp->name;
      int *typx = lp->typx;
      double *lb = lp->lb;
      double *ub = lp->ub;
      double *rs = lp->rs;
      int *mark = lp->mark;
      double *coef = lp->coef;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_prev = lp->sv_prev;
      int *sv_next = lp->sv_next;
      int *tagx = lp->tagx;
      int *posx = lp->posx;
      int *indx = lp->indx;
      double *bbar = lp->bbar;
      double *pi = lp->pi;
      int m_new, i, k;
      if (nrs < 1)
         fault("lpx_add_rows: nrs = %d; invalid parameter", nrs);
      /* determine new number of rows in the problem */
      m_new = m + nrs;
      /* enlarge the arrays (if necessary) */
      if (lp->m_max < m_new)
      {  int m_max = lp->m_max;
         while (m_max < m_new) m_max += m_max;
         lpx_realloc_prob(lp, m_max, lp->n_max);
         name = lp->name;
         typx = lp->typx;
         lb = lp->lb;
         ub = lp->ub;
         rs = lp->rs;
         mark = lp->mark;
         coef = lp->coef;
         aa_ptr = lp->aa_ptr;
         aa_len = lp->aa_len;
         aa_cap = lp->aa_cap;
         sv_prev = lp->sv_prev;
         sv_next = lp->sv_next;
         tagx = lp->tagx;
         posx = lp->posx;
         indx = lp->indx;
         bbar = lp->bbar;
         pi = lp->pi;
      }
      /* correct references to column numbers */
      if (lp->sv_head > m) lp->sv_head += nrs;
      if (lp->sv_tail > m) lp->sv_tail += nrs;
      for (k = 1; k <= m+n; k++)
      {  if (sv_prev[k] > m) sv_prev[k] += nrs;
         if (sv_next[k] > m) sv_next[k] += nrs;
         if (posx[k] > m) posx[k] += nrs;
         if (indx[k] > m) indx[k] += nrs;
      }
      /* we need a free place in the arrays, which contain information
         about rows and columns (i.e. which have m+n locations) */
      memmove(&name[m_new+1], &name[m+1], n * sizeof(STR *));
      memmove(&typx[m_new+1], &typx[m+1], n * sizeof(int));
      memmove(&lb[m_new+1], &lb[m+1], n * sizeof(double));
      memmove(&ub[m_new+1], &ub[m+1], n * sizeof(double));
      memmove(&rs[m_new+1], &rs[m+1], n * sizeof(double));
      memmove(&mark[m_new+1], &mark[m+1], n * sizeof(int));
      memmove(&coef[m_new+1], &coef[m+1], n * sizeof(double));
      memmove(&aa_ptr[m_new+1], &aa_ptr[m+1], n * sizeof(int));
      memmove(&aa_len[m_new+1], &aa_len[m+1], n * sizeof(int));
      memmove(&aa_cap[m_new+1], &aa_cap[m+1], n * sizeof(int));
      memmove(&sv_prev[m_new+1], &sv_prev[m+1], n * sizeof(int));
      memmove(&sv_next[m_new+1], &sv_next[m+1], n * sizeof(int));
      memmove(&tagx[m_new+1], &tagx[m+1], n * sizeof(int));
      memmove(&posx[m_new+1], &posx[m+1], n * sizeof(int));
      memmove(&indx[m_new+1], &indx[m+1], n * sizeof(int));
      /* initialize new rows */
      for (i = m+1; i <= m_new; i++)
      {  /* the new row has no symbolic name */
         name[i] = NULL;
         /* the new row is free */
         typx[i] = LPX_FR;
         lb[i] = ub[i] = 0.0;
         /* the new row is not scaled */
         rs[i] = 1.0;
         /* the new row is unmarked */
         mark[i] = 0;
         /* the new row has zero objective coefficient */
         coef[i] = 0.0;
         /* the new row has zero constraint coefficients */
         aa_ptr[i] = lp->sv_used + 1;
         aa_len[i] = aa_cap[i] = 0;
         /* formally the new row of the constraint matrix is placed in
            the rightmost position of SVA */
         sv_prev[i] = lp->sv_tail;
         sv_next[i] = 0;
         if (sv_prev[i] == 0)
            lp->sv_head = i;
         else
            sv_next[sv_prev[i]] = i;
         lp->sv_tail = i;
         /* the new row is basic */
         tagx[i] = LPX_BS;
         posx[i] = indx[i] = i;
         bbar[i] = pi[i] = 0.0;
      }
      /* set new number of rows */
      lp->m = m_new;
      /* invalidate the current basis */
      lp->b_stat = LPX_B_UNDEF;
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_add_cols - add new columns to LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_add_cols(LPX *lp, int ncs);
--
-- *Description*
--
-- The routine lpx_add_cols adds ncs columns (structural variables) to
-- LP problem object, which the parameter lp points to. New columns are
-- always added to the end of the column list, therefore the numbers of
-- existing columns remain unchanged.
--
-- Being added each a new column is fixed at zero and has no constraint
-- coefficients. */

void lpx_add_cols(LPX *lp, int ncs)
{     int m = lp->m;
      int n = lp->n;
      STR **name = lp->name;
      int *typx = lp->typx;
      double *lb = lp->lb;
      double *ub = lp->ub;
      double *rs = lp->rs;
      int *mark = lp->mark;
      double *coef = lp->coef;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_prev = lp->sv_prev;
      int *sv_next = lp->sv_next;
      int *tagx = lp->tagx;
      int *posx = lp->posx;
      int *indx = lp->indx;
      double *cbar = lp->cbar;
      int n_new, j;
      if (ncs < 1)
         fault("lpx_add_cols: ncs = %d; invalid parameter", ncs);
      /* determine new number of columns in the problem */
      n_new = n + ncs;
      /* enlarge the arrays (if necessary) */
      if (lp->n_max < n_new)
      {  int n_max = lp->n_max;
         while (n_max < n_new) n_max += n_max;
         lpx_realloc_prob(lp, lp->m_max, n_max);
         name = lp->name;
         typx = lp->typx;
         lb = lp->lb;
         ub = lp->ub;
         rs = lp->rs;
         mark = lp->mark;
         coef = lp->coef;
         aa_ptr = lp->aa_ptr;
         aa_len = lp->aa_len;
         aa_cap = lp->aa_cap;
         sv_prev = lp->sv_prev;
         sv_next = lp->sv_next;
         tagx = lp->tagx;
         posx = lp->posx;
         indx = lp->indx;
         cbar = lp->cbar;
      }
      /* initialize new columns */
      for (j = m+n+1; j <= m+n_new; j++)
      {  /* the new column has no symbolic name */
         name[j] = NULL;
         /* the new column is fixed at zero */
         typx[j] = LPX_FX;
         lb[j] = ub[j] = 0.0;
         /* the new column is not scaled */
         rs[j] = 1.0;
         /* the new column is unmarked */
         mark[j] = 0;
         /* the new column has zero objective coefficient */
         coef[j] = 0.0;
         /* the new column has zero constraint coefficients */
         aa_ptr[j] = lp->sv_used + 1;
         aa_len[j] = aa_cap[j] = 0;
         /* formally the new column of the constraint matrix is placed
            in the rightmost position of SVA */
         sv_prev[j] = lp->sv_tail;
         sv_next[j] = 0;
         if (sv_prev[j] == 0)
            lp->sv_head = j;
         else
            sv_next[sv_prev[j]] = j;
         lp->sv_tail = j;
         /* the new column is non-basic */
         tagx[j] = LPX_NS;
         posx[j] = indx[j] = j;
         cbar[j-m] = 0.0;
      }
      /* set new number of columns */
      lp->n = n_new;
      /* invalidate the current basis */
      lp->b_stat = LPX_B_UNDEF;
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_check_name - check correctness of symbolic name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_check_name(char *name);
--
-- *Description*
--
-- The routine lpx_check_name is intended to check if a given symbolic
-- name is correct.
--
-- A symbolic name is considered as correct, if it consists of 1 up to
-- 255 graphic characters.
--
-- *Returns*
--
-- If the symbolic name is correct, the routine lpx_check_name returns
-- zero. Otherwise the routine returns non-zero. */

int lpx_check_name(char *name)
{     int t;
      for (t = 0; name[t] != '\0'; t++)
         if (t == 255 || !isgraph(name[t])) return 1;
      return 0;
}

/*----------------------------------------------------------------------
-- lpx_set_prob_name - assign (change) problem name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_prob_name(LPX *lp, char *name);
--
-- *Description*
--
-- The routine lpx_set_prob_name assigns a given symbolic name to the
-- problem object, which the parameter lp points to.
--
-- If the parameter name is NULL, the routine just erases an existing
-- name of the problem object. */

void lpx_set_prob_name(LPX *lp, char *name)
{     if (name == NULL)
      {  if (lp->name[0] != NULL)
         {  delete_str(lp->name[0]);
            lp->name[0] = NULL;
         }
      }
      else
      {  if (lpx_check_name(name))
            fault("lpx_set_prob_name: invalid problem name");
         if (lp->name[0] == NULL) lp->name[0] = create_str(lp->pool);
         set_str(lp->name[0], name);
      }
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_row_name - assign (change) row name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_row_name(LPX *lp, int i, char *name);
--
-- *Description*
--
-- The routine lpx_set_row_name assigns a given symbolic name to the
-- i-th row (auxiliary variable).
--
-- If the parameter name is NULL, the routine just erases an existing
-- name of the i-th row. */

void lpx_set_row_name(LPX *lp, int i, char *name)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_set_row_name: i = %d; row number out of range", i);
      if (name == NULL)
      {  if (lp->name[i] != NULL)
         {  delete_str(lp->name[i]);
            lp->name[i] = NULL;
         }
      }
      else
      {  if (lpx_check_name(name))
            fault("lpx_set_row_name: i = %d; invalid row name", i);
         if (lp->name[i] == NULL) lp->name[i] = create_str(lp->pool);
         set_str(lp->name[i], name);
      }
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_col_name - assign (change) column name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_col_name(LPX *lp, int j, char *name);
--
-- *Description*
--
-- The routine lpx_set_col_name assigns a given symbolic name to the
-- j-th column (structural variable).
--
-- If the parameter name is NULL, the routine just erases an existing
-- name of the j-th column. */

void lpx_set_col_name(LPX *lp, int j, char *name)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_set_col_name: j = %d; column number out of range",
            j);
      j += lp->m;
      if (name == NULL)
      {  if (lp->name[j] != NULL)
         {  delete_str(lp->name[j]);
            lp->name[j] = NULL;
         }
      }
      else
      {  if (lpx_check_name(name))
            fault("lpx_set_col_name: j = %d; invalid column name", j);
         if (lp->name[j] == NULL) lp->name[j] = create_str(lp->pool);
         set_str(lp->name[j], name);
      }
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_row_bnds - set (change) row bounds.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_row_bnds(LPX *lp, int i, int typx, double lb,
--    double ub);
--
-- *Description*
--
-- The routine lpx_set_row_bnds sets (changes) type and bounds of the
-- i-th row.
--
-- Parameters typx, lb, and ub specify respectively the type, the lower
-- bound, and the upper bound, which should be set for the i-th row:
--
--     Type          Bounds            Note
--    -------------------------------------------
--    LPX_FR   -inf <  x <  +inf   free variable
--    LPX_LO     lb <= x <  +inf   lower bound
--    LPX_UP   -inf <  x <=  ub    upper bound
--    LPX_DB     lb <= x <=  ub    double bound
--    LPX_FX           x  =  lb    fixed variable
--
-- where x is the corresponding auxiliary variable.
--
-- If the row has no lower bound, the parameter lb is ignored. If the
-- row has no upper bound, the parameter ub is ignored. If the row is
-- of fixed type, the parameter lb is used, and the parameter ub is
-- ignored. */

void lpx_set_row_bnds(LPX *lp, int i, int typx, double lb, double ub)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_set_row_bnds: i = %d; row number out of range", i);
      lp->typx[i] = typx;
      switch (typx)
      {  case LPX_FR:
            lp->lb[i] = lp->ub[i] = 0.0;
            if (lp->tagx[i] != LPX_BS) lp->tagx[i] = LPX_NF;
            break;
         case LPX_LO:
            lp->lb[i] = lb * lp->rs[i], lp->ub[i] = 0.0;
            if (lp->tagx[i] != LPX_BS) lp->tagx[i] = LPX_NL;
            break;
         case LPX_UP:
            lp->lb[i] = 0.0, lp->ub[i] = ub * lp->rs[i];
            if (lp->tagx[i] != LPX_BS) lp->tagx[i] = LPX_NU;
            break;
         case LPX_DB:
            lp->lb[i] = lb * lp->rs[i], lp->ub[i] = ub * lp->rs[i];
            if (lp->tagx[i] != LPX_BS)
               lp->tagx[i] = (fabs(lb) <= fabs(ub) ? LPX_NL : LPX_NU);
            break;
         case LPX_FX:
            lp->lb[i] = lp->ub[i] = lb * lp->rs[i];
            if (lp->tagx[i] != LPX_BS) lp->tagx[i] = LPX_NS;
            break;
         default:
            fault("lpx_set_row_bnds: typx = %d; invalid row type",
               typx);
      }
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_col_bnds - set (change) column bounds.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_col_bnds(LPX *lp, int j, int typx, double lb,
--    double ub);
--
-- *Description*
--
-- The routine lpx_set_col_bnds sets (changes) type and bounds of the
-- j-th column.
--
-- Parameters typx, lb, and ub specify respectively the type, the lower
-- bound, and the upper bound, which should be set for the i-th row:
--
--     Type          Bounds            Note
--    -------------------------------------------
--    LPX_FR   -inf <  x <  +inf   free variable
--    LPX_LO     lb <= x <  +inf   lower bound
--    LPX_UP   -inf <  x <=  ub    upper bound
--    LPX_DB     lb <= x <=  ub    double bound
--    LPX_FX           x  =  lb    fixed variable
--
-- where x is the corresponding structural variable.
--
-- If the column has no lower bound, the parameter lb is ignored. If
-- the column has no upper bound, the parameter ub is ignored. If the
-- column is of fixed type, the parameter lb is used, and the parameter
-- ub is ignored. */

void lpx_set_col_bnds(LPX *lp, int j, int typx, double lb, double ub)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_set_col_bnds: j = %d; column number out of range",
            j);
      j += lp->m;
      lp->typx[j] = typx;
      switch (typx)
      {  case LPX_FR:
            lp->lb[j] = lp->ub[j] = 0.0;
            if (lp->tagx[j] != LPX_BS) lp->tagx[j] = LPX_NF;
            break;
         case LPX_LO:
            lp->lb[j] = lb / lp->rs[j], lp->ub[j] = 0.0;
            if (lp->tagx[j] != LPX_BS) lp->tagx[j] = LPX_NL;
            break;
         case LPX_UP:
            lp->lb[j] = 0.0, lp->ub[j] = ub / lp->rs[j];
            if (lp->tagx[j] != LPX_BS) lp->tagx[j] = LPX_NU;
            break;
         case LPX_DB:
            lp->lb[j] = lb / lp->rs[j], lp->ub[j] = ub / lp->rs[j];
            if (lp->tagx[j] != LPX_BS)
               lp->tagx[j] = (fabs(lb) <= fabs(ub) ? LPX_NL : LPX_NU);
            break;
         case LPX_FX:
            lp->lb[j] = lp->ub[j] = lb / lp->rs[j];
            if (lp->tagx[j] != LPX_BS) lp->tagx[j] = LPX_NS;
            break;
         default:
            fault("lpx_set_col_bnds: typx = %d; invalid column type",
               typx);
      }
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_obj_dir - set (change) optimization direction.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_obj_dir(LPX *lp, int dir);
--
-- *Description*
--
-- The routine lpx_set_obj_dir sets (changes) optimization direction
-- (i.e. sense of the objective function), which is specified by the
-- parameter dir:
--
-- LPX_MIN - the objective function should be minimized;
-- LPX_MAX - the objective function should be maximized. */

void lpx_set_obj_dir(LPX *lp, int dir)
{     if (!(dir == LPX_MIN || dir == LPX_MAX))
         fault("lpx_set_obj_dir: dir = %d; invalid parameter");
      lp->dir = dir;
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_obj_c0 - set (change) constant term of the obj. function.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_obj_c0(LPX *lp, double c0);
--
-- *Description*
--
-- The routine lpx_set_obj_c0 sets (changes) a constant term of the
-- objective function for an LP problem object, which the parameter lp
-- points to. */

void lpx_set_obj_c0(LPX *lp, double c0)
{     lp->coef[0] = c0;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_row_coef - set (change) row objective coefficient.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_row_coef(LPX *lp, int i, double coef);
--
-- *Description*
--
-- The routine lpx_set_row_coef sets (changes) a coefficient of the
-- objective function at the i-th auxiliary variable (row). */

void lpx_set_row_coef(LPX *lp, int i, double coef)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_set_row_coef: i = %d; row number out of range", i);
      lp->coef[i] = coef / lp->rs[i];
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_col_coef - set (change) column objective coefficient.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_col_coef(LPX *lp, int j, double coef);
--
-- *Description*
--
-- The routine lpx_set_col_coef sets (changes) a coefficient of the
-- objective function at the j-th structural variable (column). */

void lpx_set_col_coef(LPX *lp, int j, double coef)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_set_col_coef: j = %d; column number out of range",
            j);
      j += lp->m;
      lp->coef[j] = coef * lp->rs[j];
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_defrag_sva - defragment the sparse vector area.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_defrag_sva(LPX *lp);
--
-- *Description*
--
-- The routine lpx_defrag_sva defragments the sparse vector area (SVA),
-- which contains sparse rows and columns of the constraint matrix, in
-- order to gather all unused locations on one continuous extent.
--
-- Due to "garbage collection" this operation may change row and column
-- pointers of the constraint matrix.
--
-- This routine is intended for auxiliary purposes and should not be
-- used directly. */

void lpx_defrag_sva(LPX *lp)
{     int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int *sv_next = lp->sv_next;
      int beg = 1, k;
      /* skip rows and columns, which needn't to be relocated */
      for (k = lp->sv_head; k != 0; k = sv_next[k])
      {  if (aa_ptr[k] != beg) break;
         aa_cap[k] = aa_len[k];
         beg += aa_cap[k];
      }
      /* relocate other rows and columns in order to gather all unused
         locations in one continuous extent */
      for (k = k; k != 0; k = sv_next[k])
      {  memmove(&sv_ndx[beg], &sv_ndx[aa_ptr[k]], aa_len[k] *
            sizeof(int));
         memmove(&sv_val[beg], &sv_val[aa_ptr[k]], aa_len[k] *
            sizeof(double));
         aa_ptr[k] = beg;
         aa_cap[k] = aa_len[k];
         beg += aa_cap[k];
      }
      /* set new pointer to the last used location */
      lp->sv_used = beg - 1;
      return;
}

/*----------------------------------------------------------------------
-- lpx_enlarge_cap - enlarge capacity of row or column.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_enlarge_cap(LPX *lp, int k, int cap);
--
-- *Description*
--
-- The routine lpx_enlarge_cap enlarges capacity of either the i-th row
-- (if k = i) or the j-th column (if k = m+j) of the constraint matrix
-- to cap locations (it is assumed that the current capacity of the row
-- or the column is less than cap). If it is necessary, the routine may
-- defragment the sparse vector area (SVA) or even reallocate it.
--
-- Due to "garbage collection" this operation may change row and column
-- pointers of the constraint matrix.
--
-- This routine is intended for auxiliary purposes and should not be
-- used directly.
--
-- *Returns*
--
-- Non-zero return code means that SVA has been reallocated, therefore
-- the pointers sv_ndx and sv_val have been changed. */

int lpx_enlarge_cap(LPX *lp, int k, int cap)
{     int m = lp->m;
      int n = lp->n;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int *sv_prev = lp->sv_prev;
      int *sv_next = lp->sv_next;
      int ret = 0, extra, cur;
      insist(1 <= k && k <= m+n);
      insist(aa_cap[k] < cap);
      /* if there are less than cap free locations, defragment SVA */
      if (lp->sv_size - lp->sv_used < cap)
      {  lpx_defrag_sva(lp);
         /* it is reasonable to have extra amount of free locations in
            order to prevent frequent defragmentations in the future */
         extra = m + n + cap + 100;
         /* if there are less than extra free locations, reallocate SVA
            in order to increase its size */
         if (lp->sv_size - lp->sv_used < extra)
         {  while (lp->sv_size - lp->sv_used < extra)
               lp->sv_size += lp->sv_size;
            lp->sv_ndx = ucalloc(1+lp->sv_size, sizeof(int));
            memmove(&lp->sv_ndx[1], &sv_ndx[1], lp->sv_used *
               sizeof(int));
            ufree(sv_ndx), sv_ndx = lp->sv_ndx;
            lp->sv_val = ucalloc(1+lp->sv_size, sizeof(double));
            memmove(&lp->sv_val[1], &sv_val[1], lp->sv_used *
               sizeof(double));
            ufree(sv_val), sv_val = lp->sv_val;
            /* SVA has been reallocated */
            ret = 1;
         }
      }
      /* set current capacity of the k-th row/column */
      cur = aa_cap[k];
      /* copy existing elements to the beginning of the free part */
      memmove(&sv_ndx[lp->sv_used + 1], &sv_ndx[aa_ptr[k]], aa_len[k] *
         sizeof(int));
      memmove(&sv_val[lp->sv_used + 1], &sv_val[aa_ptr[k]], aa_len[k] *
         sizeof(double));
      /* set new pointer and new capacity of the k-th row/column */
      aa_ptr[k] = lp->sv_used + 1;
      aa_cap[k] = cap;
      /* set new pointer to the last used location */
      lp->sv_used += cap;
      /* now the k-th row/column starts in the rightmost location among
         other rows and columns of the constraint matrix, therefore its
         node should be moved to the end of the row/column list */
      /* remove the k-th row/column from the linked list */
      if (sv_prev[k] == 0)
         lp->sv_head = sv_next[k];
      else
      {  /* capacity of the previous row/column can be increased at the
            expense of old locations of the k-th row/column */
         aa_cap[sv_prev[k]] += cur;
         sv_next[sv_prev[k]] = sv_next[k];
      }
      if (sv_next[k] == 0)
         lp->sv_tail = sv_prev[k];
      else
         sv_prev[sv_next[k]] = sv_prev[k];
      /* insert the k-th row/column to the end of the linked list */
      sv_prev[k] = lp->sv_tail;
      sv_next[k] = 0;
      if (sv_prev[k] == 0)
         lp->sv_head = k;
      else
         sv_next[sv_prev[k]] = k;
      lp->sv_tail = k;
      return ret;
}

/*----------------------------------------------------------------------
-- lpx_set_mat_row - set (replace) row of the constraint matrix.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_mat_row(LPX *lp, int i, int len, int ndx[],
--    double val[]);
--
-- *Description*
--
-- The routine lpx_set_mat_row sets (replaces) the i-th row of the
-- constraint matrix for the LP problem object, which the parameter lp
-- points to.
--
-- Column indices and numerical values of new non-zero coefficients of
-- the i-th row should be placed in the locations ndx[1], ..., ndx[len]
-- and val[1], ..., val[len] respectively, where 0 <= len <= n is the
-- new length of the i-th row, n is number of columns.
--
-- Note that zero coefficients and multiplets (i.e. coefficients with
-- identical column indices) are not allowed. */

void lpx_set_mat_row(LPX *lp, int i, int len, int ndx[], double val[])
{     int m = lp->m;
      int n = lp->n;
      double *rs = lp->rs;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int *tagx = lp->tagx;
      int i_beg, i_end, i_ptr, j_beg, j_end, j_ptr, j, t;
      double aij;
      if (!(1 <= i && i <= m))
         fault("lpx_set_mat_row: i = %d; row number out of range", i);
      if (!(0 <= len && len <= n))
         fault("lpx_set_mat_row: len = %d; invalid row length", len);
      /* remove elements of the i-th row from the corresponding column
         lists (this inefficient operation can be avoided, if the i-th
         row was made empty before) */
      i_beg = aa_ptr[i];
      i_end = i_beg + aa_len[i] - 1;
      for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
      {  /* find a[i,j] in the j-th column */
         j = m + sv_ndx[i_ptr];
         j_beg = aa_ptr[j];
         j_end = j_beg + aa_len[j] - 1;
         for (j_ptr = j_beg; sv_ndx[j_ptr] != i; j_ptr++) /* nop */;
         insist(j_ptr <= j_end);
         /* remove a[i,j] from the j-th column list */
         sv_ndx[j_ptr] = sv_ndx[j_end];
         sv_val[j_ptr] = sv_val[j_end];
         aa_len[j]--;
         /* if the j-th column is basic (i.e. belongs to the current
            basis matrix), invalidate the factorization */
         if (lp->b_stat == LPX_B_VALID && tagx[j] == LPX_BS)
            lp->b_stat = LPX_B_UNDEF;
      }
      /* remove all elements from the i-th row list */
      aa_len[i] = 0;
      /* at least len free locations are needed in the i-th row */
      if (aa_cap[i] < len)
      {  /* enlarge capacity of the i-th row */
         if (lpx_enlarge_cap(lp, i, len))
         {  /* SVA has been reallocated */
            sv_ndx = lp->sv_ndx;
            sv_val = lp->sv_val;
         }
      }
      /* add new elements to the i-th row list */
      i_beg = aa_ptr[i];
      memcpy(&sv_ndx[i_beg], &ndx[1], len * sizeof(int));
      memcpy(&sv_val[i_beg], &val[1], len * sizeof(double));
      aa_len[i] = len;
      i_end = i_beg + len - 1;
      for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
         sv_val[i_ptr] = rs[i] * sv_val[i_ptr] * rs[m+sv_ndx[i_ptr]];
      /* add new elements to the corresponding column lists */
      for (t = 1; t <= len; t++)
      {  /* get column index and numerical value of new a[i,j] */
         j = ndx[t];
         if (!(1 <= j && j <= n))
            fault("lpx_set_mat_row: ndx[%d] = %d; column index out of r"
               "ange", t, ndx[t]);
         aij = val[t];
         if (aij == 0.0)
            fault("lpx_set_mat_row: val[%d] = 0; zero coefficient not a"
               "llowed", t);
         /* check if there is an element with the same row index in the
            j-th column (if such element exists, it is the last element
            in the column list) */
         j += m;
         j_beg = aa_ptr[j];
         j_end = j_beg + aa_len[j] - 1;
         if (j_beg <= j_end && sv_ndx[j_end] == i)
            fault("lpx_set_mat_row: ndx[%d] = %d; duplicate column indi"
               "ces not allowed", t, ndx[t]);
         /* at least one free location is needed in the j-th column */
         if (aa_cap[j] < aa_len[j] + 1)
         {  /* enlarge capacity of the j-th column */
            if (lpx_enlarge_cap(lp, j, aa_len[j] + 5))
            {  /* SVA has been reallocated */
               sv_ndx = lp->sv_ndx;
               sv_val = lp->sv_val;
            }
         }
         /* add new a[i,j] to the end of the j-th column list */
         j_ptr = aa_ptr[j] + aa_len[j];
         sv_ndx[j_ptr] = i;
         sv_val[j_ptr] = rs[i] * aij * rs[j];
         aa_len[j]++;
         /* if the j-th column is basic (i.e. belongs to the current
            basis matrix), invalidate the factorization */
         if (lp->b_stat == LPX_B_VALID && tagx[j] == LPX_BS)
            lp->b_stat = LPX_B_UNDEF;
      }
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_set_mat_col - set (replace) column of the constraint matrix.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_set_mat_col(LPX *lp, int j, int len, int ndx[],
--    double val[]);
--
-- *Description*
--
-- The routine lpx_set_mat_col sets (replaces) the j-th column of the
-- constraint matrix for the LP problem object, which the parameter lp
-- points to.
--
-- Row indices and numerical values of new non-zero coefficients of the
-- j-th column should be placed in the locations ndx[1], ..., ndx[len]
-- and val[1], ..., val[len] respectively, where 0 <= len <= m is the
-- new length of the j-th column, m is number of rows.
--
-- Note that zero coefficients and multiplets (i.e. coefficients with
-- identical row indices) are not allowed. */

void lpx_set_mat_col(LPX *lp, int j, int len, int ndx[], double val[])
{     int m = lp->m;
      int n = lp->n;
      double *rs = lp->rs;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int *tagx = lp->tagx;
      int i_beg, i_end, i_ptr, j_beg, j_end, j_ptr, i, t;
      double aij;
      if (!(1 <= j && j <= n))
         fault("lpx_set_mat_col: j = %d; column number out of range",
            j);
      if (!(0 <= len && len <= m))
         fault("lpx_set_mat_col: len = %d; invalid column length", len);
      /* remove elements of the j-th column from the corresponding row
         lists (this inefficient operation can be avoided, if the j-th
         column was made empty before) */
      j_beg = aa_ptr[m+j];
      j_end = j_beg + aa_len[m+j] - 1;
      for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
      {  /* find a[i,j] in the i-th row */
         i = sv_ndx[j_ptr];
         i_beg = aa_ptr[i];
         i_end = i_beg + aa_len[i] - 1;
         for (i_ptr = i_beg; sv_ndx[i_ptr] != j; i_ptr++) /* nop */;
         insist(i_ptr <= i_end);
         /* remove a[i,j] from the i-th row list */
         sv_ndx[i_ptr] = sv_ndx[i_end];
         sv_val[i_ptr] = sv_val[i_end];
         aa_len[i]--;
      }
      /* remove all elements from the j-th column list */
      aa_len[m+j] = 0;
      /* at least len free locations are needed in the j-th column */
      if (aa_cap[m+j] < len)
      {  /* enlarge capacity of the j-th column */
         if (lpx_enlarge_cap(lp, m+j, len))
         {  /* SVA has been reallocated */
            sv_ndx = lp->sv_ndx;
            sv_val = lp->sv_val;
         }
      }
      /* add new elements to the j-th column list */
      j_beg = aa_ptr[m+j];
      memcpy(&sv_ndx[j_beg], &ndx[1], len * sizeof(int));
      memcpy(&sv_val[j_beg], &val[1], len * sizeof(double));
      aa_len[m+j] = len;
      j_end = j_beg + len - 1;
      for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
         sv_val[j_ptr] = rs[sv_ndx[j_ptr]] * sv_val[j_ptr] * rs[m+j];
      /* add new elements to the corresponding row lists */
      for (t = 1; t <= len; t++)
      {  /* get row index and numerical value of new a[i,j] */
         i = ndx[t];
         if (!(1 <= i && i <= m))
            fault("lpx_set_mat_col: ndx[%d] = %d; row index out of rang"
               "e", t, ndx[t]);
         aij = val[t];
         if (aij == 0.0)
            fault("lpx_set_mat_col: val[%d] = 0; zero coefficient not a"
               "llowed", t);
         /* check if there is an element with the same column index in
            the i-th row (if such element exists, it is the last element
            in the row list) */
         i_beg = aa_ptr[i];
         i_end = i_beg + aa_len[i] - 1;
         if (i_beg <= i_end && sv_ndx[i_end] == j)
            fault("lpx_set_mat_col: ndx[%d] = %d; duplicate row indices"
               " not allowed", t, ndx[t]);
         /* at least one free location is needed in the i-th row */
         if (aa_cap[i] < aa_len[i] + 1)
         {  /* enlarge capacity of the i-th row */
            if (lpx_enlarge_cap(lp, i, aa_len[i] + 5))
            {  /* SVA has been reallocated */
               sv_ndx = lp->sv_ndx;
               sv_val = lp->sv_val;
            }
         }
         /* add new a[i,j] to the end of the i-th row list */
         i_ptr = aa_ptr[i] + aa_len[i];
         sv_ndx[i_ptr] = j;
         sv_val[i_ptr] = rs[i] * aij * rs[m+j];
         aa_len[i]++;
      }
      /* if the j-th column is basic (i.e. belongs to the current basis
         matrix), invalidate the factorization */
      if (lp->b_stat == LPX_B_VALID && tagx[m+j] == LPX_BS)
         lp->b_stat = LPX_B_UNDEF;
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_unmark_all - unmark all rows and columns.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_unmark_all(LPX *lp);
--
-- *Description*
--
-- The routine lpx_unmark_all resets marks of all rows and columns of
-- the LP problem object to zero. */

void lpx_unmark_all(LPX *lp)
{     int m = lp->m;
      int n = lp->n;
      int *mark = lp->mark;
      int k;
      for (k = 1; k <= m+n; k++) mark[k] = 0;
      return;
}

/*----------------------------------------------------------------------
-- lpx_mark_row - assign mark to row.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_mark_row(LPX *lp, int i, int mark);
--
-- *Description*
--
-- The routine lpx_mark_row assigns an integer mark the i-th row.
--
-- The sense of marking depends on what operation will be performed on
-- the LP problem object. */

void lpx_mark_row(LPX *lp, int i, int mark)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_mark_row: i = %d; row number out of range", i);
      lp->mark[i] = mark;
      return;
}

/*----------------------------------------------------------------------
-- lpx_mark_col - assign mark to column.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_mark_col(LPX *lp, int j, int mark);
--
-- The routine lpx_mark_col assigns an integer mark to the j-th column.
--
-- The sense of marking depends on what operation will be performed on
-- the LP problem object. */

void lpx_mark_col(LPX *lp, int j, int mark)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_mark_col: j = %d; column number out of range", j);
      lp->mark[lp->m + j] = mark;
      return;
}

/*----------------------------------------------------------------------
-- lpx_change_ordn - change ordinal numbers of rows and columns.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_change_ordn(LPX *lp)
--
-- *Description*
--
-- The routine lpx_change_ordn changes the ordinal numbers of rows and
-- columns of the LP problem object, which the parameter lp points to.
--
-- Directly before call to this routine new ordinal numbers of all rows
-- and columns should be assigned by means of the routines lpx_mark_row
-- and lpx_mark_col as follows:
--
--    lpx_mark_row(lp, i, <new ordinal number of the i-th row>);
--
--    lpx_mark_col(lp, j, <new ordinal number of the j-th column>);
--
-- On exit the routine unmarks all the rows and columns.
--
-- After this operation all the rows and columns should be referenced
-- using their new ordinal numbers. */

void lpx_change_ordn(LPX *lp)
{     int m = lp->m;
      int n = lp->n;
      STR **name = lp->name;
      int *typx = lp->typx;
      double *lb = lp->lb;
      double *ub = lp->ub;
      double *rs = lp->rs;
      int *mark = lp->mark;
      double *coef = lp->coef;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      int *sv_prev = lp->sv_prev;
      int *sv_next = lp->sv_next;
      int *tagx = lp->tagx;
      int i, i_beg, i_end, i_ptr, j, j_beg, j_end, j_ptr, k;
      STR **semp;
      int *temp;
      double *work;
      /* allocate working space */
      work = ucalloc(1+m+n, sizeof(double));
      insist(sizeof(STR *) <= sizeof(double));
      semp = (STR **)work;
      insist(sizeof(int) <= sizeof(double));
      temp = (int *)work;
      /* check new ordinal numbers of rows */
      for (i = 1; i <= m; i++) temp[i] = 0;
      for (i = 1; i <= m; i++)
      {  int i_new = mark[i];
         if (!(1 <= i_new && i_new <= m))
            fault("lpx_change_ordn: i = %d; row has invalid new ordinal"
               " number", i);
         if (temp[i_new])
            fault("lpx_change_ordn: i = %d; row has ambiguous new ordin"
               "al number", i);
         temp[i_new] = 1;
      }
      /* check new ordinal numbers of columns */
      for (j = 1; j <= n; j++) temp[j] = 0;
      for (j = 1; j <= n; j++)
      {  int j_new = mark[m+j];
         if (!(1 <= j_new && j_new <= n))
            fault("lpx_change_ordn: j = %d; column has invalid new ordi"
               "nal number", j);
         if (temp[j_new])
            fault("lpx_change_ordn: j = %d; column has ambiguous new or"
               "dinal number", j);
         temp[j_new] = 1;
      }
      /* correct new ordinal numbers of columns */
      for (j = 1; j <= n; j++) mark[m+j] += m;
      /* apply new numbering to the LP problem object */
      for (k = 1; k <= m+n; k++) semp[k] = name[k];
      for (k = 1; k <= m+n; k++) name[mark[k]] = semp[k];
      for (k = 1; k <= m+n; k++) temp[k] = typx[k];
      for (k = 1; k <= m+n; k++) typx[mark[k]] = temp[k];
      for (k = 1; k <= m+n; k++) work[k] = lb[k];
      for (k = 1; k <= m+n; k++) lb[mark[k]] = work[k];
      for (k = 1; k <= m+n; k++) work[k] = ub[k];
      for (k = 1; k <= m+n; k++) ub[mark[k]] = work[k];
      for (k = 1; k <= m+n; k++) work[k] = rs[k];
      for (k = 1; k <= m+n; k++) rs[mark[k]] = work[k];
      for (k = 1; k <= m+n; k++) work[k] = coef[k];
      for (k = 1; k <= m+n; k++) coef[mark[k]] = work[k];
      for (i = 1; i <= m; i++)
      {  i_beg = aa_ptr[i];
         i_end = i_beg + aa_len[i] - 1;
         for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
            sv_ndx[i_ptr] = mark[m + sv_ndx[i_ptr]] - m;
      }
      for (j = 1; j <= n; j++)
      {  j_beg = aa_ptr[m+j];
         j_end = j_beg + aa_len[m+j] - 1;
         for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
            sv_ndx[j_ptr] = mark[sv_ndx[j_ptr]];
      }
      for (k = 1; k <= m+n; k++) temp[k] = aa_ptr[k];
      for (k = 1; k <= m+n; k++) aa_ptr[mark[k]] = temp[k];
      for (k = 1; k <= m+n; k++) temp[k] = aa_len[k];
      for (k = 1; k <= m+n; k++) aa_len[mark[k]] = temp[k];
      for (k = 1; k <= m+n; k++) temp[k] = aa_cap[k];
      for (k = 1; k <= m+n; k++) aa_cap[mark[k]] = temp[k];
      if (lp->sv_head != 0) lp->sv_head = mark[lp->sv_head];
      if (lp->sv_tail != 0) lp->sv_tail = mark[lp->sv_tail];
      for (k = 1; k <= m+n; k++) temp[k] = sv_prev[k];
      for (k = 1; k <= m+n; k++)
         sv_prev[mark[k]] = (temp[k] == 0 ? 0 : mark[temp[k]]);
      for (k = 1; k <= m+n; k++) temp[k] = sv_next[k];
      for (k = 1; k <= m+n; k++)
         sv_next[mark[k]] = (temp[k] == 0 ? 0 : mark[temp[k]]);
      for (k = 1; k <= m+n; k++) temp[k] = tagx[k];
      for (k = 1; k <= m+n; k++) tagx[mark[k]] = temp[k];
      /* free working space */
      ufree(work);
      /* unmark all rows and columns */
      lpx_unmark_all(lp);
      /* invalidate the current basis */
      lp->b_stat = LPX_B_UNDEF;
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_clear_mat - clear rows and columns of the constraint matrix.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_clear_mat(LPX *lp);
--
-- *Description*
--
-- The routine lpx_clear_mat clears all marked rows and columns of the
-- constraint matrix.
--
-- On exit the routine remains the row and column marks unchanged. */

void lpx_clear_mat(LPX *lp)
{     int m = lp->m;
      int n = lp->n;
      int *mark = lp->mark;
      int *aa_len = lp->aa_len;
      int *aa_ptr = lp->aa_ptr;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int *tagx = lp->tagx;
      int i, i_ptr, i_end, j, j_ptr, j_end;
      /* clear marked rows */
      for (i = 1; i <= m; i++)
         if (mark[i]) aa_len[i] = 0;
      /* remove elements, which belong to the marked rows, from the
         corresponding column lists */
      for (j = m+1; j <= m+n; j++)
      {  j_ptr = aa_ptr[j];
         j_end = j_ptr + aa_len[j] - 1;
         while (j_ptr <= j_end)
         {  i = sv_ndx[j_ptr];
            if (mark[i])
            {  /* remove a[i,j] from the j-th column list */
               sv_ndx[j_ptr] = sv_ndx[j_end];
               sv_val[j_ptr] = sv_val[j_end];
               aa_len[j]--;
               j_end--;
               /* if the j-th column is basic, the factorization should
                  be invalidated */
               if (lp->b_stat == LPX_B_VALID && tagx[j] == LPX_BS)
                  lp->b_stat = LPX_B_UNDEF;
            }
            else
            {  /* keep a[i,j] in the j-th column list */
               j_ptr++;
            }
         }
      }
      /* clear marked columns */
      for (j = m+1; j <= m+n; j++)
      {  if (mark[j])
         {  aa_len[j] = 0;
            /* if the j-th column is basic, the factorization should be
               invalidated */
            if (lp->b_stat == LPX_B_VALID && tagx[j] == LPX_BS)
               lp->b_stat = LPX_B_UNDEF;
         }
      }
      /* remove elements, which belong to the marked columns, from the
         corresponding row lists */
      for (i = 1; i <= m; i++)
      {  i_ptr = aa_ptr[i];
         i_end = i_ptr + aa_len[i] - 1;
         while (i_ptr <= i_end)
         {  j = m + sv_ndx[i_ptr];
            if (mark[j])
            {  /* remove a[i,j] from the i-th row list */
               sv_ndx[i_ptr] = sv_ndx[i_end];
               sv_val[i_ptr] = sv_val[i_end];
               aa_len[i]--;
               i_end--;
            }
            else
            {  /* keep a[i,j] in the i-th row list */
               i_ptr++;
            }
         }
      }
      /* invalidate the current solution */
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_del_items - remove rows and columns from LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_del_items(LPX *lp);
--
-- *Description*
--
-- The routine lpx_del_items removes all marked rows and columns from
-- an LP problem object, which the parameter lp points to. */

void lpx_del_items(LPX *lp)
{     int m = lp->m;
      int n = lp->n;
      STR **name = lp->name;
      int *typx = lp->typx;
      double *lb = lp->lb;
      double *ub = lp->ub;
      double *rs = lp->rs;
      int *mark = lp->mark;
      double *coef = lp->coef;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *aa_cap = lp->aa_cap;
      int *sv_ndx = lp->sv_ndx;
      int *sv_prev = lp->sv_prev;
      int *sv_next = lp->sv_next;
      int *tagx = lp->tagx;
      int m_new, n_new, k_new, i, j, k;
      /* clear marked rows and columns in the matrix */
      lpx_clear_mat(lp);
      /* and remove them from the row/column linked list */
      for (k = 1; k <= m+n; k++)
      {  if (mark[k])
         {  if (sv_prev[k] == 0)
               lp->sv_head = sv_next[k];
            else
               sv_next[sv_prev[k]] = sv_next[k];
            if (sv_next[k] == 0)
               lp->sv_tail = sv_prev[k];
            else
               sv_prev[sv_next[k]] = sv_prev[k];
         }
      }
      /* delete marked rows and columns from the LP problem object and
         determine new ordinal numbers of remaining rows and columns */
      m_new = n_new = k_new = 0;
      for (k = 1; k <= m+n; k++)
      {  if (!mark[k])
         {  /* the row/column should be kept */
            if (k <= m) m_new++; else n_new++;
            mark[k] = ++k_new; /* new ordinal number */
            name[k_new] = name[k];
            typx[k_new] = typx[k];
            lb[k_new] = lb[k];
            ub[k_new] = ub[k];
            rs[k_new] = rs[k];
            coef[k_new] = coef[k];
            aa_ptr[k_new] = aa_ptr[k];
            aa_len[k_new] = aa_len[k];
            aa_cap[k_new] = aa_cap[k];
            tagx[k_new] = tagx[k];
            sv_prev[k_new] = sv_prev[k];
            sv_next[k_new] = sv_next[k];
         }
         else
         {  /* the row/column should be deleted */
            if (name[k] != NULL) delete_str(name[k]);
            mark[k] = 0;
         }
      }
      /* apply new numbering to the row/column linked list */
      if (lp->sv_head != 0) lp->sv_head = mark[lp->sv_head];
      if (lp->sv_tail != 0) lp->sv_tail = mark[lp->sv_tail];
      for (k = 1; k <= m_new+n_new; k++)
      {  if (sv_prev[k] != 0) sv_prev[k] = mark[sv_prev[k]];
         if (sv_next[k] != 0) sv_next[k] = mark[sv_next[k]];
      }
      /* apply new numbering to column indices in the row lists */
      for (i = 1; i <= m_new; i++)
      {  int i_beg, i_end, i_ptr;
         i_beg = aa_ptr[i];
         i_end = i_beg + aa_len[i] - 1;
         for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
            sv_ndx[i_ptr] = mark[sv_ndx[i_ptr] + m] - m_new;
      }
      /* apply new numbering to row indices in the column lists */
      for (j = m_new+1; j <= m_new+n_new; j++)
      {  int j_beg, j_end, j_ptr;
         j_beg = aa_ptr[j];
         j_end = j_beg + aa_len[j] - 1;
         for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
            sv_ndx[j_ptr] = mark[sv_ndx[j_ptr]];
      }
      /* set the new problem size */
      lp->m = m_new, lp->n = n_new;
      /* unmark all rows and columns */
      lpx_unmark_all(lp);
      /* invalidate the current basis */
      lp->b_stat = LPX_B_UNDEF;
      lp->p_stat = LPX_P_UNDEF;
      lp->d_stat = LPX_D_UNDEF;
      return;
}

/*----------------------------------------------------------------------
-- lpx_delete_prob - delete LP problem object.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_delete_prob(LPX *lp);
--
-- *Description*
--
-- The routine lpx_delete_prob deletes an LP problem object, which the
-- parameter lp points to, freeing all the memory allocated to it. */

void lpx_delete_prob(LPX *lp)
{     delete_pool(lp->pool);
      ufree(lp->buf);
      ufree(lp->name);
      ufree(lp->typx);
      ufree(lp->lb);
      ufree(lp->ub);
      ufree(lp->rs);
      ufree(lp->mark);
      ufree(lp->coef);
      ufree(lp->aa_ptr);
      ufree(lp->aa_len);
      ufree(lp->aa_cap);
      ufree(lp->sv_ndx);
      ufree(lp->sv_val);
      ufree(lp->sv_prev);
      ufree(lp->sv_next);
      ufree(lp->tagx);
      ufree(lp->posx);
      ufree(lp->indx);
      if (lp->inv != NULL) inv_delete(lp->inv);
      ufree(lp->bbar);
      ufree(lp->pi);
      ufree(lp->cbar);
      ufree(lp);
      return;
}

/*----------------------------------------------------------------------
-- lpx_simplex - solve LP problem by means of the simplex method.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_simplex(LPX *lp);
--
-- *Description*
--
-- The routine lpx_simplex is intended to solve LP problem, which the
-- parameter lp points to, by means of the simplex method.
--
-- *Returns*
--
-- See exit codes in the header 'glplpx.h'. */

int lpx_simplex(LPX *lp)
{     int ret;
      ret = spx_solve_lp(lp);
      return ret;
}

/*----------------------------------------------------------------------
-- lpx_get_num_rows - determine number of rows.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_num_rows(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_num_rows returns current number of rows in an LP
-- problem object, which the parameter lp points to. */

int lpx_get_num_rows(LPX *lp)
{     int m = lp->m;
      return m;
}

/*----------------------------------------------------------------------
-- lpx_get_num_cols - determine number of columns.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_num_cols(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_num_cols returns current number of columns in an
-- LP problem object, which the parameter lp points to. */

int lpx_get_num_cols(LPX *lp)
{     int n = lp->n;
      return n;
}

/*----------------------------------------------------------------------
-- lpx_get_num_nz - determine number of constraint coefficients.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_num_nz(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_num_nz returns current number non-zero elements
-- in the constraint matrix that belongs to an LP problem object, which
-- the parameter lp points to. */

int lpx_get_num_nz(LPX *lp)
{     int m = lp->m;
      int *aa_len = lp->aa_len;
      int i, count = 0;
      for (i = 1; i <= m; i++) count += aa_len[i];
      return count;
}

/*----------------------------------------------------------------------
-- lpx_get_prob_name - obtain problem name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- char *lpx_get_prob_name(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_prob_name returns a pointer to a static buffer
-- that contains symbolic name of the problem. However, if the problem
-- has no assigned name, the routine returns NULL. */

char *lpx_get_prob_name(LPX *lp)
{     return
         lp->name[0] == NULL ? NULL : get_str(lp->buf, lp->name[0]);
}

/*----------------------------------------------------------------------
-- lpx_get_row_name - obtain row name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- char *lpx_get_row_name(LPX *lp, int i);
--
-- *Returns*
--
-- The routine lpx_get_row_name returns a pointer to a static buffer
-- that contains symbolic name of the i-th row. However, if the i-th
-- row has no assigned name, the routine returns NULL. */

char *lpx_get_row_name(LPX *lp, int i)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_get_row_name: i = %d; row number out of range", i);
      return
         lp->name[i] == NULL ? NULL : get_str(lp->buf, lp->name[i]);
}

/*----------------------------------------------------------------------
-- lpx_get_col_name - obtain column name.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- char *lpx_get_col_name(LPX *lp, int j);
--
-- *Returns*
--
-- The routine lpx_get_col_name returns a pointer to a static buffer
-- that contains symbolic name of the j-th column. However, if the j-th
-- column has no assigned name, the routine returns NULL. */

char *lpx_get_col_name(LPX *lp, int j)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_get_col_name: j = %d; column number out of range",
            j);
      j += lp->m;
      return
         lp->name[j] == NULL ? NULL : get_str(lp->buf, lp->name[j]);
}

/*----------------------------------------------------------------------
-- glp_get_row_bnds - obtain row bounds.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_get_row_bnds(LPX *lp, int i, int *typx, double *lb,
--    double *ub);
--
-- *Description*
--
-- The routine lpx_get_row_bnds stores the type, lower bound and upper
-- bound of the i-th row to locations, which the parameters typx, lb,
-- and ub point to, respectively.
--
-- If some of the parameters typx, lb, or ub is NULL, the corresponding
-- value is not stored.
--
-- Types and bounds have the following meaning:
--
--     Type          Bounds            Note
--    -------------------------------------------
--    LPX_FR   -inf <  x <  +inf   free variable
--    LPX_LO     lb <= x <  +inf   lower bound
--    LPX_UP   -inf <  x <=  ub    upper bound
--    LPX_DB     lb <= x <=  ub    double bound
--    LPX_FX           x  =  lb    fixed variable
--
-- where x is the corresponding auxiliary variable.
--
-- If the row has no lower bound, *lb is set to zero. If the row has no
-- upper bound, *ub is set to zero. If the row is of fixed type, *lb and
-- *ub are set to the same value. */

void lpx_get_row_bnds(LPX *lp, int i, int *typx, double *lb, double *ub)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_get_row_bnds: i = %d; row number out of range", i);
      if (typx != NULL) *typx = lp->typx[i];
      if (lb != NULL) *lb = lp->lb[i] / lp->rs[i];
      if (ub != NULL) *ub = lp->ub[i] / lp->rs[i];
      return;
}

/*----------------------------------------------------------------------
-- glp_get_col_bnds - obtain column bounds.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- void lpx_get_col_bnds(LPX *lp, int j, int *typx, double *lb,
--    double *ub);
--
-- *Description*
--
-- The routine lpx_get_col_bnds stores the type, lower bound and upper
-- bound of the j-th column to locations, which the parameters typx, lb,
-- and ub point to, respectively.
--
-- If some of the parameters typx, lb, or ub is NULL, the corresponding
-- value is not stored.
--
-- Types and bounds have the following meaning:
--
--     Type          Bounds            Note
--    -------------------------------------------
--    LPX_FR   -inf <  x <  +inf   free variable
--    LPX_LO     lb <= x <  +inf   lower bound
--    LPX_UP   -inf <  x <=  ub    upper bound
--    LPX_DB     lb <= x <=  ub    double bound
--    LPX_FX           x  =  lb    fixed variable
--
-- where x is the corresponding structural variable.
--
-- If the column has no lower bound, *lb is set to zero. If the column
-- has no upper bound, *ub is set to zero. If the column is of fixed
-- type, *lb and *ub are set to the same value. */

void lpx_get_col_bnds(LPX *lp, int j, int *typx, double *lb, double *ub)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_get_col_bnds: j = %d; column number out of range",
            j);
      j += lp->m;
      if (typx != NULL) *typx = lp->typx[j];
      if (lb != NULL) *lb = lp->lb[j] * lp->rs[j];
      if (ub != NULL) *ub = lp->ub[j] * lp->rs[j];
      return;
}

/*----------------------------------------------------------------------
-- lpx_get_obj_dir - determine optimization direction.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_obj_dir(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_obj_dir returns a flag that defines optimization
-- direction (i.e. sense of the objective function):
--
-- LPX_MIN - the objective function has to be minimized;
-- LPX_MAX - the objective function has to be maximized. */

int lpx_get_obj_dir(LPX *lp)
{     int dir = lp->dir;
      return dir;
}

/*----------------------------------------------------------------------
-- lpx_get_obj_c0 - obtain constant term of the obj. function.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- double lpx_get_obj_c0(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_obj_c0 return a constant term of the objective
-- function for an LP problem, which the parameter lp points to. */

double lpx_get_obj_c0(LPX *lp)
{     double c0 = lp->coef[0];
      return c0;
}

/*----------------------------------------------------------------------
-- lpx_get_row_coef - obtain row objective coefficient.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- double lpx_get_row_coef(LPX *lp, int i);
--
-- *Returns*
--
-- The routine lpx_get_row_coef returns a coefficient of the objective
-- function at the i-th auxiliary variable (row). */

double lpx_get_row_coef(LPX *lp, int i)
{     if (!(1 <= i && i <= lp->m))
         fault("lpx_get_row_coef: i = %d; row number out of range", i);
      return lp->coef[i] * lp->rs[i];
}

/*----------------------------------------------------------------------
-- lpx_get_col_coef - obtain column objective coefficient.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- double lpx_get_col_coef(LPX *lp, int j);
--
-- *Returns*
--
-- The routine lpx_get_col_coef returns a coefficient of the objective
-- function at the j-th structural variable (column). */

double lpx_get_col_coef(LPX *lp, int j)
{     if (!(1 <= j && j <= lp->n))
         fault("lpx_get_col_coef: j = %d; column number out of range",
            j);
      j += lp->m;
      return lp->coef[j] / lp->rs[j];
}

/*----------------------------------------------------------------------
-- lpx_get_mat_row - get row of the constraint matrix.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_mat_row(LPX *lp, int i, int ndx[], double val[]);
--
-- *Description*
--
-- The routine lpx_get_mat_row scans (non-zero) elements of the i-th
-- row of the constraint matrix and stores their column indices and
-- values to locations ndx[1], ..., ndx[len] and val[1], ..., val[len]
-- respectively, where 0 <= len <= n is number of elements in the i-th
-- row, n is number of columns. It is allowed to specify val as NULL,
-- in which case only column indices are stored.
--
-- *Returns*
--
-- The routine returns len, which is number of stored elements (length
-- of the i-th row). */

int lpx_get_mat_row(LPX *lp, int i, int ndx[], double val[])
{     int m = lp->m;
      double *rs = lp->rs;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int beg, len, t;
      double rs_i;
      if (!(1 <= i && i <= m))
         fault("lpx_get_mat_row: i = %d; row number out of range", i);
      beg = aa_ptr[i];
      len = aa_len[i];
      memcpy(&ndx[1], &sv_ndx[beg], len * sizeof(int));
      if (val != NULL)
      {  memcpy(&val[1], &sv_val[beg], len * sizeof(double));
         rs_i = rs[i];
         for (t = 1; t <= len; t++)
            val[t] /= (rs_i * rs[m + ndx[t]]);
      }
      return len;
}

/*----------------------------------------------------------------------
-- lpx_get_mat_col - get column of the constraint matrix.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_mat_col(LPX *lp, int j, int ndx[], double val[]);
--
-- *Description*
--
-- The routine lpx_get_mat_col scans (non-zero) elements of the j-th
-- column of the constraint matrix and stores their row indices and
-- values to locations ndx[1], ..., ndx[len] and val[1], ..., val[len]
-- respectively, where 0 <= len <= m is number of elements in the j-th
-- column, m is number of rows. It is allowed to specify val as NULL,
-- in which case only row indices are stored.
--
-- *Returns*
--
-- The routine returns len, which is number of stored elements (length
-- of the j-th column). */

int lpx_get_mat_col(LPX *lp, int j, int ndx[], double val[])
{     int m = lp->m;
      int n = lp->n;
      double *rs = lp->rs;
      int *aa_ptr = lp->aa_ptr;
      int *aa_len = lp->aa_len;
      int *sv_ndx = lp->sv_ndx;
      double *sv_val = lp->sv_val;
      int beg, len, t;
      double rs_j;
      if (!(1 <= j && j <= n))
         fault("lpx_get_mat_col: j = %d; column number out of range",
            j);
      j += m;
      beg = aa_ptr[j];
      len = aa_len[j];
      memcpy(&ndx[1], &sv_ndx[beg], len * sizeof(int));
      if (val != NULL)
      {  memcpy(&val[1], &sv_val[beg], len * sizeof(double));
         rs_j = rs[j];
         for (t = 1; t <= len; t++)
            val[t] /= (rs[ndx[t]] * rs_j);
      }
      return len;
}

/*----------------------------------------------------------------------
-- lpx_get_prim_stat - query primal status of basic solution.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_prim_stat(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_prim_stat reports the primal status of the basic
-- solution obtained by the solver for an LP problem object, which the
-- parameter lp points to:
--
-- LPX_P_UNDEF  - the primal status is undefined;
-- LPX_P_FEAS   - the solution is primal feasible;
-- LPX_P_INFEAS - the solution is primal infeasible;
-- LPX_P_NOFEAS - no primal feasible solution exists. */

int lpx_get_prim_stat(LPX *lp)
{     int p_stat = lp->p_stat;
      return p_stat;
}

/*----------------------------------------------------------------------
-- lpx_get_dual_stat - query dual status of basic solution.
--
-- *Synopsis*
--
-- #include "glplpx.h"
-- int lpx_get_dual_stat(LPX *lp);
--
-- *Returns*
--
-- The routine lpx_get_dual_stat reports the dual status of the basic
-- solution obtained by the solver for an LP problem object, which the
-- parameter lp points to:
--
-- LPX_D_UNDEF  - the dual status is undefined;
-- LPX_D_FEAS   - the solution is dual feasible;
-- LPX_D_INFEAS - the solution is dual infeasible;
-- LPX_D_NOFEAS - no dual feasible solution exists. */

int lpx_get_dual_stat(LPX *lp)
{     int d_stat = lp->d_stat;
      return d_stat;
}

/* eof */
