/* glpspx4.c (simplex method solver routines) */

/*----------------------------------------------------------------------
-- 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 <math.h>
#include <string.h>
#include "glpspx.h"

/* internal codes used by the solver routines: */
#define SPX_OK     0 /* no error */
#define SPX_ITLIM  1 /* iteration limit reached */
#define SPX_NOPFS  2 /* no primal feasible solution */
#define SPX_NODFS  3 /* no dual feasible solution */
#define SPX_INSTAB 4 /* numerical instability */
#define SPX_SING   5 /* singular basis detected */

/*----------------------------------------------------------------------
-- prim_opt - search for optimal solution (primal simplex).
--
-- This routine searches for optimal solution of a specified LP problem
-- using the primal simplex method.
--
-- Structure of this routine can be an example for other variants based
-- on the primal simplex method. */

static void prim_opt_dpy(SPX *spx)
{     /* this auxiliary routine displays information about the current
         basic solution */
      LPX *lp = spx->lp;
      int i, def = 0;
      for (i = 1; i <= lp->m; i++)
         if (lp->typx[lp->indx[i]] == LPX_FX) def++;
      print("*%6d:   objval = %17.9e   infeas = %17.9e (%d)",
         lp->it_cnt, spx_eval_obj(lp), spx_check_bbar(lp, 0.0), def);
      return;
}

static int prim_opt(LPX *lp)
{     SPX *spx;
      int m = lp->m;
      int n = lp->n;
      int ret;
      /* on entry the following conditions are required:
         factorization of the current basis matrix should be valid;
         all components of the current basic soultion must be computed
         directly;
         the current basic solution should be primal feasible and dual
         infeasible */
      insist(lp->b_stat == LPX_B_VALID);
      insist(lp->p_stat == LPX_P_FEAS);
      insist(lp->d_stat == LPX_D_INFEAS);
      /* allocate common block */
      spx = umalloc(sizeof(SPX));
      spx->lp = lp;
      spx->meth = 'P';
      spx->p = 0;
      spx->p_tag = 0;
      spx->q = 0;
      spx->zeta = ucalloc(1+m, sizeof(double));
      spx->ap = ucalloc(1+n, sizeof(double));
      spx->aq = ucalloc(1+m, sizeof(double));
      spx->gvec = ucalloc(1+n, sizeof(double));
      spx->dvec = NULL;
      spx->refsp = (lp->price ? ucalloc(1+m+n, sizeof(int)) : NULL);
      spx->reset = 0;
      spx->work = ucalloc(1+m+n, sizeof(double));
      spx->orig_typx = NULL;
      spx->orig_lb = spx->orig_ub = NULL;
      spx->orig_dir = 0;
      spx->orig_coef = NULL;
      /* initialize weights of non-basic variables */
      if (!lp->price)
      {  /* textbook pricing will be used */
         int j;
         for (j = 1; j <= n; j++) spx->gvec[j] = 1.0;
      }
      else
      {  /* steepest edge pricing will be used */
         spx_reset_refsp(spx);
      }
      /* main loop starts here */
      for (;;)
      {  /* display information about the current basic solution */
         if (lp->msg_lev >= 2 && lp->it_cnt % 100 == 0)
            prim_opt_dpy(spx);
         /* check if iteration limit has been reached */
         if (lp->it_lim >= 0 && lp->it_cnt >= lp->it_lim)
         {  ret = SPX_ITLIM;
            break;
         }
         /* choose non-basic variable xN[q] */
         if (spx_prim_chuzc(spx, lp->tol_dj))
         {  /* basic solution components were recomputed; check primal
               feasibility */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
         /* if no xN[q] has been chosen, the current basic solution is
            dual feasible and therefore optimal */
         if (spx->q == 0)
         {  ret = SPX_OK;
            break;
         }
         /* compute the q-th column of the current simplex table (later
            this column will enter the basis) */
         spx_eval_col(lp, spx->q, spx->aq, 1);
         /* choose basic variable xB[p] */
         if (spx_prim_chuzr(spx, lp->relax * lp->tol_bnd))
         {  /* the basis matrix should be reinverted, because the q-th
               column of the simplex table is unreliable */
            insist("not implemented yet" == NULL);
         }
         /* if no xB[p] has been chosen, the problem is unbounded (has
            no dual feasible solution) */
         if (spx->p == 0)
         {  ret = SPX_NODFS;
            break;
         }
         /* update values of basic variables */
         spx_update_bbar(spx);
         if (spx->p > 0)
         {  /* compute the p-th row of the inverse inv(B) */
            spx_eval_rho(lp, spx->p, spx->zeta);
            /* compute the p-th row of the current simplex table */
            spx_eval_row(lp, spx->zeta, spx->ap);
            /* update simplex multipliers */
            spx_update_pi(spx);
            /* update reduced costs of non-basic variables */
            spx_update_cbar(spx, 0);
            /* update weights of non-basic variables */
            if (lp->price) spx_update_gvec(spx);
         }
         /* jump to the adjacent vertex of the LP polyhedron */
         if (spx_change_basis(spx))
         {  /* the basis matrix should be reinverted */
            if (spx_invert(lp))
            {  /* some numerical problems with the basis matrix */
               ret = SPX_SING;
               break;
            }
            /* compute values of basic variables */
            spx_eval_bbar(lp);
            /* compute simplex multipliers */
            spx_eval_pi(lp);
            /* compute reduced costs of non-basic variables */
            spx_eval_cbar(lp);
            /* check primal feasibility */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
#if 0
         /* check accuracy of main solution components after updating
            (for debugging purposes only) */
         {  double ae_bbar = spx_err_in_bbar(spx);
            double ae_pi   = spx_err_in_pi(spx);
            double ae_cbar = spx_err_in_cbar(spx, 0);
            double ae_gvec = lp->price ? spx_err_in_gvec(spx) : 0.0;
            print("bbar: %g; pi: %g; cbar: %g; gvec: %g",
               ae_bbar, ae_pi, ae_cbar, ae_gvec);
            if (ae_bbar > 1e-9 || ae_pi > 1e-9 || ae_cbar > 1e-9 ||
                ae_gvec > 1e-3)
               insist("solution accuracy too low" == NULL);
         }
#endif
      }
      /* end of main loop */
      if (lp->b_stat == LPX_B_VALID)
      {  if (lp->msg_lev >= 2 && lp->it_cnt % 100 != 0)
            prim_opt_dpy(spx);
      }
      /* deallocate common block */
      ufree(spx->zeta);
      ufree(spx->ap);
      ufree(spx->aq);
      ufree(spx->gvec);
      if (lp->price) ufree(spx->refsp);
      ufree(spx->work);
      ufree(spx);
      /* compute basic solution components at the final point */
      if (lp->b_stat == LPX_B_VALID)
      {  spx_eval_bbar(lp);
         spx_eval_pi(lp);
         spx_eval_cbar(lp);
         if (spx_check_bbar(lp, lp->tol_bnd) == 0.0)
            lp->p_stat = LPX_P_FEAS;
         else
            lp->p_stat = LPX_P_INFEAS;
         if (spx_check_cbar(lp, lp->tol_dj) == 0.0)
            lp->d_stat = LPX_D_FEAS;
         else
            lp->d_stat = LPX_D_INFEAS;
      }
      else
      {  lp->p_stat = LPX_P_UNDEF;
         lp->d_stat = LPX_D_UNDEF;
      }
      /* analyze exit code */
      switch (ret)
      {  case SPX_OK:
            /* optimal solution found */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            insist(lp->d_stat == LPX_D_FEAS);
            if (lp->msg_lev >= 2)
               print("OPTIMAL SOLUTION FOUND");
            break;
         case SPX_ITLIM:
            /* iteration limit reached */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            insist(lp->d_stat == LPX_D_INFEAS);
            if (lp->msg_lev >= 2)
               print("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED");
            break;
         case SPX_NODFS:
            /* no dual feasible solution */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            insist(lp->d_stat == LPX_D_INFEAS);
            if (lp->msg_lev >= 2)
               print("PROBLEM HAS UNBOUNDED SOLUTION");
            lp->d_stat = LPX_D_NOFEAS;
            break;
         case SPX_INSTAB:
            /* numerical instability */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 3)
               print("Numerical instability");
            break;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            insist(lp->b_stat == LPX_B_UNDEF);
            insist(lp->p_stat == LPX_P_UNDEF);
            insist(lp->d_stat == LPX_D_UNDEF);
            if (lp->msg_lev >= 1)
               print("Numerical problems with basis matrix");
            break;
         default:
            insist(ret != ret);
      }
      /* return to the driver routine */
      return ret;
}

/*----------------------------------------------------------------------
-- prim_feas - search for primal feasible solution (primal simplex).
--
-- This routine searches for primal feasible solution of a specified
-- LP problem using the method of implicit artificial variables, which
-- is based on the primal simplex method.
--
-- This method needs to add no additional rows and/or columns to the
-- problem (which is its main advantage). */

static double orig_objval(SPX *spx)
{     /* this auxliary routine computes value of the objective function
         for the original LP problem */
      LPX *lp = spx->lp;
      double objval;
      void *t;
      t = lp->typx, lp->typx = spx->orig_typx, spx->orig_typx = t;
      t = lp->lb, lp->lb = spx->orig_lb, spx->orig_lb = t;
      t = lp->ub, lp->ub = spx->orig_ub, spx->orig_ub = t;
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      objval = spx_eval_obj(lp);
      t = lp->typx, lp->typx = spx->orig_typx, spx->orig_typx = t;
      t = lp->lb, lp->lb = spx->orig_lb, spx->orig_lb = t;
      t = lp->ub, lp->ub = spx->orig_ub, spx->orig_ub = t;
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      return objval;
}

static double orig_infsum(SPX *spx, double tol)
{     /* this auxiliary routine computes the sum of infeasibilities for
         the original LP problem */
      LPX *lp = spx->lp;
      double infsum;
      void *t;
      t = lp->typx, lp->typx = spx->orig_typx, spx->orig_typx = t;
      t = lp->lb, lp->lb = spx->orig_lb, spx->orig_lb = t;
      t = lp->ub, lp->ub = spx->orig_ub, spx->orig_ub = t;
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      infsum = spx_check_bbar(lp, tol);
      t = lp->typx, lp->typx = spx->orig_typx, spx->orig_typx = t;
      t = lp->lb, lp->lb = spx->orig_lb, spx->orig_lb = t;
      t = lp->ub, lp->ub = spx->orig_ub, spx->orig_ub = t;
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      return infsum;
}

static void prim_feas_dpy(SPX *spx, double sum_0)
{     /* this auxiliary routine displays information about the current
         basic solution */
      LPX *lp = spx->lp;
      int i, def = 0;
      for (i = 1; i <= lp->m; i++)
         if (lp->typx[lp->indx[i]] == LPX_FX) def++;
      print(" %6d:   objval = %17.9e   infeas = %17.9e (%d)",
         lp->it_cnt, orig_objval(spx), orig_infsum(spx, 0.0) / sum_0,
         def);
      return;
}

static int prim_feas(LPX *lp)
{     SPX *spx;
      int m = lp->m;
      int n = lp->n;
      int i, k, ret;
      double sum_0;
      /* on entry the following conditions are required:
         factorization of the current basis matrix should be valid;
         all components of the current basic soultion must be computed
         directly;
         the current basic solution should be primal infeasible */
      insist(lp->b_stat == LPX_B_VALID);
      insist(lp->p_stat == LPX_P_INFEAS);
      /* allocate common block */
      spx = umalloc(sizeof(SPX));
      spx->lp = lp;
      spx->meth = 'P';
      spx->p = 0;
      spx->p_tag = 0;
      spx->q = 0;
      spx->zeta = ucalloc(1+m, sizeof(double));
      spx->ap = ucalloc(1+n, sizeof(double));
      spx->aq = ucalloc(1+m, sizeof(double));
      spx->gvec = ucalloc(1+n, sizeof(double));
      spx->dvec = NULL;
      spx->refsp = (lp->price ? ucalloc(1+m+n, sizeof(int)) : NULL);
      spx->reset = 0;
      spx->work = ucalloc(1+m+n, sizeof(double));
      spx->orig_typx = ucalloc(1+m+n, sizeof(int));
      spx->orig_lb = ucalloc(1+m+n, sizeof(double));
      spx->orig_ub = ucalloc(1+m+n, sizeof(double));
      spx->orig_dir = 0;
      spx->orig_coef = ucalloc(1+m+n, sizeof(double));
      /* save components of the original LP problem, which are changed
         by the routine */
      memcpy(spx->orig_typx, lp->typx, (1+m+n) * sizeof(int));
      memcpy(spx->orig_lb, lp->lb, (1+m+n) * sizeof(double));
      memcpy(spx->orig_ub, lp->ub, (1+m+n) * sizeof(double));
      spx->orig_dir = lp->dir;
      memcpy(spx->orig_coef, lp->coef, (1+m+n) * sizeof(double));
      /* build an artificial basic solution, which is primal feasible;
         build an objective function to minimize sum of infeasibilities
         (residuals) for the original problem */
      lp->dir = LPX_MIN;
      for (k = 0; k <= m+n; k++) lp->coef[k] = 0.0;
      for (i = 1; i <= m; i++)
      {  int typx_k;
         double lb_k, ub_k, bbar_i;
         double eps = 0.70 * lp->tol_bnd;
         k = lp->indx[i]; /* x[k] = xB[i] */
         typx_k = spx->orig_typx[k];
         lb_k = spx->orig_lb[k];
         ub_k = spx->orig_ub[k];
         bbar_i = lp->bbar[i];
         if (typx_k == LPX_LO || typx_k == LPX_DB || typx_k == LPX_FX)
         {  /* in the original problem x[k] has an lower bound */
            if (bbar_i < lb_k - eps)
            {  /* and violates it */
               lp->typx[k] = LPX_UP;
               lp->lb[k] = 0.0;
               lp->ub[k] = lb_k;
               lp->coef[k] = -1.0; /* x[k] should be increased */
            }
         }
         if (typx_k == LPX_UP || typx_k == LPX_DB || typx_k == LPX_FX)
         {  /* in the original problem x[k] has an upper bound */
            if (bbar_i > ub_k + eps)
            {  /* and violates it */
               lp->typx[k] = LPX_LO;
               lp->lb[k] = ub_k;
               lp->ub[k] = 0.0;
               lp->coef[k] = +1.0; /* x[k] should be decreased */
            }
         }
      }
      /* now the initial basic solution should be primal feasible due
         to changes of bounds of some basic variables, which turned to
         implicit artifical variables */
      insist(spx_check_bbar(lp, lp->tol_bnd) == 0.0);
      /* compute the initial sum of infeasibilities for the original
         problem */
      sum_0 = orig_infsum(spx, 0.0);
      /* it can't be zero, because the initial basic solution is primal
         infeasible */
      insist(sum_0 != 0.0);
      /* compute simplex multipliers and reduced costs of non-basic
         variables once again (because the objective function has been
         changed) */
      spx_eval_pi(lp);
      spx_eval_cbar(lp);
      /* initialize weights of non-basic variables */
      if (!lp->price)
      {  /* textbook pricing will be used */
         int j;
         for (j = 1; j <= n; j++) spx->gvec[j] = 1.0;
      }
      else
      {  /* steepest edge pricing will be used */
         spx_reset_refsp(spx);
      }
      /* main loop starts here */
      for (;;)
      {  /* display information about the current basic solution */
         if (lp->msg_lev >= 2 && lp->it_cnt % 100 == 0)
            prim_feas_dpy(spx, sum_0);
         /* check if iteration limit has been reached */
         if (lp->it_lim >= 0 && lp->it_cnt >= lp->it_lim)
         {  ret = SPX_ITLIM;
            break;
         }
         /* choose non-basic variable xN[q] */
         if (spx_prim_chuzc(spx, lp->tol_dj))
         {  /* basic solution components were recomputed; check primal
               feasibility (of the artificial solution) */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
         /* if no xN[q] has been chosen, the current artificial basic
            solution is minimal */
         if (spx->q == 0)
         {  if (orig_infsum(spx, lp->tol_bnd) == 0.0)
            {  /* the sum of infeasibilities is zero, therefore the
                  current solution is primal feasible for the original
                  problem */
               ret = SPX_OK;
            }
            else
            {  /* the sum of infeasibilities is minimal but non-zero,
                  therefore the original problem has no primal feasible
                  solution */
               ret = SPX_NOPFS;
            }
            break;
         }
         /* compute the q-th column of the current simplex table (later
            this column will enter the basis) */
         spx_eval_col(lp, spx->q, spx->aq, 1);
         /* choose basic variable xB[p] */
         if (spx_prim_chuzr(spx, lp->relax * lp->tol_bnd))
         {  /* the basis matrix should be reinverted, because the q-th
               column of the simplex table is unreliable */
            insist("not implemented yet" == NULL);
         }
         /* the sum of infeasibilities can't be negative, therefore
            the modified problem can't have unbounded solution */
         insist(spx->p != 0);
         /* update values of basic variables */
         spx_update_bbar(spx);
         if (spx->p > 0)
         {  /* compute the p-th row of the inverse inv(B) */
            spx_eval_rho(lp, spx->p, spx->zeta);
            /* compute the p-th row of the current simplex table */
            spx_eval_row(lp, spx->zeta, spx->ap);
            /* update simplex multipliers */
            spx_update_pi(spx);
            /* update reduced costs of non-basic variables */
            spx_update_cbar(spx, 0);
            /* update weights of non-basic variables */
            if (lp->price) spx_update_gvec(spx);
         }
         /* xB[p] is leaving the basis; if it is implicit artificial
            variable, the corresponding residual vanishes; therefore
            bounds of this variable should be restored to the original
            ones */
         if (spx->p > 0)
         {  k = lp->indx[spx->p]; /* x[k] = xB[p] */
            if (lp->typx[k] != spx->orig_typx[k])
            {  /* x[k] is implicit artificial variable */
               lp->typx[k] = spx->orig_typx[k];
               lp->lb[k] = spx->orig_lb[k];
               lp->ub[k] = spx->orig_ub[k];
               insist(spx->p_tag == LPX_NL || spx->p_tag == LPX_NU);
               spx->p_tag = (spx->p_tag == LPX_NL ? LPX_NU : LPX_NL);
               if (lp->typx[k] == LPX_FX) spx->p_tag = LPX_NS;
               /* nullify the objective coefficient at x[k] */
               lp->coef[k] = 0.0;
               /* since coef[k] has been changed, we need to compute
                  new reduced cost of x[k], which it will have in the
                  adjacent basis */
               /* the formula d[j] = cn[j] - pi' * N[j] is used (note
                  that the vector pi is not changed, because it depends
                  on objective coefficients at basic variables, but in
                  the adjacent basis, for which the vector pi has been
                  just recomputed, x[k] is non-basic) */
               if (k <= m)
               {  /* x[k] is auxiliary variable */
                  lp->cbar[spx->q] = - lp->pi[k];
               }
               else
               {  /* x[k] is structural variable */
                  int ptr = lp->aa_ptr[k];
                  int end = ptr + lp->aa_len[k] - 1;
                  double d = 0.0;
                  for (ptr = ptr; ptr <= end; ptr++)
                     d += lp->pi[lp->sv_ndx[ptr]] * lp->sv_val[ptr];
                  lp->cbar[spx->q] = d;
               }
            }
         }
         /* jump to the adjacent vertex of the LP polyhedron */
         if (spx_change_basis(spx))
         {  /* the basis matrix should be reinverted */
            if (spx_invert(lp))
            {  /* some numerical problems with the basis matrix */
               ret = SPX_SING;
               break;
            }
            /* compute values of basic variables */
            spx_eval_bbar(lp);
            /* compute simplex multipliers */
            spx_eval_pi(lp);
            /* compute reduced costs of non-basic variables */
            spx_eval_cbar(lp);
            /* check primal feasibility (of the artificial solution) */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
            /* we needn't to wait until all artificial variables leave
               the basis */
            if (orig_infsum(spx, lp->tol_bnd) == 0.0)
            {  /* the sum of infeasibilities is zero, therefore the
                  current solution is primal feasible for the original
                  problem */
               ret = SPX_OK;
               break;
            }
         }
#if 0
         /* check accuracy of main solution components after updating
            (for debugging purposes only) */
         {  double ae_bbar = spx_err_in_bbar(spx);
            double ae_pi   = spx_err_in_pi(spx);
            double ae_cbar = spx_err_in_cbar(spx, 0);
            double ae_gvec = lp->price ? spx_err_in_gvec(spx) : 0.0;
            print("bbar: %g; pi: %g; cbar: %g; gvec: %g",
               ae_bbar, ae_pi, ae_cbar, ae_gvec);
            if (ae_bbar > 1e-9 || ae_pi > 1e-9 || ae_cbar > 1e-9 ||
                ae_gvec > 1e-3)
               insist("solution accuracy too low" == NULL);
         }
#endif
      }
      /* end of main loop */
      if (lp->b_stat == LPX_B_VALID)
      {  if (lp->msg_lev >= 2 && lp->it_cnt % 100 != 0)
            prim_feas_dpy(spx, sum_0);
      }
      /* restore components of the original problem, which were changed
         by the routine */
      memcpy(lp->typx, spx->orig_typx, (1+m+n) * sizeof(int));
      memcpy(lp->lb, spx->orig_lb, (1+m+n) * sizeof(double));
      memcpy(lp->ub, spx->orig_ub, (1+m+n) * sizeof(double));
      lp->dir = spx->orig_dir;
      memcpy(lp->coef, spx->orig_coef, (1+m+n) * sizeof(double));
      /* deallocate common block */
      ufree(spx->zeta);
      ufree(spx->ap);
      ufree(spx->aq);
      ufree(spx->gvec);
      if (lp->price) ufree(spx->refsp);
      ufree(spx->work);
      ufree(spx->orig_typx);
      ufree(spx->orig_lb);
      ufree(spx->orig_ub);
      ufree(spx->orig_coef);
      ufree(spx);
      /* compute basic solution components at the final point */
      if (lp->b_stat == LPX_B_VALID)
      {  spx_eval_bbar(lp);
         spx_eval_pi(lp);
         spx_eval_cbar(lp);
         if (spx_check_bbar(lp, lp->tol_bnd) == 0.0)
            lp->p_stat = LPX_P_FEAS;
         else
            lp->p_stat = LPX_P_INFEAS;
         if (spx_check_cbar(lp, lp->tol_dj) == 0.0)
            lp->d_stat = LPX_D_FEAS;
         else
            lp->d_stat = LPX_D_INFEAS;
      }
      else
      {  lp->p_stat = LPX_P_UNDEF;
         lp->d_stat = LPX_D_UNDEF;
      }
      /* analyze exit code */
      switch (ret)
      {  case SPX_OK:
            /* primal feasible solution found */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            break;
         case SPX_ITLIM:
            /* iteration limit reached */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 2)
               print("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED");
            break;
         case SPX_NOPFS:
            /* no primal feasible solution */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 2)
               print("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION");
            lp->p_stat = LPX_P_NOFEAS;
            break;
         case SPX_INSTAB:
            /* numerical instability */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 3)
               print("Numerical instability");
            break;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            insist(lp->b_stat == LPX_B_UNDEF);
            insist(lp->p_stat == LPX_P_UNDEF);
            insist(lp->d_stat == LPX_D_UNDEF);
            if (lp->msg_lev >= 1)
               print("Numerical problems with basis matrix");
            break;
         default:
            insist(ret != ret);
      }
      /* return to the driver routine */
      return ret;
}

/*----------------------------------------------------------------------
-- prim_art - search for primal feasible solution (primal simplex).
--
-- This routine searches for primal feasible solution of a specified
-- LP problem using the method of single artificial variable, which is
-- based on the primal simplex method.
--
-- A brief mathematical description of this method is given below.
--
-- Let the current simplex table be
--
--    xB = A^ * xN,                                                  (1)
--
-- where
--
--    A^ = - inv(B) * N,                                             (2)
--
-- and some basic variables xB violates their (lower or upper) bounds.
-- We can make the current basic solution to be primal feasible if we
-- add some appropriate quantities to each right part of the simplex
-- table:
--
--    xB = A^ * xN + av,                                             (3)
--
-- where
--
--    av[i] = (lB)i - bbar[i] + delta[i], if bbar[i] < (lB)i,        (4)
--
--    av[i] = (uB)i - bbar[i] - delta[i], if bbar[i] > (uB)i,        (5)
--
-- and delta[i] > 0 is a non-negative offset intended to avoid primal
-- degeneracy, because after introducing the vector av basic variable
-- xB[i] is equal to (lB)i + delta[i] or (uB)i - delta[i].
--
-- Formally (3) is equivalent to introducing an artificial variable xv,
-- which is non-basic with the initial value 1 and has the column av as
-- its coefficients in the simplex table:
--
--    xB = A^ * xN + av * xv.                                        (6)
--
-- Multiplying both parts of (6) on B and accounting (2) we have:
--
--    B * xB + N * xN - B * av * xv = 0.                             (7)
--
-- We can consider the column (-B * av) as an additional column of the
-- augmented constraint matrix A~ = (I | -A), or, that is the same, the
-- column (B * av) as an additional column of the original constraint
-- matrix A, which corresponds to the artificial variable xv.
--
-- If the variable xv is non-basic and equal to 1, the artificial basic
-- solution is primal feasible and therefore can be used as an initial
-- solution on the phase I. Thus, in order to find a primal feasible
-- basic solution of the original LP problem, which has no artificial
-- variables, xv should be minimized to zero (if it is impossible, the
-- original problem has no feasible solution). Note also that the value
-- of xv, which is in the range [0,1], can be considered as a measure
-- of primal infeasibility. */

static double orig_objfun(SPX *spx)
{     /* this auxiliary routine computes the objective function value
         for the original LP problem */
      LPX *lp = spx->lp;
      double objval;
      void *t;
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      objval = spx_eval_obj(lp);
      t = lp->coef, lp->coef = spx->orig_coef, spx->orig_coef = t;
      return objval;
}

static double orig_infeas(SPX *spx)
{     /* this auxiliary routine computes the infeasibilitiy measure for
         the original LP problem */
      LPX *lp = spx->lp;
      double infeas;
      /* the infeasibility measure is a current value of the artificial
         variable */
      if (lp->tagx[lp->m+lp->n] == LPX_BS)
         infeas = lp->bbar[lp->posx[lp->m+lp->n]];
      else
         infeas = spx_eval_xn_j(lp, lp->posx[lp->m+lp->n] - lp->m);
      return infeas;
}

static void prim_art_dpy(SPX *spx)
{     /* this auxiliary routine displays information about the current
         basic solution */
      LPX *lp = spx->lp;
      int i, def = 0;
      for (i = 1; i <= lp->m; i++)
         if (lp->typx[lp->indx[i]] == LPX_FX) def++;
      print(" %6d:   objval = %17.9e   infeas = %17.9e (%d)",
         lp->it_cnt, orig_objfun(spx), orig_infeas(spx), def);
      return;
}

static int prim_art(LPX *lp)
{     SPX *spx;
      int m = lp->m;
      int n = lp->n;
      int i, j, k, ret, *ndx;
      double *av, *col;
      /* on entry the following conditions are required:
         factorization of the current basis matrix should be valid;
         all components of the current basic soultion must be computed
         directly;
         the current basic solution should be primal infeasible */
      insist(lp->b_stat == LPX_B_VALID);
      insist(lp->p_stat == LPX_P_INFEAS);
      /* allocate common block (one extra location in the arrays ap,
         gvec, refsp, work, coef is reserved for the artificial column,
         which will be introduced to the problem) */
      spx = umalloc(sizeof(SPX));
      spx->lp = lp;
      spx->meth = 'P';
      spx->p = 0;
      spx->p_tag = 0;
      spx->q = 0;
      spx->zeta = ucalloc(1+m, sizeof(double));
      spx->ap = ucalloc(1+n+1, sizeof(double));
      spx->aq = ucalloc(1+m, sizeof(double));
      spx->gvec = ucalloc(1+n+1, sizeof(double));
      spx->dvec = NULL;
      spx->refsp = (lp->price ? ucalloc(1+m+n+1, sizeof(int)) : NULL);
      spx->reset = 0;
      spx->work = ucalloc(1+m+n+1, sizeof(double));
      spx->orig_typx = NULL;
      spx->orig_lb = spx->orig_ub = NULL;
      spx->orig_dir = 0;
      spx->orig_coef = ucalloc(1+m+n+1, sizeof(double));
      /* save the original objective function, because it is changed by
         the routine */
      spx->orig_dir = lp->dir;
      memcpy(spx->orig_coef, lp->coef, (1+m+n) * sizeof(double));
      spx->orig_coef[m+n+1] = 0.0;
      /* compute the vector av */
      av = lp->bbar;
      for (i = 1; i <= m; i++)
      {  double eps = 0.10 * lp->tol_bnd, delta = 100.0, av_i, temp;
         k = lp->indx[i]; /* x[k] = xB[i] */
         av_i = 0.0;
         switch (lp->typx[k])
         {  case LPX_FR:
               /* xB[i] is free variable */
               break;
            case LPX_LO:
               /* xB[i] has lower bound */
               if (lp->bbar[i] < lp->lb[k] - eps)
                  av_i = (lp->lb[k] - lp->bbar[i]) + delta;
               break;
            case LPX_UP:
               /* xB[i] has upper bound */
               if (lp->bbar[i] > lp->ub[k] + eps)
                  av_i = (lp->ub[k] - lp->bbar[i]) - delta;
               break;
            case LPX_DB:
            case LPX_FX:
               /* xB[i] is double-bounded or fixed variable */
               if (lp->bbar[i] < lp->lb[k] - eps)
               {  temp = 0.5 * fabs(lp->lb[k] - lp->ub[k]);
                  if (temp > delta) temp = delta;
                  av_i = (lp->lb[k] - lp->bbar[i]) + temp;
               }
               if (lp->bbar[i] > lp->ub[k] + eps)
               {  temp = 0.5 * fabs(lp->lb[k] - lp->ub[k]);
                  if (temp > delta) temp = delta;
                  av_i = (lp->ub[k] - lp->bbar[i]) - temp;
               }
               break;
            default:
               insist(lp->typx != lp->typx);
         }
         av[i] = av_i;
      }
      /* compute the column B*av */
      insist(sizeof(int) <= sizeof(double));
      ndx = (int *)spx->zeta;
      col = spx->aq;
      for (i = 1; i <= m; i++) col[i] = 0.0;
      for (j = 1; j <= m; j++)
      {  int k = lp->indx[j]; /* x[k] = xB[j]; */
         if (k <= m)
         {  /* x[k] is auxiliary variable */
            col[k] += av[j];
         }
         else
         {  /* x[k] is structural variable */
            int ptr = lp->aa_ptr[k];
            int end = ptr + lp->aa_len[k] - 1;
            for (ptr = ptr; ptr <= end; ptr++)
               col[lp->sv_ndx[ptr]] -= lp->sv_val[ptr] * av[j];
         }
      }
      /* convert the column B*av to sparse format and "anti-scale" it,
         in order to avoid scaling performed by lpx_set_mat_col */
      k = 0;
      for (i = 1; i <= m; i++)
      {  if (col[i] != 0.0)
         {  k++;
            ndx[k] = i;
            col[k] = col[i] / lp->rs[i];
         }
      }
      /* add the artificial variable and its column to the problem */
      lpx_add_cols(lp, 1), n++;
      lpx_set_col_bnds(lp, n, LPX_DB, 0.0, 1.0);
      lpx_set_mat_col(lp, n, k, ndx, col);
      /* set the artificial variable to its upper unity bound in order
         to make the current basic solution primal feasible */
      lp->tagx[m+n] = LPX_NU;
      /* the artificial variable should be minimized to zero */
      lp->dir = LPX_MIN;
      for (k = 0; k <= m+n; k++) lp->coef[k] = 0.0;
      lp->coef[m+n] = 1.0;
      /* since the problem size has been changed, the factorization of
         the basis matrix doesn't exist; reinvert the basis matrix */
      insist(spx_warm_up(lp) == 0);
      /* compute initial basic solution components */
      spx_eval_bbar(lp);
      spx_eval_pi(lp);
      spx_eval_cbar(lp);
      /* now the current basic solution should be primal feasible */
      insist(spx_check_bbar(lp, lp->tol_bnd) == 0.0);
      /* initialize weights of non-basic variables */
      if (!lp->price)
      {  /* textbook pricing will be used */
         int j;
         for (j = 1; j <= n; j++) spx->gvec[j] = 1.0;
      }
      else
      {  /* steepest edge pricing will be used */
         spx_reset_refsp(spx);
      }
      /* main loop starts here */
      for (;;)
      {  /* display information about the current basic solution */
         if (lp->msg_lev >= 2 && lp->it_cnt % 100 == 0)
            prim_art_dpy(spx);
         /* we needn't to wait until the artificial variable has left
            the basis */
         if (orig_infeas(spx) < 1e-10)
         {  /* the infeasibility is near to zero, therefore the current
               solution is primal feasible for the original problem */
            ret = SPX_OK;
            break;
         }
         /* check if iteration limit has been reached */
         if (lp->it_lim >= 0 && lp->it_cnt >= lp->it_lim)
         {  ret = SPX_ITLIM;
            break;
         }
         /* choose non-basic variable xN[q] */
         if (spx_prim_chuzc(spx, lp->tol_dj))
         {  /* basic solution components were recomputed; check primal
               feasibility (of the artificial solution) */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
         /* if no xN[q] has been chosen, the infeasibility is minimal
            but non-zero; therefore the original problem has no primal
            feasible solution */
         if (spx->q == 0)
         {  ret = SPX_NOPFS;
            break;
         }
         /* compute the q-th column of the current simplex table (later
            this column will enter the basis) */
         spx_eval_col(lp, spx->q, spx->aq, 1);
         /* choose basic variable xB[p] */
         if (spx_prim_chuzr(spx, lp->relax * lp->tol_bnd))
         {  /* the basis matrix should be reinverted, because the q-th
               column of the simplex table is unreliable */
            insist("not implemented yet" == NULL);
         }
         /* the infeasibility can't be negative, therefore the modified
            problem can't have unbounded solution */
         insist(spx->p != 0);
         /* update values of basic variables */
         spx_update_bbar(spx);
         if (spx->p > 0)
         {  /* compute the p-th row of the inverse inv(B) */
            spx_eval_rho(lp, spx->p, spx->zeta);
            /* compute the p-th row of the current simplex table */
            spx_eval_row(lp, spx->zeta, spx->ap);
            /* update simplex multipliers */
            spx_update_pi(spx);
            /* update reduced costs of non-basic variables */
            spx_update_cbar(spx, 0);
            /* update weights of non-basic variables */
            if (lp->price) spx_update_gvec(spx);
         }
         /* jump to the adjacent vertex of the LP polyhedron */
         if (spx_change_basis(spx))
         {  /* the basis matrix should be reinverted */
            if (spx_invert(lp))
            {  /* some numerical problems with the basis matrix */
               ret = SPX_SING;
               break;
            }
            /* compute values of basic variables */
            spx_eval_bbar(lp);
            /* compute simplex multipliers */
            spx_eval_pi(lp);
            /* compute reduced costs of non-basic variables */
            spx_eval_cbar(lp);
            /* check primal feasibility */
            if (spx_check_bbar(lp, lp->tol_bnd) != 0.0)
            {  /* the current solution became primal infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
#if 0
         /* check accuracy of main solution components after updating
            (for debugging purposes only) */
         {  double ae_bbar = spx_err_in_bbar(spx);
            double ae_pi   = spx_err_in_pi(spx);
            double ae_cbar = spx_err_in_cbar(spx, 0);
            double ae_gvec = lp->price ? spx_err_in_gvec(spx) : 0.0;
            print("bbar: %g; pi: %g; cbar: %g; gvec: %g",
               ae_bbar, ae_pi, ae_cbar, ae_gvec);
            if (ae_bbar > 1e-9 || ae_pi > 1e-9 || ae_cbar > 1e-9 ||
                ae_gvec > 1e-3)
               insist("solution accuracy too low" == NULL);
         }
#endif
      }
      /* end of main loop */
      if (lp->b_stat == LPX_B_VALID)
      {  if (lp->msg_lev >= 2 && lp->it_cnt % 100 != 0)
            prim_art_dpy(spx);
      }
      /* if the artificial variable still is basic, we have to pull it
         from the basis, because the original problem has no artificial
         variables */
      if (lp->b_stat == LPX_B_VALID && lp->tagx[m+n] == LPX_BS)
      {  /* replace the artificial variable by a non-basic variable,
            which has greatest influence coefficient (for the sake of
            numerical stability); note that this operation is a dual
            simplex iteration */
         int j;
         double big;
         spx->p = lp->posx[m+n]; /* x[m+n] = xB[p] */
         insist(1 <= spx->p && spx->p <= m);
         /* the artificial variable will be set on its lower bound */
         spx->p_tag = LPX_NL;
         /* compute the p-th row of the inverse inv(B) */
         spx_eval_rho(lp, spx->p, spx->zeta);
         /* compute the p-th row of the current simplex table */
         spx_eval_row(lp, spx->zeta, spx->ap);
         /* choose non-basic variable xN[q] with greatest (in absolute
            value) influence coefficient */
         spx->q = 0, big = 0.0;
         for (j = 1; j <= n; j++)
         {  if (big < fabs(spx->ap[j]))
               spx->q = j, big = fabs(spx->ap[j]);
         }
         insist(spx->q != 0);
         /* perform forward transformation of the column of xN[q] (to
            prepare it for replacing the artificial column in the basis
            matrix) */
         spx_eval_col(lp, spx->q, spx->aq, 1);
         /* jump to the adjacent basis, where the artificial variable
            is non-basic */
         spx_change_basis(spx);
         insist(lp->tagx[m+n] == LPX_NL);
      }
      /* remove the artificial variable from the problem */
      lpx_unmark_all(lp);
      lpx_mark_col(lp, n, 1);
      lpx_del_items(lp), n--;
      /* restore the original objective function */
      lp->dir = spx->orig_dir;
      memcpy(lp->coef, spx->orig_coef, (1+m+n) * sizeof(double));
      /* deallocate common block */
      ufree(spx->zeta);
      ufree(spx->ap);
      ufree(spx->aq);
      ufree(spx->gvec);
      if (lp->price) ufree(spx->refsp);
      ufree(spx->work);
      ufree(spx->orig_coef);
      ufree(spx);
      /* since the problem size has been changed, the factorization of
         the basis matrix doesn't exist; reinvert the basis matrix */
      if (ret != SPX_SING) insist(spx_warm_up(lp) == 0);
      /* compute basic solution components at the final point */
      if (lp->b_stat == LPX_B_VALID)
      {  spx_eval_bbar(lp);
         spx_eval_pi(lp);
         spx_eval_cbar(lp);
         if (spx_check_bbar(lp, lp->tol_bnd) == 0.0)
            lp->p_stat = LPX_P_FEAS;
         else
            lp->p_stat = LPX_P_INFEAS;
         if (spx_check_cbar(lp, lp->tol_dj) == 0.0)
            lp->d_stat = LPX_D_FEAS;
         else
            lp->d_stat = LPX_D_INFEAS;
      }
      else
      {  lp->p_stat = LPX_P_UNDEF;
         lp->d_stat = LPX_D_UNDEF;
      }
      /* analyze exit code */
      switch (ret)
      {  case SPX_OK:
            /* primal feasible solution found */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            break;
         case SPX_ITLIM:
            /* iteration limit reached */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 2)
               print("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED");
            break;
         case SPX_NOPFS:
            /* no primal feasible solution */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 2)
               print("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION");
            lp->p_stat = LPX_P_NOFEAS;
            break;
         case SPX_INSTAB:
            /* numerical instability */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            if (lp->msg_lev >= 3)
               print("Numerical instability");
            break;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            insist(lp->b_stat == LPX_B_UNDEF);
            insist(lp->p_stat == LPX_P_UNDEF);
            insist(lp->d_stat == LPX_D_UNDEF);
            if (lp->msg_lev >= 1)
               print("Numerical problems with basis matrix");
            break;
         default:
            insist(ret != ret);
      }
      /* return to the driver routine */
      return ret;
}

/*----------------------------------------------------------------------
-- dual_opt - search for optimal solution (dual simplex).
--
-- This routine searches for optimal solution of a specified LP problem
-- using the dual simplex method.
--
-- Structure of this routine can be an example for other variants based
-- on the dual simplex method. */

static void dual_opt_dpy(SPX *spx)
{     /* this auxiliary routine displays information about the current
         basic solution */
      LPX *lp = spx->lp;
      int i, def = 0;
      for (i = 1; i <= lp->m; i++)
         if (lp->typx[lp->indx[i]] == LPX_FX) def++;
      print("d%6d:   objval = %17.9e   infeas = %17.9e (%d)",
         lp->it_cnt, spx_eval_obj(lp), spx_check_bbar(lp, 0.0), def);
      return;
}

static int dual_opt(LPX *lp)
{     SPX *spx;
      int m = lp->m;
      int n = lp->n;
      int ret;
      /* on entry the following conditions are required:
         factorization of the current basis matrix should be valid;
         all components of the current basic soultion must be computed
         directly;
         the current basic solution should be primal infeasible and
         dual feasible */
      insist(lp->b_stat == LPX_B_VALID);
      insist(lp->p_stat == LPX_P_INFEAS);
      insist(lp->d_stat == LPX_D_FEAS);
      /* allocate common block */
      spx = umalloc(sizeof(SPX));
      spx->lp = lp;
      spx->meth = 'D';
      spx->p = 0;
      spx->p_tag = 0;
      spx->q = 0;
      spx->zeta = ucalloc(1+m, sizeof(double));
      spx->ap = ucalloc(1+n, sizeof(double));
      spx->aq = ucalloc(1+m, sizeof(double));
      spx->gvec = NULL;
      spx->dvec = ucalloc(1+m, sizeof(double));
      spx->refsp = (lp->price ? ucalloc(1+m+n, sizeof(double)) : NULL);
      spx->reset = 0;
      spx->work = ucalloc(1+m+n, sizeof(double));
      spx->orig_typx = NULL;
      spx->orig_lb = spx->orig_ub = NULL;
      spx->orig_dir = 0;
      spx->orig_coef = NULL;
      /* initialize weights of basic variables */
      if (!lp->price)
      {  /* textbook pricing will be used */
         int i;
         for (i = 1; i <= m; i++) spx->dvec[i] = 1.0;
      }
      else
      {  /* steepest edge pricing will be used */
         spx_reset_refsp(spx);
      }
      /* main loop starts here */
      for (;;)
      {  /* display information about the current basic solution */
         if (lp->it_cnt % 100 == 0) dual_opt_dpy(spx);
         /* check if iteration limit has been reached */
         if (lp->it_lim >= 0 && lp->it_cnt >= lp->it_lim)
         {  ret = SPX_ITLIM;
            break;
         }
         /* choose basic variable */
         spx_dual_chuzr(spx, lp->tol_bnd);
         /* if no xB[p] has been chosen, the current basic solution is
            primal feasible and therefore optimal */
         if (spx->p == 0)
         {  ret = SPX_OK;
            break;
         }
         /* compute the p-th row of the inverse inv(B) */
         spx_eval_rho(lp, spx->p, spx->zeta);
         /* compute the p-th row of the current simplex table */
         spx_eval_row(lp, spx->zeta, spx->ap);
         /* choose non-basic variable xN[q] */
         if (spx_dual_chuzc(spx, lp->relax * lp->tol_dj))
         {  /* the basis matrix should be reinverted, because the p-th
               row of the simplex table is unreliable */
            insist("not implemented yet" == NULL);
         }
         /* if no xN[q] has been chosen, there is no primal feasible
            solution (the dual problem has unbounded solution) */
         if (spx->q == 0)
         {  ret = SPX_NOPFS;
            break;
         }
         /* compute the q-th column of the current simplex table (later
            this column will enter the basis) */
         spx_eval_col(lp, spx->q, spx->aq, 1);
         /* update values of basic variables */
         spx_update_bbar(spx);
         /* update simplex multipliers */
         spx_update_pi(spx);
         /* update reduced costs of non-basic variables */
         spx_update_cbar(spx, 0);
         /* update weights of basic variables */
         if (lp->price) spx_update_dvec(spx);
         /* if xB[p] is fixed variable, adjust its non-basic tag */
         if (lp->typx[lp->indx[spx->p]] == LPX_FX) spx->p_tag = LPX_NS;
         /* jump to the adjacent vertex of the LP polyhedron */
         if (spx_change_basis(spx))
         {  /* the basis matrix should be reinverted */
            if (spx_invert(lp))
            {  /* some numerical problems with the basis matrix */
               ret = SPX_SING;
               break;
            }
            /* compute values of basic variables */
            spx_eval_bbar(lp);
            /* compute simplex multipliers */
            spx_eval_pi(lp);
            /* compute reduced costs of non-basic variables */
            spx_eval_cbar(lp);
            /* check dual feasibility */
            if (spx_check_cbar(lp, lp->tol_dj) != 0.0)
            {  /* the current solution became dual infeasible due to
                  round-off errors */
               ret = SPX_INSTAB;
               break;
            }
         }
#if 0
         /* check accuracy of main solution components after updating
            (for debugging purposes only) */
         {  double ae_bbar = spx_err_in_bbar(spx);
            double ae_pi   = spx_err_in_pi(spx);
            double ae_cbar = spx_err_in_cbar(spx, 0);
            double ae_dvec = lp->price ? spx_err_in_dvec(spx) : 0.0;
            print("bbar: %g; pi: %g; cbar: %g; dvec: %g",
               ae_bbar, ae_pi, ae_cbar, ae_dvec);
            if (ae_bbar > 1e-9 || ae_pi > 1e-9 || ae_cbar > 1e-9 ||
                ae_dvec > 1e-3)
               insist("solution accuracy too low" == NULL);
         }
#endif
      }
      /* end of main loop */
      if (lp->b_stat == LPX_B_VALID)
      {  if (lp->msg_lev >= 2 && lp->it_cnt % 100 != 0)
            dual_opt_dpy(spx);
      }
      /* deallocate common block */
      ufree(spx->zeta);
      ufree(spx->ap);
      ufree(spx->aq);
      ufree(spx->dvec);
      if (lp->price) ufree(spx->refsp);
      ufree(spx->work);
      ufree(spx);
      /* compute basic solution components at the final point */
      if (lp->b_stat == LPX_B_VALID)
      {  spx_eval_bbar(lp);
         spx_eval_pi(lp);
         spx_eval_cbar(lp);
         if (spx_check_bbar(lp, lp->tol_bnd) == 0.0)
            lp->p_stat = LPX_P_FEAS;
         else
            lp->p_stat = LPX_P_INFEAS;
         if (spx_check_cbar(lp, lp->tol_dj) == 0.0)
            lp->d_stat = LPX_D_FEAS;
         else
            lp->d_stat = LPX_D_INFEAS;
      }
      else
      {  lp->p_stat = LPX_P_UNDEF;
         lp->d_stat = LPX_D_UNDEF;
      }
      /* analyze exit code */
      switch (ret)
      {  case SPX_OK:
            /* optimal solution found */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_FEAS);
            insist(lp->d_stat == LPX_D_FEAS);
            if (lp->msg_lev >= 2)
               print("OPTIMAL SOLUTION FOUND");
            break;
         case SPX_ITLIM:
            /* iteration limit reached */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            insist(lp->d_stat == LPX_D_FEAS);
            if (lp->msg_lev >= 2)
               print("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED");
            break;
         case SPX_NOPFS:
            /* no primal feasible solution */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->p_stat == LPX_P_INFEAS);
            insist(lp->d_stat == LPX_D_FEAS);
            if (lp->msg_lev >= 2)
               print("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION");
            lp->p_stat = LPX_P_NOFEAS;
            break;
         case SPX_INSTAB:
            /* numerical instability */
            insist(lp->b_stat == LPX_B_VALID);
            insist(lp->d_stat == LPX_D_INFEAS);
            if (lp->msg_lev >= 3)
               print("Numerical instability");
            break;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            insist(lp->b_stat == LPX_B_UNDEF);
            insist(lp->p_stat == LPX_P_UNDEF);
            insist(lp->d_stat == LPX_D_UNDEF);
            if (lp->msg_lev >= 1)
               print("Numerical problems with basis matrix");
            break;
         default:
            insist(ret != ret);
      }
      /* return to the driver routine */
      return ret;
}

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

int spx_solve_lp(LPX *lp)
{     int m = lp->m;
      int n = lp->n;
      int ret;
      /* reset iteration count */
      lp->it_cnt = 0;
      /* check if the problem is empty */
      if (!(m > 0 && n > 0))
      {  if (lp->msg_lev >= 1)
            print("spx_solve_lp: problem has no rows/columns");
         ret = LPX_E_EMPTY;
         goto done;
      }
      /* build an initial basis and reinvert the basis matrix */
      switch (lp->start)
      {  case 0:
            /* standard initial basis */
            spx_std_basis(lp);
            break;
         case 1:
            /* advanced initial basis */
            spx_adv_basis(lp);
            break;
         case 2:
            /* warm start */
            switch (spx_warm_up(lp))
            {  case 0:
                  /* the basis has been warmed up successfully */
                  break;
               case 1:
                  if (lp->msg_lev >= 1)
                     print("spx_solve_lp: invalid basis; warm start imp"
                        "ossible");
                  ret = LPX_E_BADB;
                  goto done;
               case 2:
                  if (lp->msg_lev >= 1)
                     print("spx_solve_lp: singular basis; warm start im"
                        "possible");
                  ret = LPX_E_BADB;
                  goto done;
               default:
                  insist(spx_warm_up != spx_warm_up);
            }
            break;
         default:
            insist(lp->start != lp->start);
      }
      /* now the basis is valid */
      insist(lp->b_stat == LPX_B_VALID);
      /* compute initial basic solution components */
      spx_eval_bbar(lp);
      spx_eval_pi(lp);
      spx_eval_cbar(lp);
      if (spx_check_bbar(lp, lp->tol_bnd) == 0.0)
         lp->p_stat = LPX_P_FEAS;
      else
         lp->p_stat = LPX_P_INFEAS;
      if (spx_check_cbar(lp, lp->tol_dj) == 0.0)
         lp->d_stat = LPX_D_FEAS;
      else
         lp->d_stat = LPX_D_INFEAS;
      /* if no iterations are allowed, all is done */
      if (lp->it_lim == 0)
      {  ret = LPX_E_ITLIM;
         goto done;
      }
#if 1
      /* try to find optimal solution using the dual simplex */
      if (!lp->dual) goto feas;
      if (lp->d_stat != LPX_D_FEAS)
      {  if (lp->msg_lev >= 1)
            print("spx_solve_lp: starting basis dual infeasible; primal"
               " simplex used");
         goto feas;
      }
      if (lp->p_stat == LPX_P_FEAS)
         ret = SPX_OK;
      else
         ret = dual_opt(lp);
      switch (ret)
      {  case SPX_OK:
            /* optimal solution found */
            ret = LPX_E_OK;
            goto done;
         case SPX_ITLIM:
            /* iteration limit reached */
            ret = LPX_E_ITLIM;
            goto done;
         case SPX_NOPFS:
            /* no primal feasible solution */
            ret = LPX_E_OK;
            goto done;
         case SPX_INSTAB:
            /* numerical instability */
            break;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            ret = LPX_E_SING;
            goto done;
         default:
            insist(ret != ret);
      }
#endif
feas: /* phase I: find primal feasible solution */
      if (lp->p_stat == LPX_P_FEAS)
         ret = SPX_OK;
      else
      {  /* there are two routines for phase I (both they work) */
         insist(prim_feas == prim_feas);
         insist(prim_art == prim_art);
#if 0
         /* this is a bit faster but takes more iterations */
         ret = prim_feas(lp);
#else
         /* this is a bit slower but takes less iterations */
         ret = prim_art(lp);
#endif
      }
      switch (ret)
      {  case SPX_OK:
            /* primal feasible solution found */
            break;
         case SPX_ITLIM:
            /* iteration limit reached */
            ret = LPX_E_ITLIM;
            goto done;
         case SPX_NOPFS:
            /* no primal feasible solution */
            ret = LPX_E_OK;
            goto done;
         case SPX_INSTAB:
            /* numerical instability */
            goto feas;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            ret = LPX_E_SING;
            goto done;
         default:
            insist(ret != ret);
      }
      /* phase II: find optimal solution */
      if (lp->d_stat == LPX_D_FEAS)
         ret = SPX_OK;
      else
         ret = prim_opt(lp);
      switch (ret)
      {  case SPX_OK:
            /* optimal solution found */
            ret = LPX_E_OK;
            goto done;
         case SPX_ITLIM:
            /* iteration limit reached */
            ret = LPX_E_ITLIM;
            goto done;
         case SPX_NODFS:
            /* no dual feasible solution */
            ret = LPX_E_OK;
            goto done;
         case SPX_INSTAB:
            /* numerical instability */
            goto feas;
         case SPX_SING:
            /* singular or ill-conditioned basis */
            ret = LPX_E_SING;
            goto done;
         default:
            insist(ret != ret);
      }
done: /* return to the calling program */
      return ret;
}

/* eof */
