/* $Id$ */
// Copyright (C) 2002, International Business Machines
// Corporation and others.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

//#define	PRESOLVE_CONSISTENCY	1
//#define	PRESOLVE_DEBUG	1

#include <stdio.h>

#include <cassert>
#include <iostream>

#include "CoinHelperFunctions.hpp"
#include "ClpConfig.h"
#ifdef CLP_HAS_ABC
#include "CoinAbcCommon.hpp"
#endif

#include "CoinPackedMatrix.hpp"
#include "ClpPackedMatrix.hpp"
#include "ClpSimplex.hpp"
#include "ClpSimplexOther.hpp"
#ifndef SLIM_CLP
#include "ClpQuadraticObjective.hpp"
#endif

#include "ClpPresolve.hpp"
#include "CoinPresolveMatrix.hpp"

#include "CoinPresolveEmpty.hpp"
#include "CoinPresolveFixed.hpp"
#include "CoinPresolvePsdebug.hpp"
#include "CoinPresolveSingleton.hpp"
#include "CoinPresolveDoubleton.hpp"
#include "CoinPresolveTripleton.hpp"
#include "CoinPresolveZeros.hpp"
#include "CoinPresolveSubst.hpp"
#include "CoinPresolveForcing.hpp"
#include "CoinPresolveDual.hpp"
#include "CoinPresolveTighten.hpp"
#include "CoinPresolveUseless.hpp"
#include "CoinPresolveDupcol.hpp"
#include "CoinPresolveImpliedFree.hpp"
#include "CoinPresolveIsolated.hpp"
#include "CoinMessage.hpp"

ClpPresolve::ClpPresolve()
  : originalModel_(NULL)
  , presolvedModel_(NULL)
  , nonLinearValue_(0.0)
  , originalColumn_(NULL)
  , originalRow_(NULL)
  , rowObjective_(NULL)
  , paction_(0)
  , ncols_(0)
  , nrows_(0)
  , nelems_(0)
  ,
#ifdef CLP_INHERIT_MODE
  numberPasses_(20)
  ,
#else
  numberPasses_(5)
  ,
#endif
  substitution_(3)
  ,
#ifndef CLP_NO_STD
  saveFile_("")
  ,
#endif
  presolveActions_(0)
{
}

ClpPresolve::~ClpPresolve()
{
  destroyPresolve();
}
// Gets rid of presolve actions (e.g.when infeasible)
void ClpPresolve::destroyPresolve()
{
  const CoinPresolveAction *paction = paction_;
  while (paction) {
    const CoinPresolveAction *next = paction->next;
    delete paction;
    paction = next;
  }
  delete[] originalColumn_;
  delete[] originalRow_;
  paction_ = NULL;
  originalColumn_ = NULL;
  originalRow_ = NULL;
  delete[] rowObjective_;
  rowObjective_ = NULL;
}

/* This version of presolve returns a pointer to a new presolved
   model.  NULL if infeasible
*/
ClpSimplex *
ClpPresolve::presolvedModel(ClpSimplex &si,
  double feasibilityTolerance,
  bool keepIntegers,
  int numberPasses,
  bool dropNames,
  bool doRowObjective,
  const char *prohibitedRows,
  const char *prohibitedColumns)
{
  // Check matrix
  int checkType = ((si.specialOptions() & 128) != 0) ? 14 : 15;
  if (!si.clpMatrix()->allElementsInRange(&si, si.getSmallElementValue(),
        1.0e20, checkType))
    return NULL;
  else
    return gutsOfPresolvedModel(&si, feasibilityTolerance, keepIntegers, numberPasses, dropNames,
      doRowObjective,
      prohibitedRows,
      prohibitedColumns);
}
#ifndef CLP_NO_STD
/* This version of presolve updates
   model and saves original data to file.  Returns non-zero if infeasible
*/
int ClpPresolve::presolvedModelToFile(ClpSimplex &si, std::string fileName,
  double feasibilityTolerance,
  bool keepIntegers,
  int numberPasses,
  bool dropNames,
  bool doRowObjective)
{
  // Check matrix
  if (!si.clpMatrix()->allElementsInRange(&si, si.getSmallElementValue(),
        1.0e20))
    return 2;
  saveFile_ = fileName;
  si.saveModel(saveFile_.c_str());
  ClpSimplex *model = gutsOfPresolvedModel(&si, feasibilityTolerance, keepIntegers, numberPasses, dropNames,
    doRowObjective);
  if (model == &si) {
    return 0;
  } else {
    si.restoreModel(saveFile_.c_str());
    remove(saveFile_.c_str());
    return 1;
  }
}
#endif
// Return pointer to presolved model
ClpSimplex *
ClpPresolve::model() const
{
  return presolvedModel_;
}
// Return pointer to original model
ClpSimplex *
ClpPresolve::originalModel() const
{
  return originalModel_;
}
// Return presolve status (0,1,2)
int ClpPresolve::presolveStatus() const
{
  if (nelems_ >= 0) {
    // feasible (or not done yet)
    return 0;
  } else {
    int presolveStatus = -static_cast< int >(nelems_);
    // If both infeasible and unbounded - say infeasible
    if (presolveStatus > 2)
      presolveStatus = 1;
    return presolveStatus;
  }
}
void ClpPresolve::postsolve(bool updateStatus)
{
  // Return at once if no presolved model
  if (!presolvedModel_)
    return;
  // Messages
  CoinMessages messages = originalModel_->coinMessages();
  if (!presolvedModel_->isProvenOptimal()) {
    presolvedModel_->messageHandler()->message(COIN_PRESOLVE_NONOPTIMAL,
      messages)
      << CoinMessageEol;
  }

  // this is the size of the original problem
  const int ncols0 = ncols_;
  const int nrows0 = nrows_;
  const CoinBigIndex nelems0 = nelems_;

  // this is the reduced problem
  int ncols = presolvedModel_->getNumCols();
  int nrows = presolvedModel_->getNumRows();

  double *acts = NULL;
  double *sol = NULL;
  unsigned char *rowstat = NULL;
  unsigned char *colstat = NULL;
#ifndef CLP_NO_STD
  if (saveFile_ == "") {
#endif
    // reality check
    assert(ncols0 == originalModel_->getNumCols());
    assert(nrows0 == originalModel_->getNumRows());
    acts = originalModel_->primalRowSolution();
    sol = originalModel_->primalColumnSolution();
    if (updateStatus) {
      // postsolve does not know about fixed
      int i;
      for (i = 0; i < nrows + ncols; i++) {
        if (presolvedModel_->getColumnStatus(i) == ClpSimplex::isFixed)
          presolvedModel_->setColumnStatus(i, ClpSimplex::atLowerBound);
      }
      unsigned char *status = originalModel_->statusArray();
      if (!status) {
        originalModel_->createStatus();
        status = originalModel_->statusArray();
      }
      rowstat = status + ncols0;
      colstat = status;
      CoinMemcpyN(presolvedModel_->statusArray(), ncols, colstat);
      CoinMemcpyN(presolvedModel_->statusArray() + ncols, nrows, rowstat);
    }
#ifndef CLP_NO_STD
  } else {
    // from file
    acts = new double[nrows0];
    sol = new double[ncols0];
    CoinZeroN(acts, nrows0);
    CoinZeroN(sol, ncols0);
    if (updateStatus) {
      unsigned char *status = new unsigned char[nrows0 + ncols0];
      rowstat = status + ncols0;
      colstat = status;
      CoinMemcpyN(presolvedModel_->statusArray(), ncols, colstat);
      CoinMemcpyN(presolvedModel_->statusArray() + ncols, nrows, rowstat);
    }
  }
#endif

  // CoinPostsolveMatrix object assumes ownership of sol, acts, colstat;
  // will be deleted by ~CoinPostsolveMatrix. delete[] operations below
  // cause duplicate free. In case where saveFile == "", as best I can see
  // arrays are owned by originalModel_. fix is to
  // clear fields in prob to avoid delete[] in ~CoinPostsolveMatrix.
  CoinPostsolveMatrix prob(presolvedModel_,
    ncols0,
    nrows0,
    nelems0,
    presolvedModel_->getObjSense(),
    // end prepost

    sol, acts,
    colstat, rowstat);

  postsolve(prob);

#ifndef CLP_NO_STD
  if (saveFile_ != "") {
    // From file
    assert(originalModel_ == presolvedModel_);
    originalModel_->restoreModel(saveFile_.c_str());
    remove(saveFile_.c_str());
    CoinMemcpyN(acts, nrows0, originalModel_->primalRowSolution());
    // delete [] acts;
    CoinMemcpyN(sol, ncols0, originalModel_->primalColumnSolution());
    // delete [] sol;
    if (updateStatus) {
      CoinMemcpyN(colstat, nrows0 + ncols0, originalModel_->statusArray());
      // delete [] colstat;
    }
  } else {
#endif
    prob.sol_ = 0;
    prob.acts_ = 0;
    prob.colstat_ = 0;
#ifndef CLP_NO_STD
  }
#endif
  // put back duals
  CoinMemcpyN(prob.rowduals_, nrows_, originalModel_->dualRowSolution());
  double maxmin = originalModel_->getObjSense();
  if (maxmin < 0.0) {
    // swap signs
    int i;
    double *pi = originalModel_->dualRowSolution();
    for (i = 0; i < nrows_; i++)
      pi[i] = -pi[i];
  }
  // Now check solution
  double offset;
  CoinMemcpyN(originalModel_->objectiveAsObject()->gradient(originalModel_,
                originalModel_->primalColumnSolution(), offset, true),
    ncols_, originalModel_->dualColumnSolution());
  originalModel_->clpMatrix()->transposeTimes(-1.0,
    originalModel_->dualRowSolution(),
    originalModel_->dualColumnSolution());
  memset(originalModel_->primalRowSolution(), 0, nrows_ * sizeof(double));
  originalModel_->clpMatrix()->times(1.0,
    originalModel_->primalColumnSolution(),
    originalModel_->primalRowSolution());
  originalModel_->checkSolutionInternal();
  if (originalModel_->sumDualInfeasibilities() > 1.0e-1) {
    // See if we can fix easily
    static_cast< ClpSimplexOther * >(originalModel_)->cleanupAfterPostsolve();
  }
  // Messages
  presolvedModel_->messageHandler()->message(COIN_PRESOLVE_POSTSOLVE,
    messages)
    << originalModel_->objectiveValue()
    << originalModel_->sumDualInfeasibilities()
    << originalModel_->numberDualInfeasibilities()
    << originalModel_->sumPrimalInfeasibilities()
    << originalModel_->numberPrimalInfeasibilities()
    << CoinMessageEol;

  //originalModel_->objectiveValue_=objectiveValue_;
  originalModel_->setNumberIterations(presolvedModel_->numberIterations());
  if (!presolvedModel_->status()) {
    if (!originalModel_->numberDualInfeasibilities() && !originalModel_->numberPrimalInfeasibilities()) {
      originalModel_->setProblemStatus(0);
    } else {
      originalModel_->setProblemStatus(-1);
      // Say not optimal after presolve
      originalModel_->setSecondaryStatus(7);
      presolvedModel_->messageHandler()->message(COIN_PRESOLVE_NEEDS_CLEANING,
        messages)
        << CoinMessageEol;
    }
  } else {
    originalModel_->setProblemStatus(presolvedModel_->status());
    // but not if close to feasible
    if (originalModel_->sumPrimalInfeasibilities() < 1.0e-1) {
      originalModel_->setProblemStatus(-1);
      // Say not optimal after presolve
      originalModel_->setSecondaryStatus(7);
    }
  }
#ifndef CLP_NO_STD
  if (saveFile_ != "")
    presolvedModel_ = NULL;
#endif
}

// return pointer to original columns
const int *
ClpPresolve::originalColumns() const
{
  return originalColumn_;
}
// return pointer to original rows
const int *
ClpPresolve::originalRows() const
{
  return originalRow_;
}
// Set pointer to original model
void ClpPresolve::setOriginalModel(ClpSimplex *model)
{
  originalModel_ = model;
}
#if 0
// A lazy way to restrict which transformations are applied
// during debugging.
static int ATOI(const char *name)
{
     return true;
#if PRESOLVE_DEBUG || PRESOLVE_SUMMARY
     if (getenv(name)) {
          int val = atoi(getenv(name));
          printf("%s = %d\n", name, val);
          return (val);
     } else {
          if (strcmp(name, "off"))
               return (true);
          else
               return (false);
     }
#else
     return (true);
#endif
}
#endif
#ifdef PRESOLVE_DEBUG
#define PRESOLVE_CHECK_SOL 1
#endif
//#define PRESOLVE_CHECK_SOL 1
#if PRESOLVE_CHECK_SOL
void check_sol(CoinPresolveMatrix *prob, double tol)
{
  double *colels = prob->colels_;
  int *hrow = prob->hrow_;
  int *mcstrt = prob->mcstrt_;
  int *hincol = prob->hincol_;
  int *hinrow = prob->hinrow_;
  int ncols = prob->ncols_;

  double *csol = prob->sol_;
  double *acts = prob->acts_;
  double *clo = prob->clo_;
  double *cup = prob->cup_;
  int nrows = prob->nrows_;
  double *rlo = prob->rlo_;
  double *rup = prob->rup_;

  int colx;

  double *rsol = new double[nrows];
  memset(rsol, 0, nrows * sizeof(double));

  for (colx = 0; colx < ncols; ++colx) {
    if (1) {
      CoinBigIndex k = mcstrt[colx];
      int nx = hincol[colx];
      double solutionValue = csol[colx];
      for (int i = 0; i < nx; ++i) {
        int row = hrow[k];
        double coeff = colels[k];
        k++;
        rsol[row] += solutionValue * coeff;
      }
      if (csol[colx] < clo[colx] - tol) {
        printf("low CSOL:  %d  - %g %g %g\n",
          colx, clo[colx], csol[colx], cup[colx]);
      } else if (csol[colx] > cup[colx] + tol) {
        printf("high CSOL:  %d  - %g %g %g\n",
          colx, clo[colx], csol[colx], cup[colx]);
      }
    }
  }
  int rowx;
  for (rowx = 0; rowx < nrows; ++rowx) {
    if (hinrow[rowx]) {
      if (fabs(rsol[rowx] - acts[rowx]) > tol)
        printf("inacc RSOL:  %d - %g %g (acts_ %g) %g\n",
          rowx, rlo[rowx], rsol[rowx], acts[rowx], rup[rowx]);
      if (rsol[rowx] < rlo[rowx] - tol) {
        printf("low RSOL:  %d - %g %g %g\n",
          rowx, rlo[rowx], rsol[rowx], rup[rowx]);
      } else if (rsol[rowx] > rup[rowx] + tol) {
        printf("high RSOL:  %d - %g %g %g\n",
          rowx, rlo[rowx], rsol[rowx], rup[rowx]);
      }
    }
  }
  delete[] rsol;
}
#endif
static int tightenDoubletons2(CoinPresolveMatrix *prob)
{
  // column-major representation
  const int ncols = prob->ncols_;
  const CoinBigIndex *const mcstrt = prob->mcstrt_;
  const int *const hincol = prob->hincol_;
  const int *const hrow = prob->hrow_;
  double *colels = prob->colels_;
  double *cost = prob->cost_;

  // column type, bounds, solution, and status
  const unsigned char *const integerType = prob->integerType_;
  double *clo = prob->clo_;
  double *cup = prob->cup_;
  // row-major representation
  //const int nrows = prob->nrows_ ;
  const CoinBigIndex *const mrstrt = prob->mrstrt_;
  const int *const hinrow = prob->hinrow_;
  const int *const hcol = prob->hcol_;
  double *rowels = prob->rowels_;

  // row bounds
  double *const rlo = prob->rlo_;
  double *const rup = prob->rup_;

  // tolerances
  //const double ekkinf2 = PRESOLVE_SMALL_INF ;
  //const double ekkinf = ekkinf2*1.0e8 ;
  //const double ztolcbarj = prob->ztoldj_ ;
  //const CoinRelFltEq relEq(prob->ztolzb_) ;
  int numberChanged = 0;
  double bound[2];
  double alpha[2] = { 0.0, 0.0 };
#if ABC_NORMAL_DEBUG > 0
  double offset = 0.0;
#endif

  for (int icol = 0; icol < ncols; icol++) {
    if (hincol[icol] == 2) {
      CoinBigIndex start = mcstrt[icol];
      int row0 = hrow[start];
      if (hinrow[row0] != 2)
        continue;
      int row1 = hrow[start + 1];
      if (hinrow[row1] != 2)
        continue;
      double element0 = colels[start];
      double rowUpper0 = rup[row0];
      bool swapSigns0 = false;
      if (rlo[row0] > -1.0e30) {
        if (rup[row0] > 1.0e30) {
          swapSigns0 = true;
          rowUpper0 = -rlo[row0];
          element0 = -element0;
        } else {
          // range or equality
          continue;
        }
      } else if (rup[row0] > 1.0e30) {
        // free
        continue;
      }
#if 0
      // skip here for speed
      // skip if no cost (should be able to get rid of)
      if (!cost[icol]) {
	printf("should be able to get rid of %d with no cost\n",icol);
	continue;
      }
      // skip if negative cost for now
      if (cost[icol]<0.0) {
	printf("code for negative cost\n");
	continue;
      }
#endif
      double element1 = colels[start + 1];
      double rowUpper1 = rup[row1];
      bool swapSigns1 = false;
      if (rlo[row1] > -1.0e30) {
        if (rup[row1] > 1.0e30) {
          swapSigns1 = true;
          rowUpper1 = -rlo[row1];
          element1 = -element1;
        } else {
          // range or equality
          continue;
        }
      } else if (rup[row1] > 1.0e30) {
        // free
        continue;
      }
      double lowerX = clo[icol];
      double upperX = cup[icol];
      int otherCol = -1;
      CoinBigIndex startRow = mrstrt[row0];
      for (CoinBigIndex j = startRow; j < startRow + 2; j++) {
        int jcol = hcol[j];
        if (jcol != icol) {
          alpha[0] = swapSigns0 ? -rowels[j] : rowels[j];
          otherCol = jcol;
        }
      }
      startRow = mrstrt[row1];
      bool possible = true;
      for (CoinBigIndex j = startRow; j < startRow + 2; j++) {
        int jcol = hcol[j];
        if (jcol != icol) {
          if (jcol == otherCol) {
            alpha[1] = swapSigns1 ? -rowels[j] : rowels[j];
          } else {
            possible = false;
          }
        }
      }
      if (possible) {
        // skip if no cost (should be able to get rid of)
        if (!cost[icol]) {
          PRESOLVE_DETAIL_PRINT(printf("should be able to get rid of %d with no cost\n", icol));
          continue;
        }
        // skip if negative cost for now
        if (cost[icol] < 0.0) {
          PRESOLVE_DETAIL_PRINT(printf("code for negative cost\n"));
          continue;
        }
        bound[0] = clo[otherCol];
        bound[1] = cup[otherCol];
        double lowestLowest = COIN_DBL_MAX;
        double highestLowest = -COIN_DBL_MAX;
        double lowestHighest = COIN_DBL_MAX;
        double highestHighest = -COIN_DBL_MAX;
        int binding0 = 0;
        int binding1 = 0;
        for (int k = 0; k < 2; k++) {
          bool infLow0 = false;
          bool infLow1 = false;
          double sum0 = 0.0;
          double sum1 = 0.0;
          double value = bound[k];
          if (fabs(value) < 1.0e30) {
            sum0 += alpha[0] * value;
            sum1 += alpha[1] * value;
          } else {
            if (alpha[0] > 0.0) {
              if (value < 0.0)
                infLow0 = true;
            } else if (alpha[0] < 0.0) {
              if (value > 0.0)
                infLow0 = true;
            }
            if (alpha[1] > 0.0) {
              if (value < 0.0)
                infLow1 = true;
            } else if (alpha[1] < 0.0) {
              if (value > 0.0)
                infLow1 = true;
            }
          }
          /* Got sums
	   */
          double thisLowest0 = -COIN_DBL_MAX;
          double thisHighest0 = COIN_DBL_MAX;
          if (element0 > 0.0) {
            // upper bound unless inf&2 !=0
            if (!infLow0)
              thisHighest0 = (rowUpper0 - sum0) / element0;
          } else {
            // lower bound unless inf&2 !=0
            if (!infLow0)
              thisLowest0 = (rowUpper0 - sum0) / element0;
          }
          double thisLowest1 = -COIN_DBL_MAX;
          double thisHighest1 = COIN_DBL_MAX;
          if (element1 > 0.0) {
            // upper bound unless inf&2 !=0
            if (!infLow1)
              thisHighest1 = (rowUpper1 - sum1) / element1;
          } else {
            // lower bound unless inf&2 !=0
            if (!infLow1)
              thisLowest1 = (rowUpper1 - sum1) / element1;
          }
          if (thisLowest0 > thisLowest1 + 1.0e-12) {
            if (thisLowest0 > lowerX + 1.0e-12)
              binding0 |= 1 << k;
          } else if (thisLowest1 > thisLowest0 + 1.0e-12) {
            if (thisLowest1 > lowerX + 1.0e-12)
              binding1 |= 1 << k;
            thisLowest0 = thisLowest1;
          }
          if (thisHighest0 < thisHighest1 - 1.0e-12) {
            if (thisHighest0 < upperX - 1.0e-12)
              binding0 |= 1 << k;
          } else if (thisHighest1 < thisHighest0 - 1.0e-12) {
            if (thisHighest1 < upperX - 1.0e-12)
              binding1 |= 1 << k;
            thisHighest0 = thisHighest1;
          }
          lowestLowest = CoinMin(lowestLowest, thisLowest0);
          highestHighest = CoinMax(highestHighest, thisHighest0);
          lowestHighest = CoinMin(lowestHighest, thisHighest0);
          highestLowest = CoinMax(highestLowest, thisLowest0);
        }
        // see if any good
        //#define PRINT_VALUES
        if (!binding0 || !binding1) {
          PRESOLVE_DETAIL_PRINT(printf("Row redundant for column %d\n", icol));
        } else {
#ifdef PRINT_VALUES
          printf("Column %d bounds %g,%g lowest %g,%g highest %g,%g\n",
            icol, lowerX, upperX, lowestLowest, lowestHighest,
            highestLowest, highestHighest);
#endif
          // if integer adjust
          if (integerType[icol]) {
            lowestLowest = ceil(lowestLowest - 1.0e-5);
            highestLowest = ceil(highestLowest - 1.0e-5);
            lowestHighest = floor(lowestHighest + 1.0e-5);
            highestHighest = floor(highestHighest + 1.0e-5);
          }
          // if costed may be able to adjust
          if (cost[icol] >= 0.0) {
            if (highestLowest < upperX && highestLowest >= lowerX && highestHighest < 1.0e30) {
              highestHighest = CoinMin(highestHighest, highestLowest);
            }
          }
          if (cost[icol] <= 0.0) {
            if (lowestHighest > lowerX && lowestHighest <= upperX && lowestHighest > -1.0e30) {
              lowestLowest = CoinMax(lowestLowest, lowestHighest);
            }
          }
#if 1
          if (lowestLowest > lowerX + 1.0e-8) {
#ifdef PRINT_VALUES
            printf("Can increase lower bound on %d from %g to %g\n",
              icol, lowerX, lowestLowest);
#endif
            lowerX = lowestLowest;
          }
          if (highestHighest < upperX - 1.0e-8) {
#ifdef PRINT_VALUES
            printf("Can decrease upper bound on %d from %g to %g\n",
              icol, upperX, highestHighest);
#endif
            upperX = highestHighest;
          }
#endif
          // see if we can move costs
          double xValue;
          double yValue0;
          double yValue1;
          double newLower = COIN_DBL_MAX;
          double newUpper = -COIN_DBL_MAX;
#ifdef PRINT_VALUES
          double ranges0[2];
          double ranges1[2];
#endif
          double costEqual;
          double slope[2];
          assert(binding0 + binding1 == 3);
          // get where equal
          xValue = (rowUpper0 * element1 - rowUpper1 * element0) / (alpha[0] * element1 - alpha[1] * element0);
          yValue0 = (rowUpper0 - xValue * alpha[0]) / element0;
          yValue1 = (rowUpper1 - xValue * alpha[1]) / element1;
          newLower = CoinMin(newLower, CoinMax(yValue0, yValue1));
          newUpper = CoinMax(newUpper, CoinMax(yValue0, yValue1));
          double xValueEqual = xValue;
          double yValueEqual = yValue0;
          costEqual = xValue * cost[otherCol] + yValueEqual * cost[icol];
          if (binding0 == 1) {
#ifdef PRINT_VALUES
            ranges0[0] = bound[0];
            ranges0[1] = yValue0;
            ranges1[0] = yValue0;
            ranges1[1] = bound[1];
#endif
            // take x 1.0 down
            double x = xValue - 1.0;
            double y = (rowUpper0 - x * alpha[0]) / element0;
            double costTotal = x * cost[otherCol] + y * cost[icol];
            slope[0] = costEqual - costTotal;
            // take x 1.0 up
            x = xValue + 1.0;
            y = (rowUpper1 - x * alpha[1]) / element0;
            costTotal = x * cost[otherCol] + y * cost[icol];
            slope[1] = costTotal - costEqual;
          } else {
#ifdef PRINT_VALUES
            ranges1[0] = bound[0];
            ranges1[1] = yValue0;
            ranges0[0] = yValue0;
            ranges0[1] = bound[1];
#endif
            // take x 1.0 down
            double x = xValue - 1.0;
            double y = (rowUpper1 - x * alpha[1]) / element0;
            double costTotal = x * cost[otherCol] + y * cost[icol];
            slope[1] = costEqual - costTotal;
            // take x 1.0 up
            x = xValue + 1.0;
            y = (rowUpper0 - x * alpha[0]) / element0;
            costTotal = x * cost[otherCol] + y * cost[icol];
            slope[0] = costTotal - costEqual;
          }
#ifdef PRINT_VALUES
          printf("equal value of %d is %g, value of %d is max(%g,%g) - %g\n",
            otherCol, xValue, icol, yValue0, yValue1, CoinMax(yValue0, yValue1));
          printf("Cost at equality %g for constraint 0 ranges %g -> %g slope %g for constraint 1 ranges %g -> %g slope %g\n",
            costEqual, ranges0[0], ranges0[1], slope[0], ranges1[0], ranges1[1], slope[1]);
#endif
          xValue = bound[0];
          yValue0 = (rowUpper0 - xValue * alpha[0]) / element0;
          yValue1 = (rowUpper1 - xValue * alpha[1]) / element1;
#ifdef PRINT_VALUES
          printf("value of %d is %g, value of %d is max(%g,%g) - %g\n",
            otherCol, xValue, icol, yValue0, yValue1, CoinMax(yValue0, yValue1));
#endif
          newLower = CoinMin(newLower, CoinMax(yValue0, yValue1));
          // cost>0 so will be at lower
          //double yValueAtBound0=newLower;
          newUpper = CoinMax(newUpper, CoinMax(yValue0, yValue1));
          xValue = bound[1];
          yValue0 = (rowUpper0 - xValue * alpha[0]) / element0;
          yValue1 = (rowUpper1 - xValue * alpha[1]) / element1;
#ifdef PRINT_VALUES
          printf("value of %d is %g, value of %d is max(%g,%g) - %g\n",
            otherCol, xValue, icol, yValue0, yValue1, CoinMax(yValue0, yValue1));
#endif
          newLower = CoinMin(newLower, CoinMax(yValue0, yValue1));
          // cost>0 so will be at lower
          //double yValueAtBound1=newLower;
          newUpper = CoinMax(newUpper, CoinMax(yValue0, yValue1));
          lowerX = CoinMax(lowerX, newLower - 1.0e-12 * fabs(newLower));
          upperX = CoinMin(upperX, newUpper + 1.0e-12 * fabs(newUpper));
          // Now make duplicate row
          // keep row 0 so need to adjust costs so same
#ifdef PRINT_VALUES
          printf("Costs for x %g,%g,%g are %g,%g,%g\n",
            xValueEqual - 1.0, xValueEqual, xValueEqual + 1.0,
            costEqual - slope[0], costEqual, costEqual + slope[1]);
#endif
          double costOther = cost[otherCol] + slope[1];
          double costThis = cost[icol] + slope[1] * (element0 / alpha[0]);
          xValue = xValueEqual;
          yValue0 = CoinMax((rowUpper0 - xValue * alpha[0]) / element0, lowerX);
#if ABC_NORMAL_DEBUG > 0 || defined(PRINT_VALUES) || !defined(NDEBUG)
          double thisOffset = costEqual - (costOther * xValue + costThis * yValue0);
#endif
#if ABC_NORMAL_DEBUG > 0
          offset += thisOffset;
#endif
#ifdef PRINT_VALUES
          printf("new cost at equal %g\n", costOther * xValue + costThis * yValue0 + thisOffset);
#endif
          xValue = xValueEqual - 1.0;
          yValue0 = CoinMax((rowUpper0 - xValue * alpha[0]) / element0, lowerX);
#ifdef PRINT_VALUES
          printf("new cost at -1 %g\n", costOther * xValue + costThis * yValue0 + thisOffset);
#endif
          assert(fabs((costOther * xValue + costThis * yValue0 + thisOffset) - (costEqual - slope[0])) < 1.0e-5);
          xValue = xValueEqual + 1.0;
          yValue0 = CoinMax((rowUpper0 - xValue * alpha[0]) / element0, lowerX);
#ifdef PRINT_VALUES
          printf("new cost at +1 %g\n", costOther * xValue + costThis * yValue0 + thisOffset);
#endif
          assert(fabs((costOther * xValue + costThis * yValue0 + thisOffset) - (costEqual + slope[1])) < 1.0e-5);
          numberChanged++;
          //	  continue;
          cost[otherCol] = costOther;
          cost[icol] = costThis;
          clo[icol] = lowerX;
          cup[icol] = upperX;
          CoinBigIndex startCol[2];
          CoinBigIndex endCol[2];
          startCol[0] = mcstrt[icol];
          endCol[0] = startCol[0] + 2;
          startCol[1] = mcstrt[otherCol];
          endCol[1] = startCol[1] + hincol[otherCol];
          double values[2] = { 0.0, 0.0 };
          for (int k = 0; k < 2; k++) {
            for (CoinBigIndex i = startCol[k]; i < endCol[k]; i++) {
              if (hrow[i] == row0)
                values[k] = colels[i];
            }
            for (CoinBigIndex i = startCol[k]; i < endCol[k]; i++) {
              if (hrow[i] == row1)
                colels[i] = values[k];
            }
          }
          for (CoinBigIndex i = mrstrt[row1]; i < mrstrt[row1] + 2; i++) {
            if (hcol[i] == icol)
              rowels[i] = values[0];
            else
              rowels[i] = values[1];
          }
        }
      }
    }
  }
#if ABC_NORMAL_DEBUG > 0
  if (offset)
    printf("Cost offset %g\n", offset);
#endif
  return numberChanged;
}
//#define COIN_PRESOLVE_BUG
#ifdef COIN_PRESOLVE_BUG
static int counter = 1000000;
static int startEmptyRows = 0;
static int startEmptyColumns = 0;
static bool break2(CoinPresolveMatrix *prob)
{
  int droppedRows = prob->countEmptyRows() - startEmptyRows;
  int droppedColumns = prob->countEmptyCols() - startEmptyColumns;
  startEmptyRows = prob->countEmptyRows();
  startEmptyColumns = prob->countEmptyCols();
  printf("Dropped %d rows and %d columns - current empty %d, %d\n", droppedRows,
    droppedColumns, startEmptyRows, startEmptyColumns);
  counter--;
  if (!counter) {
    printf("skipping next and all\n");
  }
  return (counter <= 0);
}
#define possibleBreak \
  if (break2(prob))   \
  break
#define possibleSkip if (!break2(prob))
#else
#define possibleBreak
#define possibleSkip
#endif
#define SOME_PRESOLVE_DETAIL
#ifndef SOME_PRESOLVE_DETAIL
#define printProgress(x, y) \
  {                         \
  }
#else
#define printProgress(x, y)                                                                \
  {                                                                                        \
    if ((presolveActions_ & 0x80000000) != 0)                                              \
      printf("%c loop %d %d empty rows, %d empty columns\n", x, y, prob->countEmptyRows(), \
        prob->countEmptyCols());                                                           \
  }
#endif
// This is the presolve loop.
// It is a separate virtual function so that it can be easily
// customized by subclassing CoinPresolve.
const CoinPresolveAction *ClpPresolve::presolve(CoinPresolveMatrix *prob)
{
  // Messages
  CoinMessages messages = CoinMessage(prob->messages().language());
  paction_ = 0;
  prob->maxSubstLevel_ = CoinMax(3, prob->maxSubstLevel_);
#ifndef PRESOLVE_DETAIL
  if (prob->tuning_) {
#endif
    int numberEmptyRows = 0;
    for (int i = 0; i < prob->nrows_; i++) {
      if (!prob->hinrow_[i]) {
        PRESOLVE_DETAIL_PRINT(printf("pre_empty row %d\n", i));
        //printf("pre_empty row %d\n",i);
        numberEmptyRows++;
      }
    }
    int numberEmptyCols = 0;
    for (int i = 0; i < prob->ncols_; i++) {
      if (!prob->hincol_[i]) {
        PRESOLVE_DETAIL_PRINT(printf("pre_empty col %d\n", i));
        //printf("pre_empty col %d\n",i);
        numberEmptyCols++;
      }
    }
    printf("CoinPresolve initial state %d empty rows and %d empty columns\n",
      numberEmptyRows, numberEmptyCols);
#ifndef PRESOLVE_DETAIL
  }
#endif
  prob->status_ = 0; // say feasible
  printProgress('A', 0);
  paction_ = make_fixed(prob, paction_);
  paction_ = testRedundant(prob, paction_);
  printProgress('B', 0);
  // if integers then switch off dual stuff
  // later just do individually
  bool doDualStuff = (presolvedModel_->integerInformation() == NULL);
  // but allow in some cases
  if ((presolveActions_ & 512) != 0)
    doDualStuff = true;
  if (prob->anyProhibited())
    doDualStuff = false;
  if (!doDual())
    doDualStuff = false;
#if PRESOLVE_CONSISTENCY
  //  presolve_links_ok(prob->rlink_, prob->mrstrt_, prob->hinrow_, prob->nrows_);
  presolve_links_ok(prob, false, true);
#endif

  if (!prob->status_) {
    bool slackSingleton = doSingletonColumn();
    slackSingleton = true;
    const bool slackd = doSingleton();
    const bool doubleton = doDoubleton();
    const bool tripleton = doTripleton();
    //#define NO_FORCING
#ifndef NO_FORCING
    const bool forcing = doForcing();
#endif
    const bool ifree = doImpliedFree();
    const bool zerocost = doTighten();
    const bool dupcol = doDupcol();
    const bool duprow = doDuprow();
    const bool dual = doDualStuff;
    // Whether we want to allow duplicate intersections
    if (doIntersection())
      prob->presolveOptions_ |= 0x10;
    // zero small elements in aggregation
    prob->presolveOptions_ |= zeroSmall() * 0x20000;
    // some things are expensive so just do once (normally)

    int i;
    // say look at all
    if (!prob->anyProhibited()) {
      for (i = 0; i < nrows_; i++)
        prob->rowsToDo_[i] = i;
      prob->numberRowsToDo_ = nrows_;
      for (i = 0; i < ncols_; i++)
        prob->colsToDo_[i] = i;
      prob->numberColsToDo_ = ncols_;
    } else {
      // some stuff must be left alone
      prob->numberRowsToDo_ = 0;
      for (i = 0; i < nrows_; i++)
        if (!prob->rowProhibited(i))
          prob->rowsToDo_[prob->numberRowsToDo_++] = i;
      prob->numberColsToDo_ = 0;
      for (i = 0; i < ncols_; i++)
        if (!prob->colProhibited(i))
          prob->colsToDo_[prob->numberColsToDo_++] = i;
    }

    // transfer costs (may want to do it in OsiPresolve)
    // need a transfer back at end of postsolve transferCosts(prob);

    int iLoop;
#if PRESOLVE_CHECK_SOL
    check_sol(prob, 1.0e0);
#endif
    if (dupcol) {
      // maybe allow integer columns to be checked
      if ((presolveActions_ & 512) != 0)
        prob->setPresolveOptions(prob->presolveOptions() | 1);
      possibleSkip;
      paction_ = dupcol_action::presolve(prob, paction_);
      printProgress('C', 0);
    }
    if (doTwoxTwo()) {
      possibleSkip;
      paction_ = twoxtwo_action::presolve(prob, paction_);
    }
    if (duprow) {
      possibleSkip;
      if (doTwoxTwo()) {
        int nTightened = tightenDoubletons2(prob);
        if (nTightened)
          PRESOLVE_DETAIL_PRINT(printf("%d doubletons tightened\n",
            nTightened));
      }
      paction_ = duprow_action::presolve(prob, paction_);
      printProgress('D', 0);
      //paction_ = doubleton_action::presolve(prob, paction_);
      //printProgress('d',0);
      //if (doDependency()) {
      //paction_ = duprow3_action::presolve(prob, paction_);
      //printProgress('Z',0);
      //}
    }
    if (doGubrow()) {
      possibleSkip;
      paction_ = gubrow_action::presolve(prob, paction_);
      printProgress('E', 0);
    }
    if (ifree) {
      int fill_level = CoinMax(10, prob->maxSubstLevel_);
      const CoinPresolveAction *lastAction = NULL;
      int iPass = 4;
      while (lastAction != paction_ && iPass) {
        lastAction = paction_;
        paction_ = implied_free_action::presolve(prob, paction_, fill_level);
        printProgress('l', 0);
        iPass--;
      }
    }

    if ((presolveActions_ & 16384) != 0)
      prob->setPresolveOptions(prob->presolveOptions() | 16384);
    // For inaccurate data in implied free
    if ((presolveActions_ & 1024) != 0)
      prob->setPresolveOptions(prob->presolveOptions() | 0x20000);
    // Check number rows dropped
    int lastDropped = 0;
    prob->pass_ = 0;
#if CLP_INHERIT_MODE > 1
    int numberRowsStart = nrows_ - prob->countEmptyRows();
    int numberColumnsStart = ncols_ - prob->countEmptyCols();
    int numberRowsLeft = numberRowsStart;
    int numberColumnsLeft = numberColumnsStart;
    bool lastPassWasGood = true;
#if ABC_NORMAL_DEBUG
    printf("Original rows,columns %d,%d starting first pass with %d,%d\n",
      nrows_, ncols_, numberRowsLeft, numberColumnsLeft);
#endif
#endif
    if (numberPasses_ <= 5)
      prob->presolveOptions_ |= 0x10000; // say more lightweight
    for (iLoop = 0; iLoop < numberPasses_; iLoop++) {
      // See if we want statistics
      if ((presolveActions_ & 0x80000000) != 0)
        printf("Starting major pass %d after %g seconds with %d rows, %d columns\n", iLoop + 1, CoinCpuTime() - prob->startTime_,
          nrows_ - prob->countEmptyRows(),
          ncols_ - prob->countEmptyCols());
#ifdef PRESOLVE_SUMMARY
      printf("Starting major pass %d\n", iLoop + 1);
#endif
      const CoinPresolveAction *const paction0 = paction_;
      // look for substitutions with no fill
      //#define IMPLIED 3
#ifdef IMPLIED
      int fill_level = 3;
#define IMPLIED2 99
#if IMPLIED != 3
#if IMPLIED > 2 && IMPLIED < 11
      fill_level = IMPLIED;
      COIN_DETAIL_PRINT(printf("** fill_level == %d !\n", fill_level));
#endif
#if IMPLIED > 11 && IMPLIED < 21
      fill_level = -(IMPLIED - 10);
      COIN_DETAIL_PRINT(printf("** fill_level == %d !\n", fill_level));
#endif
#endif
#else
      int fill_level = prob->maxSubstLevel_;
#endif
      int whichPass = 0;
      while (1) {
        whichPass++;
        prob->pass_++;
        const CoinPresolveAction *const paction1 = paction_;

        if (slackd) {
          bool notFinished = true;
          while (notFinished) {
            possibleBreak;
            paction_ = slack_doubleton_action::presolve(prob, paction_,
              notFinished);
          }
          printProgress('F', iLoop + 1);
          if (prob->status_)
            break;
        }
        if (zerocost) {
          possibleBreak;
          paction_ = do_tighten_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('J', iLoop + 1);
        }
        if (dual && whichPass == 1) {
          // this can also make E rows so do one bit here
          possibleBreak;
          paction_ = remove_dual_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('G', iLoop + 1);
        }

        if (doubleton) {
          possibleBreak;
          paction_ = doubleton_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('H', iLoop + 1);
        }
        if (tripleton) {
          possibleBreak;
          paction_ = tripleton_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('I', iLoop + 1);
        }

#ifndef NO_FORCING
        if (forcing) {
          possibleBreak;
          paction_ = forcing_constraint_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('K', iLoop + 1);
        }
#endif

        if (ifree && (whichPass % 5) == 1) {
          possibleBreak;
          paction_ = implied_free_action::presolve(prob, paction_, fill_level);
          if (prob->status_)
            break;
          printProgress('L', iLoop + 1);
        }

#if PRESOLVE_CHECK_SOL
        check_sol(prob, 1.0e0);
#endif

#if PRESOLVE_CONSISTENCY
        //	presolve_links_ok(prob->rlink_, prob->mrstrt_, prob->hinrow_,
        //			  prob->nrows_);
        presolve_links_ok(prob, false, true);
#endif

//#if	PRESOLVE_DEBUG
//	presolve_no_zeros(prob->mcstrt_, prob->colels_, prob->hincol_,
//			  prob->ncols_);
//#endif
//#if	PRESOLVE_CONSISTENCY
//	prob->consistent();
//#endif
#if PRESOLVE_CONSISTENCY
        presolve_no_zeros(prob, true, false);
        presolve_consistent(prob, true);
#endif

        {
          // set up for next pass
          // later do faster if many changes i.e. memset and memcpy
          const int *count = prob->hinrow_;
          const int *nextToDo = prob->nextRowsToDo_;
          int *toDo = prob->rowsToDo_;
          int nNext = prob->numberNextRowsToDo_;
          int n = 0;
          for (int i = 0; i < nNext; i++) {
            int index = nextToDo[i];
            prob->unsetRowChanged(index);
            if (count[index])
              toDo[n++] = index;
          }
          prob->numberRowsToDo_ = n;
          prob->numberNextRowsToDo_ = 0;
          count = prob->hincol_;
          nextToDo = prob->nextColsToDo_;
          toDo = prob->colsToDo_;
          nNext = prob->numberNextColsToDo_;
          n = 0;
          for (int i = 0; i < nNext; i++) {
            int index = nextToDo[i];
            prob->unsetColChanged(index);
            if (count[index])
              toDo[n++] = index;
          }
          prob->numberColsToDo_ = n;
          prob->numberNextColsToDo_ = 0;
        }
        if (paction_ == paction1 && fill_level > 0)
          break;
      }
      // say look at all
      int i;
      if (!prob->anyProhibited()) {
        const int *count = prob->hinrow_;
        int *toDo = prob->rowsToDo_;
        int n = 0;
        for (int i = 0; i < nrows_; i++) {
          prob->unsetRowChanged(i);
          if (count[i])
            toDo[n++] = i;
        }
        prob->numberRowsToDo_ = n;
        prob->numberNextRowsToDo_ = 0;
        count = prob->hincol_;
        toDo = prob->colsToDo_;
        n = 0;
        for (int i = 0; i < ncols_; i++) {
          prob->unsetColChanged(i);
          if (count[i])
            toDo[n++] = i;
        }
        prob->numberColsToDo_ = n;
        prob->numberNextColsToDo_ = 0;
      } else {
        // some stuff must be left alone
        prob->numberRowsToDo_ = 0;
        for (i = 0; i < nrows_; i++)
          if (!prob->rowProhibited(i))
            prob->rowsToDo_[prob->numberRowsToDo_++] = i;
        prob->numberColsToDo_ = 0;
        for (i = 0; i < ncols_; i++)
          if (!prob->colProhibited(i))
            prob->colsToDo_[prob->numberColsToDo_++] = i;
      }
      // now expensive things
      // this caused world.mps to run into numerical difficulties
#ifdef PRESOLVE_SUMMARY
      printf("Starting expensive\n");
#endif

      if (dual) {
        int itry;
        for (itry = 0; itry < 5; itry++) {
          possibleBreak;
          paction_ = remove_dual_action::presolve(prob, paction_);
          if (prob->status_)
            break;
          printProgress('M', iLoop + 1);
          const CoinPresolveAction *const paction2 = paction_;
          if (ifree) {
#ifdef IMPLIED
#if IMPLIED2 == 0
            int fill_level = 0; // switches off substitution
#elif IMPLIED2 != 99
            int fill_level = IMPLIED2;
#endif
#endif
            if ((itry & 1) == 0) {
              possibleBreak;
              paction_ = implied_free_action::presolve(prob, paction_, fill_level);
            }
            if (prob->status_)
              break;
            printProgress('N', iLoop + 1);
          }
          if (paction_ == paction2)
            break;
        }
      } else if (ifree) {
        // just free
#ifdef IMPLIED
#if IMPLIED2 == 0
        int fill_level = 0; // switches off substitution
#elif IMPLIED2 != 99
        int fill_level = IMPLIED2;
#endif
#endif
        possibleBreak;
        paction_ = implied_free_action::presolve(prob, paction_, fill_level);
        if (prob->status_)
          break;
        printProgress('O', iLoop + 1);
      }
#if PRESOLVE_CHECK_SOL
      check_sol(prob, 1.0e0);
#endif
      if (dupcol) {
        // maybe allow integer columns to be checked
        if ((presolveActions_ & 512) != 0)
          prob->setPresolveOptions(prob->presolveOptions() | 1);
        possibleBreak;
        paction_ = dupcol_action::presolve(prob, paction_);
        if (prob->status_)
          break;
        printProgress('P', iLoop + 1);
      }
#if PRESOLVE_CHECK_SOL
      check_sol(prob, 1.0e0);
#endif

      if (duprow) {
        possibleBreak;
        paction_ = duprow_action::presolve(prob, paction_);
        if (prob->status_)
          break;
        printProgress('Q', iLoop + 1);
      }
      // Marginally slower on netlib if this call is enabled.
      // paction_ = testRedundant(prob,paction_) ;
#if PRESOLVE_CHECK_SOL
      check_sol(prob, 1.0e0);
#endif
      bool stopLoop = false;
      {
        int *hinrow = prob->hinrow_;
        int numberDropped = 0;
        for (i = 0; i < nrows_; i++)
          if (!hinrow[i])
            numberDropped++;

        prob->messageHandler()->message(COIN_PRESOLVE_PASS,
          messages)
          << numberDropped << iLoop + 1
          << CoinMessageEol;
        //printf("%d rows dropped after pass %d\n",numberDropped,
        //     iLoop+1);
        if (numberDropped == lastDropped)
          stopLoop = true;
        else
          lastDropped = numberDropped;
      }
      // Do this here as not very loopy
      if (slackSingleton) {
        // On most passes do not touch costed slacks
        if (paction_ != paction0 && !stopLoop) {
          possibleBreak;
          paction_ = slack_singleton_action::presolve(prob, paction_, NULL);
        } else {
          // do costed if Clp (at end as ruins rest of presolve)
          possibleBreak;
#ifndef CLP_MOVE_COSTS
          paction_ = slack_singleton_action::presolve(prob, paction_, rowObjective_);
#else
          double *fakeRowObjective = new double[prob->nrows_];
          memset(fakeRowObjective, 0, prob->nrows_ * sizeof(double));
          paction_ = slack_singleton_action::presolve(prob, paction_, fakeRowObjective);
          delete[] fakeRowObjective;
#endif
          stopLoop = true;
        }
        printProgress('R', iLoop + 1);
      }
#if PRESOLVE_CHECK_SOL
      check_sol(prob, 1.0e0);
#endif
      if (paction_ == paction0 || stopLoop)
        break;
#if CLP_INHERIT_MODE > 1
      // see whether to stop anyway
      int numberRowsNow = nrows_ - prob->countEmptyRows();
      int numberColumnsNow = ncols_ - prob->countEmptyCols();
#if ABC_NORMAL_DEBUG
      printf("Original rows,columns %d,%d - last %d,%d end of pass %d has %d,%d\n",
        nrows_, ncols_, numberRowsLeft, numberColumnsLeft, iLoop + 1, numberRowsNow,
        numberColumnsNow);
#endif
      int rowsDeleted = numberRowsLeft - numberRowsNow;
      int columnsDeleted = numberColumnsLeft - numberColumnsNow;
      if (iLoop > 15) {
        if (rowsDeleted * 100 < numberRowsStart && columnsDeleted * 100 < numberColumnsStart)
          break;
        lastPassWasGood = true;
      } else if (rowsDeleted * 100 < numberRowsStart && rowsDeleted < 500 && columnsDeleted * 100 < numberColumnsStart && columnsDeleted < 500) {
        if (!lastPassWasGood)
          break;
        else
          lastPassWasGood = false;
      } else {
        lastPassWasGood = true;
      }
      numberRowsLeft = numberRowsNow;
      numberColumnsLeft = numberColumnsNow;
#endif
    }
  }
  if (!prob->status_ && doDependency()) {
    paction_ = duprow3_action::presolve(prob, paction_);
    printProgress('Z', 0);
  }
  prob->presolveOptions_ &= ~0x10000;
  if (!prob->status_) {
    paction_ = drop_zero_coefficients(prob, paction_);
#if PRESOLVE_CHECK_SOL
    check_sol(prob, 1.0e0);
#endif

    paction_ = drop_empty_cols_action::presolve(prob, paction_);
    paction_ = drop_empty_rows_action::presolve(prob, paction_);
#if PRESOLVE_CHECK_SOL
    check_sol(prob, 1.0e0);
#endif
  }

  if (prob->status_) {
    if (prob->status_ == 1)
      prob->messageHandler()->message(COIN_PRESOLVE_INFEAS,
        messages)
        << prob->feasibilityTolerance_
        << CoinMessageEol;
    else if (prob->status_ == 2)
      prob->messageHandler()->message(COIN_PRESOLVE_UNBOUND,
        messages)
        << CoinMessageEol;
    else
      prob->messageHandler()->message(COIN_PRESOLVE_INFEASUNBOUND,
        messages)
        << CoinMessageEol;
    // get rid of data
    destroyPresolve();
  }
  return (paction_);
}

void check_djs(CoinPostsolveMatrix *prob);

// We could have implemented this by having each postsolve routine
// directly call the next one, but this may make it easier to add debugging checks.
void ClpPresolve::postsolve(CoinPostsolveMatrix &prob)
{
  {
    // Check activities
    double *colels = prob.colels_;
    int *hrow = prob.hrow_;
    CoinBigIndex *mcstrt = prob.mcstrt_;
    int *hincol = prob.hincol_;
    CoinBigIndex *link = prob.link_;
    int ncols = prob.ncols_;

    char *cdone = prob.cdone_;

    double *csol = prob.sol_;
    int nrows = prob.nrows_;

    int colx;

    double *rsol = prob.acts_;
    memset(rsol, 0, nrows * sizeof(double));

    for (colx = 0; colx < ncols; ++colx) {
      if (cdone[colx]) {
        CoinBigIndex k = mcstrt[colx];
        int nx = hincol[colx];
        double solutionValue = csol[colx];
        for (int i = 0; i < nx; ++i) {
          int row = hrow[k];
          double coeff = colels[k];
          k = link[k];
          assert(k != NO_LINK || i == nx - 1);
          rsol[row] += solutionValue * coeff;
        }
      }
    }
  }
  if (prob.maxmin_ < 0) {
    //for (int i=0;i<presolvedModel_->numberRows();i++)
    //prob.rowduals_[i]=-prob.rowduals_[i];
    for (int i = 0; i < ncols_; i++) {
      prob.cost_[i] = -prob.cost_[i];
      //prob.rcosts_[i]=-prob.rcosts_[i];
    }
    prob.maxmin_ = 1.0;
  }
  const CoinPresolveAction *paction = paction_;
  //#define PRESOLVE_DEBUG 1
#if PRESOLVE_DEBUG
  // Check only works after first one
  int checkit = -1;
#endif

  while (paction) {
#if PRESOLVE_DEBUG
    printf("POSTSOLVING %s\n", paction->name());
#endif

    paction->postsolve(&prob);

#if PRESOLVE_DEBUG
#if 0
          /*
	    This check fails (on exmip1 (!) in osiUnitTest) because clp
	    enters postsolve with a solution that seems to have incorrect
	    status for a logical. You can see similar behaviour with
	    column status --- incorrect entering postsolve.
	    -- lh, 111207 --
	  */
          {
               int nr = 0;
               int i;
               for (i = 0; i < prob.nrows_; i++) {
                    if ((prob.rowstat_[i] & 7) == 1) {
                         nr++;
                    } else if ((prob.rowstat_[i] & 7) == 2) {
                         // at ub
                         assert (prob.acts_[i] > prob.rup_[i] - 1.0e-6);
                    } else if ((prob.rowstat_[i] & 7) == 3) {
                         // at lb
                         assert (prob.acts_[i] < prob.rlo_[i] + 1.0e-6);
                    }
               }
               int nc = 0;
               for (i = 0; i < prob.ncols_; i++) {
                    if ((prob.colstat_[i] & 7) == 1)
                         nc++;
               }
               printf("%d rows (%d basic), %d cols (%d basic)\n", prob.nrows_, nr, prob.ncols_, nc);
          }
#endif // if 0
    checkit++;
    if (prob.colstat_ && checkit > 0) {
      presolve_check_nbasic(&prob);
      presolve_check_sol(&prob, 2, 2, 1);
    }
#endif
    paction = paction->next;
#if PRESOLVE_DEBUG
    //  check_djs(&prob);
    if (checkit > 0)
      presolve_check_reduced_costs(&prob);
#endif
  }
#if PRESOLVE_DEBUG
  if (prob.colstat_) {
    presolve_check_nbasic(&prob);
    presolve_check_sol(&prob, 2, 2, 1);
  }
#endif
#undef PRESOLVE_DEBUG

#if 0 && PRESOLVE_DEBUG
     for (i = 0; i < ncols0; i++) {
          if (!cdone[i]) {
               printf("!cdone[%d]\n", i);
               abort();
          }
     }

     for (i = 0; i < nrows0; i++) {
          if (!rdone[i]) {
               printf("!rdone[%d]\n", i);
               abort();
          }
     }


     for (i = 0; i < ncols0; i++) {
          if (sol[i] < -1e10 || sol[i] > 1e10)
               printf("!!!%d %g\n", i, sol[i]);

     }

#endif

#if 0 && PRESOLVE_DEBUG
     // debug check:  make sure we ended up with same original matrix
     {
          int identical = 1;

          for (int i = 0; i < ncols0; i++) {
               PRESOLVEASSERT(hincol[i] == &prob->mcstrt0[i+1] - &prob->mcstrt0[i]);
               CoinBigIndex kcs0 = &prob->mcstrt0[i];
               CoinBigIndex kcs = mcstrt[i];
               int n = hincol[i];
               for (int k = 0; k < n; k++) {
                    CoinBigIndex k1 = presolve_find_row1(&prob->hrow0[kcs0+k], kcs, kcs + n, hrow);

                    if (k1 == kcs + n) {
                         printf("ROW %d NOT IN COL %d\n", &prob->hrow0[kcs0+k], i);
                         abort();
                    }

                    if (colels[k1] != &prob->dels0[kcs0+k])
                         printf("BAD COLEL[%d %d %d]:  %g\n",
                                k1, i, &prob->hrow0[kcs0+k], colels[k1] - &prob->dels0[kcs0+k]);

                    if (kcs0 + k != k1)
                         identical = 0;
               }
          }
          printf("identical? %d\n", identical);
     }
#endif
}

static inline double getTolerance(const ClpSimplex *si, ClpDblParam key)
{
  double tol;
  if (!si->getDblParam(key, tol)) {
    CoinPresolveAction::throwCoinError("getDblParam failed",
      "CoinPrePostsolveMatrix::CoinPrePostsolveMatrix");
  }
  return (tol);
}

// Assumptions:
// 1. nrows>=m.getNumRows()
// 2. ncols>=m.getNumCols()
//
// In presolve, these values are equal.
// In postsolve, they may be inequal, since the reduced problem
// may be smaller, but we need room for the large problem.
// ncols may be larger than si.getNumCols() in postsolve,
// this at that point si will be the reduced problem,
// but we need to reserve enough space for the original problem.
CoinPrePostsolveMatrix::CoinPrePostsolveMatrix(const ClpSimplex *si,
  int ncols_in,
  int nrows_in,
  CoinBigIndex nelems_in,
  double bulkRatio)
  : ncols_(si->getNumCols())
  , nrows_(si->getNumRows())
  , nelems_(si->getNumElements())
  , ncols0_(ncols_in)
  , nrows0_(nrows_in)
  , bulkRatio_(bulkRatio)
  , mcstrt_(new CoinBigIndex[ncols_in + 1])
  , hincol_(new int[ncols_in + 1])
  , cost_(new double[ncols_in])
  , clo_(new double[ncols_in])
  , cup_(new double[ncols_in])
  , rlo_(new double[nrows_in])
  , rup_(new double[nrows_in])
  , originalColumn_(new int[ncols_in])
  , originalRow_(new int[nrows_in])
  , ztolzb_(getTolerance(si, ClpPrimalTolerance))
  , ztoldj_(getTolerance(si, ClpDualTolerance))
  , maxmin_(si->getObjSense())
  , sol_(NULL)
  , rowduals_(NULL)
  , acts_(NULL)
  , rcosts_(NULL)
  , colstat_(NULL)
  , rowstat_(NULL)
  , handler_(NULL)
  , defaultHandler_(false)
  , messages_()

{
  bulk0_ = static_cast< CoinBigIndex >(bulkRatio_ * CoinMax(nelems_in, nelems_)
    + ncols_in);
  // allow for temporary overflow
  hrow_ = new int[bulk0_ + ncols_in];
  colels_ = new double[bulk0_ + ncols_in];
  si->getDblParam(ClpObjOffset, originalOffset_);
  int ncols = si->getNumCols();
  int nrows = si->getNumRows();

  setMessageHandler(si->messageHandler());

  ClpDisjointCopyN(si->getColLower(), ncols, clo_);
  ClpDisjointCopyN(si->getColUpper(), ncols, cup_);
  //ClpDisjointCopyN(si->getObjCoefficients(), ncols, cost_);
  double offset;
  ClpDisjointCopyN(si->objectiveAsObject()->gradient(si, si->getColSolution(), offset, true), ncols, cost_);
  ClpDisjointCopyN(si->getRowLower(), nrows, rlo_);
  ClpDisjointCopyN(si->getRowUpper(), nrows, rup_);
  int i;
  for (i = 0; i < ncols_in; i++)
    originalColumn_[i] = i;
  for (i = 0; i < nrows_in; i++)
    originalRow_[i] = i;
  sol_ = NULL;
  rowduals_ = NULL;
  acts_ = NULL;

  rcosts_ = NULL;
  colstat_ = NULL;
  rowstat_ = NULL;
}

// I am not familiar enough with CoinPackedMatrix to be confident
// that I will implement a row-ordered version of toColumnOrderedGapFree
// properly.
static bool isGapFree(const CoinPackedMatrix &matrix)
{
  const CoinBigIndex *start = matrix.getVectorStarts();
  const int *length = matrix.getVectorLengths();
  int i = matrix.getSizeVectorLengths() - 1;
  // Quick check
  if (matrix.getNumElements() == start[i]) {
    return true;
  } else {
    for (i = matrix.getSizeVectorLengths() - 1; i >= 0; --i) {
      if (start[i + 1] - start[i] != length[i])
        break;
    }
    return (!(i >= 0));
  }
}
#if PRESOLVE_DEBUG
static void matrix_bounds_ok(const double *lo, const double *up, int n)
{
  int i;
  for (i = 0; i < n; i++) {
    PRESOLVEASSERT(lo[i] <= up[i]);
    PRESOLVEASSERT(lo[i] < PRESOLVE_INF);
    PRESOLVEASSERT(-PRESOLVE_INF < up[i]);
  }
}
#endif
CoinPresolveMatrix::CoinPresolveMatrix(int ncols0_in,
  double /*maxmin*/,
  // end prepost members

  ClpSimplex *si,

  // rowrep
  int nrows_in,
  CoinBigIndex nelems_in,
  bool doStatus,
  double nonLinearValue,
  double bulkRatio)
  :

  CoinPrePostsolveMatrix(si,
    ncols0_in, nrows_in, nelems_in, bulkRatio)
  , clink_(new presolvehlink[ncols0_in + 1])
  , rlink_(new presolvehlink[nrows_in + 1])
  ,

  dobias_(0.0)
  ,

  // temporary init
  integerType_(new unsigned char[ncols0_in])
  , anyInteger_(false)
  , tuning_(false)
  , startTime_(0.0)
  , feasibilityTolerance_(0.0)
  , status_(-1)
  , pass_(0)
  , colsToDo_(new int[ncols0_in])
  , numberColsToDo_(0)
  , nextColsToDo_(new int[ncols0_in])
  , numberNextColsToDo_(0)
  , rowsToDo_(new int[nrows_in])
  , numberRowsToDo_(0)
  , nextRowsToDo_(new int[nrows_in])
  , numberNextRowsToDo_(0)
  , presolveOptions_(0)
{
  const CoinBigIndex bufsize = bulk0_;

  nrows_ = si->getNumRows();

  // Set up change bits etc
  rowChanged_ = new unsigned char[nrows_];
  memset(rowChanged_, 0, nrows_);
  colChanged_ = new unsigned char[ncols_];
  memset(colChanged_, 0, ncols_);
  CoinPackedMatrix *m = si->matrix();

  // The coefficient matrix is a big hunk of stuff.
  // Do the copy here to try to avoid running out of memory.

  const CoinBigIndex *start = m->getVectorStarts();
  const int *row = m->getIndices();
  const double *element = m->getElements();
  int icol, nel = 0;
  mcstrt_[0] = 0;
  ClpDisjointCopyN(m->getVectorLengths(), ncols_, hincol_);
  if (si->getObjSense() < 0.0) {
    for (int i = 0; i < ncols_; i++)
      cost_[i] = -cost_[i];
    maxmin_ = 1.0;
  }
  for (icol = 0; icol < ncols_; icol++) {
    CoinBigIndex j;
    for (j = start[icol]; j < start[icol] + hincol_[icol]; j++) {
      hrow_[nel] = row[j];
      if (fabs(element[j]) > ZTOLDP)
        colels_[nel++] = element[j];
    }
    mcstrt_[icol + 1] = nel;
    hincol_[icol] = static_cast< int >(nel - mcstrt_[icol]);
  }

  // same thing for row rep
  CoinPackedMatrix *mRow = new CoinPackedMatrix();
  mRow->setExtraGap(0.0);
  mRow->setExtraMajor(0.0);
  mRow->reverseOrderedCopyOf(*m);
  //mRow->removeGaps();
  //mRow->setExtraGap(0.0);

  // Now get rid of matrix
  si->createEmptyMatrix();

  double *el = mRow->getMutableElements();
  int *ind = mRow->getMutableIndices();
  CoinBigIndex *strt = mRow->getMutableVectorStarts();
  int *len = mRow->getMutableVectorLengths();
  // Do carefully to save memory
  rowels_ = new double[bulk0_];
  ClpDisjointCopyN(el, nelems_, rowels_);
  mRow->nullElementArray();
  delete[] el;
  hcol_ = new int[bulk0_];
  ClpDisjointCopyN(ind, nelems_, hcol_);
  mRow->nullIndexArray();
  delete[] ind;
  mrstrt_ = new CoinBigIndex[nrows_in + 1];
  ClpDisjointCopyN(strt, nrows_, mrstrt_);
  mRow->nullStartArray();
  mrstrt_[nrows_] = nelems_;
  delete[] strt;
  hinrow_ = new int[nrows_in + 1];
  ClpDisjointCopyN(len, nrows_, hinrow_);
  if (nelems_ > nel) {
    nelems_ = nel;
    // Clean any small elements
    int irow;
    nel = 0;
    CoinBigIndex start = 0;
    for (irow = 0; irow < nrows_; irow++) {
      CoinBigIndex j;
      for (j = start; j < start + hinrow_[irow]; j++) {
        hcol_[nel] = hcol_[j];
        if (fabs(rowels_[j]) > ZTOLDP)
          rowels_[nel++] = rowels_[j];
      }
      start = mrstrt_[irow + 1];
      mrstrt_[irow + 1] = nel;
      hinrow_[irow] = static_cast< int >(nel - mrstrt_[irow]);
    }
  }

  delete mRow;
  if (si->integerInformation()) {
    CoinMemcpyN(reinterpret_cast< unsigned char * >(si->integerInformation()), ncols_, integerType_);
  } else {
    ClpFillN< unsigned char >(integerType_, ncols_, static_cast< unsigned char >(0));
  }

#ifndef SLIM_CLP
#ifndef NO_RTTI
  ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(si->objectiveAsObject()));
#else
  ClpQuadraticObjective *quadraticObj = NULL;
  if (si->objectiveAsObject()->type() == 2)
    quadraticObj = (static_cast< ClpQuadraticObjective * >(si->objectiveAsObject()));
#endif
#endif
  // Set up prohibited bits if needed
  if (nonLinearValue) {
    anyProhibited_ = true;
    for (icol = 0; icol < ncols_; icol++) {
      CoinBigIndex j;
      bool nonLinearColumn = false;
      if (cost_[icol] == nonLinearValue)
        nonLinearColumn = true;
      for (j = mcstrt_[icol]; j < mcstrt_[icol + 1]; j++) {
        if (colels_[j] == nonLinearValue) {
          nonLinearColumn = true;
          setRowProhibited(hrow_[j]);
        }
      }
      if (nonLinearColumn)
        setColProhibited(icol);
    }
#ifndef SLIM_CLP
  } else if (quadraticObj) {
    CoinPackedMatrix *quadratic = quadraticObj->quadraticObjective();
    //const int * columnQuadratic = quadratic->getIndices();
    //const CoinBigIndex * columnQuadraticStart = quadratic->getVectorStarts();
    const int *columnQuadraticLength = quadratic->getVectorLengths();
    //double * quadraticElement = quadratic->getMutableElements();
    int numberColumns = quadratic->getNumCols();
    anyProhibited_ = true;
    for (int iColumn = 0; iColumn < numberColumns; iColumn++) {
      if (columnQuadraticLength[iColumn]) {
        setColProhibited(iColumn);
        //printf("%d prohib\n",iColumn);
      }
    }
#endif
  } else {
    anyProhibited_ = false;
  }

  if (doStatus) {
    // allow for status and solution
    sol_ = new double[ncols_];
    CoinMemcpyN(si->primalColumnSolution(), ncols_, sol_);
    ;
    acts_ = new double[nrows_];
    CoinMemcpyN(si->primalRowSolution(), nrows_, acts_);
    if (!si->statusArray())
      si->createStatus();
    colstat_ = new unsigned char[nrows_ + ncols_];
    CoinMemcpyN(si->statusArray(), (nrows_ + ncols_), colstat_);
    rowstat_ = colstat_ + ncols_;
  }

  // the original model's fields are now unneeded - free them

  si->resize(0, 0);

#if PRESOLVE_DEBUG
  matrix_bounds_ok(rlo_, rup_, nrows_);
  matrix_bounds_ok(clo_, cup_, ncols_);
#endif

#if 0
     for (i = 0; i < nrows; ++i)
          printf("NR: %6d\n", hinrow[i]);
     for (int i = 0; i < ncols; ++i)
          printf("NC: %6d\n", hincol[i]);
#endif

  presolve_make_memlists(/*mcstrt_,*/ hincol_, clink_, ncols_);
  presolve_make_memlists(/*mrstrt_,*/ hinrow_, rlink_, nrows_);

  // this allows last col/row to expand up to bufsize-1 (22);
  // this must come after the calls to presolve_prefix
  mcstrt_[ncols_] = bufsize - 1;
  mrstrt_[nrows_] = bufsize - 1;
  // Allocate useful arrays
  initializeStuff();

#if PRESOLVE_CONSISTENCY
  //consistent(false);
  presolve_consistent(this, false);
#endif
}

// avoid compiler warnings
#if PRESOLVE_SUMMARY > 0
void CoinPresolveMatrix::update_model(ClpSimplex *si,
  int nrows0, int ncols0,
  CoinBigIndex nelems0)
#else
void CoinPresolveMatrix::update_model(ClpSimplex *si,
  int /*nrows0*/,
  int /*ncols0*/,
  CoinBigIndex /*nelems0*/)
#endif
{
  if (si->getObjSense() < 0.0) {
    for (int i = 0; i < ncols_; i++)
      cost_[i] = -cost_[i];
    dobias_ = -dobias_;
  }
  si->loadProblem(ncols_, nrows_, mcstrt_, hrow_, colels_, hincol_,
    clo_, cup_, cost_, rlo_, rup_);
  //delete [] si->integerInformation();
  int numberIntegers = 0;
  for (int i = 0; i < ncols_; i++) {
    if (integerType_[i])
      numberIntegers++;
  }
  if (numberIntegers)
    si->copyInIntegerInformation(reinterpret_cast< const char * >(integerType_));
  else
    si->copyInIntegerInformation(NULL);

#if PRESOLVE_SUMMARY
  printf("NEW NCOL/NROW/NELS:  %d(-%d) %d(-%d) %d(-%d)\n",
    ncols_, ncols0 - ncols_,
    nrows_, nrows0 - nrows_,
    si->getNumElements(), nelems0 - si->getNumElements());
#endif
  si->setDblParam(ClpObjOffset, originalOffset_ - dobias_);
  if (si->getObjSense() < 0.0) {
    // put back
    for (int i = 0; i < ncols_; i++)
      cost_[i] = -cost_[i];
    dobias_ = -dobias_;
    maxmin_ = -1.0;
  }
}

////////////////  POSTSOLVE

CoinPostsolveMatrix::CoinPostsolveMatrix(ClpSimplex *si,
  int ncols0_in,
  int nrows0_in,
  CoinBigIndex nelems0,

  double maxmin,
  // end prepost members

  double *sol_in,
  double *acts_in,

  unsigned char *colstat_in,
  unsigned char *rowstat_in)
  : CoinPrePostsolveMatrix(si,
      ncols0_in, nrows0_in, nelems0, 2.0)
  ,

  free_list_(0)
  ,
  // link, free_list, maxlink
  maxlink_(bulk0_)
  , link_(new CoinBigIndex[/*maxlink*/ bulk0_])
  ,

  cdone_(new char[ncols0_])
  , rdone_(new char[nrows0_in])

{
  bulk0_ = maxlink_;
  nrows_ = si->getNumRows();
  ncols_ = si->getNumCols();

  sol_ = sol_in;
  rowduals_ = NULL;
  acts_ = acts_in;

  rcosts_ = NULL;
  colstat_ = colstat_in;
  rowstat_ = rowstat_in;

  // this is the *reduced* model, which is probably smaller
  int ncols1 = ncols_;
  int nrows1 = nrows_;

  const CoinPackedMatrix *m = si->matrix();

  const CoinBigIndex nelemsr = m->getNumElements();
  if (m->getNumElements() && !isGapFree(*m)) {
    // Odd - gaps
    CoinPackedMatrix mm(*m);
    mm.removeGaps();
    mm.setExtraGap(0.0);

    ClpDisjointCopyN(mm.getVectorStarts(), ncols1, mcstrt_);
    CoinZeroN(mcstrt_ + ncols1, ncols0_ - ncols1);
    mcstrt_[ncols1] = nelems0; // ??    (should point to end of bulk store   -- lh --)
    ClpDisjointCopyN(mm.getVectorLengths(), ncols1, hincol_);
    ClpDisjointCopyN(mm.getIndices(), nelemsr, hrow_);
    ClpDisjointCopyN(mm.getElements(), nelemsr, colels_);
  } else {
    // No gaps

    ClpDisjointCopyN(m->getVectorStarts(), ncols1, mcstrt_);
    CoinZeroN(mcstrt_ + ncols1, ncols0_ - ncols1);
    mcstrt_[ncols1] = nelems0; // ??    (should point to end of bulk store   -- lh --)
    ClpDisjointCopyN(m->getVectorLengths(), ncols1, hincol_);
    ClpDisjointCopyN(m->getIndices(), nelemsr, hrow_);
    ClpDisjointCopyN(m->getElements(), nelemsr, colels_);
  }

#if 0 && PRESOLVE_DEBUG
     presolve_check_costs(model, &colcopy);
#endif

  // This determines the size of the data structure that contains
  // the matrix being postsolved.  Links are taken from the free_list
  // to recreate matrix entries that were presolved away,
  // and links are added to the free_list when entries created during
  // presolve are discarded.  There is never a need to gc this list.
  // Naturally, it should contain
  // exactly nelems0 entries "in use" when postsolving is done,
  // but I don't know whether the matrix could temporarily get
  // larger during postsolving.  Substitution into more than two
  // rows could do that, in principle.  I am being very conservative
  // here by reserving much more than the amount of space I probably need.
  // If this guess is wrong, check_free_list may be called.
  //  int bufsize = 2*nelems0;

  memset(cdone_, -1, ncols0_);
  memset(rdone_, -1, nrows0_);

  rowduals_ = new double[nrows0_];
  ClpDisjointCopyN(si->getRowPrice(), nrows1, rowduals_);

  rcosts_ = new double[ncols0_];
  ClpDisjointCopyN(si->getReducedCost(), ncols1, rcosts_);
  if (maxmin < 0.0) {
    // change so will look as if minimize
    int i;
    for (i = 0; i < nrows1; i++)
      rowduals_[i] = -rowduals_[i];
    for (i = 0; i < ncols1; i++) {
      rcosts_[i] = -rcosts_[i];
    }
  }

  //ClpDisjointCopyN(si->getRowUpper(), nrows1, rup_);
  //ClpDisjointCopyN(si->getRowLower(), nrows1, rlo_);

  ClpDisjointCopyN(si->getColSolution(), ncols1, sol_);
  si->setDblParam(ClpObjOffset, originalOffset_);
  // Test below only needed for QP ..... but .....
  // To switch off define COIN_SLOW_PRESOLVE=0
#ifndef COIN_SLOW_PRESOLVE
#define COIN_SLOW_PRESOLVE 1
#endif
  for (int j = 0; j < ncols1; j++) {
#if COIN_SLOW_PRESOLVE
    if (hincol_[j]) {
#endif
      CoinBigIndex kcs = mcstrt_[j];
      CoinBigIndex kce = kcs + hincol_[j];
      for (CoinBigIndex k = kcs; k < kce; ++k) {
        link_[k] = k + 1;
      }
      link_[kce - 1] = NO_LINK;
#if COIN_SLOW_PRESOLVE
    }
#endif
  }
  {
    CoinBigIndex ml = maxlink_;
    for (CoinBigIndex k = nelemsr; k < ml; ++k)
      link_[k] = k + 1;
    if (ml)
      link_[ml - 1] = NO_LINK;
  }
  free_list_ = nelemsr;
#if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
  /*
       These are used to track the action of postsolve transforms during debugging.
     */
  CoinFillN(cdone_, ncols1, PRESENT_IN_REDUCED);
  CoinZeroN(cdone_ + ncols1, ncols0_in - ncols1);
  CoinFillN(rdone_, nrows1, PRESENT_IN_REDUCED);
  CoinZeroN(rdone_ + nrows1, nrows0_in - nrows1);
#endif
}
/* This is main part of Presolve */
ClpSimplex *
ClpPresolve::gutsOfPresolvedModel(ClpSimplex *originalModel,
  double feasibilityTolerance,
  bool keepIntegers,
  int numberPasses,
  bool dropNames,
  bool doRowObjective,
  const char *prohibitedRows,
  const char *prohibitedColumns)
{
  ncols_ = originalModel->getNumCols();
  nrows_ = originalModel->getNumRows();
  nelems_ = originalModel->getNumElements();
  numberPasses_ = numberPasses;

  double maxmin = originalModel->getObjSense();
  originalModel_ = originalModel;
  delete[] originalColumn_;
  originalColumn_ = new int[ncols_];
  delete[] originalRow_;
  originalRow_ = new int[nrows_];
  // and fill in case returns early
  int i;
  for (i = 0; i < ncols_; i++)
    originalColumn_[i] = i;
  for (i = 0; i < nrows_; i++)
    originalRow_[i] = i;
  delete[] rowObjective_;
  if (doRowObjective) {
    rowObjective_ = new double[nrows_];
    memset(rowObjective_, 0, nrows_ * sizeof(double));
  } else {
    rowObjective_ = NULL;
  }

  // result is 0 - okay, 1 infeasible, -1 go round again, 2 - original model
  int result = -1;

  // User may have deleted - its their responsibility
  presolvedModel_ = NULL;
  // Messages
  CoinMessages messages = originalModel->coinMessages();
  // Only go round 100 times even if integer preprocessing
  int totalPasses = 100;
  while (result == -1) {

#ifndef CLP_NO_STD
    // make new copy
    if (saveFile_ == "") {
#endif
      delete presolvedModel_;
#ifndef CLP_NO_STD
      // So won't get names
      int lengthNames = originalModel->lengthNames();
      originalModel->setLengthNames(0);
#endif
      presolvedModel_ = new ClpSimplex(*originalModel);
#ifndef CLP_NO_STD
      originalModel->setLengthNames(lengthNames);
      presolvedModel_->dropNames();
    } else {
      presolvedModel_ = originalModel;
      if (dropNames)
        presolvedModel_->dropNames();
    }
#endif

    // drop integer information if wanted
    if (!keepIntegers)
      presolvedModel_->deleteIntegerInformation();
    totalPasses--;

    double ratio = 2.0;
    if (substitution_ > 3)
      ratio = sqrt((substitution_ - 3) + 5.0);
    else if (substitution_ == 2)
      ratio = 1.5;
    CoinPresolveMatrix prob(ncols_,
      maxmin,
      presolvedModel_,
      nrows_, nelems_, true, nonLinearValue_, ratio);
    if (prohibitedRows) {
      prob.setAnyProhibited();
      for (int i = 0; i < nrows_; i++) {
        if (prohibitedRows[i])
          prob.setRowProhibited(i);
      }
    }
    if (prohibitedColumns) {
      prob.setAnyProhibited();
      for (int i = 0; i < ncols_; i++) {
        if (prohibitedColumns[i])
          prob.setColProhibited(i);
      }
    }
    prob.setMaximumSubstitutionLevel(substitution_);
    if (doRowObjective)
      memset(rowObjective_, 0, nrows_ * sizeof(double));
    // See if we want statistics
    if ((presolveActions_ & 0x80000000) != 0)
      prob.statistics();
    if (doTransfer())
      transferCosts(&prob);
    // make sure row solution correct
    {
      double *colels = prob.colels_;
      int *hrow = prob.hrow_;
      CoinBigIndex *mcstrt = prob.mcstrt_;
      int *hincol = prob.hincol_;
      int ncols = prob.ncols_;

      double *csol = prob.sol_;
      double *acts = prob.acts_;
      int nrows = prob.nrows_;

      int colx;

      memset(acts, 0, nrows * sizeof(double));

      for (colx = 0; colx < ncols; ++colx) {
        double solutionValue = csol[colx];
        for (CoinBigIndex i = mcstrt[colx]; i < mcstrt[colx] + hincol[colx]; ++i) {
          int row = hrow[i];
          double coeff = colels[i];
          acts[row] += solutionValue * coeff;
        }
      }
    }

    // move across feasibility tolerance
    prob.feasibilityTolerance_ = feasibilityTolerance;

    // Do presolve
    paction_ = presolve(&prob);
    // Get rid of useful arrays
    prob.deleteStuff();

    result = 0;

    bool fixInfeasibility = (prob.presolveOptions_ & 16384) != 0;
    bool hasSolution = (prob.presolveOptions_ & 32768) != 0;
    if (prob.status_ == 0 && paction_ && (!hasSolution || !fixInfeasibility)) {
      // Looks feasible but double check to see if anything slipped through
      int n = prob.ncols_;
      double *lo = prob.clo_;
      double *up = prob.cup_;
      int i;

      for (i = 0; i < n; i++) {
        if (up[i] < lo[i]) {
          if (up[i] < lo[i] - feasibilityTolerance && !fixInfeasibility) {
            // infeasible
            prob.status_ = 1;
          } else {
            up[i] = lo[i];
          }
        }
      }

      n = prob.nrows_;
      lo = prob.rlo_;
      up = prob.rup_;

      for (i = 0; i < n; i++) {
        if (up[i] < lo[i]) {
          if (up[i] < lo[i] - feasibilityTolerance && !fixInfeasibility) {
            // infeasible
            prob.status_ = 1;
          } else {
            up[i] = lo[i];
          }
        }
      }
    }
    if (prob.status_ == 0 && paction_) {
      // feasible

      prob.update_model(presolvedModel_, nrows_, ncols_, nelems_);
      // copy status and solution
      CoinMemcpyN(prob.sol_, prob.ncols_, presolvedModel_->primalColumnSolution());
      CoinMemcpyN(prob.acts_, prob.nrows_, presolvedModel_->primalRowSolution());
      CoinMemcpyN(prob.colstat_, prob.ncols_, presolvedModel_->statusArray());
      CoinMemcpyN(prob.rowstat_, prob.nrows_, presolvedModel_->statusArray() + prob.ncols_);
      if (fixInfeasibility && hasSolution) {
        // Looks feasible but double check to see if anything slipped through
        int n = prob.ncols_;
        double *lo = prob.clo_;
        double *up = prob.cup_;
        double *rsol = prob.acts_;
        //memset(prob.acts_,0,prob.nrows_*sizeof(double));
        presolvedModel_->matrix()->times(prob.sol_, rsol);
        int i;

        for (i = 0; i < n; i++) {
          double gap = up[i] - lo[i];
          if (rsol[i] < lo[i] - feasibilityTolerance && fabs(rsol[i] - lo[i]) < 1.0e-3) {
            lo[i] = rsol[i];
            if (gap < 1.0e5)
              up[i] = lo[i] + gap;
          } else if (rsol[i] > up[i] + feasibilityTolerance && fabs(rsol[i] - up[i]) < 1.0e-3) {
            up[i] = rsol[i];
            if (gap < 1.0e5)
              lo[i] = up[i] - gap;
          }
          if (up[i] < lo[i]) {
            up[i] = lo[i];
          }
        }
      }

      int n = prob.nrows_;
      double *lo = prob.rlo_;
      double *up = prob.rup_;

      for (i = 0; i < n; i++) {
        if (up[i] < lo[i]) {
          if (up[i] < lo[i] - feasibilityTolerance && !fixInfeasibility) {
            // infeasible
            prob.status_ = 1;
          } else {
            up[i] = lo[i];
          }
        }
      }
      delete[] prob.sol_;
      delete[] prob.acts_;
      delete[] prob.colstat_;
      prob.sol_ = NULL;
      prob.acts_ = NULL;
      prob.colstat_ = NULL;

      int ncolsNow = presolvedModel_->getNumCols();
      CoinMemcpyN(prob.originalColumn_, ncolsNow, originalColumn_);
#ifndef SLIM_CLP
#ifndef NO_RTTI
      ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(originalModel->objectiveAsObject()));
#else
      ClpQuadraticObjective *quadraticObj = NULL;
      if (originalModel->objectiveAsObject()->type() == 2)
        quadraticObj = (static_cast< ClpQuadraticObjective * >(originalModel->objectiveAsObject()));
#endif
      if (quadraticObj) {
        // set up for subset
        char *mark = new char[ncols_];
        memset(mark, 0, ncols_);
        CoinPackedMatrix *quadratic = quadraticObj->quadraticObjective();
        //const int * columnQuadratic = quadratic->getIndices();
        //const CoinBigIndex * columnQuadraticStart = quadratic->getVectorStarts();
        const int *columnQuadraticLength = quadratic->getVectorLengths();
        //double * quadraticElement = quadratic->getMutableElements();
        int numberColumns = quadratic->getNumCols();
        ClpQuadraticObjective *newObj = new ClpQuadraticObjective(*quadraticObj,
          ncolsNow,
          originalColumn_);
        // and modify linear and check
        double *linear = newObj->linearObjective();
        CoinMemcpyN(presolvedModel_->objective(), ncolsNow, linear);
        int iColumn;
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          if (columnQuadraticLength[iColumn])
            mark[iColumn] = 1;
        }
        // and new
        quadratic = newObj->quadraticObjective();
        columnQuadraticLength = quadratic->getVectorLengths();
        int numberColumns2 = quadratic->getNumCols();
        for (iColumn = 0; iColumn < numberColumns2; iColumn++) {
          if (columnQuadraticLength[iColumn])
            mark[originalColumn_[iColumn]] = 0;
        }
        presolvedModel_->setObjective(newObj);
        delete newObj;
        // final check
        for (iColumn = 0; iColumn < numberColumns; iColumn++)
          if (mark[iColumn])
            printf("Quadratic column %d modified - may be okay\n", iColumn);
        delete[] mark;
      }
#endif
      delete[] prob.originalColumn_;
      prob.originalColumn_ = NULL;
      int nrowsNow = presolvedModel_->getNumRows();
      CoinMemcpyN(prob.originalRow_, nrowsNow, originalRow_);
      delete[] prob.originalRow_;
      prob.originalRow_ = NULL;
#ifndef CLP_NO_STD
      if (!dropNames && originalModel->lengthNames()) {
        // Redo names
        int iRow;
        std::vector< std::string > rowNames;
        rowNames.reserve(nrowsNow);
        for (iRow = 0; iRow < nrowsNow; iRow++) {
          int kRow = originalRow_[iRow];
          rowNames.push_back(originalModel->rowName(kRow));
        }

        int iColumn;
        std::vector< std::string > columnNames;
        columnNames.reserve(ncolsNow);
        for (iColumn = 0; iColumn < ncolsNow; iColumn++) {
          int kColumn = originalColumn_[iColumn];
          columnNames.push_back(originalModel->columnName(kColumn));
        }
        presolvedModel_->copyNames(rowNames, columnNames);
      } else {
        presolvedModel_->setLengthNames(0);
      }
#endif
      if (rowObjective_) {
        int iRow;
#ifndef NDEBUG
        int k = -1;
#endif
        int nObj = 0;
        for (iRow = 0; iRow < nrowsNow; iRow++) {
          int kRow = originalRow_[iRow];
#ifndef NDEBUG
          assert(kRow > k);
          k = kRow;
#endif
          rowObjective_[iRow] = rowObjective_[kRow];
          if (rowObjective_[iRow])
            nObj++;
        }
        if (nObj) {
          printf("%d costed slacks\n", nObj);
          presolvedModel_->setRowObjective(rowObjective_);
        }
      }
      /* now clean up integer variables.  This can modify original
               		  Don't do if dupcol added columns together */
      int i;
      const char *information = presolvedModel_->integerInformation();
      if ((prob.presolveOptions_ & 0x80000000) == 0 && information) {
        int numberChanges = 0;
        double *lower0 = originalModel_->columnLower();
        double *upper0 = originalModel_->columnUpper();
        double *lower = presolvedModel_->columnLower();
        double *upper = presolvedModel_->columnUpper();
        for (i = 0; i < ncolsNow; i++) {
          if (!information[i])
            continue;
          int iOriginal = originalColumn_[i];
          double lowerValue0 = lower0[iOriginal];
          double upperValue0 = upper0[iOriginal];
          double lowerValue = ceil(lower[i] - 1.0e-5);
          double upperValue = floor(upper[i] + 1.0e-5);
          lower[i] = lowerValue;
          upper[i] = upperValue;
          if (lowerValue > upperValue) {
            numberChanges++;
            presolvedModel_->messageHandler()->message(COIN_PRESOLVE_COLINFEAS,
              messages)
              << iOriginal
              << lowerValue
              << upperValue
              << CoinMessageEol;
            result = 1;
          } else {
            if (lowerValue > lowerValue0 + 1.0e-8) {
              lower0[iOriginal] = lowerValue;
              numberChanges++;
            }
            if (upperValue < upperValue0 - 1.0e-8) {
              upper0[iOriginal] = upperValue;
              numberChanges++;
            }
          }
        }
        if (numberChanges) {
          presolvedModel_->messageHandler()->message(COIN_PRESOLVE_INTEGERMODS,
            messages)
            << numberChanges
            << CoinMessageEol;
          if (!result && totalPasses > 0) {
            result = -1; // round again
            const CoinPresolveAction *paction = paction_;
            while (paction) {
              const CoinPresolveAction *next = paction->next;
              delete paction;
              paction = next;
            }
            paction_ = NULL;
          }
        }
      }
    } else if (prob.status_) {
      // infeasible or unbounded
      result = 1;
      // Put status in nelems_!
      nelems_ = -prob.status_;
      originalModel->setProblemStatus(prob.status_);
    } else {
      // no changes - model needs restoring after Lou's changes
#ifndef CLP_NO_STD
      if (saveFile_ == "") {
#endif
        delete presolvedModel_;
        presolvedModel_ = new ClpSimplex(*originalModel);
        // but we need to remove gaps
        ClpPackedMatrix *clpMatrix = dynamic_cast< ClpPackedMatrix * >(presolvedModel_->clpMatrix());
        if (clpMatrix) {
          clpMatrix->getPackedMatrix()->removeGaps();
        }
#ifndef CLP_NO_STD
      } else {
        presolvedModel_ = originalModel;
      }
      presolvedModel_->dropNames();
#endif

      // drop integer information if wanted
      if (!keepIntegers)
        presolvedModel_->deleteIntegerInformation();
      result = 2;
    }
  }
  if (result == 0 || result == 2) {
    int nrowsAfter = presolvedModel_->getNumRows();
    int ncolsAfter = presolvedModel_->getNumCols();
    CoinBigIndex nelsAfter = presolvedModel_->getNumElements();
    presolvedModel_->messageHandler()->message(COIN_PRESOLVE_STATS,
      messages)
      << nrowsAfter << -(nrows_ - nrowsAfter)
      << ncolsAfter << -(ncols_ - ncolsAfter)
      << nelsAfter << -(nelems_ - nelsAfter)
      << CoinMessageEol;
  } else {
    destroyPresolve();
    if (presolvedModel_ != originalModel_)
      delete presolvedModel_;
    presolvedModel_ = NULL;
  }
  return presolvedModel_;
}

/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/
