/*
  Scan the columns and collect indices of columns that have upper and lower
  bounds within the zero tolerance of one another. Hand this list to
  make_fixed_action::presolve() to do the heavy lifting.

  make_fixed_action will compensate for variables which are infeasible, forcing
  them to feasibility and correcting the row activity, before invoking
  remove_fixed_action to remove the variable from the problem. If you're
  confident of feasibility, consider remove_fixed.
*/
const CoinPresolveAction *make_fixed (CoinPresolveMatrix *prob,
                                      const CoinPresolveAction *next)
{
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
    std::cout
            << "Entering make_fixed, checking " << prob->ncols_ << " columns."
            << std::endl ;
# endif
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# endif

    int ncols = prob->ncols_ ;
    int *fcols = prob->usefulColumnInt_ ;
    int nfcols = 0 ;

    int *hincol = prob->hincol_ ;

    double *clo = prob->clo_ ;
    double *cup = prob->cup_ ;

    for (int i = 0 ; i < ncols ; i++)
    {   if (hincol[i] > 0 &&
                fabs(cup[i]-clo[i]) < ZTOLDP && !prob->colProhibited2(i))
        {
            fcols[nfcols++] = i ;
        }
    }

    /*
      Call m_f_a::presolve to do the heavy lifting. This will create a new
      CoinPresolveAction, which will become the head of the list of
      CoinPresolveAction's currently pointed to by next.

      No point in going through the effort of a call if there are no fixed
      variables.
    */
    if (nfcols > 0)
        next = make_fixed_action::presolve(prob,fcols,nfcols,true,next) ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
    std::cout
            << "Leaving make_fixed, fixed " << nfcols << " columns." << std::endl ;
# endif
# endif

    return (next) ;
}
Exemple #2
0
// 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 OsiPresolve::postsolve(CoinPostsolveMatrix &prob)
{
  const CoinPresolveAction *paction = paction_;

#if	PRESOLVE_DEBUG
  printf("Begin POSTSOLVING\n") ;
  if (prob.colstat_)
  { presolve_check_nbasic(&prob);
    presolve_check_sol(&prob); }
  presolve_check_duals(&prob);
#endif


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

    paction->postsolve(&prob);

#   if PRESOLVE_DEBUG
    if (prob.colstat_)
    { presolve_check_nbasic(&prob);
      presolve_check_sol(&prob); }
#   endif
    paction = paction->next;
#   if PRESOLVE_DEBUG
    presolve_check_duals(&prob);
#   endif
  }
# if PRESOLVE_DEBUG
    printf("End POSTSOLVING\n") ;
# endif

#if	0 && PRESOLVE_DEBUG

  << This block of checks will require some work to get it to compile. >>
/*
  Recall that in presolve, make_fixed_action forced a bound to fix a variable,
  then called remove_fixed_action to empty the column. removed_fixed_action
  left a postsolve object hanging off faction_, and our first act here is to
  call r_f_a::postsolve to repopulate the columns. The m_f_a postsolve activity
  consists of relaxing one of the bounds and making sure that the status is
  still viable (we can potentially eliminate the bound here).
*/
void make_fixed_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const action *const actions = actions_;
  const int nactions = nactions_;
  const bool fix_to_lower = fix_to_lower_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  double *sol	= prob->sol_ ;
  unsigned char *colstat = prob->colstat_;
/*
  Repopulate the columns.
*/
  assert(nactions == faction_->nactions_) ;
  faction_->postsolve(prob);
/*
  Walk the actions: restore each bound and check that the status is still
  appropriate. Given that we're unfixing a fixed variable, it's safe to assume
  that the unaffected bound is finite.
*/
  for (int cnt = nactions-1 ; cnt >= 0 ; cnt--)
  { const action *f = &actions[cnt];
    int icol = f->col;
    double xj = sol[icol] ;

    assert(faction_->actions_[cnt].col == icol) ;

    if (fix_to_lower)
    { double ub = f->bound ;
      cup[icol] = ub ;
      if (colstat)
      { if (ub >= PRESOLVE_INF || xj != ub)
	{ prob->setColumnStatus(icol,
				CoinPrePostsolveMatrix::atLowerBound) ; } } }
    else
    { double lb = f->bound ;
      clo[icol] = lb ;
      if (colstat)
      { if (lb <= -PRESOLVE_INF || xj != lb)
	{ prob->setColumnStatus(icol,
				CoinPrePostsolveMatrix::atUpperBound) ; } } } }

# if PRESOLVE_CONSISTENCY || PRESOLVE_DEBUG
  presolve_check_threads(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
  std::cout << "Leaving make_fixed_action::postsolve." << std::endl ;
# endif
  return ; }
const CoinPresolveAction *implied_free_action::presolve (
    CoinPresolveMatrix *prob, const CoinPresolveAction *next, int &fill_level)
{
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering implied_free_action::presolve, fill level " << fill_level
    << "." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  const int startEmptyRows = prob->countEmptyRows() ;
  const int startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

/*
  Unpack the row- and column-major representations.
*/
  const int m = prob->nrows_ ;
  const int n = prob->ncols_ ;

  const CoinBigIndex *rowStarts = prob->mrstrt_ ;
  int *rowLengths = prob->hinrow_ ;
  const int *colIndices = prob->hcol_ ;
  const double *rowCoeffs = prob->rowels_ ;
  presolvehlink *rlink = prob->rlink_ ;

  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  int *rowIndices = prob->hrow_ ;
  double *colCoeffs = prob->colels_ ;
  presolvehlink *clink = prob->clink_ ;

/*
  Column bounds, row bounds, cost, integrality.
*/
  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;
  double *cost = prob->cost_ ;
  const unsigned char *integerType = prob->integerType_ ;

/*
  Documented as `inhibit x+y+z = 1 mods'.  From the code below, it's clear
  that this is intended to avoid removing SOS equalities with length >= 5
  (hardcoded). 
*/
  const bool stopSomeStuff = ((prob->presolveOptions()&0x04) != 0) ;
/*
  Ignore infeasibility. `Fix' is overly optimistic.
*/
  const bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;
/*
  Defaults to 0.0.
*/
  const double feasTol = prob->feasibilityTolerance_ ;

# if 0  
/*
  Tentatively moved to be a front-end function for useless_constraint_action,
  much as make_fixed is a front-end for make_fixed_action. This bit of code
  left for possible tuning.
  -- lh, 121127 --

  Original comment: This needs to be made faster.
*/
# ifdef COIN_LIGHTWEIGHT_PRESOLVE
  if (prob->pass_ == 1) {
#else
    if (prob->presolveOptions_&0x10) {
# endif
    next = testRedundant(prob,next) ;
    if (prob->status_&0x01 != 0) {
      if ((prob->presolveOptions_&0x4000) != 0)
        prob->status_ &= !0x01 ;
      else
	return (next) ;
    }
# if 1 //def COIN_LIGHTWEIGHT_PRESOLVE
  }
# endif
# endif

/*
  implied_free and subst take a fair bit of effort to scan for candidates.
  This is a hook to allow a presolve driver to avoid that work.
*/
  if (prob->pass_ > 15 && (prob->presolveOptions_&0x10000) != 0) { 
    fill_level = 2 ;
    return (next) ;
  }

/*
  Set up to collect implied_free actions.
*/
  action *actions = new action [n] ;
# ifdef ZEROFAULT
  CoinZeroN(reinterpret_cast<char *>(actions),n*sizeof(action)) ;
# endif
  int nactions = 0 ;

  int *implied_free = prob->usefulColumnInt_ ;
  int *whichFree = implied_free+n ;
  int numberFree = 0 ;
/*
  Arrays to hold row activity (row lhs) min and max values. Each row lhs bound
  is held as two components: a sum of finite column bounds and a count of
  infinite column bounds.
*/
  int *infiniteDown = new int[m] ;
  int *infiniteUp = new int[m] ;
  double *maxDown = new double[m] ;
  double *maxUp = new double[m] ;
/*
  Overload infiniteUp with row status codes:
  -1: L(i)/U(i) not yet computed,
  -2: do not use (empty or useless row),
  -3: give up (infeasible)
  -4: chosen as implied free row
*/
  for (int i = 0 ; i < m ; i++) {
    if (rowLengths[i] > 1)
      infiniteUp[i] = -1 ;
    else
      infiniteUp[i] = -2 ;
  }
  // Get rid of rows with prohibited columns
  if (prob->anyProhibited_) {
    for (int i = 0 ; i < m ; i++) {
      CoinBigIndex rStart = rowStarts[i];
      CoinBigIndex rEnd = rStart+rowLengths[i];
      bool badRow=false;
      for (CoinBigIndex j = rStart; j < rEnd; ++j) {
	if (prob->colProhibited(colIndices[j])) {
	  badRow=true;
	  break;
	}
      }
      if (badRow)
	infiniteUp[i] = -2 ;
    }
  }

// Can't go on without a suitable finite infinity, can we?
#ifdef USE_SMALL_LARGE
  const double large = 1.0e10 ;
#else
  const double large = 1.0e20 ;
#endif

/*
  Decide which columns we're going to look at. There are columns already queued
  in colsToDo_, but sometimes we want to look at all of them. Don't suck in
  prohibited columns. See comments at the head of the routine for fill_level.

  NOTE the overload on usefulColumnInt_. It was assigned above to whichFree
       (indices of interesting columns). We'll be ok because columns are
       consumed out of look at one per main loop iteration, but not all
       columns are interesting.

  Original comment: if gone from 2 to 3 look at all
*/
  int numberLook = prob->numberColsToDo_ ;
  int iLook ;
  int *look = prob->colsToDo_ ;
  if (fill_level < 0) {
    look = prob->usefulColumnInt_+n ;
    if (!prob->anyProhibited()) {
      CoinIotaN(look,n,0) ;
      numberLook = n ;
    } else {
      numberLook = 0 ;
      for (iLook = 0 ; iLook < n ; iLook++) 
	if (!prob->colProhibited(iLook))
	  look[numberLook++] = iLook ;
    }
  }
/*
  Step through the columns of interest looking for suitable x(tgt).

  Interesting columns are limited by number of nonzeros to minimise fill-in
  during substitution.
*/
  bool infeas = false ;
  const int maxLook = abs(fill_level) ;
  for (iLook = 0 ; iLook < numberLook ; iLook++) {
    const int tgtcol = look[iLook] ;
    const int tgtcol_len = colLengths[tgtcol] ;

    if (tgtcol_len <= 0 || tgtcol_len > maxLook) continue ;
/*
  Set up to reconnoiter the column.

  The initial value for ait_max is chosen to make sure that anything that
  satisfies the stability check is big enough to use (though we'd clearly like
  something better).
*/
    const CoinBigIndex kcs = colStarts[tgtcol] ;
    const CoinBigIndex kce = kcs+tgtcol_len ;
    const bool singletonCol = (tgtcol_len == 1) ;
    bool possibleRow = false ;
    bool singletonRow = false ;
    double ait_max = 20*ZTOLDP2 ;
/*
  If this is a singleton column, the only concern is that the row is not a
  singleton row (that has its own, simpler, transform: slack_doubleton). But
  make sure we're not dealing with some tiny a(it).

  Note that there's no point in marking a singleton row. By definition, we
  won't encounter it again.
*/
    if (singletonCol) {
      const int i = rowIndices[kcs] ;
      singletonRow = (rowLengths[i] == 1) ;
      possibleRow = (fabs(colCoeffs[kcs]) > ZTOLDP2) ;
    } else {
      
/*
  If the column is not a singleton, we'll need a numerically stable
  substitution formula. Check that this is possible.  One of the entangled
  rows must be an equality with a numerically stable coefficient, at least
  .1*MAX{i}a(it).
*/
      for (CoinBigIndex kcol = kcs ; kcol < kce ; ++kcol) {
	const int i = rowIndices[kcol] ;
	if (rowLengths[i] == 1) {
	  singletonRow = true ;
	  break ;
	}
	const double abs_ait = fabs(colCoeffs[kcol]) ;
	ait_max = CoinMax(ait_max,abs_ait) ;
	if (fabs(rlo[i]-rup[i]) < feasTol && abs_ait > .1*ait_max) {
	  possibleRow = true ;
	}
      }
    }
    if (singletonRow || !possibleRow) continue ;
/*
  The column has possibilities. Walk the column, calculate row activity
  bounds L(i) and U(i) for suitable entangled rows, then calculate the
  improvement (if any) on the column bounds for l(j) and u(j). The goal is to
  satisfy the implied free condition over all entangled rows and find at least
  one row suitable for a substitution formula (if the column is not a natural
  singleton).

  Suitable: If this is a natural singleton, we need to look at the single
  entangled constraint.  If we're attempting to create a singleton by
  substitution, only look at equalities with stable coefficients. If x(t) is
  integral, make sure the scaled rhs will be integral.
*/
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  Checking x(" << tgtcol << "), " << tgtcol_len << " nonzeros"
      << ", l(" << tgtcol << ") " << clo[tgtcol] << ", u(" << tgtcol
      << ") " << cup[tgtcol] << ", c(" << tgtcol << ") " << cost[tgtcol]
      << "." << std::endl ;
#   endif
    const double lt = clo[tgtcol] ;
    const double ut = cup[tgtcol] ;
    double impliedLow = -COIN_DBL_MAX ;
    double impliedHigh = COIN_DBL_MAX ;
    int subst_ndx = -1 ;
    int subst_len = n ;
    for (CoinBigIndex kcol = kcs ; kcol < kce ; ++kcol) {
      const int i = rowIndices[kcol] ;

      assert(infiniteUp[i] != -3) ;
      if (infiniteUp[i] <= -2) continue ;

      const double ait = colCoeffs[kcol] ;
      const int leni = rowLengths[i] ;
      const double rloi = rlo[i] ;
      const double rupi = rup[i] ;
/*
  A suitable row for substitution must
    * be an equality;
    * the entangled coefficient must be large enough to be numerically stable;
    * if x(t) is integer, the constant term in the substitution formula must be
      integer.
*/
      bool rowiOK = (fabs(rloi-rupi) < feasTol) && (fabs(ait) > .1*ait_max) ;
      rowiOK = rowiOK && ((integerType[tgtcol] == 0) ||
                          (fabs((rloi/ait)-floor((rloi/ait)+0.5)) < feasTol)) ;
/*
  If we don't already have L(i) and U(i), calculate now. Check for useless and
  infeasible constraints when that's done.
*/
      int infUi = 0 ;
      int infLi = 0 ;
      double maxUi = 0.0 ;
      double maxLi = 0.0 ;
      const CoinBigIndex krs = rowStarts[i] ;
      const CoinBigIndex kre = krs+leni ;

      if (infiniteUp[i] == -1) {
	for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	  const double aik = rowCoeffs[krow] ;
	  const int k = colIndices[krow] ;
	  const double lk = clo[k] ;
	  const double uk = cup[k] ;
	  if (aik > 0.0) {
	    if (uk < large) 
	      maxUi += uk*aik ;
	    else
	      ++infUi ;
	    if (lk > -large) 
	      maxLi += lk*aik ;
	    else
	      ++infLi ;
	  } else if (aik < 0.0) {
	    if (uk < large) 
	      maxLi += uk*aik ;
	    else
	      ++infLi ;
	    if (lk > -large) 
	      maxUi += lk*aik ;
	    else
	      ++infUi ;
	  }
	}
	const double maxUinf = maxUi+infUi*1.0e31 ;
	const double maxLinf = maxLi-infLi*1.0e31 ;
	if (maxUinf <= rupi+feasTol && maxLinf >= rloi-feasTol) {
	  infiniteUp[i] = -2 ;
	} else if (maxUinf < rloi-feasTol && !fixInfeasibility) {
	  prob->status_|= 1 ;
	  infeas = true ;
	  prob->messageHandler()->message(COIN_PRESOLVE_ROWINFEAS,
					  prob->messages())
	    << i << rloi << rupi << CoinMessageEol ;
	  infiniteUp[i] = -3 ;
	} else if (maxLinf > rupi+feasTol && !fixInfeasibility) {
	  prob->status_|= 1 ;
	  infeas = true ;
	  prob->messageHandler()->message(COIN_PRESOLVE_ROWINFEAS,
					  prob->messages())
	    << i << rloi << rupi << CoinMessageEol ;
	  infiniteUp[i] = -3 ;
	} else {
	  infiniteUp[i] = infUi ;
	  infiniteDown[i] = infLi ;
	  maxUp[i] = maxUi ;
	  maxDown[i] = maxLi ;
	}
      } else {
        infUi = infiniteUp[i] ;
	infLi = infiniteDown[i] ;
	maxUi = maxUp[i] ;
	maxLi = maxDown[i] ;
      }
#     if PRESOLVE_DEBUG > 2
      std::cout
        << "    row(" << i << ") " << leni << " nonzeros, blow " << rloi
	<< ", L (" << infLi << "," << maxLi
	<< "), U (" << infUi << "," << maxUi
	<< "), b " << rupi ;
      if (infeas) std::cout << " infeas" ;
      if (infiniteUp[i] == -2) std::cout << " useless" ;
      std::cout << "." << std::endl ;
#     endif
/*
  If we're infeasible, no sense checking further; escape the implied bound
  loop. The other possibility is that we've just discovered the constraint
  is useless, in which case we just move on to the next one in the column.
*/
      if (infeas) break ;
      if (infiniteUp[i] == -2) continue ;
      assert(infiniteUp[i] >= 0 && infiniteUp[i] <= leni) ;
/*
  At this point we have L(i) and U(i), expressed as finite and infinite
  components, and constraint i is neither useless or infeasible. Calculate
  the implied bounds l'(t) and u'(t) on x(t). The calculation (for a(it) > 0)
  is
    u'(t) <= (b(i) - (L(i)-a(it)l(t)))/a(it) = l(t)+(b(i)-L(i))/a(it)
    l'(t) >= (blow(i) - (U(i)-a(it)u(t)))/a(it) = u(t)+(blow(i)-U(i))/a(it)
  Insert the appropriate flips for a(it) < 0. Notice that if there's exactly
  one infinite contribution to L(i) or U(i) and x(t) is responsible, then the
  finite portion of L(i) or U(i) is already correct.

  Cut some slack for possible numerical inaccuracy if the finite portion of
  L(i) or U(i) is very large. If the new bound is very large, force it to
  infinity.
*/
      double ltprime = -COIN_DBL_MAX ;
      double utprime = COIN_DBL_MAX ;
      if (ait > 0.0) {
	if (rloi > -large) {
	  if (!infUi) {
	    assert(ut < large) ;
	    ltprime = ut+(rloi-maxUi)/ait ;
	    if (fabs(maxUi) > 1.0e8 && !singletonCol)
	      ltprime -= 1.0e-12*fabs(maxUi) ;
	  } else if (infUi == 1 && ut > large) {
	    ltprime = (rloi-maxUi)/ait ;
	    if (fabs(maxUi) > 1.0e8 && !singletonCol)
	      ltprime -= 1.0e-12*fabs(maxUi) ;
	  } else {
	    ltprime = -COIN_DBL_MAX ;
	  }
	  impliedLow = CoinMax(impliedLow,ltprime) ;
	}
	if (rupi < large) {
	  if (!infLi) {
	    assert(lt > -large) ;
	    utprime = lt+(rupi-maxLi)/ait ;
	    if (fabs(maxLi) > 1.0e8 && !singletonCol)
	      utprime += 1.0e-12*fabs(maxLi) ;
	  } else if (infLi == 1 && lt < -large) {
	    utprime = (rupi-maxLi)/ait ;
	    if (fabs(maxLi) > 1.0e8 && !singletonCol)
	      utprime += 1.0e-12*fabs(maxLi) ;
	  } else {
	    utprime = COIN_DBL_MAX ;
	  }
	  impliedHigh = CoinMin(impliedHigh,utprime) ;
	}
      } else {
	if (rloi > -large) {
	  if (!infUi) {
	    assert(lt > -large) ;
	    utprime = lt+(rloi-maxUi)/ait ;
	    if (fabs(maxUi) > 1.0e8 && !singletonCol)
	      utprime += 1.0e-12*fabs(maxUi) ;
	  } else if (infUi == 1 && lt < -large) {
	    utprime = (rloi-maxUi)/ait ;
	    if (fabs(maxUi) > 1.0e8 && !singletonCol)
	      utprime += 1.0e-12*fabs(maxUi) ;
	  } else {
	    utprime = COIN_DBL_MAX ;
	  }
	  impliedHigh = CoinMin(impliedHigh,utprime) ;
	}
	if (rupi < large) {
	  if (!infLi) {
	    assert(ut < large) ;
	    ltprime = ut+(rupi-maxLi)/ait ;
	    if (fabs(maxLi) > 1.0e8 && !singletonCol)
	      ltprime -= 1.0e-12*fabs(maxLi) ;
	  } else if (infLi == 1 && ut > large) {
	    ltprime = (rupi-maxLi)/ait ;
	    if (fabs(maxLi) > 1.0e8 && !singletonCol)
	      ltprime -= 1.0e-12*fabs(maxLi) ;
	  } else {
	    ltprime = -COIN_DBL_MAX ;
	  }
	  impliedLow = CoinMax(impliedLow,ltprime) ;
	}
      }
#     if PRESOLVE_DEBUG > 2
      std::cout
        << "    row(" << i << ") l'(" << tgtcol << ") " << ltprime
	<< ", u'(" << tgtcol << ") " << utprime ;
      if (lt <= impliedLow && impliedHigh <= ut)
	std::cout << "; implied free satisfied" ;
      std::cout << "." << std::endl ;
#     endif
/*
  For x(t) integral, see if a substitution formula based on row i will
  preserve integrality.  The final check in this clause aims to preserve
  SOS equalities (i.e., don't eliminate a non-trivial SOS equality from
  the system using this transform).

  Note that this can't be folded into the L(i)/U(i) loop because the answer
  changes with x(t).

  Original comment: can only accept if good looking row
*/
      if (integerType[tgtcol]) {
	possibleRow = true ;
	bool allOnes = true ;
	for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	  const int j = colIndices[krow] ;
	  const double scaled_aij = rowCoeffs[krow]/ait ;
	  if (fabs(scaled_aij) != 1.0)
	    allOnes = false ;
	  if (!integerType[j] ||
	      fabs(scaled_aij-floor(scaled_aij+0.5)) > feasTol) {
	    possibleRow = false ;
	    break ;
	  }
	}
	if (rloi == 1.0 && leni >= 5 && stopSomeStuff && allOnes)
	  possibleRow = false ;
	rowiOK = rowiOK && possibleRow ;
      }
/*
  Do we have a winner? If we have an incumbent, prefer the one with fewer
  coefficients.
*/
      if (rowiOK) {
	if (subst_ndx < 0 || (leni < subst_len)) {
#         if PRESOLVE_DEBUG > 2
          std::cout
	    << "    row(" << i << ") now candidate for x(" << tgtcol << ")."
	    << std::endl ;
#         endif
	  subst_ndx = i ;
	  subst_len = leni ;
	}
      }
    }

    if (infeas) break ;
/*
  Can we do the transform? If so, subst_ndx will have a valid row.
  Record the implied free variable and the equality we'll use to substitute
  it out. Take the row out of the running --- we can't use the same row
  for two substitutions.
*/
    if (lt <= impliedLow && impliedHigh <= ut &&
        (subst_ndx >= 0 || singletonRow)) {
      implied_free[numberFree] = subst_ndx ;
      infiniteUp[subst_ndx] = -4 ;
      whichFree[numberFree++] = tgtcol ;
#     if PRESOLVE_DEBUG > 1
      std::cout
        << "  x(" << tgtcol << ") implied free by row " << subst_ndx
	<< std::endl ;
#     endif
    }
  }

  delete[] infiniteDown ;
  delete[] infiniteUp ;
  delete[] maxDown ;
  delete[] maxUp ;

/*
  If we're infeasible, there's nothing more to be done.
*/
  if (infeas) {
#   if PRESOLVE_SUMMARY > 0 || PRESOLVE_DEBUG > 0
    std::cout << "  IMPLIED_FREE: infeasible." << std::endl ;
#   endif
    return (next) ;
  }

/*
  We have a list of implied free variables, each with a row that can be used
  to substitute the variable to singleton status if the variable is not a
  natural singleton. The loop here will only process natural singletons.
  We'll hand the remainder to subst_constraint_action below, if there is
  a remainder.

  The natural singletons processed here are compressed out of whichFree and
  implied_free.
*/
  int unprocessed = 0 ;
  for (iLook = 0 ; iLook < numberFree ; iLook++) {
    const int tgtcol = whichFree[iLook] ;
    
    if (colLengths[tgtcol] != 1) {
      whichFree[unprocessed] = whichFree[iLook] ;
      implied_free[unprocessed] = implied_free[iLook] ;
      unprocessed++ ;
      continue ;
    }
    
    const int tgtrow = implied_free[iLook] ;
    const int tgtrow_len = rowLengths[tgtrow] ;

    const CoinBigIndex kcs = colStarts[tgtcol] ;
    const double tgtcol_coeff = colCoeffs[kcs] ;
    const double tgtcol_cost = cost[tgtcol] ;

    const CoinBigIndex krs = rowStarts[tgtrow] ;
    const CoinBigIndex kre = krs+tgtrow_len ;
    if (tgtcol_cost != 0.0) {
      // Check costs don't make unstable
      //double minOldCost=COIN_DBL_MAX;
      double maxOldCost=0.0;
      //double minNewCost=COIN_DBL_MAX;
      double maxNewCost=0.0;
      for (CoinBigIndex krow = krs ; krow < kre ; krow++) {
	const int j = colIndices[krow] ;
	if (j != tgtcol) {
	  double oldCost = cost[j] ;
	  double newCost = oldCost - (tgtcol_cost*rowCoeffs[krow])/tgtcol_coeff ;
	  oldCost = fabs(oldCost);
	  newCost = fabs(newCost);
	  //minOldCost=CoinMin(minOldCost,oldCost);
	  maxOldCost=CoinMax(maxOldCost,oldCost);
	  //minNewCost=CoinMin(minNewCost,newCost);
	  maxNewCost=CoinMax(maxNewCost,newCost);
	}
      }
      if (maxNewCost>1000.0*(maxOldCost+1.0) && maxOldCost) {
	//printf("too big %d tgtcost %g maxOld %g maxNew %g\n",
	//   tgtcol,tgtcol_cost,maxOldCost,maxNewCost);
	continue;
      }
    }
/*
  Initialise the postsolve action. We need to remember the row and column.
*/
    action *s = &actions[nactions++] ;
    s->row = tgtrow ;
    s->col = tgtcol ;
    s->clo = clo[tgtcol] ;
    s->cup = cup[tgtcol] ;
    s->rlo = rlo[tgtrow] ;
    s->rup = rup[tgtrow] ;
    s->ninrow = tgtrow_len ;
    s->rowels = presolve_dupmajor(rowCoeffs,colIndices,tgtrow_len,krs) ;
    s->costs = NULL ;
/*
  We're processing a singleton, hence no substitutions in the matrix, but we
  do need to fix up the cost vector. The substitution formula is
    x(t) = (rhs(i) - SUM{j\t}a(ik)x(k))/a(it)
  hence
    c'(k) = c(k)-c(t)a(ik)/a(it)
  and there's a constant offset
    c(t)rhs(i)/a(it).
  where rhs(i) is one of blow(i) or b(i).

  For general constraints where blow(i) != b(i), we need to take a bit
  of care. If only one of blow(i) or b(i) is finite, that's the one to
  use. Where we have two finite but unequal bounds, choose the bound that
  will result in the most favourable value for x(t). For minimisation, if
  c(t) < 0 we want to maximise x(t), so choose b(i) if a(it) > 0, blow(i)
  if a(it) < 0.  A bit of case analysis says choose b(i) when c(t)a(it) <
  0, blow(i) when c(t)a(it) > 0. We shouldn't be here if both row bounds
  are infinite.

  Fortunately, the objective coefficients are not affected by this.
*/
    if (tgtcol_cost != 0.0) {
      double tgtrow_rhs = rup[tgtrow] ;
      if (fabs(rlo[tgtrow]-rup[tgtrow]) > feasTol) {
	const double rlot = rlo[tgtrow] ;
	const double rupt = rup[tgtrow] ;
        if (rlot > -COIN_DBL_MAX && rupt < COIN_DBL_MAX) {
	  if ((tgtcol_cost*tgtcol_coeff) > 0)
	    tgtrow_rhs = rlot ;
	  else
	    tgtrow_rhs = rupt ;
	} else if (rupt >= COIN_DBL_MAX) {
	  tgtrow_rhs = rlot ;
	}
      }
      assert(fabs(tgtrow_rhs) <= large) ;
      double *save_costs = new double[tgtrow_len] ;

      for (CoinBigIndex krow = krs ; krow < kre ; krow++) {
	const int j = colIndices[krow] ;
	save_costs[krow-krs] = cost[j] ;
	cost[j] -= (tgtcol_cost*rowCoeffs[krow])/tgtcol_coeff ;
      }
      prob->change_bias((tgtcol_cost*tgtrow_rhs)/tgtcol_coeff) ;
      cost[tgtcol] = 0.0 ;
      s->costs = save_costs ;
    }
/*
  Remove the row from the column-major representation, queuing up each column
  for reconsideration. Then remove the row from the row-major representation.
*/
    for (CoinBigIndex krow = krs ; krow < kre ; krow++) {
      const int j = colIndices[krow] ;
      presolve_delete_from_col(tgtrow,j,colStarts,colLengths,rowIndices,
      			       colCoeffs) ;
      if (colLengths[j] == 0) {
        PRESOLVE_REMOVE_LINK(prob->clink_,j) ;
      } else {
	prob->addCol(j) ;
      }
    }
    PRESOLVE_REMOVE_LINK(clink,tgtcol) ;
    colLengths[tgtcol] = 0 ;

    PRESOLVE_REMOVE_LINK(rlink,tgtrow) ;
    rowLengths[tgtrow] = 0 ;
    rlo[tgtrow] = 0.0 ;
    rup[tgtrow] = 0.0 ;
  }
/*
  We're done with the natural singletons. Trim actions to length and create
  the postsolve object.
*/
  if (nactions) {
#   if PRESOLVE_SUMMARY > 0 || PRESOLVE_DEBUG > 0
    printf("NIMPLIED FREE:  %d\n", nactions) ;
#   endif
    action *actions1 = new action[nactions] ;
    CoinMemcpyN(actions, nactions, actions1) ;
    next = new implied_free_action(nactions,actions1,next) ;
  } 
  delete [] actions ;
# if PRESOLVE_DEBUG > 0
  std::cout
    << "  IMPLIED_FREE: identified " << numberFree
    << " implied free transforms, processed " << numberFree-unprocessed
    << " natural singletons." << std::endl ;
# endif

/*
  Now take a stab at the columns that aren't natural singletons, if there are
  any left.
*/
  if (unprocessed != 0) {
    // if not integer - don't allow much fill
    if (!prob->anyInteger())
    {
      int numberFree=unprocessed;
      int nBad=0;
      unprocessed=0;
      // Take out ones that make much denser or might lead to instability
      /*
	Unpack the row- and column-major representations.
      */
      CoinBigIndex *rowStarts = prob->mrstrt_ ;
      int *rowLengths = prob->hinrow_ ;
      double *rowCoeffs = prob->rowels_ ;
      int *colIndices = prob->hcol_ ;
      
      CoinBigIndex *colStarts = prob->mcstrt_ ;
      int *colLengths = prob->hincol_ ;
      double *colCoeffs = prob->colels_ ;
      int *rowIndices = prob->hrow_ ;
      
      /*
	This array is used to hold the indices of columns involved in substitutions,
	where we have the potential for cancellation. At the end they'll be
	checked to eliminate any actual zeros that may result.
	
	NOTE that usefulColumnInt_ is already in use for parameters implied_free and
	whichFree when this routine is called from implied_free.
      */
      
      int *rowsUsed = &prob->usefulRowInt_[0] ;
      int nRowsUsed = 0 ;
      /*
	Open a loop to process the (equality r, implied free variable t) pairs
	in whichFree and implied_free.
	
	It can happen that removal of (row, natural singleton) pairs back in
	implied_free will reduce the length of column t. It can also happen
	that previous processing here has resulted in fillin or cancellation. So
	check again for column length and exclude natural singletons and overly
	dense columns.
      */
      for (int iLook = 0 ; iLook < numberFree ; iLook++) {
	const int tgtcol = whichFree[iLook] ;
	const int tgtrow = implied_free[iLook] ;
	
	if (colLengths[tgtcol] < 2 || colLengths[tgtcol] > maxLook) {
#     if PRESOLVE_DEBUG > 3
	  std::cout
	    << "    skipping eqn " << tgtrow << " x(" << tgtcol
	    << "); length now " << colLengths[tgtcol] << "." << std::endl ;
#     endif
	  continue ;
	}
	
	CoinBigIndex tgtcs = colStarts[tgtcol] ;
	CoinBigIndex tgtce = tgtcs+colLengths[tgtcol] ;
	/*
	  A few checks to make sure that the candidate pair is still suitable.
	  Processing candidates earlier in the list can eliminate coefficients.
	  * Don't use this pair if any involved row i has become a row singleton
	  or empty.
	  * Don't use this pair if any involved row has been modified as part of
	  the processing for a previous candidate pair on this call.
	  * Don't use this pair if a(i,tgtcol) has become zero.
	  
	  The checks on a(i,tgtcol) seem superfluous but it's possible that
	  implied_free identified two candidate pairs to eliminate the same column. If
	  we've already processed one of them, we could be in trouble.
	*/
	double tgtcoeff = 0.0 ;
	bool dealBreaker = false ;
	for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
	  const int i = rowIndices[kcol] ;
	  if (rowLengths[i] < 2 || prob->rowUsed(i)) {
	    dealBreaker = true ;
	    break ;
	  }
	  const double aij = colCoeffs[kcol] ;
	  if (fabs(aij) <= ZTOLDP2) {
	    dealBreaker = true ;
	    break ;
	  }
	  if (i == tgtrow) tgtcoeff = aij ;
	}
	
	if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
	  std::cout
	    << "    skipping eqn " << tgtrow << " x(" << tgtcol
	    << "); deal breaker (1)." << std::endl ;
#     endif
	  continue ;
	}
	/*
	  Check for numerical stability.A large coeff_factor will inflate the
	  coefficients in the substitution formula.
	*/
	dealBreaker = false ;
	for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
	  const double coeff_factor = fabs(colCoeffs[kcol]/tgtcoeff) ;
	  if (coeff_factor > 10.0)
	    dealBreaker = true ;
	}
	if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
	  std::cout
	    << "    skipping eqn " << tgtrow << " x(" << tgtcol
	    << "); deal breaker (2)." << std::endl ;
#     endif
	  continue ;
	}
	/*
	  Count up the total number of coefficients in entangled rows and mark them as
	  contaminated.
	*/
	int ntotels = 0 ;
	for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
	  const int i = rowIndices[kcol] ;
	  ntotels += rowLengths[i] ;
	  PRESOLVEASSERT(!prob->rowUsed(i)) ;
	  prob->setRowUsed(i) ;
	  rowsUsed[nRowsUsed++] = i ;
	}
	
	CoinBigIndex tgtrs = rowStarts[tgtrow] ;
	CoinBigIndex tgtre = tgtrs+rowLengths[tgtrow] ;
	
	// kill small if wanted
	int relax= (prob->presolveOptions()&0x60000)>>17;
	double tolerance = 1.0e-12;
	for (int i=0;i<relax;i++)
	  tolerance *= 10.0;
	
	/*
	  Sort the target row for efficiency
	*/
	CoinSort_2(colIndices+tgtrs,colIndices+tgtre,rowCoeffs+tgtrs) ;
	CoinBigIndex start=colStarts[tgtcol];
	CoinBigIndex end = start+colLengths[tgtcol];
	numberBadElements=0;
	int numberFill=-rowLengths[tgtrow];
	for (int colndx = start ; colndx < end ; ++colndx) {
	  int i = rowIndices[colndx] ;
	  if (i == tgtrow) continue ;
	  
	  double ait = colCoeffs[colndx] ;
	  double coeff_factor = -ait/tgtcoeff ;
	  
	  CoinBigIndex krs = rowStarts[i] ;
	  CoinBigIndex kre = krs+rowLengths[i] ;
	  /*
	    Sort the row for efficiency and call add_row to do the actual business of
	    changing coefficients due to substitution. This has the potential to trigger
	    compaction of the row-major bulk store, so update bulk store indices.
	  */
	  CoinSort_2(colIndices+krs,colIndices+kre,rowCoeffs+krs) ;
	  
	  numberFill += check_row(rowStarts,rowCoeffs,colIndices,
				  rowLengths,coeff_factor,tolerance,i,tgtrow);
	}
	if (numberBadElements||3*numberFill>2*(colLengths[tgtcol]+rowLengths[tgtrow])) {
	  //printf("Bad subst col %d row %d - %d small elements, fill %d\n",
	  //	 tgtcol,tgtrow,numberBadElements,numberFill);
	  if (numberBadElements)
	    nBad++;
	} else {
	  whichFree[unprocessed]=tgtcol;
	  implied_free[unprocessed++]=tgtrow;
	  //printf("Good subst col %d row %d - fill %d\n",
	  //	 tgtcol,tgtrow,numberFill);
	}
      }
      /*
	That's it, we've processed all the candidate pairs.
	
	Clear the row used flags.
      */
      for (int i = 0 ; i < nRowsUsed ; i++) prob->unsetRowUsed(rowsUsed[i]) ;
#if CLP_USEFUL_PRINTOUT
      printf("%d allowed through out of %d - %d on coefficient\n",
	     unprocessed,numberFree,nBad);
#endif      
    }
    next = subst_constraint_action::presolve(prob,implied_free,whichFree,
    					     unprocessed,next,maxLook) ;
  }
/*
  Give some feedback to the presolve driver. If we aren't finding enough
  candidates and haven't reached the limit, bump fill_level and return a
  negated value. The presolve driver can tweak this value or simply return
  it on the next call. See the top of the routine for a full explanation.
*/
  if (numberFree < 30 && maxLook < prob->maxSubstLevel_) {
    fill_level = -(maxLook+1) ;
  } else {
    fill_level = maxLook ;
  }

# if COIN_PRESOLVE_TUNING > 0
  double thisTime ;
  if (prob->tuning_) thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving implied_free_action::presolve, fill level " << fill_level
    << ", " << droppedRows << " rows, "
    << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
void implied_free_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const action *const actions = actions_ ;
  const int nactions = nactions_ ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  char *cdone	= prob->cdone_ ;
  char *rdone	= prob->rdone_ ;
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering implied_free_action::postsolve, " << nactions
    << " transforms to undo." << std::endl ;
# endif
  presolve_check_threads(prob) ;
  presolve_check_free_list(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# endif

/*
  Unpack the column-major representation.
*/
  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  int *rowIndices = prob->hrow_ ;
  double *colCoeffs = prob->colels_ ;
  CoinBigIndex *link = prob->link_ ;
  CoinBigIndex &free_list = prob->free_list_ ;
/*
  Column bounds, row bounds, and cost.
*/
  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;
  double *cost = prob->cost_ ;
/*
  Solution, reduced costs, duals, row activity.
*/
  double *sol = prob->sol_ ;
  double *rcosts = prob->rcosts_ ;
  double *acts = prob->acts_ ;
  double *rowduals = prob->rowduals_ ;
/*
  In your dreams ... hardwired to minimisation.
*/
  const double maxmin = 1.0 ;
/*
  And a suitably small infinity.
*/
  const double large = 1.0e20 ;
/*
  Open a loop to restore the row and column for each action. Start by
  unpacking the action. There won't be saved costs if the original cost c(t)
  was zero.
*/
  for (const action *f = &actions[nactions-1] ; actions <= f ; f--) {

    const int tgtrow = f->row ;
    const int tgtcol = f->col ;
    const int tgtrow_len = f->ninrow ;
    const double *tgtrow_coeffs = f->rowels ;
    const int *tgtrow_cols =
        reinterpret_cast<const int *>(tgtrow_coeffs+tgtrow_len) ;
    const double *saved_costs = f->costs ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  restoring col " << tgtcol << " row " << tgtrow ;
    if (saved_costs != 0) std::cout << ", modified costs" ;
    std::cout << "." << std::endl ;
#   endif

/*
  Restore the target row and column and the original cost coefficients.
  We need to initialise the target column; for others, just bump the
  coefficient count. While we're restoring the row, pick off the coefficient
  for x(t) and calculate the row activity.
*/
    double tgt_coeff = 0.0 ;
    double tgtrow_act = 0.0 ;
    for (int krow = 0 ; krow < tgtrow_len ; krow++) {
      const int j = tgtrow_cols[krow] ;
      const double atj = tgtrow_coeffs[krow] ;

      assert(free_list >= 0 && free_list < prob->bulk0_) ;
      CoinBigIndex kk = free_list ;
      free_list = link[free_list] ;
      link[kk] = colStarts[j] ;
      colStarts[j] = kk ;
      colCoeffs[kk] = atj ;
      rowIndices[kk] = tgtrow ;

      if (saved_costs) cost[j] = saved_costs[krow] ;

      if (j == tgtcol) {
	colLengths[j] = 1 ;
	clo[tgtcol] = f->clo ;
	cup[tgtcol] = f->cup ;
	rcosts[j] = -cost[tgtcol]/atj ;
	tgt_coeff = atj ;
      } else {
	colLengths[j]++ ;
	tgtrow_act += atj*sol[j] ;
      }
    }
    rlo[tgtrow] = f->rlo ;
    rup[tgtrow] = f->rup ;

    PRESOLVEASSERT(fabs(tgt_coeff) > ZTOLDP) ;
#   if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
    cdone[tgtcol] = IMPLIED_FREE ;
    rdone[tgtrow] = IMPLIED_FREE ;
#   endif
#   if PRESOLVE_CONSISTENCY > 0
    presolve_check_free_list(prob) ;
#   endif
/*
  Calculate a value for x(t).  We have two possible values for x(t),
  calculated against the upper and lower bound of the constraint. x(t)
  could end up at one of its original bounds or it could end up strictly
  within bounds. In either event, the constraint will be tight.

  The code simply forces the calculated value for x(t) to be within
  bounds. Arguably it should complain more loudly as this likely indicates
  algorithmic error or numerical inaccuracy. You'll get a warning if
  debugging is enabled.
*/
    double xt_lo,xt_up ;
    if (tgt_coeff > 0) {
      xt_lo = (rlo[tgtrow]-tgtrow_act)/tgt_coeff ;
      xt_up = (rup[tgtrow]-tgtrow_act)/tgt_coeff ;
    } else {
      xt_lo = (rup[tgtrow]-tgtrow_act)/tgt_coeff ;
      xt_up = (rlo[tgtrow]-tgtrow_act)/tgt_coeff ;
    }
    const double lt = clo[tgtcol] ;
    const double ut = cup[tgtcol] ;

#   if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
    bool chklo = true ;
    bool chkup = true ;
    if (tgt_coeff > 0) {
      if (rlo[tgtrow] < -large) chklo = false ;
      if (rup[tgtrow] > large) chkup = false ;
    } else {
      if (rup[tgtrow] > large) chklo = false ;
      if (rlo[tgtrow] < -large) chkup = false ;
    }
    if (chklo && (xt_lo < lt-prob->ztolzb_)) {
      std::cout
        << "  LOW CSOL (implied_free): x(" << tgtcol << ") lb " << lt
	<< ", sol = " << xt_lo << ", err " << (lt-xt_lo)
	<< "." << std::endl ;
    }
    if (chkup && (xt_up > ut+prob->ztolzb_)) {
      std::cout
        << "  HIGH CSOL (implied_free): x(" << tgtcol << ") ub " << ut
	<< ", sol = " << xt_up << ", err " << (xt_up-ut)
	<< "." << std::endl ;
    }
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  x(" << tgtcol << ") lb " << lt << " lo " << xt_lo
      << ", up " << xt_up << " ub " << ut << "." << std::endl ;
#   endif
#   endif

    xt_lo = CoinMax(xt_lo,lt) ;
    xt_up = CoinMin(xt_up,ut) ;

/*
  Time to make x(t) basic and the logical nonbasic.  The sign of the
  dual determines the tight row bound, which in turn determines the value
  of x(t). Because the row is tight, activity is by definition equal to
  the bound.

  Coin convention is that a <= constraint puts a lower bound on the slack and
  a >= constraint puts an upper bound on the slack. Case analysis
  (minimisation) says:

  dual >= 0 ==> reduced cost <= 0 ==> NBUB ==> finite rlo
  dual <= 0 ==> reduced cost >= 0 ==> NBLB ==> finite rup
*/
    const double ct = maxmin*cost[tgtcol] ;
    double possibleDual = ct/tgt_coeff ;
    rowduals[tgtrow] = possibleDual ;
    if (possibleDual >= 0 && rlo[tgtrow] > -large) {
      sol[tgtcol] = (rlo[tgtrow]-tgtrow_act)/tgt_coeff ;
      acts[tgtrow] = rlo[tgtrow] ;
      prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atUpperBound) ;
    } else
    if (possibleDual <= 0 && rup[tgtrow] < large) {
      sol[tgtcol] = (rup[tgtrow]-tgtrow_act)/tgt_coeff ;
      acts[tgtrow] = rup[tgtrow] ;
      prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atLowerBound) ;
    } else {
      assert(rup[tgtrow] < large || rlo[tgtrow] > -large) ;
      if (rup[tgtrow] < large) {
	sol[tgtcol] = (rup[tgtrow]-tgtrow_act)/tgt_coeff ;
	acts[tgtrow] = rup[tgtrow] ;
	prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atLowerBound) ;
      } else {
	sol[tgtcol] = (rlo[tgtrow]-tgtrow_act)/tgt_coeff ;
	acts[tgtrow] = rlo[tgtrow] ;
	prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atUpperBound) ;
      }
#     if PRESOLVE_DEBUG > 0
      std::cout
        << "BAD ROW STATUS row " << tgtrow << ": dual "
	<< rowduals[tgtrow] << " but row "
	<< ((rowduals[tgtrow] > 0)?"upper":"lower")
	<< " bound is not finite; forcing status "
	<< prob->rowStatusString(tgtrow)
	<< "." << std::endl ;
#     endif
    }
    prob->setColumnStatus(tgtcol,CoinPrePostsolveMatrix::basic) ;
    rcosts[tgtcol] = 0.0 ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  x(" << tgtcol << ") B dj " << rcosts[tgtcol] << "." << std::endl ;
    std::cout
      << "  row " << tgtrow << " dual " << rowduals[tgtrow] << "."
      << std::endl ;
#   endif
    PRESOLVEASSERT(acts[tgtrow] >= rlo[tgtrow]-1.0e-5 &&
		   acts[tgtrow] <= rup[tgtrow]+1.0e-5) ;

#   if PRESOLVE_DEBUG > 2
/*
   Debug code to compare the reduced costs against a calculation from
   scratch as c(j)-ya(j).
*/
    for (int krow = 0 ; krow < tgtrow_len ; krow++) {
      const int j = tgtrow_cols[krow] ;
      const int lenj = colLengths[j] ;
      double dj = cost[j] ;
      CoinBigIndex kcol = colStarts[j] ;
      for (int cntj = 0 ; cntj < lenj ; ++cntj) {
	const int i = rowIndices[kcol] ;
	const double aij = colCoeffs[kcol] ;
	dj -= rowduals[i]*aij ;
	kcol = link[kcol] ;
      }
      if (fabs(dj-rcosts[j]) > 1.0e-3) {
        std::cout
	  << "  cbar(" << j << ") update " << rcosts[j]
	  << " expected " << dj << " err " << fabs(dj-rcosts[j])
	  << "." << std::endl ;
      }
    }
#   endif
  }

# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving implied_free_action::postsolve." << std::endl ;
# endif
# endif

  return ;
}
Exemple #6
0
/* This version of presolve returns a pointer to a new presolved
   model.  NULL if infeasible

   doStatus controls activities required to transform an existing
   solution to match the presolved problem. I'd (lh) argue that this should
   default to false, but to maintain previous behaviour it defaults to true.
   Really, this is only useful if you've already optimised before applying
   presolve and also want to work with the solution after presolve.  I think
   that this is the less common case. The more common situation is to apply
   presolve before optimising.
*/
OsiSolverInterface *
OsiPresolve::presolvedModel(OsiSolverInterface & si,
			    double feasibilityTolerance,
			    bool keepIntegers,
			    int numberPasses,
                            const char * prohibited,
			    bool doStatus,
			    const char * rowProhibited)
{
  ncols_ = si.getNumCols();
  nrows_ = si.getNumRows();
  nelems_ = si.getNumElements();
  numberPasses_ = numberPasses;

  double maxmin = si.getObjSense();
  originalModel_ = &si;
  delete [] originalColumn_;
  originalColumn_ = new int[ncols_];
  delete [] originalRow_;
  originalRow_ = new int[nrows_];
  int i;
  for (i=0;i<ncols_;i++)
    originalColumn_[i]=i;
  for (i=0;i<nrows_;i++)
    originalRow_[i]=i;

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

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

    // make new copy
    delete presolvedModel_;
    presolvedModel_ = si.clone();
    totalPasses--;

    // drop integer information if wanted
    if (!keepIntegers) {
      int i;
      for (i=0;i<ncols_;i++)
	presolvedModel_->setContinuous(i);
    }


    CoinPresolveMatrix prob(ncols_,
			    maxmin,
			    presolvedModel_,
			    nrows_, nelems_,doStatus,nonLinearValue_,prohibited,
			    rowProhibited);
    // make sure row solution correct
    if (doStatus) {
      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 (int 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. Allow for the possibility that presolve might be ineffective
  (i.e., we're feasible but no postsolve actions are queued.
*/
    paction_ = presolve(&prob) ;
    result = 0 ;
    // Get rid of useful arrays
    prob.deleteStuff();
/*
  This we don't need to do unless presolve actually reduced the system.
*/
    if (prob.status_==0&&paction_) {
      // 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]-1.0e-8) {
	    // 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]-1.0e-8) {
	    // infeasible
	    prob.status_=1;
	  } else {
	    up[i]=lo[i];
	  }
	}
      }
    }
/*
  If we're feasible, load the presolved system into the solver. Presumably we
  could skip model update and copying of status and solution if presolve took
  no action.
*/
    if (prob.status_ == 0) {

      prob.update_model(presolvedModel_, nrows_, ncols_, nelems_);

# if PRESOLVE_CONSISTENCY
      if (doStatus)
      { int basicCnt = 0 ;
	int basicColumns = 0;
	int i ;
	CoinPresolveMatrix::Status status ;
	for (i = 0 ; i < prob.ncols_ ; i++)
	{ status = prob.getColumnStatus(i);
	  if (status == CoinPrePostsolveMatrix::basic) basicColumns++ ; }
	basicCnt = basicColumns;
	for (i = 0 ; i < prob.nrows_ ; i++)
	{ status = prob.getRowStatus(i);
	  if (status == CoinPrePostsolveMatrix::basic) basicCnt++ ; }

# if PRESOLVE_DEBUG
	presolve_check_nbasic(&prob) ;
# endif
	if (basicCnt>prob.nrows_) {
	  // Take out slacks
	  double * acts = prob.acts_;
	  double * rlo = prob.rlo_;
	  double * rup = prob.rup_;
	  double infinity = si.getInfinity();
	  for (i = 0 ; i < prob.nrows_ ; i++) {
	    status = prob.getRowStatus(i);
	    if (status == CoinPrePostsolveMatrix::basic) {
	      basicCnt-- ;
	      double down = acts[i]-rlo[i];
	      double up = rup[i]-acts[i];
	      if (CoinMin(up,down)<infinity) {
		if (down<=up)
		  prob.setRowStatus(i,CoinPrePostsolveMatrix::atLowerBound);
		else
		  prob.setRowStatus(i,CoinPrePostsolveMatrix::atUpperBound);
	      } else {
		prob.setRowStatus(i,CoinPrePostsolveMatrix::isFree);
	      }
	    }
	    if (basicCnt==prob.nrows_)
	      break;
	  }
	}
      }
#endif

/*
  Install the status and primal solution, if we've been carrying them along.

  The code that copies status is efficient but brittle. The current definitions
  for CoinWarmStartBasis::Status and CoinPrePostsolveMatrix::Status are in
  one-to-one correspondence. This code will fail if that ever changes.
*/
      if (doStatus) {
	presolvedModel_->setColSolution(prob.sol_);
	CoinWarmStartBasis *basis =
	  dynamic_cast<CoinWarmStartBasis *>(presolvedModel_->getEmptyWarmStart());
	basis->resize(prob.nrows_,prob.ncols_);
	int i;
	for (i=0;i<prob.ncols_;i++) {
	  CoinWarmStartBasis::Status status =
	    static_cast<CoinWarmStartBasis::Status> (prob.getColumnStatus(i));
	  basis->setStructStatus(i,status);
	}
	for (i=0;i<prob.nrows_;i++) {
	  CoinWarmStartBasis::Status status =
	    static_cast<CoinWarmStartBasis::Status> (prob.getRowStatus(i));
	  basis->setArtifStatus(i,status);
	}
	presolvedModel_->setWarmStart(basis);
	delete basis ;
	delete [] prob.sol_;
	delete [] prob.acts_;
	delete [] prob.colstat_;
	prob.sol_=NULL;
	prob.acts_=NULL;
	prob.colstat_=NULL;
      }
/*
  Copy original column and row information from the CoinPresolveMatrix object
  so it'll be available for postsolve.
*/
      int ncolsNow = presolvedModel_->getNumCols();
      memcpy(originalColumn_,prob.originalColumn_,ncolsNow*sizeof(int));
      delete [] prob.originalColumn_;
      prob.originalColumn_=NULL;
      int nrowsNow = presolvedModel_->getNumRows();
      memcpy(originalRow_,prob.originalRow_,nrowsNow*sizeof(int));
      delete [] prob.originalRow_;
      prob.originalRow_=NULL;

      // now clean up integer variables.  This can modify original
      {
	int numberChanges=0;
	const double * lower0 = originalModel_->getColLower();
	const double * upper0 = originalModel_->getColUpper();
	const double * lower = presolvedModel_->getColLower();
	const double * upper = presolvedModel_->getColUpper();
	for (i=0;i<ncolsNow;i++) {
	  if (!presolvedModel_->isInteger(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);
	  presolvedModel_->setColBounds(i,lowerValue,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) {
	      originalModel_->setColLower(iOriginal,lowerValue);
	      numberChanges++;
	    }
	    if (upperValue<upperValue0-1.0e-8) {
	      originalModel_->setColUpper(iOriginal,upperValue);
	      numberChanges++;
	    }
	  }
	}
	if (numberChanges) {
	  presolvedModel_->messageHandler()->message(COIN_PRESOLVE_INTEGERMODS,
						     messages)
						       <<numberChanges
						       <<CoinMessageEol;
	  if (!result&&totalPasses>0&&
	      // we can't go round again in integer if dupcols
	      (prob.presolveOptions_ & 0x80000000) == 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_ != 0) {
      // infeasible or unbounded
      result = 1 ;
    }
  }
  if (!result) {
    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 {
    gutsOfDestroy();
    delete presolvedModel_;
    presolvedModel_=NULL;
  }
  return presolvedModel_;
}
const CoinPresolveAction *subst_constraint_action::presolve (
    CoinPresolveMatrix *prob,
    const int *implied_free, const int *whichFree, int numberFree,
    const CoinPresolveAction *next, int maxLook)
{

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering subst_constraint_action::presolve, fill level "
    << maxLook << ", " << numberFree << " candidates." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

/*
  Unpack the row- and column-major representations.
*/
  const int ncols = prob->ncols_ ;
  const int nrows = prob->nrows_ ;

  CoinBigIndex *rowStarts = prob->mrstrt_ ;
  int *rowLengths = prob->hinrow_ ;
  double *rowCoeffs = prob->rowels_ ;
  int *colIndices = prob->hcol_ ;
  presolvehlink *rlink = prob->rlink_ ;

  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  double *colCoeffs = prob->colels_ ;
  int *rowIndices = prob->hrow_ ;
  presolvehlink *clink = prob->clink_ ;

/*
  Row bounds and activity, objective.
*/
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;
  double *acts = prob->acts_ ;
  double *cost = prob->cost_ ;


  const double tol = prob->feasibilityTolerance_ ;

  action *actions = new action [ncols] ;
# ifdef ZEROFAULT
  CoinZeroN(reinterpret_cast<char *>(actions),ncols*sizeof(action)) ;
# endif
  int nactions = 0 ;
/*
  This array is used to hold the indices of columns involved in substitutions,
  where we have the potential for cancellation. At the end they'll be
  checked to eliminate any actual zeros that may result. At the end of
  processing of each target row, the column indices of the target row are
  copied into zerocols.

  NOTE that usefulColumnInt_ is already in use for parameters implied_free and
  whichFree when this routine is called from implied_free.
*/
  int *zerocols = new int[ncols] ;
  int nzerocols = 0 ;

  int *x_to_y = new int[ncols] ;

  int *rowsUsed = &prob->usefulRowInt_[0] ;
  int nRowsUsed = 0 ;
/*
  Open a loop to process the (equality r, implied free variable t) pairs
  in whichFree and implied_free.

  It can happen that removal of (row, natural singleton) pairs back in
  implied_free will reduce the length of column t. It can also happen
  that previous processing here has resulted in fillin or cancellation. So
  check again for column length and exclude natural singletons and overly
  dense columns.
*/
  for (int iLook = 0 ; iLook < numberFree ; iLook++) {
    const int tgtcol = whichFree[iLook] ;
    const int tgtcol_len = colLengths[tgtcol] ;
    const int tgtrow = implied_free[iLook] ;
    const int tgtrow_len = rowLengths[tgtrow] ;

    assert(fabs(rlo[tgtrow]-rup[tgtrow]) < tol) ;

    if (colLengths[tgtcol] < 2 || colLengths[tgtcol] > maxLook) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); length now " << colLengths[tgtcol] << "." << std::endl ;
#     endif
      continue ;
    }

    CoinBigIndex tgtcs = colStarts[tgtcol] ;
    CoinBigIndex tgtce = tgtcs+colLengths[tgtcol] ;
/*
  A few checks to make sure that the candidate pair is still suitable.
  Processing candidates earlier in the list can eliminate coefficients.
    * Don't use this pair if any involved row i has become a row singleton
      or empty.
    * Don't use this pair if any involved row has been modified as part of
      the processing for a previous candidate pair on this call.
    * Don't use this pair if a(i,tgtcol) has become zero.

  The checks on a(i,tgtcol) seem superfluous but it's possible that
  implied_free identified two candidate pairs to eliminate the same column. If
  we've already processed one of them, we could be in trouble.
*/
    double tgtcoeff = 0.0 ;
    bool dealBreaker = false ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int i = rowIndices[kcol] ;
      if (rowLengths[i] < 2 || prob->rowUsed(i)) {
        dealBreaker = true ;
	break ;
      }
      const double aij = colCoeffs[kcol] ;
      if (fabs(aij) <= ZTOLDP2) {
	dealBreaker = true ;
	break ;
      }
      if (i == tgtrow) tgtcoeff = aij ;
    }

    if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); deal breaker (1)." << std::endl ;
#     endif
      continue ;
    }
/*
  Check for numerical stability.A large coeff_factor will inflate the
  coefficients in the substitution formula.
*/
    dealBreaker = false ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const double coeff_factor = fabs(colCoeffs[kcol]/tgtcoeff) ;
      if (coeff_factor > 10.0)
	dealBreaker = true ;
    }
/*
  Given enough target rows with sufficient overlap, there's an outside chance
  we could overflow zerocols. Unlikely to ever happen.
*/
    if (!dealBreaker && nzerocols+rowLengths[tgtrow] >= ncols)
      dealBreaker = true ;
    if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); deal breaker (2)." << std::endl ;
#     endif
      continue ;
    }
/*
  If c(t) != 0, we will need to modify the objective coefficients and remember
  the original objective.
*/
    const bool nonzero_cost = (fabs(cost[tgtcol]) > tol) ;
    double *costsx = (nonzero_cost?new double[rowLengths[tgtrow]]:0) ;

#   if PRESOLVE_DEBUG > 1
    std::cout << "  Eliminating row " << tgtrow << ", col " << tgtcol ;
    if (nonzero_cost) std::cout << ", cost " << cost[tgtcol] ;
    std::cout << "." << std::endl ;
#   endif

/*
  Count up the total number of coefficients in entangled rows and mark them as
  contaminated.
*/
    int ntotels = 0 ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int i = rowIndices[kcol] ;
      ntotels += rowLengths[i] ;
      PRESOLVEASSERT(!prob->rowUsed(i)) ;
      prob->setRowUsed(i) ;
      rowsUsed[nRowsUsed++] = i ;
    }
/*
  Create the postsolve object. Copy in all the affected rows. Take the
  opportunity to mark the entangled rows as changed and put them on the list
  of rows to process in the next round.

  coeffxs in particular holds the coefficients of the target column.
*/
    action *ap = &actions[nactions++] ;

    ap->col = tgtcol ;
    ap->rowy = tgtrow ;
    PRESOLVE_DETAIL_PRINT(printf("pre_subst %dC %dR E\n",tgtcol,tgtrow)) ;

    ap->nincol = tgtcol_len ;
    ap->rows = new int[tgtcol_len] ;
    ap->rlos = new double[tgtcol_len] ;
    ap->rups = new double[tgtcol_len] ;

    ap->costsx = costsx ;
    ap->coeffxs = new double[tgtcol_len] ;

    ap->ninrowxs = new int[tgtcol_len] ;
    ap->rowcolsxs = new int[ntotels] ;
    ap->rowelsxs = new double[ntotels] ;

    ntotels = 0 ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int ndx = kcol-tgtcs ;
      const int i = rowIndices[kcol] ;
      const CoinBigIndex krs = rowStarts[i] ;
      prob->addRow(i) ;
      ap->rows[ndx] = i ;
      ap->ninrowxs[ndx] = rowLengths[i] ;
      ap->rlos[ndx] = rlo[i] ;
      ap->rups[ndx] = rup[i] ;
      ap->coeffxs[ndx] = colCoeffs[kcol] ;

      CoinMemcpyN(&colIndices[krs],rowLengths[i],&ap->rowcolsxs[ntotels]) ;
      CoinMemcpyN(&rowCoeffs[krs],rowLengths[i],&ap->rowelsxs[ntotels]) ;

      ntotels += rowLengths[i] ;
    }

    CoinBigIndex tgtrs = rowStarts[tgtrow] ;
    CoinBigIndex tgtre = tgtrs+rowLengths[tgtrow] ;

/*
  Adjust the objective coefficients based on the substitution formula
    c'(j) = c(j) - a(rj)c(t)/a(rt)
*/
    if (nonzero_cost) {
      const double tgtcost = cost[tgtcol] ;
      for (CoinBigIndex krow = tgtrs ; krow < tgtre ; krow ++) {
	const int j = colIndices[krow] ;
	prob->addCol(j) ;
	costsx[krow-tgtrs] = cost[j] ;
	double coeff = rowCoeffs[krow] ;
	cost[j] -= (tgtcost*coeff)/tgtcoeff ;
      }
      prob->change_bias(tgtcost*rlo[tgtrow]/tgtcoeff) ;
      cost[tgtcol] = 0.0 ;
    }

#   if PRESOLVE_DEBUG > 1
    std::cout << "  tgt (" << tgtrow << ") (" << tgtrow_len << "): " ;
    for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) {
      const int j = colIndices[krow] ;
      const double arj = rowCoeffs[krow] ;
      std::cout
	<< "x(" << j << ") = " << arj << " (" << colLengths[j] << ") " ;
    }
    std::cout << std::endl ;
#   endif
    // kill small if wanted
    int relax= (prob->presolveOptions()&0x60000)>>17;
    double tolerance = 1.0e-12;
    for (int i=0;i<relax;i++)
      tolerance *= 10.0;

/*
  Sort the target row for efficiency when doing elimination.
*/
      CoinSort_2(colIndices+tgtrs,colIndices+tgtre,rowCoeffs+tgtrs) ;
/*
  Get down to the business of substituting for tgtcol in the entangled rows.
  Open a loop to walk the target column. We walk the saved column because the
  bulk store can change as we work. We don't want to repeat or miss a row.
*/
    for (int colndx = 0 ; colndx < tgtcol_len ; ++colndx) {
      int i = ap->rows[colndx] ;
      if (i == tgtrow) continue ;

      double ait = ap->coeffxs[colndx] ;
      double coeff_factor = -ait/tgtcoeff ;
      
      CoinBigIndex krs = rowStarts[i] ;
      CoinBigIndex kre = krs+rowLengths[i] ;

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "  subst pre (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif

/*
  Sort the row for efficiency and call add_row to do the actual business of
  changing coefficients due to substitution. This has the potential to trigger
  compaction of the row-major bulk store, so update bulk store indices.
*/
      CoinSort_2(colIndices+krs,colIndices+kre,rowCoeffs+krs) ;
      
      bool outOfSpace = add_row(rowStarts,rlo,acts,rup,rowCoeffs,colIndices,
				rowLengths,rlink,nrows,coeff_factor,tolerance,i,tgtrow,
				x_to_y) ;
      if (outOfSpace)
	throwCoinError("out of memory","CoinImpliedFree::presolve") ;

      krs = rowStarts[i] ;
      kre = krs+rowLengths[i] ;
      tgtrs = rowStarts[tgtrow] ;
      tgtre = tgtrs+rowLengths[tgtrow] ;

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "  subst aft (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif

/*
  Now update the column-major representation from the row-major
  representation. This is easy if the coefficient already exists, but
  painful if there's fillin. presolve_find_row1 will return the index of
  the row in the column vector, or one past the end if it's missing. If the
  coefficient is fill, presolve_expand_col will make sure that there's room in
  the column for one more coefficient. This may require that the column be
  moved in the bulk store, so we need to update kcs and kce.

  Once we're done, a(it) = 0 (i.e., we've eliminated x(t) from row i).
  Physically remove the explicit zero from the row-major representation
  with presolve_delete_from_row.
*/
      for (CoinBigIndex rowndx = 0 ; rowndx < tgtrow_len ; ++rowndx) {
	const CoinBigIndex ktgt = tgtrs+rowndx ;
	const int j = colIndices[ktgt] ;
	CoinBigIndex kcs = colStarts[j] ;
	CoinBigIndex kce = kcs+colLengths[j] ;

	assert(colIndices[krs+x_to_y[rowndx]] == j) ;

	const double coeff = rowCoeffs[krs+x_to_y[rowndx]] ;

	CoinBigIndex kcol = presolve_find_row1(i,kcs,kce,rowIndices) ;
	
	if (kcol < kce) {
	  colCoeffs[kcol] = coeff ;
	} else {
	  outOfSpace = presolve_expand_col(colStarts,colCoeffs,rowIndices,
	  				   colLengths,clink,ncols,j) ;
	  if (outOfSpace)
	    throwCoinError("out of memory","CoinImpliedFree::presolve") ;
	  kcs = colStarts[j] ;
	  kce = kcs+colLengths[j] ;
	  
	  rowIndices[kce] = i ;
	  colCoeffs[kce] = coeff ;
	  colLengths[j]++ ;
	}
      }
      presolve_delete_from_row(i,tgtcol,
      			       rowStarts,rowLengths,colIndices,rowCoeffs) ;
#     if PRESOLVE_DEBUG > 1
      kre-- ;
      std::cout
	<< "  subst fin (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif
      
    }
/*
  End of the substitution loop.

  Record the column indices of the target row so we can groom these columns
  later to remove possible explicit zeros.
*/
    CoinMemcpyN(&colIndices[rowStarts[tgtrow]],rowLengths[tgtrow],
    		&zerocols[nzerocols]) ;
    nzerocols += rowLengths[tgtrow] ;
/*
  Remove the target equality from the column- and row-major representations
  Somewhat painful in the colum-major representation.  We have to walk the
  target row in the row-major representation and look up each coefficient
  in the column-major representation.
*/
    for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) {
      const int j = colIndices[krow] ;
#     if PRESOLVE_DEBUG > 1
      std::cout
        << "  removing row " << tgtrow << " from col " << j << std::endl ;
#     endif
      presolve_delete_from_col(tgtrow,j,
      			       colStarts,colLengths,rowIndices,colCoeffs) ;
      if (colLengths[j] == 0) {
        PRESOLVE_REMOVE_LINK(clink,j) ;
      }
    }
/*
  Finally, physically remove the column from the column-major representation
  and the row from the row-major representation.
*/
    PRESOLVE_REMOVE_LINK(clink, tgtcol) ;
    colLengths[tgtcol] = 0 ;

    PRESOLVE_REMOVE_LINK(rlink, tgtrow) ;
    rowLengths[tgtrow] = 0 ;

    rlo[tgtrow] = 0.0 ;
    rup[tgtrow] = 0.0 ;

#   if PRESOLVE_CONSISTENCY > 0
    presolve_links_ok(prob) ;
    presolve_consistent(prob) ;
#   endif

  }
/*
  That's it, we've processed all the candidate pairs.

  Clear the row used flags.
*/
  for (int i = 0 ; i < nRowsUsed ; i++) prob->unsetRowUsed(rowsUsed[i]) ;
/*
  Trim the array of substitution transforms and queue up objects for postsolve.
  Also groom the problem representation to remove explicit zeros.
*/
  if (nactions) {
#   if PRESOLVE_SUMMARY > 0
    std::cout << "NSUBSTS: " << nactions << std::endl ;
#   endif
    next = new subst_constraint_action(nactions,
				   CoinCopyOfArray(actions,nactions),next) ;
    next = drop_zero_coefficients_action::presolve(prob,zerocols,
    						   nzerocols, next) ;
#   if PRESOLVE_CONSISTENCY > 0
    presolve_links_ok(prob) ;
    presolve_consistent(prob) ;
#   endif
  }

  deleteAction(actions,action*) ;
  delete [] x_to_y ;
  delete [] zerocols ;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving subst_constraint_action::presolve, "
    << droppedRows << " rows, " << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
/*
  Postsolve: replace the original row bounds.

  The catch here is that each constraint was an equality in the presolved
  problem, with a logical s<i> that had l<i> = u<i> = 0. We're about to
  convert the equality back to an inequality. One row bound will go to
  infinity, as will one of the bounds of the logical. We may need to patch the
  basis. The logical for a <= constraint cannot be NBUB, and the logical for a
  >= constraint cannot be NBLB.
*/
void remove_dual_action::postsolve (CoinPostsolveMatrix *prob) const
{
  const action *const &bndRecords = actions_ ;
  const int &numRecs = nactions_ ;

  double *&rlo = prob->rlo_ ;
  double *&rup = prob->rup_ ;
  unsigned char *&rowstat = prob->rowstat_ ;

# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering remove_dual_action::postsolve, " << numRecs
    << " bounds to restore." << std::endl ;
# endif
  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# endif

/*
  For each record, restore the row bounds. If we have status arrays, check
  the status of the logical and adjust if necessary.

  In spite of the fact that the status array is an unsigned char array,
  we still need to use getRowStatus to make sure we're only looking at the
  bottom three bits. Why is this an issue? Because the status array isn't
  necessarily cleared to zeros, and setRowStatus carefully changes only
  the bottom three bits!
*/
  for (int k = 0 ; k < numRecs ; k++) {
    const action &bndRec = bndRecords[k] ;
    const int &i = bndRec.ndx_ ;
    const double &rloi = bndRec.rlo_ ;
    const double &rupi = bndRec.rup_ ;

#   if PRESOLVE_DEBUG > 1
    std::cout << "NDUAL(eq): row(" << i << ")" ;
    if (rlo[i] != rloi) std::cout << " LB " << rlo[i] << " -> " << rloi ;
    if (rup[i] != rupi) std::cout << " UB " << rup[i] << " -> " << rupi ;
#   endif

    rlo[i] = rloi ;
    rup[i] = rupi ;
    if (rowstat) {
      unsigned char stati = prob->getRowStatus(i) ;
      if (stati == CoinPresolveMatrix::atUpperBound) {
        if (rloi <= -PRESOLVE_INF) {
	  rowstat[i] = CoinPresolveMatrix::atLowerBound ;
#         if PRESOLVE_DEBUG > 1
	  std::cout
	    << ", status forced to "
	    << statusName(static_cast<CoinPresolveMatrix::Status>(rowstat[i])) ;
#         endif
	}
      } else if (stati == CoinPresolveMatrix::atLowerBound) {
        if (rupi >= PRESOLVE_INF) {
	  rowstat[i] = CoinPresolveMatrix::atUpperBound ;
#         if PRESOLVE_DEBUG > 1
	  std::cout
	    << ", status forced to "
	    << statusName(static_cast<CoinPresolveMatrix::Status>(rowstat[i])) ;
#         endif
	}
      }
#     if PRESOLVE_DEBUG > 2
        else if (stati == CoinPresolveMatrix::basic) {
        std::cout << ", status is basic." ;
      } else if (stati == CoinPresolveMatrix::isFree) {
        std::cout << ", status is free?!" ;
      } else {
        unsigned int tmp = static_cast<unsigned int>(stati) ;
        std::cout << ", status is invalid (" << tmp << ")!" ;
      }
#     endif
    }
#   if PRESOLVE_DEBUG > 1
    std::cout << "." << std::endl ;
#   endif
  }

# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving remove_dual_action::postsolve." << std::endl ;
# endif
# endif

  return ;
}
/*
  This routine empties the columns for the list of fixed variables passed in
  (fcols, nfcols). As each coefficient a<ij> is set to 0, rlo<i> and rup<i>
  are adjusted accordingly. Note, however, that c<j> is not considered to be
  removed from the objective until column j is physically removed from the
  matrix (drop_empty_cols_action), so the correction to the objective is
  adjusted there.

  If a column solution is available, row activity (acts_) is adjusted.
  remove_fixed_action implicitly assumes that the value of the variable has
  already been forced within bounds. If this isn't true, the correction to
  acts_ will be wrong. See make_fixed_action if you need to force the value
  within bounds first.
*/
const remove_fixed_action*
  remove_fixed_action::presolve (CoinPresolveMatrix *prob,
				 int *fcols,
				 int nfcols,
				 const CoinPresolveAction *next)
{
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt	= prob->mcstrt_;
  int *hincol		= prob->hincol_;

  double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  CoinBigIndex *mrstrt	= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;

  double *clo	= prob->clo_;
  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;
  double *sol	= prob->sol_;
  double *acts	= prob->acts_;

  presolvehlink *clink = prob->clink_;
  presolvehlink *rlink = prob->rlink_;

  action *actions 	= new  action[nfcols+1];

# if PRESOLVE_DEBUG
  std::cout << "Entering remove_fixed_action::presolve." << std::endl ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

/*
  Scan columns to be removed and total up the number of coefficients.
*/
  int estsize=0;
  int ckc;
  for (ckc = 0 ; ckc < nfcols ; ckc++) {
    int j = fcols[ckc];
    estsize += hincol[j];
  }
// Allocate arrays to hold coefficients and associated row indices
  double * els_action = new double[estsize];
  int * rows_action = new int[estsize];
  int actsize=0;
  // faster to do all deletes in row copy at once
  int nrows		= prob->nrows_;
  CoinBigIndex * rstrt = new int[nrows+1];
  CoinZeroN(rstrt,nrows);
/*
  Open a loop to excise each column a<j>. The first thing to do is load the
  action entry with the index j, the value of x<j>, and the number of
  entries in a<j>. After we walk the column and tweak the row-major
  representation, we'll simply claim this column is empty by setting
  hincol[j] = 0.
*/
  for (ckc = 0 ; ckc < nfcols ; ckc++) {
    int j = fcols[ckc];
    double solj = clo[j];
    CoinBigIndex kcs = mcstrt[j];
    CoinBigIndex kce = kcs + hincol[j];
    CoinBigIndex k;

    { action &f = actions[ckc];
      f.col = j;
      f.sol = solj;
      f.start = actsize;
    }
/*
  Now walk a<j>. For each row i with a coefficient a<ij> != 0:
    * save the coefficient and row index,
    * substitute the value of x<j>, adjusting the row bounds and lhs value
      accordingly, then
    * delete a<ij> from the row-major representation.
    * Finally: mark the row as changed and add it to the list of rows to be
	processed next. Then, for each remaining column in the row, do the same.
	(It makes sense to put the columns on the `to be processed' list, but
	I'm wondering about the wisdom of marking them as changed.
	-- lh, 040824 -- )
*/
    for (k = kcs ; k < kce ; k++) {
      int row = hrow[k];
      double coeff = colels[k];
     
      els_action[actsize]=coeff;
      rstrt[row]++; // increase counts
      rows_action[actsize++]=row;

      // Avoid reducing finite infinity.
      if (-PRESOLVE_INF < rlo[row])
	rlo[row] -= solj*coeff;
      if (rup[row] < PRESOLVE_INF)
	rup[row] -= solj*coeff;
      if (sol) {
	acts[row] -= solj*coeff;
      }
#define TRY2
#ifndef TRY2
      presolve_delete_from_row(row,j,mrstrt,hinrow,hcol,rowels);
      if (hinrow[row] == 0)
      { PRESOLVE_REMOVE_LINK(rlink,row) ; }

      // mark unless already marked
      if (!prob->rowChanged(row)) {
	prob->addRow(row);
	CoinBigIndex krs = mrstrt[row];
	CoinBigIndex kre = krs + hinrow[row];
	for (CoinBigIndex k=krs; k<kre; k++) {
	  int jcol = hcol[k];
	  prob->addCol(jcol);
	}
      }
#endif
    }
/*
  Remove the column's link from the linked list of columns, and declare
  it empty in the column-major representation. Link removal must execute
  even if the column is already of length 0 when it arrives.
*/
    PRESOLVE_REMOVE_LINK(clink, j);
    hincol[j] = 0;
  }
/*
  Set the actual end of the coefficient and row index arrays.
*/
  actions[nfcols].start=actsize;
# if PRESOLVE_SUMMARY
  printf("NFIXED:  %d", nfcols);
  if (estsize-actsize > 0)
  { printf(", overalloc %d",estsize-actsize) ; }
  printf("\n") ;
# endif
  // Now get columns by row
  int * column = new int[actsize];
  int nel=0;
  int iRow;
  for (iRow=0;iRow<nrows;iRow++) {
    int n=rstrt[iRow];
    rstrt[iRow]=nel;
    nel += n;
  }
  rstrt[nrows]=nel;
  for (ckc = 0 ; ckc < nfcols ; ckc++) {
    int kcs = actions[ckc].start;
    int j=actions[ckc].col;
    int kce;
    if (ckc<nfcols-1)
      kce = actions[ckc+1].start;
    else
      kce = actsize;
    for (int k=kcs;k<kce;k++) {
      int iRow = rows_action[k];
      CoinBigIndex put = rstrt[iRow];
      rstrt[iRow]++;
      column[put]=j;
    }
  }
  // Now do rows
  int ncols		= prob->ncols_;
  char * mark = new char[ncols];
  memset(mark,0,ncols);
  // rstrts are now one out i.e. rstrt[0] is end of row 0
  nel=0;
#ifdef TRY2
  for (iRow=0;iRow<nrows;iRow++) {
    int k;
    for (k=nel;k<rstrt[iRow];k++) {
      mark[column[k]]=1;
    }
    presolve_delete_many_from_major(iRow,mark,mrstrt,hinrow,hcol,rowels);
#ifndef NDEBUG
    for (k=nel;k<rstrt[iRow];k++) {
      assert(mark[column[k]]==0);
    }
#endif
    if (hinrow[iRow] == 0)
      {
        PRESOLVE_REMOVE_LINK(rlink,iRow) ;
      }
    // mark unless already marked
    if (!prob->rowChanged(iRow)) {
      prob->addRow(iRow);
      CoinBigIndex krs = mrstrt[iRow];
      CoinBigIndex kre = krs + hinrow[iRow];
      for (CoinBigIndex k=krs; k<kre; k++) {
        int jcol = hcol[k];
        prob->addCol(jcol);
      }
    }
    nel=rstrt[iRow];
  }
#endif
  delete [] mark;
  delete [] column;
  delete [] rstrt;

# if PRESOLVE_DEBUG
  presolve_check_sol(prob) ;
  std::cout << "Leaving remove_fixed_action::presolve." << std::endl ;
# endif

/*
  Create the postsolve object, link it at the head of the list of postsolve
  objects, and return a pointer.
*/
  return (new remove_fixed_action(nfcols,
				  actions,els_action,rows_action,next));
}
/*
  It is always the case that one of the variables of a doubleton is, or
  can be made, implied free, but neither will necessarily be a singleton.
  Since in the case of a doubleton the number of non-zero entries will never
  increase if one is eliminated, it makes sense to always eliminate them.

  The col rep and row rep must be consistent.
 */
const CoinPresolveAction
  *doubleton_action::presolve (CoinPresolveMatrix *prob,
			      const CoinPresolveAction *next)

{
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering doubleton_action::presolve; considering "
    << prob->numberRowsToDo_ << " rows." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

  const int n = prob->ncols_ ;
  const int m = prob->nrows_ ;

/*
  Unpack column-major and row-major representations, along with rim vectors.
*/
  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  double *colCoeffs = prob->colels_ ;
  int *rowIndices = prob->hrow_ ;
  presolvehlink *clink = prob->clink_ ;

  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;

  CoinBigIndex *rowStarts = prob->mrstrt_ ;
  int *rowLengths = prob->hinrow_ ;
  double *rowCoeffs = prob->rowels_ ;
  int *colIndices = prob->hcol_ ;
  presolvehlink *rlink = prob->rlink_ ;

  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;

  const unsigned char *integerType = prob->integerType_ ;

  double *cost = prob->cost_ ;

  int numberLook = prob->numberRowsToDo_ ;
  int *look = prob->rowsToDo_ ;
  const double ztolzb	= prob->ztolzb_ ;
  const double ztolzero = 1.0e-12 ;

  action *actions = new action [m] ;
  int nactions = 0 ;

/*
  zeros will hold columns that should be groomed to remove explicit zeros when
  we're finished.

  fixed will hold columns that have ended up as fixed variables.
*/
  int *zeros = prob->usefulColumnInt_ ;
  int nzeros = 0 ;

  int *fixed = zeros+n ;
  int nfixed = 0 ;

  unsigned char *rowstat = prob->rowstat_ ;
  double *acts	= prob->acts_ ;
  double *sol = prob->sol_ ;
/*
  More like `ignore infeasibility'.
*/
  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;

/*
  Open the main loop to scan for doubleton candidates.
*/
  for (int iLook = 0 ; iLook < numberLook ; iLook++) {
    const int tgtrow = look[iLook] ;
/*
  We need an equality with two coefficients. Avoid isolated constraints, lest
  both variables vanish.

  Failure of the assert indicates that the row- and column-major
  representations are out of sync.
*/
    if ((rowLengths[tgtrow] != 2) ||
        (fabs(rup[tgtrow]-rlo[tgtrow]) > ZTOLDP)) continue ;

    const CoinBigIndex krs = rowStarts[tgtrow] ;
    int tgtcolx = colIndices[krs] ;
    int tgtcoly = colIndices[krs+1] ;

    PRESOLVEASSERT(colLengths[tgtcolx] > 0 || colLengths[tgtcoly] > 0) ;
    if (colLengths[tgtcolx] == 1 && colLengths[tgtcoly] == 1) continue ;
/*
  Avoid prohibited columns and fixed columns. Make sure the coefficients are
  nonzero.
  JJF - test should allow one to be prohibited as long as you leave that
  one.  I modified earlier code but hope I have got this right.
*/
    if (prob->colProhibited(tgtcolx) && prob->colProhibited(tgtcoly))
      continue ;
    if (fabs(rowCoeffs[krs]) < ZTOLDP2 || fabs(rowCoeffs[krs+1]) < ZTOLDP2)
      continue ;
    if ((fabs(cup[tgtcolx]-clo[tgtcolx]) < ZTOLDP) ||
	(fabs(cup[tgtcoly]-clo[tgtcoly]) < ZTOLDP)) continue ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  row " << tgtrow << " colx " << tgtcolx << " coly " << tgtcoly
      << " passes preliminary eval." << std::endl ;
#   endif

/*
  Find this row in each column. The indices are not const because we may flip
  them below, once we decide which column will be eliminated.
*/
    CoinBigIndex krowx =
        presolve_find_row(tgtrow,colStarts[tgtcolx],
			  colStarts[tgtcolx]+colLengths[tgtcolx],rowIndices) ;
    double coeffx = colCoeffs[krowx] ;
    CoinBigIndex krowy =
        presolve_find_row(tgtrow,colStarts[tgtcoly],
			  colStarts[tgtcoly]+colLengths[tgtcoly],rowIndices) ;
    double coeffy = colCoeffs[krowy] ;
    const double rhs = rlo[tgtrow] ;
/*
  Avoid obscuring a requirement for integrality.

  If only one variable is integer, keep it and substitute for the continuous
  variable.

  If both are integer, substitute only for the forms x = k*y (k integral
  and non-empty intersection on bounds on x) or x = 1-y, where both x and
  y are binary.

  flag bits for integerStatus: 0x01: x integer;  0x02: y integer

  This bit of code works because 0 is continuous, 1 is integer. Make sure
  that's true.
*/
    assert((integerType[tgtcolx] == 0) || (integerType[tgtcolx] == 1)) ;
    assert((integerType[tgtcoly] == 0) || (integerType[tgtcoly] == 1)) ;

    int integerX = integerType[tgtcolx];
    int integerY = integerType[tgtcoly];
    /* if one prohibited then treat that as integer. This
       may be pessimistic - but will catch SOS etc */
    if (prob->colProhibited2(tgtcolx))
      integerX=1;
    if (prob->colProhibited2(tgtcoly))
      integerY=1;
    int integerStatus = (integerY<<1)|integerX ;

    if (integerStatus == 3) {
      int good = 0 ;
      double rhs2 = rhs ;
      if (coeffx < 0.0) {
	coeffx = -coeffx ;
	rhs2 += 1 ;
      }
      if ((cup[tgtcolx] == 1.0) && (clo[tgtcolx] == 0.0) &&
	  (fabs(coeffx-1.0) < 1.0e-7) && !prob->colProhibited2(tgtcoly))
	good = 1 ;
      if (coeffy < 0.0) {
	coeffy = -coeffy ;
	rhs2 += 1 ;
      }
      if ((cup[tgtcoly] == 1.0) && (clo[tgtcoly] == 0.0) &&
	  (fabs(coeffy-1.0) < 1.0e-7) && !prob->colProhibited2(tgtcolx))
	good |= 2 ;
      if (!(good == 3 && fabs(rhs2-1.0) < 1.0e-7))
	integerStatus = -1 ;
/*
  Not x+y = 1. Try for ax+by = 0
*/
      if (integerStatus < 0 && rhs == 0.0) {
	coeffx = colCoeffs[krowx] ;
	coeffy = colCoeffs[krowy] ;
	double ratio ;
	bool swap = false ;
	if (fabs(coeffx) > fabs(coeffy)) {
	  ratio = coeffx/coeffy ;
	} else {
	  ratio = coeffy/coeffx ;
	  swap = true ;
	}
	ratio = fabs(ratio) ;
	if (fabs(ratio-floor(ratio+0.5)) < ztolzero) {
	  integerStatus = swap ? 2 : 1 ;
	}
      }
/*
  One last try --- just require an integral substitution formula.

  But ax+by = 0 above is a subset of ax+by = c below and should pass the
  test below. For that matter, so will x+y = 1. Why separate special cases
  above?  -- lh, 121106 --
*/
      if (integerStatus < 0) {
	bool canDo = false ;
	coeffx = colCoeffs[krowx] ;
	coeffy = colCoeffs[krowy] ;
	double ratio ;
	bool swap = false ;
	double rhsRatio ;
	if (fabs(coeffx) > fabs(coeffy)) {
	  ratio = coeffx/coeffy ;
	  rhsRatio = rhs/coeffx ;
	} else {
	  ratio = coeffy/coeffx ;
	  rhsRatio = rhs/coeffy ;
	  swap = true ;
	}
	ratio = fabs(ratio) ;
	if (fabs(ratio-floor(ratio+0.5)) < ztolzero) {
	  // possible
	  integerStatus = swap ? 2 : 1 ;
	  // but check rhs
	  if (rhsRatio==floor(rhsRatio+0.5))
	    canDo=true ;
	}
#       ifdef COIN_DEVELOP2
	if (canDo)
	  printf("Good CoinPresolveDoubleton tgtcolx %d (%g and bounds %g %g) tgtcoly %d (%g and bound %g %g) - rhs %g\n",
		 tgtcolx,colCoeffs[krowx],clo[tgtcolx],cup[tgtcolx],
		 tgtcoly,colCoeffs[krowy],clo[tgtcoly],cup[tgtcoly],rhs) ;
	else
	printf("Bad CoinPresolveDoubleton tgtcolx %d (%g) tgtcoly %d (%g) - rhs %g\n",
	       tgtcolx,colCoeffs[krowx],tgtcoly,colCoeffs[krowy],rhs) ;
#       endif
	if (!canDo)
	  continue ;
      }
    }
/*
  We've resolved integrality concerns. If we concluded that we need to
  switch the roles of x and y because of integrality, do that now. If both
  variables are continuous, we may still want to swap for numeric stability.
  Eliminate the variable with the larger coefficient.
*/
    if (integerStatus == 2) {
      CoinSwap(tgtcoly,tgtcolx) ;
      CoinSwap(krowy,krowx) ;
    } else if (integerStatus == 0) {
      if (fabs(colCoeffs[krowy]) < fabs(colCoeffs[krowx])) {
	CoinSwap(tgtcoly,tgtcolx) ;
	CoinSwap(krowy,krowx) ;
      }
    }
/*
  Don't eliminate y just yet if it's entangled in a singleton row (we want to
  capture that explicit bound in a column bound).
*/
    const CoinBigIndex kcsy = colStarts[tgtcoly] ;
    const CoinBigIndex kcey = kcsy+colLengths[tgtcoly] ;
    bool singletonRow = false ;
    for (CoinBigIndex kcol = kcsy ; kcol < kcey ; kcol++) {
      if (rowLengths[rowIndices[kcol]] == 1) {
        singletonRow = true ;
	break ;
      }
    }
    // skip if y prohibited
    if (singletonRow || prob->colProhibited2(tgtcoly)) continue ;

    coeffx = colCoeffs[krowx] ;
    coeffy = colCoeffs[krowy] ;
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  doubleton row " << tgtrow << ", keep x(" << tgtcolx
      << ") elim x(" << tgtcoly << ")." << std::endl ;
#   endif
    PRESOLVE_DETAIL_PRINT(printf("pre_doubleton %dC %dC %dR E\n",
				 tgtcoly,tgtcolx,tgtrow)) ;
/*
  Capture the existing columns and other information before we start to modify
  the constraint system. Save the shorter column.
*/
    action *s = &actions[nactions] ;
    nactions++ ;
    s->row = tgtrow ;
    s->icolx = tgtcolx ;
    s->clox = clo[tgtcolx] ;
    s->cupx = cup[tgtcolx] ;
    s->costx = cost[tgtcolx] ;
    s->icoly = tgtcoly ;
    s->costy = cost[tgtcoly] ;
    s->rlo = rlo[tgtrow] ;
    s->coeffx = coeffx ;
    s->coeffy = coeffy ;
    s->ncolx = colLengths[tgtcolx] ;
    s->ncoly = colLengths[tgtcoly] ;
    if (s->ncoly < s->ncolx) {
      s->colel	= presolve_dupmajor(colCoeffs,rowIndices,colLengths[tgtcoly],
				    colStarts[tgtcoly],tgtrow) ;
      s->ncolx = 0 ;
    } else {
      s->colel = presolve_dupmajor(colCoeffs,rowIndices,colLengths[tgtcolx],
				   colStarts[tgtcolx],tgtrow) ;
      s->ncoly = 0 ;
    }
/*
  Move finite bound information from y to x, so that y is implied free.
    a x + b y = c
    l1 <= x <= u1
    l2 <= y <= u2
   
    l2 <= (c - a x) / b <= u2
    b/-a > 0 ==> (b l2 - c) / -a <= x <= (b u2 - c) / -a
    b/-a < 0 ==> (b u2 - c) / -a <= x <= (b l2 - c) / -a
*/
    {
      double lo1 = -PRESOLVE_INF ;
      double up1 = PRESOLVE_INF ;
      
      if (-PRESOLVE_INF < clo[tgtcoly]) {
	if (coeffx*coeffy < 0)
	  lo1 = (coeffy*clo[tgtcoly]-rhs)/-coeffx ;
	else 
	  up1 = (coeffy*clo[tgtcoly]-rhs)/-coeffx ;
      }
      
      if (cup[tgtcoly] < PRESOLVE_INF) {
	if (coeffx*coeffy < 0)
	  up1 = (coeffy*cup[tgtcoly]-rhs)/-coeffx ;
	else 
	  lo1 = (coeffy*cup[tgtcoly]-rhs)/-coeffx ;
      }
/*
  Don't forget the objective coefficient.
    costy y = costy ((c - a x) / b) = (costy c)/b + x (costy -a)/b
*/
      cost[tgtcolx] += (cost[tgtcoly]*-coeffx)/coeffy ;
      prob->change_bias((cost[tgtcoly]*rhs)/coeffy) ;
/*
  The transfer of bounds could make x infeasible. Patch it up if the problem
  is minor or if the user was so incautious as to instruct us to ignore it.
  Prefer an integer value if there's one nearby. If there's nothing to be
  done, break out of the main loop.
*/
      {
	double lo2 = CoinMax(clo[tgtcolx],lo1) ;
	double up2 = CoinMin(cup[tgtcolx],up1) ;
	if (lo2 > up2) {
	  if (lo2 <= up2+prob->feasibilityTolerance_ || fixInfeasibility) {
	    double nearest = floor(lo2+0.5) ;
	    if (fabs(nearest-lo2) < 2.0*prob->feasibilityTolerance_) {
	      lo2 = nearest ;
	      up2 = nearest ;
	    } else {
	      lo2 = up2 ;
	    }
	  } else {
	    prob->status_ |= 1 ;
	    prob->messageHandler()->message(COIN_PRESOLVE_COLINFEAS,
	    				    prob->messages())
		 << tgtcolx << lo2 << up2 << CoinMessageEol ;
	    break ;
	  }
	}
#       if PRESOLVE_DEBUG > 2
	std::cout
	  << "  x(" << tgtcolx << ") lb " << clo[tgtcolx] << " --> " << lo2
	  << ", ub " << cup[tgtcolx] << " --> " << up2 << std::endl ;
#       endif
	clo[tgtcolx] = lo2 ;
	cup[tgtcolx] = up2 ;
/*
  Do we have a solution to maintain? If so, take a stab at it. If x ends up at
  bound, prefer to set it nonbasic, but if we're short of basic variables
  after eliminating y and the logical for the row, make it basic.

  This code will snap the value of x to bound if it's within the primal
  feasibility tolerance.
*/
	if (rowstat && sol) {
	  int numberBasic = 0 ;
	  double movement = 0 ;
	  if (prob->columnIsBasic(tgtcolx))
	    numberBasic++ ;
	  if (prob->columnIsBasic(tgtcoly))
	    numberBasic++ ;
	  if (prob->rowIsBasic(tgtrow))
	    numberBasic++ ;
	  if (sol[tgtcolx] <= lo2+ztolzb) {
	    movement = lo2-sol[tgtcolx] ;
	    sol[tgtcolx] = lo2 ;
	    prob->setColumnStatus(tgtcolx,
	    			  CoinPrePostsolveMatrix::atLowerBound) ;
	  } else if (sol[tgtcolx] >= up2-ztolzb) {
	    movement = up2-sol[tgtcolx] ;
	    sol[tgtcolx] = up2 ;
	    prob->setColumnStatus(tgtcolx,
	    			  CoinPrePostsolveMatrix::atUpperBound) ;
	  }
	  if (numberBasic > 1)
	    prob->setColumnStatus(tgtcolx,CoinPrePostsolveMatrix::basic) ;
/*
  We need to compensate if x was forced to move. Beyond that, even if x
  didn't move, we've forced y = (c-ax)/b, and that might not have been
  true before. So even if x didn't move, y may have moved. Note that the
  constant term c/b is subtracted out as the constraints are modified,
  so we don't include it when calculating movement for y.
*/
	  if (movement) { 
	    const CoinBigIndex kkcsx = colStarts[tgtcolx] ;
	    const CoinBigIndex kkcex = kkcsx+colLengths[tgtcolx] ;
	    for (CoinBigIndex kcol = kkcsx ; kcol < kkcex ; kcol++) {
	      int row = rowIndices[kcol] ;
	      if (rowLengths[row])
		acts[row] += movement*colCoeffs[kcol] ;
	    }
	  }
	  movement = ((-coeffx*sol[tgtcolx])/coeffy)-sol[tgtcoly] ;
	  if (movement) {
	    const CoinBigIndex kkcsy = colStarts[tgtcoly] ;
	    const CoinBigIndex kkcey = kkcsy+colLengths[tgtcoly] ;
	    for (CoinBigIndex kcol = kkcsy ; kcol < kkcey ; kcol++) {
	      int row = rowIndices[kcol] ;
	      if (rowLengths[row])
		acts[row] += movement*colCoeffs[kcol] ;
	    }
	  }
	}
	if (lo2 == up2)
	  fixed[nfixed++] = tgtcolx ;
      }
    }
/*
  We're done transferring bounds from y to x, and we've patched up the
  solution if one existed to patch. One last thing to do before we eliminate
  column y and the doubleton row: put column x and the entangled rows on
  the lists of columns and rows to look at in the next round of transforms.
*/
    {
      prob->addCol(tgtcolx) ;
      const CoinBigIndex kkcsy = colStarts[tgtcoly] ;
      const CoinBigIndex kkcey = kkcsy+colLengths[tgtcoly] ;
      for (CoinBigIndex kcol = kkcsy ; kcol < kkcey ; kcol++) {
	int row = rowIndices[kcol] ;
	prob->addRow(row) ;
      }
      const CoinBigIndex kkcsx = colStarts[tgtcolx] ;
      const CoinBigIndex kkcex = kkcsx+colLengths[tgtcolx] ;
      for (CoinBigIndex kcol = kkcsx ; kcol < kkcex ; kcol++) {
	int row = rowIndices[kcol] ;
	prob->addRow(row) ;
      }
    }

/*
  Empty tgtrow in the column-major matrix.  Deleting the coefficient for
  (tgtrow,tgtcoly) is a bit costly (given that we're about to drop the whole
  column), but saves the trouble of checking for it in elim_doubleton.
*/
    presolve_delete_from_col(tgtrow,tgtcolx,
    			     colStarts,colLengths,rowIndices,colCoeffs) ;
    presolve_delete_from_col(tgtrow,tgtcoly,
    			     colStarts,colLengths,rowIndices,colCoeffs) ;
/*
  Drop tgtrow in the row-major representation: set the length to 0
  and reclaim the major vector space in bulk storage.
*/
    rowLengths[tgtrow] = 0 ;
    PRESOLVE_REMOVE_LINK(rlink,tgtrow) ;

/*
  Transfer the colx factors to coly. This modifies coefficients in column x
  as it removes coefficients in column y.
*/
    bool no_mem = elim_doubleton("ELIMD",
				 colStarts,rlo,rup,colCoeffs,
				 rowIndices,colIndices,rowLengths,colLengths,
				 clink,n, 
				 rowStarts,rowCoeffs,
				 -coeffx/coeffy,
				 rhs/coeffy,
				 tgtrow,tgtcolx,tgtcoly) ;
    if (no_mem) 
      throwCoinError("out of memory","doubleton_action::presolve") ;

/*
  Eliminate coly entirely from the col rep. We'll want to groom colx to remove
  explicit zeros.
*/
    colLengths[tgtcoly] = 0 ;
    PRESOLVE_REMOVE_LINK(clink, tgtcoly) ;
    cost[tgtcoly] = 0.0 ;

    rlo[tgtrow] = 0.0 ;
    rup[tgtrow] = 0.0 ;

    zeros[nzeros++] = tgtcolx ;

#   if PRESOLVE_CONSISTENCY > 0
    presolve_consistent(prob) ;
    presolve_links_ok(prob) ;
#   endif
  }
/*
  Tidy up the collected actions and clean up explicit zeros and fixed
  variables. Don't bother unless we're feasible (status of 0).
*/
  if (nactions && !prob->status_) {
#   if PRESOLVE_SUMMARY > 0
    printf("NDOUBLETONS:  %d\n", nactions) ;
#   endif
    action *actions1 = new action[nactions] ;
    CoinMemcpyN(actions, nactions, actions1) ;

    next = new doubleton_action(nactions, actions1, next) ;

    if (nzeros)
      next = drop_zero_coefficients_action::presolve(prob, zeros, nzeros, next) ;
    if (nfixed)
      next = remove_fixed_action::presolve(prob, fixed, nfixed, next) ;
  }

  deleteAction(actions,action*) ;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving doubleton_action::presolve, " << droppedRows << " rows, "
    << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
/*
  Transfer the cost coefficient of a column singleton in an equality to the
  cost coefficients of the remaining variables. Suppose x<s> is the singleton
  and x<t> is some other variable. For movement delta<t>, there must be
  compensating change delta<s> = -(a<it>/a<is>)delta<t>. Substituting in the
  objective, c<s>delta<s> + c<t>delta<t> becomes
    c<s>(-a<it>/a<is>)delta<t> + c<t>delta<t>
    (c<t> - c<s>(a<it>/a<is>))delta<t>
  This is transform (A) below.
*/
void transferCosts (CoinPresolveMatrix *prob)
{
    double *colels = prob->colels_ ;
    int *hrow = prob->hrow_ ;
    CoinBigIndex *mcstrt = prob->mcstrt_ ;
    int *hincol = prob->hincol_ ;

    double *rowels = prob->rowels_ ;
    int *hcol = prob->hcol_ ;
    CoinBigIndex *mrstrt = prob->mrstrt_ ;
    int *hinrow = prob->hinrow_ ;

    double *rlo = prob->rlo_ ;
    double *rup = prob->rup_ ;
    double *clo = prob->clo_ ;
    double *cup = prob->cup_ ;
    int ncols = prob->ncols_ ;
    double *cost	= prob->cost_ ;
    unsigned char *integerType = prob->integerType_ ;
    double bias = prob->dobias_ ;

# if PRESOLVE_DEBUG > 0
    std::cout << "Entering transferCosts." << std::endl ;
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# endif

    int numberIntegers = 0 ;
    for (int icol = 0 ; icol < ncols ; icol++) {
        if (integerType[icol]) numberIntegers++ ;
    }
    /*
      For unfixed column singletons in equalities, calculate and install transform
      (A) described in the comments at the head of the method.
    */
    int nchanged = 0 ;
    for (int js = 0 ; js < ncols ; js++) {
        if (cost[js] && hincol[js] == 1 && cup[js] > clo[js]) {
            const CoinBigIndex &jsstrt = mcstrt[js] ;
            const int &i = hrow[jsstrt];
            if (rlo[i] == rup[i]) {
                const double ratio = cost[js]/colels[jsstrt];
                bias += rlo[i]*ratio ;
                const CoinBigIndex &istrt = mrstrt[i] ;
                const CoinBigIndex iend = istrt+hinrow[i] ;
                for (CoinBigIndex jj = istrt ; jj < iend ; jj++) {
                    int j = hcol[jj] ;
                    double aij = rowels[jj] ;
                    cost[j] -= ratio*aij ;
                }
                cost[js] = 0.0 ;
                nchanged++ ;
            }
        }
    }
# if PRESOLVE_DEBUG > 0
    if (nchanged)
        std::cout
                << "  transferred costs for " << nchanged << " singleton columns."
                << std::endl ;

    int nPasses = 0 ;
# endif
    /*
      We don't really need a singleton column to do this trick, just an equality.
      But if the column's not a singleton, it's only worth doing if we can move
      costs onto integer variables that start with costs of zero. Try and find some
      unfixed variable with a nonzero cost, that's involved in an equality where
      there are integer variables with costs of zero. If there's a net gain in the
      number of integer variables with costs (nThen > nNow), do the transform.
      One per column, please.
    */
    if (numberIntegers) {
        int changed = -1 ;
        while (changed) {
            changed = 0 ;
            for (int js = 0 ; js < ncols ; js++) {
                if (cost[js] && cup[js] > clo[js]) {
                    const CoinBigIndex &jsstrt = mcstrt[js] ;
                    const CoinBigIndex jsend = jsstrt+hincol[js] ;
                    for (CoinBigIndex ii = jsstrt ; ii < jsend ; ii++) {
                        const int &i = hrow[ii] ;
                        if (rlo[i] == rup[i]) {
                            int nNow = ((integerType[js])?1:0) ;
                            int nThen = 0 ;
                            const CoinBigIndex &istrt = mrstrt[i] ;
                            const CoinBigIndex iend = istrt+hinrow[i] ;
                            for (CoinBigIndex jj = istrt ; jj < iend ; jj++) {
                                int j = hcol[jj] ;
                                if (!cost[j] && integerType[j]) nThen++ ;
                            }
                            if (nThen > nNow) {
                                const double ratio = cost[js]/colels[jsstrt] ;
                                bias += rlo[i]*ratio ;
                                for (CoinBigIndex jj = istrt ; jj < iend ; jj++) {
                                    int j = hcol[jj] ;
                                    double aij = rowels[jj] ;
                                    cost[j] -= ratio*aij ;
                                }
                                cost[js] = 0.0 ;
                                changed++ ;
                                break ;
                            }
                        }
                    }
                }
            }
            if (changed) {
                nchanged += changed ;
#	if PRESOLVE_DEBUG > 0
                std::cout
                        << "    pass " << nPasses++ << " transferred costs to "
                        << changed << " integer variables." << std::endl ;
#       endif
            }
        }
    }
# if PRESOLVE_DEBUG > 0
    if (bias != prob->dobias_)
        std::cout << "  new bias " << bias << "." << std::endl ;
# endif
    prob->dobias_ = bias;

# if PRESOLVE_DEBUG > 0
    std::cout << "Leaving transferCosts." << std::endl ;
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# endif
}
/*
  This routine does the actual job of fixing one or more variables. The set
  of indices to be fixed is specified by nfcols and fcols. fix_to_lower
  specifies the bound where the variable(s) should be fixed. The other bound
  is preserved as part of the action and the bounds are set equal. Note that
  you don't get to specify the bound on a per-variable basis.

  If a primal solution is available, make_fixed_action will adjust the the
  row activity to compensate for forcing the variable within bounds. If the
  bounds are already equal, and the variable is within bounds, you should
  consider remove_fixed_action.
*/
const CoinPresolveAction*
make_fixed_action::presolve (CoinPresolveMatrix *prob,
                             int *fcols, int nfcols,
                             bool fix_to_lower,
                             const CoinPresolveAction *next)

{   double *clo	= prob->clo_;
    double *cup	= prob->cup_;
    double *csol	= prob->sol_;

    double *colels = prob->colels_;
    int *hrow	= prob->hrow_;
    CoinBigIndex *mcstrt	= prob->mcstrt_;
    int *hincol	= prob->hincol_;

    double *acts	= prob->acts_;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
    std::cout
            << "Entering make_fixed_action::presolve, fixed = " << nfcols << "."
            << std::endl ;
# endif
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# endif

    /*
      Shouldn't happen, but ...
    */
    if (nfcols <= 0) {
#   if PRESOLVE_DEBUG > 0
        std::cout
                << "make_fixed_action::presolve: useless call, " << nfcols
                << " to fix." << std::endl ;
#   endif
        return (next) ;
    }

    action *actions = new action[nfcols] ;

    /*
      Scan the set of indices specifying variables to be fixed. For each variable,
      stash the unused bound in the action and set the bounds equal. If the client
      has passed in a primal solution, update it if the value of the variable
      changes.
    */
    for (int ckc = 0 ; ckc < nfcols ; ckc++)
    {   int j = fcols[ckc] ;
        double movement = 0 ;

        action &f = actions[ckc] ;

        f.col = j ;
        if (fix_to_lower) {
            f.bound = cup[j];
            cup[j] = clo[j];
            if (csol) {
                movement = clo[j]-csol[j] ;
                csol[j] = clo[j] ;
            }
        } else {
            f.bound = clo[j];
            clo[j] = cup[j];
            if (csol) {
                movement = cup[j]-csol[j];
                csol[j] = cup[j];
            }
        }
        if (movement) {
            CoinBigIndex k;
            for (k = mcstrt[j] ; k < mcstrt[j]+hincol[j] ; k++) {
                int row = hrow[k];
                acts[row] += movement*colels[k];
            }
        }
    }
    /*
      Original comment:
      This is unusual in that the make_fixed_action transform contains within it
      a remove_fixed_action transform. Bad idea?

      Explanatory comment:
      Now that we've adjusted the bounds, time to create the postsolve action
      that will restore the original bounds. But wait! We're not done. By calling
      remove_fixed_action::presolve, we will (virtually) remove these variables
      from the model by substituting for the variable where it occurs and emptying
      the column. Cache the postsolve transform that will repopulate the column
      inside the postsolve transform for fixing the bounds.
    */
    if (nfcols > 0) {
        next = new make_fixed_action(nfcols,actions,fix_to_lower,
                                     remove_fixed_action::presolve(prob,fcols,nfcols,0),
                                     next) ;
    }

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
    presolve_check_sol(prob) ;
    presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
    std::cout << "Leaving make_fixed_action::presolve." << std::endl ;
# endif
# endif

    return (next) ;
}
const CoinPresolveAction *do_tighten_action::presolve(CoinPresolveMatrix *prob,
					       const CoinPresolveAction *next)
{
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *dcost	= prob->cost_;

  const unsigned char *integerType = prob->integerType_;

  int *fix_cols	= prob->usefulColumnInt_;
  int nfixup_cols	= 0;

  int nfixdown_cols	= ncols;

  int *useless_rows	= prob->usefulRowInt_;
  int nuseless_rows	= 0;
  
  action *actions	= new action [ncols];
  int nactions		= 0;

  int numberLook = prob->numberColsToDo_;
  int iLook;
  int * look = prob->colsToDo_;
  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering do_tighten_action::presolve; considering " << numberLook
    << " rows." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif


  // singleton columns are especially likely to be caught here
  for (iLook=0;iLook<numberLook;iLook++) {
    int j = look[iLook];
    // modify bounds if integer
    if (integerType[j]) {
      clo[j] = ceil(clo[j]-1.0e-12);
      cup[j] = floor(cup[j]+1.0e-12);
      if (clo[j]>cup[j]&&!fixInfeasibility) {
        // infeasible
	prob->status_|= 1;
	prob->messageHandler()->message(COIN_PRESOLVE_COLINFEAS,
					     prob->messages())
				 	       <<j
					       <<clo[j]
					       <<cup[j]
					       <<CoinMessageEol;
      }
    }
    if (dcost[j]==0.0) {
      int iflag=0; /* 1 - up is towards feasibility, -1 down is towards */
      int nonFree=0; // Number of non-free rows

      CoinBigIndex kcs = mcstrt[j];
      CoinBigIndex kce = kcs + hincol[j];

      // check constraints
      for (CoinBigIndex k=kcs; k<kce; ++k) {
	int i = hrow[k];
	double coeff = colels[k];
	double rlb = rlo[i];
	double rub = rup[i];

	if (-1.0e28 < rlb && rub < 1.0e28) {
	  // bounded - we lose
	  iflag=0;
	  break;
	} else if (-1.0e28 < rlb || rub < 1.0e28) {
	  nonFree++;
	}

	PRESOLVEASSERT(fabs(coeff) > ZTOLDP);

	// see what this particular row says
	// jflag == 1 ==> up is towards feasibility
	int jflag = (coeff > 0.0
		     ? (rub >  1.0e28 ? 1 : -1)
		     : (rlb < -1.0e28 ? 1 : -1));

	if (iflag) {
	  // check that it agrees with iflag.
	  if (iflag!=jflag) {
	    iflag=0;
	    break;
	  }
	} else {
	  // first row -- initialize iflag
	  iflag=jflag;
	}
      }
      // done checking constraints
      if (!nonFree)
	iflag=0; // all free anyway
      if (iflag) {
	if (iflag==1 && cup[j]<1.0e10) {
#if	PRESOLVE_DEBUG > 1
	  printf("TIGHTEN UP:  %d\n", j);
#endif
	  fix_cols[nfixup_cols++] = j;

	} else if (iflag==-1&&clo[j]>-1.0e10) {
	  // symmetric case
	  //mpre[j] = PRESOLVE_XUP;

#if	PRESOLVE_DEBUG > 1
	  printf("TIGHTEN DOWN:  %d\n", j);
#endif

	  fix_cols[--nfixdown_cols] = j;

	} else {
#if 0
	  static int limit;
	  static int which = atoi(getenv("WZ"));
	  if (which == -1)
	    ;
	  else if (limit != which) {
	    limit++;
	    continue;
	  } else
	    limit++;

	  printf("TIGHTEN STATS %d %g %g %d:  \n", j, clo[j], cup[j], integerType[j]); 
  double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  int *mrstrt		= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
	  for (CoinBigIndex k=kcs; k<kce; ++k) {
	    int irow = hrow[k];
	    CoinBigIndex krs = mrstrt[irow];
	    CoinBigIndex kre = krs + hinrow[irow];
	    printf("%d  %g %g %g:  ",
		   irow, rlo[irow], rup[irow], colels[irow]);
	    for (CoinBigIndex kk=krs; kk<kre; ++kk)
	      printf("%d(%g) ", hcol[kk], rowels[kk]);
	    printf("\n");
	  }
#endif

	  {
	    action *s = &actions[nactions];	  
	    nactions++;
	    s->col = j;
	    PRESOLVE_DETAIL_PRINT(printf("pre_tighten %dC E\n",j));
	    if (integerType[j]) {
	      assert (iflag==-1||iflag==1);
	      iflag *= 2; // say integer
	    }
	    s->direction = iflag;

	    s->rows =   new int[hincol[j]];
	    s->lbound = new double[hincol[j]];
	    s->ubound = new double[hincol[j]];
#if         PRESOLVE_DEBUG > 1
	    printf("TIGHTEN FREE:  %d   ", j);
#endif
	    int nr = 0;
            prob->addCol(j);
	    for (CoinBigIndex k=kcs; k<kce; ++k) {
	      int irow = hrow[k];
	      // ignore this if we've already made it useless
	      if (! (rlo[irow] == -PRESOLVE_INF && rup[irow] == PRESOLVE_INF)) {
		prob->addRow(irow);
		s->rows  [nr] = irow;
		s->lbound[nr] = rlo[irow];
		s->ubound[nr] = rup[irow];
		nr++;

		useless_rows[nuseless_rows++] = irow;

		rlo[irow] = -PRESOLVE_INF;
		rup[irow] = PRESOLVE_INF;

#if             PRESOLVE_DEBUG > 1
		printf("%d ", irow);
#endif
	      }
	    }
	    s->nrows = nr;

#if         PRESOLVE_DEBUG > 1
	    printf("\n");
#endif
	  }
	}
      }
    }
  }


#if	PRESOLVE_SUMMARY > 0
  if (nfixdown_cols<ncols || nfixup_cols || nuseless_rows) {
    printf("NTIGHTENED:  %d %d %d\n", ncols-nfixdown_cols, nfixup_cols, nuseless_rows);
  }
#endif

  if (nuseless_rows) {
    next = new do_tighten_action(nactions, CoinCopyOfArray(actions,nactions), next);

    next = useless_constraint_action::presolve(prob,
					       useless_rows, nuseless_rows,
					       next);
  }
  deleteAction(actions, action*);
  //delete[]useless_rows;

  if (nfixdown_cols<ncols) {
    int * fixdown_cols = fix_cols+nfixdown_cols; 
    nfixdown_cols = ncols-nfixdown_cols;
    next = make_fixed_action::presolve(prob, fixdown_cols, nfixdown_cols,
				       true,
				       next);
  }
  //delete[]fixdown_cols;

  if (nfixup_cols) {
    next = make_fixed_action::presolve(prob, fix_cols, nfixup_cols,
				       false,
				       next);
  }
  //delete[]fixup_cols;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving do_tighten_action::presolve, " << droppedRows << " rows, "
    << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next);
}
void do_tighten_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const action *const actions = actions_;
  const int nactions	= nactions_;

  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int *link		= prob->link_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *sol	= prob->sol_;
  double *acts	= prob->acts_;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  char *cdone	= prob->cdone_;
  char *rdone	= prob->rdone_;

  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;

# if PRESOLVE_DEBUG > 0
  std::cout << "Entering do_tighten_action::postsolve." << std::endl ;
# endif
# endif

  for (const action *f = &actions[nactions-1]; actions<=f; f--) {
    int jcol = f->col;
    int iflag = f->direction;
    int nr   = f->nrows;
    const int *rows = f->rows;
    const double *lbound = f->lbound;
    const double *ubound = f->ubound;

    PRESOLVEASSERT(prob->getColumnStatus(jcol)!=CoinPrePostsolveMatrix::basic);
    int i;
    for (i=0;i<nr; ++i) {
      int irow = rows[i];

      rlo[irow] = lbound[i];
      rup[irow] = ubound[i];

     PRESOLVEASSERT(prob->getRowStatus(irow)==CoinPrePostsolveMatrix::basic);
    }

    // We have just tightened the row bounds.
    // That means we'll have to compute a new value
    // for this variable that will satisfy everybody.
    // We are supposed to be in a position where this
    // is always possible.

    // Each constraint has exactly one bound.
    // The correction should only ever be forced to move in one direction.
    //    double orig_sol = sol[jcol];
    double correction = 0.0;
    
    int last_corrected = -1;
    CoinBigIndex k = mcstrt[jcol];
    int nk = hincol[jcol];
    for (i=0; i<nk; ++i) {
      int irow = hrow[k];
      double coeff = colels[k];
      k = link[k];
      double newrlo = rlo[irow];
      double newrup = rup[irow];
      double activity = acts[irow];

      if (activity + correction * coeff < newrlo) {
	// only one of these two should fire
	PRESOLVEASSERT( ! (activity + correction * coeff > newrup) );

	last_corrected = irow;

	// adjust to just meet newrlo (solve for correction)
	double new_correction = (newrlo - activity) / coeff;
	//adjust if integer
	if (iflag==-2||iflag==2) {
	  new_correction += sol[jcol];
	  if (fabs(floor(new_correction+0.5)-new_correction)>1.0e-4) {
	    new_correction = ceil(new_correction)-sol[jcol];
#ifdef COIN_DEVELOP
	    printf("integer postsolve changing correction from %g to %g - flag %d\n",
		   (newrlo-activity)/coeff,new_correction,iflag);
#endif
	  }
	}
	correction = new_correction;
      } else if (activity + correction * coeff > newrup) {
	last_corrected = irow;

	double new_correction = (newrup - activity) / coeff;
	//adjust if integer
	if (iflag==-2||iflag==2) {
	  new_correction += sol[jcol];
	  if (fabs(floor(new_correction+0.5)-new_correction)>1.0e-4) {
	    new_correction = ceil(new_correction)-sol[jcol];
#ifdef COIN_DEVELOP
	    printf("integer postsolve changing correction from %g to %g - flag %d\n",
		   (newrup-activity)/coeff,new_correction,iflag);
#endif
	  }
	}
	correction = new_correction;
      }
    }

    if (last_corrected>=0) {
      sol[jcol] += correction;
      
      // by construction, the last row corrected (if there was one)
      // must be at its bound, so it can be non-basic.
      // All other rows may not be at a bound (but may if the difference
      // is very small, causing a new correction by a tiny amount).
      
      // now adjust the activities
      k = mcstrt[jcol];
      for (i=0; i<nk; ++i) {
	int irow = hrow[k];
	double coeff = colels[k];
	k = link[k];
	//      double activity = acts[irow];

	acts[irow] += correction * coeff;
      }
      /*
        If the col happens to get pushed to its bound, we may as well leave
	it non-basic. Otherwise, set the status to basic.

	Why do we correct the row status only when the column is made basic?
	Need to look at preceding code.  -- lh, 110528 --
      */
      if (fabs(sol[jcol]-clo[jcol]) > ZTOLDP &&
          fabs(sol[jcol]-cup[jcol]) > ZTOLDP) {
        
        prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::basic);
	if (acts[last_corrected]-rlo[last_corrected] <
				rup[last_corrected]-acts[last_corrected])
	  prob->setRowStatus(last_corrected,
	  		     CoinPrePostsolveMatrix::atUpperBound);
	else
	  prob->setRowStatus(last_corrected,
	  		     CoinPrePostsolveMatrix::atLowerBound);
      }
    }
  }

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving do_tighten_action::postsolve." << std::endl ;
# endif
# endif
}
Exemple #15
0
/*
  Undo the substitutions from presolve and reintroduce the target constraint
  and column.
*/
void subst_constraint_action::postsolve(CoinPostsolveMatrix *prob) const
{

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering subst_constraint_action::postsolve, "
    << nactions_ << " constraints to process." << std::endl ;
# endif
  int ncols = prob->ncols_ ;
  char *cdone = prob->cdone_ ;
  char *rdone = prob->rdone_ ;
  const double ztolzb = prob->ztolzb_ ;

  presolve_check_threads(prob) ;
  presolve_check_free_list(prob) ;
  presolve_check_reduced_costs(prob) ;
  presolve_check_duals(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# endif

/*
  Unpack the column-major representation.
*/
  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  int *rowIndices = prob->hrow_ ;
  double *colCoeffs = prob->colels_ ;
/*
  Rim vectors, solution, reduced costs, duals, row activity.
*/
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;
  double *cost = prob->cost_ ;
  double *sol = prob->sol_ ;
  double *rcosts = prob->rcosts_ ;
  double *acts = prob->acts_ ;
  double *rowduals = prob->rowduals_ ;

  CoinBigIndex *link = prob->link_ ;
  CoinBigIndex &free_list = prob->free_list_ ;

  const double maxmin = prob->maxmin_ ;

  const action *const actions = actions_ ;
  const int nactions = nactions_ ;

/*
  Open the main loop to step through the postsolve objects.

  First activity is to unpack the postsolve object. We have the target
  column and row indices, the full target column, and complete copies of
  all entangled rows (column indices, coefficients, lower and upper bounds).
  There may be a vector of objective coefficients which we'll get to later.
*/
  for (const action *f = &actions[nactions-1] ; actions <= f ; f--) {
    const int tgtcol = f->col ;
    const int tgtrow = f->rowy ;

    const int tgtcol_len = f->nincol ;
    const double *tgtcol_coeffs = f->coeffxs ;

    const int *entngld_rows = f->rows ;
    const int *entngld_lens = f->ninrowxs ;
    const int *entngld_colndxs = f->rowcolsxs ;
    const double *entngld_colcoeffs = f->rowelsxs ;
    const double *entngld_rlos = f->rlos ;
    const double *entngld_rups = f->rups ;
    const double *costs = f->costsx ;

#   if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
#   if PRESOLVE_DEBUG > 1
    std::cout
      << "  reintroducing column x(" << tgtcol << ") and row " << tgtrow ;
      if (costs) std::cout << ", nonzero costs" ;
      std::cout << "." << std::endl ;
#   endif
/*
  We're about to reintroduce the target row and column; empty stubs should be
  present. All other rows should already be present.
*/
    PRESOLVEASSERT(cdone[tgtcol] == DROP_COL) ;
    PRESOLVEASSERT(colLengths[tgtcol] == 0) ;
    PRESOLVEASSERT(rdone[tgtrow] == DROP_ROW) ;
    for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) {
      if (entngld_rows[cndx] != tgtrow)
	PRESOLVEASSERT(rdone[entngld_rows[cndx]]) ;
    }
/*
  In a postsolve matrix, we can't just check that the length of the row is
  zero. We need to look at all columns and confirm its absence.
*/
    for (int j = 0 ; j < ncols ; ++j) {
      if (colLengths[j] > 0 && cdone[j]) {
        const CoinBigIndex kcs = colStarts[j] ;
        const int lenj = colLengths[j] ;
	CoinBigIndex krow =
	  presolve_find_row3(tgtrow,kcs,lenj,rowIndices,link) ;
	if (krow >= 0) {
	  std::cout
	    << "  BAD COEFF! row " << tgtrow << " present in column " << j
	    << " before reintroduction; a(" << tgtrow << "," << j
	    << ") = " << colCoeffs[krow] << "; x(" << j << ") = " << sol[j]
	    << "; cdone " << static_cast<int>(cdone[j]) << "." << std::endl ;
	}
      }
    }
#   endif

/*
  Find the copy of the target row. Restore the upper and lower bounds
  of entangled rows while we're looking. Recall that the target row is
  an equality.
*/
    int tgtrow_len = -1 ;
    const int *tgtrow_colndxs = NULL ;
    const double *tgtrow_coeffs = NULL ;
    double tgtcoeff = 0.0 ;
    double tgtrhs = 1.0e50 ;

    int nel = 0 ;
    for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) {
      int i = entngld_rows[cndx] ;
      rlo[i] = entngld_rlos[cndx] ;
      rup[i] = entngld_rups[cndx] ;
      if (i == tgtrow) {
	tgtrow_len = entngld_lens[cndx] ;
	tgtrow_colndxs = &entngld_colndxs[nel] ;
	tgtrow_coeffs  = &entngld_colcoeffs[nel] ;
	tgtcoeff = tgtcol_coeffs[cndx] ;
	tgtrhs = rlo[i] ;
      }
      nel += entngld_lens[cndx] ;
    }
/*
  Solve the target equality to find the solution for the eliminated col.
  tgtcol is present in tgtrow_colndxs, so initialise sol[tgtcol] to zero
  to make sure it doesn't contribute.

  If we're debugging, check that the result is within bounds.
*/
    double tgtexp = tgtrhs ;
    sol[tgtcol] = 0.0 ;
    for (int ndx = 0 ; ndx < tgtrow_len ; ++ndx) {
      int j = tgtrow_colndxs[ndx] ;
      double coeffj = tgtrow_coeffs[ndx] ;
      tgtexp -= coeffj*sol[j] ;
    }
    sol[tgtcol] = tgtexp/tgtcoeff ;

#   if PRESOLVE_DEBUG > 0
    double *clo = prob->clo_ ;
    double *cup = prob->cup_ ;

    if (!(sol[tgtcol] > (clo[tgtcol]-ztolzb) &&
	  (cup[tgtcol]+ztolzb) > sol[tgtcol])) {
      std::cout
	<< "BAD SOL: x(" << tgtcol << ") " << sol[tgtcol]
	<< "; lb " << clo[tgtcol] << "; ub " << cup[tgtcol] << "."
	<< std::endl ;
    }
#   endif
/*
  Now restore the original entangled rows. We first delete any columns present
  in tgtrow. This will remove any fillin, but may also remove columns that
  were originally present in both the entangled row and the target row.

  Note that even cancellations (explicit zeros) are present at this
  point --- in presolve, they were removed after the substition transform
  completed, hence they're already restored. What isn't present is the target
  column, which is deleted as part of the transform.
*/
    {
#     if PRESOLVE_DEBUG > 2
      std::cout << "    removing coefficients:" ;
#     endif
      for (int rndx = 0 ; rndx < tgtrow_len ; ++rndx) {
	int j = tgtrow_colndxs[rndx] ;
	if (j != tgtcol)
	  for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) {
	    if (entngld_rows[cndx] != tgtrow) {
#             if PRESOLVE_DEBUG > 2
	      std::cout << " a(" << entngld_rows[cndx] << "," << j << ")" ;
#             endif
	      presolve_delete_from_col2(entngld_rows[cndx],j,colStarts,
				      colLengths,rowIndices,link,&free_list) ;
	    }
	  }
      }
#     if PRESOLVE_DEBUG > 2
      std::cout << std::endl ;
#     endif
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_threads(prob) ;
      presolve_check_free_list(prob) ;
#     endif
/*
  Next we restore the original coefficients. The outer loop walks tgtcol;
  cols_i and coeffs_i are advanced as we go to point to each entangled
  row. The inner loop walks the entangled row and restores the row's
  coefficients. Tgtcol is handled as any other column. Skip tgtrow, we'll
  do it below.

  Since we don't have a row-major representation, we have to look for a(i,j)
  from entangled row i in the existing column j. If we find a(i,j), simply
  update it (and a(tgtrow,j) should not exist). If we don't find a(i,j),
  introduce it (and a(tgtrow,j) should exist).

  Recalculate the row activity while we're at it.
*/
#     if PRESOLVE_DEBUG > 2
      std::cout << "    restoring coefficients:" ;
#     endif

      colLengths[tgtcol] = 0 ;
      const int *cols_i = entngld_colndxs ;
      const double *coeffs_i = entngld_colcoeffs ;

      for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) {
	const int leni = entngld_lens[cndx] ;
	const int i = entngld_rows[cndx] ;

	if (i != tgtrow) {
	  double acti = 0.0 ;
	  for (int rndx = 0 ; rndx < leni ; ++rndx) {
	    const int j = cols_i[rndx] ;
	    CoinBigIndex kcoli =
	      presolve_find_row3(i,colStarts[j],
	      			 colLengths[j],rowIndices,link) ;
	    if (kcoli != -1) {
#             if PRESOLVE_DEBUG > 2
	      std::cout << " u a(" << i << "," << j << ")" ;
	      PRESOLVEASSERT(presolve_find_col1(j,0,tgtrow_len,
	      					tgtrow_colndxs) == tgtrow_len) ;
#	      endif
	      colCoeffs[kcoli] = coeffs_i[rndx] ;
	    } else {
#             if PRESOLVE_DEBUG > 2
	      std::cout << " f a(" << i << "," << j << ")" ;
	      PRESOLVEASSERT(presolve_find_col1(j,0,tgtrow_len,
						tgtrow_colndxs) < tgtrow_len) ;
#	      endif
	      CoinBigIndex kk = free_list ;
	      assert(kk >= 0 && kk < prob->bulk0_) ;
	      free_list = link[free_list] ;
	      link[kk] = colStarts[j] ;
	      colStarts[j] = kk ;
	      colCoeffs[kk] = coeffs_i[rndx] ;
	      rowIndices[kk] = i ;
	      ++colLengths[j] ;
	    }
	    acti += coeffs_i[rndx]*sol[j] ;
	  }
	  acts[i] = acti ;
	}
	cols_i += leni ;
	coeffs_i += leni ;
      }
#     if PRESOLVE_DEBUG > 2
      std::cout << std::endl ;
#     endif
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_threads(prob) ;
      presolve_check_free_list(prob) ;
#     endif
/*
  Restore tgtrow. Arguably we could to this in the previous loop, but we'd do
  a lot of unnecessary work. By construction, the target row is tight.
*/
#     if PRESOLVE_DEBUG > 2
      std::cout << "    restoring row " << tgtrow << ":" ;
#     endif

      for (int rndx = 0 ; rndx < tgtrow_len ; ++rndx) {
	int j = tgtrow_colndxs[rndx] ;
#       if PRESOLVE_DEBUG > 2
	std::cout << " a(" << tgtrow << "," << j << ")" ;
#       endif
	CoinBigIndex kk = free_list ;
	assert(kk >= 0 && kk < prob->bulk0_) ;
	free_list = link[free_list] ;
	link[kk] = colStarts[j] ;
	colStarts[j] = kk ;
	colCoeffs[kk] = tgtrow_coeffs[rndx] ;
	rowIndices[kk] = tgtrow ;
	++colLengths[j] ;
      }
      acts[tgtrow] = tgtrhs ;

#     if PRESOLVE_DEBUG > 2
      std::cout << std::endl ;
#     endif
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_threads(prob) ;
      presolve_check_free_list(prob) ;
#     endif
    }
/*
  Restore original cost coefficients, if necessary.
*/
    if (costs) {
      for (int ndx = 0 ; ndx < tgtrow_len ; ++ndx) {
	cost[tgtrow_colndxs[ndx]] = costs[ndx] ;
      }
    }
/*
  Calculate the reduced cost for the column absent any contribution from
  tgtrow, then set the dual for tgtrow so that the reduced cost of tgtcol
  is zero.
*/
    double dj = maxmin*cost[tgtcol] ;
    rowduals[tgtrow] = 0.0 ;
    for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) {
      int i = entngld_rows[cndx] ;
      double coeff = tgtcol_coeffs[cndx] ;
      dj -= rowduals[i]*coeff ;
    }
    rowduals[tgtrow] = dj/tgtcoeff ;
    rcosts[tgtcol] = 0.0 ;
    if (rowduals[tgtrow] > 0)
      prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atUpperBound) ;
    else
      prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atLowerBound) ;
    prob->setColumnStatus(tgtcol,CoinPrePostsolveMatrix::basic) ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  row " << tgtrow << " "
      << prob->rowStatusString(prob->getRowStatus(tgtrow))
      << " dual " << rowduals[tgtrow] << std::endl ;
    std::cout
      << "  col " << tgtcol << " "
      << prob->columnStatusString(prob->getColumnStatus(tgtcol))
      << " dj " << dj << std::endl ;
#   endif

#   if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
    cdone[tgtcol] = SUBST_ROW ;
    rdone[tgtrow] = SUBST_ROW ;
#   endif
  }

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_threads(prob) ;
  presolve_check_free_list(prob) ;
  presolve_check_reduced_costs(prob) ;
  presolve_check_duals(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving subst_constraint_action::postsolve." << std::endl ;
# endif
# endif

  return ;
}
Exemple #16
0
/*
  This routine looks to be something of a work in progres. See the comment
  that begins with `Gack!'. And down in the bound propagation loop, why do we
  only work with variables with u_j = infty? The corresponding section of code
  for l_j = -infty is ifdef'd away. And why exclude the code protected by
  PRESOLVE_TIGHTEN_DUALS? And why are we using ekkinf instead of PRESOLVE_INF?

  There is no postsolve action because once we've identified a variable to fix
  we can invoke make_fixed_action.
*/
const CoinPresolveAction *remove_dual_action::presolve(CoinPresolveMatrix *prob,
				   const CoinPresolveAction *next)
{
  double startTime = 0.0;
  int startEmptyRows=0;
  int startEmptyColumns = 0;
  if (prob->tuning_) {
    startTime = CoinCpuTime();
    startEmptyRows = prob->countEmptyRows();
    startEmptyColumns = prob->countEmptyCols();
  }
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  unsigned char *colstat = prob->colstat_ ;

  // Used only in `fix if simple' section below. Remove dec'l to avoid
  // GCC compile warning.
  // double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  CoinBigIndex *mrstrt		= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
  double *csol	= prob->sol_;
  int nrows		= prob->nrows_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *dcost	= prob->cost_;
  const unsigned char *integerType = prob->integerType_;

  const double maxmin	= prob->maxmin_;

  const double ekkinf = 1e28;
  const double ekkinf2 = 1e20;
  const double ztoldj = prob->ztoldj_;

  CoinRelFltEq relEq(prob->ztolzb_) ;

  double *rdmin	= prob->usefulRowDouble_; //new double[nrows];
  double *rdmax	= reinterpret_cast<double *> (prob->usefulRowInt_); //new double[nrows];

# if PRESOLVE_DEBUG
  std::cout << "Entering remove_dual_action::presolve, " << nrows << " X " << ncols << "." << std::endl ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

  // This combines the initialization of rdmin/rdmax to extreme values
  // (PRESOLVE_INF/-PRESOLVE_INF) with a version of the next loop specialized
  // for row slacks.
  // In this case, it is always the case that dprice==0.0 and coeff==1.0.
  int i;
  for ( i = 0; i < nrows; i++) {
    double sup = -rlo[i];	// slack ub; corresponds to cup[j]
    double slo = -rup[i];	// slack lb; corresponds to clo[j]
    bool no_lb = (slo <= -ekkinf);
    bool no_ub = (sup >= ekkinf);

    // here, dj==-row dual
    // the row slack has no lower bound, but it does have an upper bound,
    // then the reduced cost must be <= 0, so the row dual must be >= 0
    rdmin[i] = (no_lb && !no_ub) ? 0.0 : -PRESOLVE_INF;

    rdmax[i] = (no_ub && !no_lb) ? 0.0 :  PRESOLVE_INF;
  }

  // Look for col singletons and update bounds on dual costs
  // Take the min of the maxes and max of the mins
  int j;
  for ( j = 0; j<ncols; j++) {
    if (integerType[j])
      continue; // even if it has infinite bound now ....
    bool no_ub = (cup[j] >= ekkinf);
    bool no_lb = (clo[j] <= -ekkinf);

    if (hincol[j] == 1 &&

	// we need singleton cols that have exactly one bound
	(no_ub ^ no_lb)) {
      int row = hrow[mcstrt[j]];
      double coeff = colels[mcstrt[j]];

      PRESOLVEASSERT(fabs(coeff) > ZTOLDP);

      // I don't think the sign of dcost[j] matters

      // this row dual would make this col's reduced cost be 0
      double dprice = maxmin * dcost[j] / coeff;

      // no_ub == !no_lb
      // no_ub ==> !(dj<0)
      // no_lb ==> !(dj>0)
      // I don't think the case where !no_ub has actually been tested
      if ((coeff > 0.0) == no_ub) {
	// coeff>0 ==> making the row dual larger would make dj *negative*
	//		==> dprice is an upper bound on dj if no *ub*
	// coeff<0 ==> making the row dual larger would make dj *positive*
	//		==> dprice is an upper bound on dj if no *lb*
	if (rdmax[row] > dprice)	// reduced cost may be positive
	  rdmax[row] = dprice;
      } else if ((coeff < 0.0) == no_lb) { // no lower bound
	if (rdmin[row] < dprice) 	// reduced cost may be negative
	  rdmin[row] = dprice;
      }
    }
  }

  int *fix_cols	= prob->usefulColumnInt_; //new int[ncols];

  //int *fixdown_cols	= new int[ncols];

#if	PRESOLVE_TIGHTEN_DUALS
  double *djmin	= new double[ncols];
  double *djmax	= new double[ncols];
#endif
  int nfixup_cols	= 0;
  int nfixdown_cols	= ncols;

  int nPass=100;
  while (nPass-->0) {
    int tightened = 0;
    /* Perform duality tests */
    for (int j = 0; j<ncols; j++) {
      if (hincol[j] > 0) {
	CoinBigIndex kcs = mcstrt[j];
	CoinBigIndex kce = kcs + hincol[j];
	// Number of infinite rows
	int nflagu = 0;
	int nflagl = 0;
	// Number of ordinary rows
	int nordu = 0;
	int nordl = 0;
	double ddjlo = maxmin * dcost[j];
	double ddjhi = ddjlo;

	for (CoinBigIndex k = kcs; k < kce; k++) {
	  int i = hrow[k];
	  double coeff = colels[k];

	  if (coeff > 0.0) {
	    if (rdmin[i] >= -ekkinf2) {
	      ddjhi -= coeff * rdmin[i];
	      nordu ++;
	    } else {
	      nflagu ++;
	    }
	    if (rdmax[i] <= ekkinf2) {
	      ddjlo -= coeff * rdmax[i];
	      nordl ++;
	    } else {
	      nflagl ++;
	    }
	  } else {
	    if (rdmax[i] <= ekkinf2) {
	      ddjhi -= coeff * rdmax[i];
	      nordu ++;
	    } else {
	      nflagu ++;
	    }
	    if (rdmin[i] >= -ekkinf2) {
	      ddjlo -= coeff * rdmin[i];
	      nordl ++;
	    } else {
	      nflagl ++;
	    }
	  }
	}
	// See if we may be able to tighten a dual
	if (!integerType[j]) {
	  if (cup[j]>ekkinf) {
	    // dj can not be negative
	    if (nflagu==1&&ddjhi<-ztoldj) {
	      // We can make bound finite one way
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff > 0.0&&rdmin[i] < -ekkinf2) {
		  // rdmax[i] has upper bound
		  if (ddjhi<rdmax[i]*coeff-ztoldj) {
		    double newValue = ddjhi/coeff;
		    // re-compute lo
		    if (rdmax[i] > ekkinf2 && newValue <= ekkinf2) {
		      nflagl--;
		      ddjlo -= coeff * newValue;
		    } else if (rdmax[i] <= ekkinf2) {
		      ddjlo -= coeff * (newValue-rdmax[i]);
		    }
		    rdmax[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		  }
		} else if (coeff < 0.0 && rdmax[i] > ekkinf2) {
		  // rdmin[i] has lower bound
		  if (ddjhi<rdmin[i]*coeff-ztoldj) {
		    double newValue = ddjhi/coeff;
		    // re-compute lo
		    if (rdmin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		      nflagl--;
		      ddjlo -= coeff * newValue;
		    } else if (rdmax[i] >= -ekkinf2) {
		      ddjlo -= coeff * (newValue-rdmin[i]);
		    }
		    rdmin[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		    ddjlo = 0.0;
		  }
		}
	      }
	    } else if (nflagl==0&&nordl==1&&ddjlo<-ztoldj) {
	      // We may be able to tighten
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff > 0.0) {
		  rdmax[i] += ddjlo/coeff;
		  ddjlo =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		} else if (coeff < 0.0 ) {
		  rdmin[i] += ddjlo/coeff;
		  ddjlo =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		}
	      }
	    }
	  }
#if 0
	  if (clo[j]<-ekkinf) {
	    // dj can not be positive
	    if (ddjlo>ztoldj&&nflagl==1) {
	      // We can make bound finite one way
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff < 0.0&&rdmin[i] < -ekkinf2) {
		  // rdmax[i] has upper bound
		  if (ddjlo>rdmax[i]*coeff+ztoldj) {
		    double newValue = ddjlo/coeff;
		    // re-compute hi
		    if (rdmax[i] > ekkinf2 && newValue <= ekkinf2) {
		      nflagu--;
		      ddjhi -= coeff * newValue;
		    } else if (rdmax[i] <= ekkinf2) {
		      ddjhi -= coeff * (newValue-rdmax[i]);
		    }
		    rdmax[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		  }
		} else if (coeff > 0.0 && rdmax[i] > ekkinf2) {
		  // rdmin[i] has lower bound
		  if (ddjlo>rdmin[i]*coeff+ztoldj) {
		    double newValue = ddjlo/coeff;
		    // re-compute lo
		    if (rdmin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		      nflagu--;
		      ddjhi -= coeff * newValue;
		    } else if (rdmax[i] >= -ekkinf2) {
		      ddjhi -= coeff * (newValue-rdmin[i]);
		    }
		    rdmin[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		  }
		}
	      }
	    } else if (nflagu==0&&nordu==1&&ddjhi>ztoldj) {
	      // We may be able to tighten
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff < 0.0) {
		  rdmax[i] += ddjhi/coeff;
		  ddjhi =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		} else if (coeff > 0.0 ) {
		  rdmin[i] += ddjhi/coeff;
		  ddjhi =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		}
	      }
	    }
	  }
#endif
	}

#if	PRESOLVE_TIGHTEN_DUALS
	djmin[j] = (nflagl ?  -PRESOLVE_INF : ddjlo);
	djmax[j] = (nflagu ? PRESOLVE_INF : ddjhi);
#endif

	if (ddjlo > ztoldj && nflagl == 0&&!prob->colProhibited2(j)) {
	  // dj>0 at optimality ==> must be at lower bound
	  if (clo[j] <= -ekkinf) {
	    prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDB,
					    prob->messages())
					      <<j
					      <<CoinMessageEol;
	    prob->status_ |= 2;
	    break;
	  } else {
	    fix_cols[--nfixdown_cols] = j;
	    PRESOLVE_DETAIL_PRINT(printf("pre_duallo %dC E\n",j));
#	    if PRESOLVE_DEBUG
	    printf("NDUAL: fixing x<%d>",fix_cols[nfixdown_cols]) ;
	    if (csol) printf(" = %g",csol[j]) ;
	    printf(" at lb = %g.\n",clo[j]) ;
#	    endif
	    //if (csol[j]-clo[j]>1.0e-7)
	    //printf("down %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]);
	    // User may have given us feasible solution - move if simple
	    if (csol) {
# if 0
	/*
	  Except it's not simple. The net result is that we end up with an
	  excess of basic variables.
	*/
	      if (csol[j]-clo[j]>1.0e-7&&hincol[j]==1) {
		double value_j = colels[mcstrt[j]];
		double distance_j = csol[j]-clo[j];
		int row=hrow[mcstrt[j]];
		// See if another column can take value
		for (CoinBigIndex kk=mrstrt[row];kk<mrstrt[row]+hinrow[row];kk++) {
		  int k = hcol[kk];
		  if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		    continue ;

		  if (hincol[k]==1&&k!=j) {
		    double value_k = rowels[kk];
		    double movement;
		    if (value_k*value_j>0.0) {
		      // k needs to increase
		      double distance_k = cup[k]-csol[k];
		      movement = CoinMin((distance_j*value_j)/value_k,distance_k);
		    } else {
		      // k needs to decrease
		      double distance_k = clo[k]-csol[k];
		      movement = CoinMax((distance_j*value_j)/value_k,distance_k);
		    }
		    if (relEq(movement,0)) continue ;

		    csol[k] += movement;
		    if (relEq(csol[k],clo[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		    else
		    if (relEq(csol[k],cup[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		    else
		    if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		    { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		    printf("NDUAL: x<%d> moved %g to %g; ",
			   k,movement,csol[k]) ;
		    printf("lb = %g, ub = %g, status now %s.\n",
			   clo[k],cup[k],columnStatusString(colstat[k])) ;
		    distance_j -= (movement*value_k)/value_j;
		    csol[j] -= (movement*value_k)/value_j;
		    if (distance_j<1.0e-7)
		      break;
		  }
		}
	      }
# endif		// repair solution.

	      csol[j] = clo[j] ;	// but the bottom line is we've changed x<j>
	      colstat[j] = CoinPrePostsolveMatrix::atLowerBound ;
	    }
	  }
	} else if (ddjhi < -ztoldj && nflagu == 0&&!prob->colProhibited2(j)) {
	  // dj<0 at optimality ==> must be at upper bound
	  if (cup[j] >= ekkinf) {
	    prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDA,
					    prob->messages())
					      <<j
					      <<CoinMessageEol;
	    prob->status_ |= 2;
	    break;
	  } else {
	    PRESOLVE_DETAIL_PRINT(printf("pre_dualup %dC E\n",j));
	    fix_cols[nfixup_cols++] = j;
#	    if PRESOLVE_DEBUG
	    printf("NDUAL: fixing x<%d>",fix_cols[nfixup_cols-1]) ;
	    if (csol) printf(" = %g",csol[j]) ;
	    printf(" at ub = %g.\n",cup[j]) ;
#	    endif
	    // User may have given us feasible solution - move if simple
	    // See comments for `fix at lb' case above.
	    //if (cup[j]-csol[j]>1.0e-7)
	    //printf("up %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]);
	    if (csol) {
# if 0
	// See comments above.
	      if (cup[j]-csol[j]>1.0e-7&&hincol[j]==1) {
		double value_j = colels[mcstrt[j]];
		double distance_j = csol[j]-cup[j];
		int row=hrow[mcstrt[j]];
		// See if another column can take value
		for (CoinBigIndex kk=mrstrt[row];kk<mrstrt[row]+hinrow[row];kk++) {
		  int k = hcol[kk];
		  if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		    continue ;

		  if (hincol[k]==1&&k!=j) {
		    double value_k = rowels[kk];
		    double movement;
		    if (value_k*value_j<0.0) {
		      // k needs to increase
		      double distance_k = cup[k]-csol[k];
		      movement = CoinMin((distance_j*value_j)/value_k,distance_k);
		    } else {
		      // k needs to decrease
		      double distance_k = clo[k]-csol[k];
		      movement = CoinMax((distance_j*value_j)/value_k,distance_k);
		    }
		    if (relEq(movement,0)) continue ;

		    csol[k] += movement;
		    if (relEq(csol[k],clo[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		    else
		    if (relEq(csol[k],cup[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		    else
		    if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		    { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		    printf("NDUAL: x<%d> moved %g to %g; ",
			   k,movement,csol[k]) ;
		    printf("lb = %g, ub = %g, status now %s.\n",
			   clo[k],cup[k],columnStatusString(colstat[k])) ;
		    distance_j -= (movement*value_k)/value_j;
		    csol[j] -= (movement*value_k)/value_j;
		    if (distance_j>-1.0e-7)
		      break;
		  }
		}
	      }
# endif
	      csol[j] = cup[j] ;	// but the bottom line is we've changed x<j>
	      colstat[j] = CoinPrePostsolveMatrix::atUpperBound ;
	    }
	  }
	}
      }
    }

    // I don't know why I stopped doing this.
#if	PRESOLVE_TIGHTEN_DUALS
    const double *rowels	= prob->rowels_;
    const int *hcol	= prob->hcol_;
    const CoinBigIndex *mrstrt	= prob->mrstrt_;
    int *hinrow	= prob->hinrow_;
    // tighten row dual bounds, as described on p. 229
    for (int i = 0; i < nrows; i++) {
      bool no_ub = (rup[i] >= ekkinf);
      bool no_lb = (rlo[i] <= -ekkinf);

      if ((no_ub ^ no_lb) == true) {
	CoinBigIndex krs = mrstrt[i];
	CoinBigIndex kre = krs + hinrow[i];
	double rmax  = rdmax[i];
	double rmin  = rdmin[i];

	// all row columns are non-empty
	for (CoinBigIndex k=krs; k<kre; k++) {
	  double coeff = rowels[k];
	  int icol = hcol[k];
	  double djmax0 = djmax[icol];
	  double djmin0 = djmin[icol];

	  if (no_ub) {
	    // dj must not be negative
	    if (coeff > ZTOLDP2 && djmax0 <PRESOLVE_INF && cup[icol]>=ekkinf) {
	      double bnd = djmax0 / coeff;
	      if (rmax > bnd) {
#if	PRESOLVE_DEBUG
		printf("MAX TIGHT[%d,%d]:  %g --> %g\n", i,hrow[k], rdmax[i], bnd);
#endif
		rdmax[i] = rmax = bnd;
		tightened ++;
	      }
	    } else if (coeff < -ZTOLDP2 && djmax0 <PRESOLVE_INF && cup[icol] >= ekkinf) {
	      double bnd = djmax0 / coeff ;
	      if (rmin < bnd) {
#if	PRESOLVE_DEBUG
		printf("MIN TIGHT[%d,%d]:  %g --> %g\n", i, hrow[k], rdmin[i], bnd);
#endif
		rdmin[i] = rmin = bnd;
		tightened ++;
	      }
	    }
	  } else {	// no_lb
	    // dj must not be positive
	    if (coeff > ZTOLDP2 && djmin0 > -PRESOLVE_INF && clo[icol]<=-ekkinf) {
	      double bnd = djmin0 / coeff ;
	      if (rmin < bnd) {
#if	PRESOLVE_DEBUG
		printf("MIN1 TIGHT[%d,%d]:  %g --> %g\n", i, hrow[k], rdmin[i], bnd);
#endif
		rdmin[i] = rmin = bnd;
		tightened ++;
	      }
	    } else if (coeff < -ZTOLDP2 && djmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      double bnd = djmin0 / coeff ;
	      if (rmax > bnd) {
#if	PRESOLVE_DEBUG
		printf("MAX TIGHT1[%d,%d]:  %g --> %g\n", i,hrow[k], rdmax[i], bnd);
#endif
		rdmax[i] = rmax = bnd;
		tightened ++;
	      }
	    }
	  }
	}
      }
    }
#endif

    if (tightened<100||nfixdown_cols<ncols||nfixup_cols)
      break;
#if	PRESOLVE_TIGHTEN_DUALS
    else
      printf("DUAL TIGHTENED!  %d\n", tightened);
#endif
  }
  assert (nfixup_cols<=nfixdown_cols);
  if (nfixup_cols) {
#if	PRESOLVE_DEBUG
    printf("NDUAL: %d up", nfixup_cols);
    for (i = 0 ; i < nfixup_cols ; i++) printf(" %d",fix_cols[i]) ;
    printf(".\n") ;
#endif
    next = make_fixed_action::presolve(prob, fix_cols, nfixup_cols, false, next);
  }

  if (nfixdown_cols<ncols) {
    int * fixdown_cols = fix_cols+nfixdown_cols;
    nfixdown_cols = ncols-nfixdown_cols;
#if	PRESOLVE_DEBUG
    printf("NDUAL: %d down", nfixdown_cols);
    for (i = 0 ; i < nfixdown_cols ; i++) printf(" %d",fixdown_cols[i]) ;
    printf(".\n") ;
#endif
    next = make_fixed_action::presolve(prob, fixdown_cols, nfixdown_cols, true, next);
  }
  // If dual says so then we can make equality row
  // Also if cost is in right direction and only one binding row for variable
  // We may wish to think about giving preference to rows with 2 or 3 elements
/*
  Gack! Ok, I can appreciate the thought here, but I'm seriously skeptical
  about writing canFix[0] before reading rdmin[0]. After that, we should be out
  of the interference zone for the typical situation where sizeof(double) is
  twice sizeof(int).
*/
  int * canFix = reinterpret_cast<int *> (rdmin);
  for ( i = 0; i < nrows; i++) {
    bool no_lb = (rlo[i] <= -ekkinf);
    bool no_ub = (rup[i] >= ekkinf);
    canFix[i]=0;
    if (no_ub && !no_lb ) {
      if ( rdmin[i]>0.0)
	canFix[i]=-1;
      else
	canFix[i]=-2;
    } else if (no_lb && !no_ub ) {
      if (rdmax[i]<0.0)
	canFix[i]=1;
      else
	canFix[i]=2;
    }
  }
  for (j = 0; j<ncols; j++) {
    if (hincol[j]<=1)
      continue;
    if (integerType[j])
      continue; // even if it has infinite bound now ....
    CoinBigIndex kcs = mcstrt[j];
    CoinBigIndex kce = kcs + hincol[j];
    int bindingUp=-1;
    int bindingDown=-1;
    if (cup[j]<ekkinf)
      bindingUp=-2;
    if (clo[j]>-ekkinf)
      bindingDown=-2;
    for (CoinBigIndex k = kcs; k < kce; k++) {
      int i = hrow[k];
      if (abs(canFix[i])!=2) {
	bindingUp=-2;
	bindingDown=-2;
	break;
      }
      double coeff = colels[k];
      if (coeff>0.0) {
	if (canFix[i]==2) {
	  // binding up
	  if (bindingUp==-1)
	    bindingUp = i;
	  else
	    bindingUp = -2;
	} else {
	  // binding down
	  if (bindingDown==-1)
	    bindingDown = i;
	  else
	    bindingDown = -2;
	}
      } else {
	if (canFix[i]==2) {
	  // binding down
	  if (bindingDown==-1)
	    bindingDown = i;
	  else
	    bindingDown = -2;
	} else {
	  // binding up
	  if (bindingUp==-1)
	    bindingUp = i;
	  else
	    bindingUp = -2;
	}
      }
    }
    double cost = maxmin * dcost[j];
    if (bindingUp>-2&&cost<=0.0) {
      // might as well make equality
      if (bindingUp>=0) {
	canFix[bindingUp] /= 2; //So -2 goes to -1 etc
	//printf("fixing row %d to ub by %d\n",bindingUp,j);
      } else {
	//printf("binding up row by %d\n",j);
      }
    } else if (bindingDown>-2 &&cost>=0.0) {
      // might as well make equality
      if (bindingDown>=0) {
	canFix[bindingDown] /= 2; //So -2 goes to -1 etc
	//printf("fixing row %d to lb by %d\n",bindingDown,j);
      } else {
	//printf("binding down row by %d\n",j);
      }
    }
  }
  // can't fix if integer and non-unit coefficient
  //const double *rowels	= prob->rowels_;
  //const int *hcol	= prob->hcol_;
  //const CoinBigIndex *mrstrt	= prob->mrstrt_;
  //int *hinrow	= prob->hinrow_;
  for ( i = 0; i < nrows; i++) {
    if (abs(canFix[i])==1) {
      CoinBigIndex krs = mrstrt[i];
      CoinBigIndex kre = krs + hinrow[i];
      for (CoinBigIndex k=krs; k<kre; k++) {
	int icol = hcol[k];
	if (cup[icol]>clo[icol]&&integerType[icol]) {
	  canFix[i]=0; // not safe
#ifdef COIN_DEVELOP
	  printf("no dual something CoinPresolveDual row %d col %d\n",
		 i,icol);
#endif
	}
      }
    }
    if (canFix[i]==1) {
      rlo[i]=rup[i];
      prob->addRow(i);
    } else if (canFix[i]==-1) {
      rup[i]=rlo[i];
      prob->addRow(i);
    }
  }

  //delete[]rdmin;
  //delete[]rdmax;

  //delete[]fixup_cols;
  //delete[]fixdown_cols;

#if	PRESOLVE_TIGHTEN_DUALS
  delete[]djmin;
  delete[]djmax;
#endif

  if (prob->tuning_) {
    double thisTime=CoinCpuTime();
    int droppedRows = prob->countEmptyRows() - startEmptyRows;
    int droppedColumns =  prob->countEmptyCols() - startEmptyColumns;
    printf("CoinPresolveDual(1) - %d rows, %d columns dropped in time %g, total %g\n",
	   droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_);
  }

# if PRESOLVE_DEBUG
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
  std::cout << "Leaving remove_dual_action::presolve." << std::endl ;
# endif

  return (next);
}
/*
 * Say we determined that cup - clo <= ztolzb, so we fixed sol at clo.
 * This involved subtracting clo*coeff from ub/lb for each row the
 * variable occurred in.
 * Now when we put the variable back in, by construction the variable
 * is within tolerance, the non-slacks are unchanged, and the 
 * distances of the affected slacks from their bounds should remain
 * unchanged (ignoring roundoff errors).
 * It may be that by adding the term back in, the affected constraints
 * now aren't as accurate due to round-off errors; this could happen
 * if only one summand and the slack in the original formulation were large
 * (and naturally had opposite signs), and the new term in the constraint
 * is about the size of the old slack, so the new slack becomes about 0.
 * It may be that there is catastrophic cancellation in the summation,
 * so it might not compute to 0.
 */
void remove_fixed_action::postsolve(CoinPostsolveMatrix *prob) const
{
  action * actions	= actions_;
  const int nactions	= nactions_;

  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt	= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int *link		= prob->link_;
  CoinBigIndex &free_list = prob->free_list_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *sol	= prob->sol_;
  double *dcost	= prob->cost_;
  double *rcosts	= prob->rcosts_;

  double *acts	= prob->acts_;
  double *rowduals = prob->rowduals_;

  unsigned char *colstat	= prob->colstat_;

  const double maxmin	= prob->maxmin_;

# if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
  char *cdone	= prob->cdone_;

  std::cout << "Entering remove_fixed_action::postsolve." << std::endl ;
  presolve_check_threads(prob) ;
  presolve_check_free_list(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

  double * els_action = colels_;
  int * rows_action = colrows_;
  int end = actions[nactions].start;

/*
  At one point, it turned out that forcing_constraint_action was putting
  duplicates in the column list it passed to remove_fixed_action. This is now
  fixed, but ... it looks to me like we could be in trouble here if we
  reinstate a column multiple times. Hence the assert.
*/
  for (const action *f = &actions[nactions-1]; actions<=f; f--) {
    int icol = f->col;
    const double thesol = f->sol;

# if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
    assert(cdone[icol] != FIXED_VARIABLE) ;
    cdone[icol] = FIXED_VARIABLE ;
# endif

    sol[icol] = thesol;
    clo[icol] = thesol;
    cup[icol] = thesol;

    int cs = NO_LINK ;
    int start = f->start;
    double dj = maxmin * dcost[icol];
    
    for (int i=start; i<end; ++i) {
      int row = rows_action[i];
      double coeff =els_action[i];
      
      // pop free_list
      CoinBigIndex k = free_list;
      assert(k >= 0 && k < prob->bulk0_) ;
      free_list = link[free_list];
      // restore
      hrow[k] = row;
      colels[k] = coeff;
      link[k] = cs;
      cs = k;

      if (-PRESOLVE_INF < rlo[row])
	rlo[row] += coeff * thesol;
      if (rup[row] < PRESOLVE_INF)
	rup[row] += coeff * thesol;
      acts[row] += coeff * thesol;
      
      dj -= rowduals[row] * coeff;
    }

#   if PRESOLVE_CONSISTENCY
    presolve_check_free_list(prob) ;
#   endif
      
    mcstrt[icol] = cs;
    
    rcosts[icol] = dj;
    hincol[icol] = end-start;
    end=start;

    /* Original comment:
     * the bounds in the reduced problem were tightened.
     * that means that this variable may not have been basic
     * because it didn't have to be,
     * but now it may have to.
     * no - the bounds aren't changed by this operation
     */
/*
  We've reintroduced the variable, but it's still fixed (equal bounds).
  Pick the nonbasic status that agrees with the reduced cost. Later, if
  postsolve unfixes the variable, we'll need to confirm that this status is
  still viable. We live in a minimisation world here.
*/
    if (colstat)
    { if (dj < 0)
	prob->setColumnStatus(icol,CoinPrePostsolveMatrix::atUpperBound);
      else
	prob->setColumnStatus(icol,CoinPrePostsolveMatrix::atLowerBound); }

  }


# if PRESOLVE_CONSISTENCY || PRESOLVE_DEBUG
  presolve_check_threads(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
  std::cout << "Leaving remove_fixed_action::postsolve." << std::endl ;
# endif

  return ;
}
/*
  Reintroduce the column (y) and doubleton row (irow) removed in presolve.
  Correct the other column (x) involved in the doubleton, update the solution,
  etc.

  A fair amount of complication arises because the presolve transform saves the
  shorter of x or y. Postsolve thus includes portions to restore either.
*/
void doubleton_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const action *const actions = actions_ ;
  const int nactions = nactions_ ;

  double *colels	= prob->colels_ ;
  int *hrow		= prob->hrow_ ;
  CoinBigIndex *mcstrt	= prob->mcstrt_ ;
  int *hincol		= prob->hincol_ ;
  int *link		= prob->link_ ;

  double *clo	= prob->clo_ ;
  double *cup	= prob->cup_ ;

  double *rlo	= prob->rlo_ ;
  double *rup	= prob->rup_ ;

  double *dcost	= prob->cost_ ;

  double *sol	= prob->sol_ ;
  double *acts	= prob->acts_ ;
  double *rowduals = prob->rowduals_ ;
  double *rcosts = prob->rcosts_ ;

  unsigned char *colstat = prob->colstat_ ;
  unsigned char *rowstat = prob->rowstat_ ;

  const double maxmin	= prob->maxmin_ ;

  CoinBigIndex &free_list = prob->free_list_ ;

  const double ztolzb	= prob->ztolzb_ ;
  const double ztoldj	= prob->ztoldj_ ;
  const double ztolzero = 1.0e-12 ;

  int nrows = prob->nrows_ ;

  // Arrays to rebuild the unsaved column.
  int *index1 = new int[nrows] ;
  double *element1 = new double[nrows] ;
  CoinZeroN(element1,nrows) ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  char *cdone	= prob->cdone_ ;
  char *rdone	= prob->rdone_ ;

  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
  presolve_check_reduced_costs(prob) ;

# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering doubleton_action::postsolve, " << nactions
    << " transforms to undo." << std::endl ;
# endif
# endif
/*
  The outer loop: step through the doubletons in this array of actions.
  The first activity is to unpack the doubleton.
*/
  for (const action *f = &actions[nactions-1] ; actions <= f ; f--) {

    const int irow = f->row ;
    const double lo0 = f->clox ;
    const double up0 = f->cupx ;


    const double coeffx = f->coeffx ;
    const double coeffy = f->coeffy ;
    const int jcolx = f->icolx ;
    const int jcoly = f->icoly ;

    const double rhs = f->rlo ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << std::endl
      << "  restoring doubleton " << irow << ", elim x(" << jcoly
      << "), kept x(" << jcolx << "); stored col " ;
    if (f->ncoly)
      std::cout << jcoly ;
    else 
      std::cout << jcolx ;
    std::cout << "." << std::endl ;
    std::cout
      << "  x(" << jcolx << ") " << prob->columnStatusString(jcolx) << " "
      << clo[jcolx] << " <= " << sol[jcolx] << " <= " << cup[jcolx]
      << "; cj " << f->costx << " dj " << rcosts[jcolx] << "." << std::endl ;
#   endif
/*
  jcolx is in the problem (for whatever reason), and the doubleton row (irow)
  and column (jcoly) have only been processed by empty row/column postsolve
  (i.e., reintroduced with length 0).
*/
    PRESOLVEASSERT(cdone[jcolx] && rdone[irow] == DROP_ROW) ;
    PRESOLVEASSERT(cdone[jcoly] == DROP_COL) ;

/*
  Restore bounds for doubleton row, bounds and objective coefficient for x,
  objective for y.

  Original comment: restoration of rlo and rup likely isn't necessary.
*/
    rlo[irow] = f->rlo ;
    rup[irow] = f->rlo ;

    clo[jcolx] = lo0 ;
    cup[jcolx] = up0 ;

    dcost[jcolx] = f->costx ;
    dcost[jcoly] = f->costy ;

/*
  Set primal solution for y (including status) and row activity for the
  doubleton row. The motivation (up in presolve) for wanting coeffx < coeffy
  is to avoid inflation into sol[y]. Since this is a (satisfied) equality,
  activity is the rhs value and the logical is nonbasic.
*/
    const double diffy = rhs-coeffx*sol[jcolx] ;
    if (fabs(diffy) < ztolzero)
      sol[jcoly] = 0 ;
    else
      sol[jcoly] = diffy/coeffy ;
    acts[irow] = rhs ;
    if (rowstat)
      prob->setRowStatus(irow,CoinPrePostsolveMatrix::atLowerBound) ;

#   if PRESOLVE_DEBUG > 2
/*
  Original comment: I've forgotten what this is about

  We have sol[y] = (rhs - coeffx*sol[x])/coeffy. As best I can figure,
  the original check here tested for the possibility of loss of significant
  digits through cancellation, followed by inflation if coeffy is small.
  The hazard is clear enough, but the test was puzzling. Overly complicated
  and it generated false warnings for the common case of sol[y] a clean zero.
  Replaced with something that I hope is more useful. The tolerances are, sad
  to say, completely arbitrary.    -- lh, 121106 --
*/
    if ((fabs(diffy) < 1.0e-6) && (fabs(diffy) >= ztolzero) &&
        (fabs(coeffy) < 1.0e-3))
      std::cout
        << "  loss of significance? rhs " << rhs
	<< " (coeffx*sol[jcolx])" << (coeffx*sol[jcolx])
	<< " diff " << diffy << "." << std::endl ;
#   endif

/*
  Time to get into the correction/restoration of coefficients for columns x
  and y, with attendant correction of row bounds and activities. Accumulate
  partial reduced costs (missing the contribution from the doubleton row) so
  that we can eventually calculate a dual for the doubleton row.
*/
    double djy = maxmin*dcost[jcoly] ;
    double djx = maxmin*dcost[jcolx] ;
/*
  We saved column y in the action, so we'll use it to reconstruct column x.
  There are two aspects: correction of existing x coefficients, and fill in.
  Given
    coeffx'[k] = coeffx[k]+coeffy[k]*coeff_factor
  we have
    coeffx[k] = coeffx'[k]-coeffy[k]*coeff_factor
  where
    coeff_factor = -coeffx[dblton]/coeffy[dblton].

  Keep in mind that the major vector stored in the action does not include
  the coefficient from the doubleton row --- the doubleton coefficients are
  held in coeffx and coeffy.
*/
    if (f->ncoly) {
      int ncoly = f->ncoly-1 ;
      int *indy = reinterpret_cast<int *>(f->colel+ncoly) ;
/*
  Rebuild a threaded column y, starting with the end of the thread and working
  back to the beginning. In the process, accumulate corrections to column x
  in element1 and index1. Fix row bounds and activity as we go (add back the
  constant correction removed in presolve), and accumulate contributions to
  the reduced cost for y. Don't tweak finite infinity.

  The PRESOLVEASSERT says this row should already be present. 
*/
      int ystart = NO_LINK ;
      int nX = 0 ;
      for (int kcol = 0 ; kcol < ncoly ; ++kcol) {
	const int i = indy[kcol] ;
	PRESOLVEASSERT(rdone[i]) ;

	double yValue = f->colel[kcol] ;

	if (-PRESOLVE_INF < rlo[i])
	  rlo[i] += (yValue*rhs)/coeffy ;
	if (rup[i] < PRESOLVE_INF)
	  rup[i] += (yValue*rhs)/coeffy ;

	acts[i] += (yValue*rhs)/coeffy ;

	djy -= rowduals[i]*yValue ;
/*
  Link the coefficient into column y: Acquire the first free slot in the
  bulk arrays and store the row index and coefficient. Then link the slot
  in front of coefficients we've already processed.
*/
	const CoinBigIndex kfree = free_list ;
	assert(kfree >= 0 && kfree < prob->bulk0_) ;
	free_list = link[free_list] ;
	hrow[kfree] = i ;
	colels[kfree] = yValue ;
	link[kfree] = ystart ;
	ystart = kfree ;

#       if PRESOLVE_DEBUG > 4
	std::cout
	  << "  link y " << kfree << " row " << i << " coeff " << yValue
	  << " dual " << rowduals[i] << std::endl ;
#       endif
/*
  Calculate and store the correction to the x coefficient.
*/
	yValue = (yValue*coeffx)/coeffy ;
	element1[i] = yValue ;
	index1[nX++] = i ;
      }
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_free_list(prob) ;
#     endif
/*
  Handle the coefficients of the doubleton row. Insert coeffy, coeffx.
*/
      const CoinBigIndex kfree = free_list ;
      assert(kfree >= 0 && kfree < prob->bulk0_) ;
      free_list = link[free_list] ;
      hrow[kfree] = irow ;
      colels[kfree] = coeffy ;
      link[kfree] = ystart ;
      ystart = kfree ;

#     if PRESOLVE_DEBUG > 4
      std::cout
	<< "  link y " << kfree << " row " << irow << " coeff " << coeffy
	<< " dual n/a" << std::endl ;
#     endif

      element1[irow] = coeffx ;
      index1[nX++] = irow ;
/*
  Attach the threaded column y to mcstrt and record the length.
*/
      mcstrt[jcoly] = ystart ;
      hincol[jcoly] = f->ncoly ;
/*
  Now integrate the corrections to column x. Scan the column and correct the
  existing entries.  The correction could cancel the existing coefficient and
  we don't want to leave an explicit zero. In this case, relink the column
  around it.  The freed slot is linked at the beginning of the free list.
*/
      CoinBigIndex kcs = mcstrt[jcolx] ;
      CoinBigIndex last_nonzero = NO_LINK ;
      int numberInColumn = hincol[jcolx] ;
      const int numberToDo = numberInColumn ;
      for (int kcol = 0 ; kcol < numberToDo ; ++kcol) {
	const int i = hrow[kcs] ;
	assert(i >= 0 && i < nrows && i != irow) ;
	double value = colels[kcs]+element1[i] ;
	element1[i] = 0.0 ;
	if (fabs(value) >= 1.0e-15) {
	  colels[kcs] = value ;
	  last_nonzero = kcs ;
	  kcs = link[kcs] ;
	  djx -= rowduals[i]*value ;

#         if PRESOLVE_DEBUG > 4
	  std::cout
	    << "  link x " << last_nonzero << " row " << i << " coeff "
	    << value << " dual " << rowduals[i] << std::endl ;
#         endif

	} else {

#         if PRESOLVE_DEBUG > 4
	  std::cout
	    << "  link x skipped row  " << i << " dual "
	    << rowduals[i] << std::endl ;
#         endif

	  numberInColumn-- ;
	  // add to free list
	  int nextk = link[kcs] ;
	  assert(free_list >= 0) ;
	  link[kcs] = free_list ;
	  free_list = kcs ;
	  assert(kcs >= 0) ;
	  kcs = nextk ;
	  if (last_nonzero != NO_LINK)
	    link[last_nonzero] = kcs ;
	  else
	    mcstrt[jcolx] = kcs ;
	}
      }
      if (last_nonzero != NO_LINK)
	link[last_nonzero] = NO_LINK ;
/*
  We've dealt with the existing nonzeros in column x. Any remaining
  nonzeros in element1 will be fill in, which we insert at the beginning of
  the column.
*/
      for (int kcol = 0 ; kcol < nX ; kcol++) {
	const int i = index1[kcol] ;
	double xValue = element1[i] ;
	element1[i] = 0.0 ;
	if (fabs(xValue) >= 1.0e-15) {
	  if (i != irow)
	    djx -= rowduals[i]*xValue ;
	  numberInColumn++ ;
	  CoinBigIndex kfree = free_list ;
	  assert(kfree >= 0 && kfree < prob->bulk0_) ;
	  free_list = link[free_list] ;
	  hrow[kfree] = i ;
	  PRESOLVEASSERT(rdone[hrow[kfree]] || (hrow[kfree] == irow)) ;
	  colels[kfree] = xValue ;
	  link[kfree] = mcstrt[jcolx] ;
	  mcstrt[jcolx] = kfree ;
#         if PRESOLVE_DEBUG > 4
	  std::cout
	    << "  link x " << kfree << " row " << i << " coeff " << xValue
	    << " dual " ;
	  if (i != irow)
	    std::cout << rowduals[i] ;
	  else
	    std::cout << "n/a" ;
	  std::cout << std::endl ;
#         endif
	}
      }
	  
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_free_list(prob) ;
#     endif
	  
/*
  Whew! Set the column length and we're done.
*/
      assert(numberInColumn) ;
      hincol[jcolx] = numberInColumn ;
    } else {
/*
  Of course, we could have saved column x in the action. Now we need to
  regenerate coefficients of column y.
  Given
    coeffx'[k] = coeffx[k]+coeffy[k]*coeff_factor
  we have
    coeffy[k] = (coeffx'[k]-coeffx[k])*(1/coeff_factor)
  where
    coeff_factor = -coeffx[dblton]/coeffy[dblton].
*/
      const int ncolx = f->ncolx-1 ;
      int *indx = reinterpret_cast<int *> (f->colel+ncolx) ;
/*
  Scan existing column x to find the end. While we're at it, accumulate part
  of the new y coefficients in index1 and element1.
*/
      CoinBigIndex kcs = mcstrt[jcolx] ;
      int nX = 0 ;
      for (int kcol = 0 ; kcol < hincol[jcolx]-1 ; ++kcol) {
	if (colels[kcs]) {
	  const int i = hrow[kcs] ;
	  index1[nX++] = i ;
	  element1[i] = -(colels[kcs]*coeffy)/coeffx ;
	}
	kcs = link[kcs] ;
      }
      if (colels[kcs]) {
        const int i = hrow[kcs] ;
        index1[nX++] = i ;
        element1[i] = -(colels[kcs]*coeffy)/coeffx ;
      }
/*
  Replace column x with the the original column x held in the doubleton action
  (recall that this column does not include coeffx). We first move column
  x to the free list, then thread a column with the original coefficients,
  back to front.  While we're at it, add the second part of the y coefficients
  to index1 and element1.
*/
      link[kcs] = free_list ;
      free_list = mcstrt[jcolx] ;
      int xstart = NO_LINK ;
      for (int kcol = 0 ; kcol < ncolx ; ++kcol) {
	const int i = indx[kcol] ;
	PRESOLVEASSERT(rdone[i] && i != irow) ;

	double xValue = f->colel[kcol] ;
	CoinBigIndex k = free_list ;
	assert(k >= 0 && k < prob->bulk0_) ;
	free_list = link[free_list] ;
	hrow[k] = i ;
	colels[k] = xValue ;
	link[k] = xstart ;
	xstart = k ;

	djx -= rowduals[i]*xValue ;

	xValue = (xValue*coeffy)/coeffx ;
	if (!element1[i]) {
	  element1[i] = xValue ;
	  index1[nX++] = i ;
	} else {
	  element1[i] += xValue ;
	}
      }
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_free_list(prob) ;
#     endif
/*
  The same, for the doubleton row.
*/
      {
	double xValue = coeffx ;
	CoinBigIndex k = free_list ;
	assert(k >= 0 && k < prob->bulk0_) ;
	free_list = link[free_list] ;
	hrow[k] = irow ;
	colels[k] = xValue ;
	link[k] = xstart ;
	xstart = k ;
	element1[irow] = coeffy ;
	index1[nX++] = irow ;
      }
/*
  Link the new column x to mcstrt and set the length.
*/
      mcstrt[jcolx] = xstart ;
      hincol[jcolx] = f->ncolx ;
/*
  Now get to work building a threaded column y from the nonzeros in element1.
  As before, build the thread in reverse.
*/
      int ystart = NO_LINK ;
      int leny = 0 ;
      for (int kcol = 0 ; kcol < nX ; kcol++) {
	const int i = index1[kcol] ;
	PRESOLVEASSERT(rdone[i] || i == irow) ;
	double yValue = element1[i] ;
	element1[i] = 0.0 ;
	if (fabs(yValue) >= ztolzero) {
	  leny++ ;
	  CoinBigIndex k = free_list ;
	  assert(k >= 0 && k < prob->bulk0_) ;
	  free_list = link[free_list] ;
	  hrow[k] = i ;
	  colels[k] = yValue ;
	  link[k] = ystart ;
	  ystart = k ;
	}
      }
#     if PRESOLVE_CONSISTENCY > 0
      presolve_check_free_list(prob) ;
#     endif
/*
  Tidy up --- link the new column into mcstrt and set the length.
*/
      mcstrt[jcoly] = ystart ;
      assert(leny) ;
      hincol[jcoly] = leny ;
/*
  Now that we have the original y, we can scan it and do the corrections to
  the row bounds and activity, and get a start on a reduced cost for y.
*/
      kcs = mcstrt[jcoly] ;
      const int ny = hincol[jcoly] ;
      for (int kcol = 0 ; kcol < ny ; ++kcol) {
	const int row = hrow[kcs] ;
	const double coeff = colels[kcs] ;
	kcs = link[kcs] ;

	if (row != irow) {
	  
	  // undo elim_doubleton(1)
	  if (-PRESOLVE_INF < rlo[row])
	    rlo[row] += (coeff*rhs)/coeffy ;
	  
	  // undo elim_doubleton(2)
	  if (rup[row] < PRESOLVE_INF)
	    rup[row] += (coeff*rhs)/coeffy ;
	  
	  acts[row] += (coeff*rhs)/coeffy ;
	  
	  djy -= rowduals[row]*coeff ;
	}
      }
    }
#   if PRESOLVE_DEBUG > 2
/*
  Sanity checks. The doubleton coefficients should be linked in the first
  position of the each column (for no good reason except that it makes it much
  easier to write these checks).
*/
#   if PRESOLVE_DEBUG > 4
    std::cout
      << "  kept: saved " << jcolx << " " << coeffx << ", reconstructed "
      << hrow[mcstrt[jcolx]] << " " << colels[mcstrt[jcolx]]
      << "." << std::endl ;
    std::cout
      << "  elim: saved " << jcoly << " " << coeffy << ", reconstructed "
      << hrow[mcstrt[jcoly]] << " " << colels[mcstrt[jcoly]]
      << "." << std::endl ;
#   endif
    assert((coeffx == colels[mcstrt[jcolx]]) &&
	   (coeffy == colels[mcstrt[jcoly]])) ;
#   endif
/*
  Time to calculate a dual for the doubleton row, and settle the status of x
  and y. Ideally, we'll leave x at whatever nonbasic status it currently has
  and make y basic. There's a potential problem, however: Remember that we
  transferred bounds from y to x when we eliminated y. If those bounds were
  tighter than x's original bounds, we may not be able to maintain x at its
  present status, or even as nonbasic.

  We'll make two claims here:

    * If the dual value for the doubleton row is chosen to keep the reduced
      cost djx of col x at its prior value, then the reduced cost djy of col
      y will be 0. (Crank through the linear algebra to convince yourself.)

    * If the bounds on x have loosened, then it must be possible to make y
      nonbasic, because we've transferred the tight bound back to y. (Yeah,
      I'm waving my hands. But it sounds good.  -- lh, 040907 --)

  So ... if we can maintain x nonbasic, then we need to set y basic, which
  means we should calculate rowduals[dblton] so that rcost[jcoly] == 0. We
  may need to change the status of x (an artifact of loosening a bound when
  x was previously a fixed variable).
  
  If we need to push x into the basis, then we calculate rowduals[dblton] so
  that rcost[jcolx] == 0 and make y nonbasic.
*/
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  pre status: x(" << jcolx << ") " << prob->columnStatusString(jcolx)
      << " " << clo[jcolx] << " <= " << sol[jcolx] << " <= " << cup[jcolx]
      << ", cj " << dcost[jcolx]
      << ", dj " << djx << "." << std::endl ;
    std::cout
      << "  pre status: x(" << jcoly << ") "
      << clo[jcoly] << " <= " << sol[jcoly] << " <= " << cup[jcoly]
      << ", cj " << dcost[jcoly]
      << ", dj " << djy << "." << std::endl ;
#   endif
    if (colstat) {
      bool basicx = prob->columnIsBasic(jcolx) ;
      bool nblbxok = (fabs(lo0 - sol[jcolx]) < ztolzb) &&
		     (rcosts[jcolx] >= -ztoldj) ;
      bool nbubxok = (fabs(up0 - sol[jcolx]) < ztolzb) &&
		     (rcosts[jcolx] <= ztoldj) ;
      if (basicx || nblbxok || nbubxok) {
        if (!basicx) {
	  if (nblbxok) {
	    prob->setColumnStatus(jcolx,
				  CoinPrePostsolveMatrix::atLowerBound) ;
	  } else if (nbubxok) {
	    prob->setColumnStatus(jcolx,
				  CoinPrePostsolveMatrix::atUpperBound) ;
	  }
	}
	prob->setColumnStatus(jcoly,CoinPrePostsolveMatrix::basic) ;
	rowduals[irow] = djy/coeffy ;
	rcosts[jcolx] = djx-rowduals[irow]*coeffx ;
	rcosts[jcoly] = 0.0 ;
      } else {
	prob->setColumnStatus(jcolx,CoinPrePostsolveMatrix::basic) ;
	prob->setColumnStatusUsingValue(jcoly) ;
	rowduals[irow] = djx/coeffx ;
	rcosts[jcoly] = djy-rowduals[irow]*coeffy ;
	rcosts[jcolx] = 0.0 ;
      }
#     if PRESOLVE_DEBUG > 2
      std::cout
        << "  post status: " << irow << " dual " << rowduals[irow]
	<< " rhs " << rlo[irow]
	<< std::endl ;
      std::cout
	<< "  post status: x(" << jcolx << ") "
	<< prob->columnStatusString(jcolx) << " "
	<< clo[jcolx] << " <= " << sol[jcolx] << " <= " << cup[jcolx]
	<< ", cj " << dcost[jcolx]
	<< ", dj = " << rcosts[jcolx] << "." << std::endl ;
      std::cout
	<< "  post status: x(" << jcoly << ") "
	<< prob->columnStatusString(jcoly) << " "
	<< clo[jcoly] << " <= " << sol[jcoly] << " <= " << cup[jcoly]
	<< ", cj " << dcost[jcoly]
	<< ", dj " << rcosts[jcoly] << "." << std::endl ;
/*
  These asserts are valid but need a scaled tolerance to work well over
  a range of problems. Occasionally useful for a hard stop while debugging.

      assert(!prob->columnIsBasic(jcolx) || (fabs(rcosts[jcolx]) < 1.0e-5)) ;
      assert(!prob->columnIsBasic(jcoly) || (fabs(rcosts[jcoly]) < 1.0e-5)) ;
*/
#     endif
    } else {
      // No status array
      // this is the coefficient we need to force col y's reduced cost to 0.0 ;
      // for example, this is obviously true if y is a singleton column
      rowduals[irow] = djy/coeffy ;
      rcosts[jcoly] = 0.0 ;
    }
    
#   if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
/*
  Mark the column and row as processed by doubleton action. Then check
  integrity of the threaded matrix.
*/
    cdone[jcoly] = DOUBLETON ;
    rdone[irow] = DOUBLETON ;
    presolve_check_threads(prob) ;
#   endif
#   if PRESOLVE_DEBUG > 0
/*
  Confirm accuracy of reduced cost for columns x and y.
*/
    {
      CoinBigIndex k = mcstrt[jcolx] ;
      const int nx = hincol[jcolx] ;
      double dj = maxmin*dcost[jcolx] ;
      
      for (int kcol = 0 ; kcol < nx ; ++kcol) {
	const int row = hrow[k] ;
	const double coeff = colels[k] ;
	k = link[k] ;
	dj -= rowduals[row]*coeff ;
      }
      if (!(fabs(rcosts[jcolx]-dj) < 100*ZTOLDP))
	printf("BAD DOUBLE X DJ:  %d %d %g %g\n",
	       irow,jcolx,rcosts[jcolx],dj) ;
      rcosts[jcolx] = dj ;
    }
    {
      CoinBigIndex k = mcstrt[jcoly] ;
      const int ny = hincol[jcoly] ;
      double dj = maxmin*dcost[jcoly] ;
      
      for (int kcol = 0 ; kcol < ny ; ++kcol) {
	const int row = hrow[k] ;
	const double coeff = colels[k] ;
	k = link[k] ;
	dj -= rowduals[row]*coeff ;
      }
      if (!(fabs(rcosts[jcoly]-dj) < 100*ZTOLDP))
	printf("BAD DOUBLE Y DJ:  %d %d %g %g\n",
	       irow,jcoly,rcosts[jcoly],dj) ;
      rcosts[jcoly] = dj ;
    }
#   endif
  }
/*
  Done at last. Delete the scratch arrays.
*/
  delete [] index1 ;
  delete [] element1 ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
  presolve_check_reduced_costs(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving doubleton_action::postsolve." << std::endl ;
# endif
# endif
}
/*
  It may be the case that the bounds on the variables in a constraint are
  such that no matter what feasible value the variables take, the constraint
  cannot be violated. In this case we can drop the constraint as useless.

  On the other hand, it may be that the only way to satisfy a constraint
  is to jam all the variables in the constraint to one of their bounds, fixing
  the variables. This is a forcing constraint, the primary target of this
  transform.

  Detection of both useless and forcing constraints requires calculation of
  bounds on the row activity (often referred to as lhs bounds, from the common
  form ax <= b). This routine will remember useless constraints as it finds
  them and invoke useless_constraint_action to deal with them.
  
  The transform applied here simply tightens the bounds on the variables.
  Other transforms will remove the fixed variables, leaving an empty row which
  is ultimately dropped.

  A reasonable question to ask is ``If a variable is already fixed, why do
  we need a record in the postsolve object?'' The answer is that in postsolve
  we'll be dealing with a column-major representation and we may need to scan
  the row (see postsolve comments). So it's useful to record all variables in
  the constraint.
  
  On the other hand, it's definitely harmful to ask remove_fixed_action
  to process a variable more than once (causes problems in
  remove_fixed_action::postsolve).

  Original comments:

  It looks like these checks could be performed in parallel, that is,
  the tests could be carried out for all rows in parallel, and then the
  rows deleted and columns tightened afterward.  Obviously, this is true
  for useless rows.  By doing it in parallel rather than sequentially,
  we may miss transformations due to variables that were fixed by forcing
  constraints, though.

  Note that both of these operations will cause problems if the variables
  in question really need to exceed their bounds in order to make the
  problem feasible.
*/
const CoinPresolveAction*
  forcing_constraint_action::presolve (CoinPresolveMatrix *prob,
  				       const CoinPresolveAction *next)
{
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering forcing_constraint_action::presolve, considering "
    << prob->numberRowsToDo_ << " rows." << std::endl ;
# endif
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if COIN_PRESOLVE_TUNING
  double startTime = 0.0 ;
  if (prob->tuning_) {
    startTime = CoinCpuTime() ;
  }
# endif

  // Column solution and bounds
  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;
  double *csol = prob->sol_ ;

  // Row-major representation
  const CoinBigIndex *mrstrt = prob->mrstrt_ ;
  const double *rowels = prob->rowels_ ;
  const int *hcol = prob->hcol_ ;
  const int *hinrow = prob->hinrow_ ;
  const int nrows = prob->nrows_ ;

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

  const double tol = ZTOLDP ;
  const double inftol = prob->feasibilityTolerance_ ;
  // for redundant rows be safe
  const double inftol2 = 0.01*prob->feasibilityTolerance_ ;
  const int ncols = prob->ncols_ ;

  int *fixed_cols = new int[ncols] ;
  int nfixed_cols = 0 ;

  action *actions = new action [nrows] ;
  int nactions = 0 ;

  int *useless_rows = new int[nrows] ;
  int nuseless_rows = 0 ;

  const int numberLook = prob->numberRowsToDo_ ;
  int *look = prob->rowsToDo_ ;

  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;
/*
  Open a loop to scan the constraints of interest. There must be variables
  left in the row.
*/
  for (int iLook = 0 ; iLook < numberLook ; iLook++) {
    int irow = look[iLook] ;
    if (hinrow[irow] <= 0) continue ;

    const CoinBigIndex krs = mrstrt[irow] ;
    const CoinBigIndex kre = krs+hinrow[irow] ;
/*
  Calculate upper and lower bounds on the row activity based on upper and lower
  bounds on the variables. If these are finite and incompatible with the given
  row bounds, we have infeasibility.
*/
    double maxup, maxdown ;
    implied_row_bounds(rowels,clo,cup,hcol,krs,kre,maxup,maxdown) ;
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  considering row " << irow << ", rlo " << rlo[irow]
      << " LB " << maxdown << " UB " << maxup << " rup " << rup[irow] ;
#   endif
/*
  If the maximum lhs value is less than L(i) or the minimum lhs value is
  greater than U(i), we're infeasible.
*/
    if (maxup < PRESOLVE_INF &&
        maxup+inftol < rlo[irow] && !fixInfeasibility) {
      CoinMessageHandler *hdlr = prob->messageHandler() ;
      prob->status_|= 1 ;
      hdlr->message(COIN_PRESOLVE_ROWINFEAS,prob->messages())
	 << irow << rlo[irow] << rup[irow] << CoinMessageEol ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; infeasible." << std::endl ;
#     endif
      break ;
    }
    if (-PRESOLVE_INF < maxdown &&
        rup[irow] < maxdown-inftol && !fixInfeasibility) {
      CoinMessageHandler *hdlr = prob->messageHandler() ;
      prob->status_|= 1 ;
      hdlr->message(COIN_PRESOLVE_ROWINFEAS,prob->messages())
	 << irow << rlo[irow] << rup[irow] << CoinMessageEol ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; infeasible." << std::endl ;
#     endif
      break ;
    }
/*
  We've dealt with prima facie infeasibility. Now check if the constraint
  is trivially satisfied. If so, add it to the list of useless rows and move
  on.

  The reason we require maxdown and maxup to be finite if the row bound is
  finite is to guard against some subsequent transform changing a column
  bound from infinite to finite. Once finite, bounds continue to tighten,
  so we're safe.
*/
    /* Test changed to use +small tolerance rather than -tolerance
       as test fails often */
    if (((rlo[irow] <= -PRESOLVE_INF) ||
	 (-PRESOLVE_INF < maxdown && rlo[irow] <= maxdown+inftol2)) &&
	((rup[irow] >= PRESOLVE_INF) ||
	 (maxup < PRESOLVE_INF && rup[irow] >= maxup-inftol2))) {
      // check none prohibited
      if (prob->anyProhibited_) {
	bool anyProhibited=false;
	for (int k=krs; k<kre; k++) {
	  int jcol = hcol[k];
	  if (prob->colProhibited(jcol)) {
	    anyProhibited=true;
	    break;
	  }
	}
	if (anyProhibited)
	  continue; // skip row
      }
      useless_rows[nuseless_rows++] = irow ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; useless." << std::endl ;
#     endif
      continue ;
    }
/*
  Is it the case that we can just barely attain L(i) or U(i)? If so, we have a
  forcing constraint. As explained above, we need maxup and maxdown to be
  finite in order for the test to be valid.
*/
    const bool tightAtLower = ((maxup < PRESOLVE_INF) &&
    			       (fabs(rlo[irow]-maxup) < tol)) ;
    const bool tightAtUpper = ((-PRESOLVE_INF < maxdown) &&
			       (fabs(rup[irow]-maxdown) < tol)) ;
#   if PRESOLVE_DEBUG > 2
    if (tightAtLower || tightAtUpper) std::cout << "; forcing." ;
    std::cout << std::endl ;
#   endif
    if (!(tightAtLower || tightAtUpper)) continue ;
    // check none prohibited
    if (prob->anyProhibited_) {
      bool anyProhibited=false;
      for (int k=krs; k<kre; k++) {
	int jcol = hcol[k];
	if (prob->colProhibited(jcol)) {
	  anyProhibited=true;
	  break;
	}
      }
      if (anyProhibited)
	continue; // skip row
    }
/*
  We have a forcing constraint.
  Get down to the business of fixing the variables at the appropriate bound.
  We need to remember the original value of the bound we're tightening.
  Allocate a pair of arrays the size of the row. Load variables fixed at l<j>
  from the start, variables fixed at u<j> from the end. Add the column to
  the list of columns to be processed further.
*/
    double *bounds = new double[hinrow[irow]] ;
    int *rowcols = new int[hinrow[irow]] ;
    CoinBigIndex lk = krs ;
    CoinBigIndex uk = kre ;
    for (CoinBigIndex k = krs ; k < kre ; k++) {
      const int j = hcol[k] ;
      const double lj = clo[j] ;
      const double uj = cup[j] ;
      const double coeff = rowels[k] ;
      PRESOLVEASSERT(fabs(coeff) > ZTOLDP) ;
/*
  If maxup is tight at L(i), then we want to force variables x<j> to the bound
  that produced maxup: u<j> if a<ij> > 0, l<j> if a<ij> < 0. If maxdown is
  tight at U(i), it'll be just the opposite.
*/
      if (tightAtLower == (coeff > 0.0)) {
	--uk ;
	bounds[uk-krs] = lj ;
	rowcols[uk-krs] = j ;
	if (csol != 0) {
	  csol[j] = uj ;
	}
	clo[j] = uj ;
      } else {
	bounds[lk-krs] = uj ;
	rowcols[lk-krs] = j ;
	++lk ;
	if (csol != 0) {
	  csol[j] = lj ;
	}
	cup[j] = lj ;
      }
/*
  Only add a column to the list of fixed columns the first time it's fixed.
*/
      if (lj != uj) {
	fixed_cols[nfixed_cols++] = j ;
	prob->addCol(j) ;
      }
    }
    PRESOLVEASSERT(uk == lk) ;
    PRESOLVE_DETAIL_PRINT(printf("pre_forcing %dR E\n",irow)) ;
#   if PRESOLVE_DEBUG > 1
    std::cout
      << "FORCING: row(" << irow << "), " << (kre-krs) << " variables."
      << std::endl ;
#   endif
/*
  Done with this row. Remember the changes in a postsolve action.
*/
    action *f = &actions[nactions] ;
    nactions++ ;
    f->row = irow ;
    f->nlo = lk-krs ;
    f->nup = kre-uk ;
    f->rowcols = rowcols ;
    f->bounds = bounds ;
  }

/*
  Done processing the rows of interest.  No sense doing any additional work
  unless we're feasible.
*/
  if (prob->status_ == 0) {
#   if PRESOLVE_DEBUG > 0
    std::cout
      << "FORCING: " << nactions << " forcing, " << nuseless_rows << " useless."
      << std::endl ;
#   endif
/*
  Trim the actions array to size and create a postsolve object.
*/
    if (nactions) {
      next = new forcing_constraint_action(nactions, 
				 CoinCopyOfArray(actions,nactions),next) ;
    }
/*
  Hand off the job of dealing with the useless rows to a specialist.
*/
    if (nuseless_rows) {
      next = useless_constraint_action::presolve(prob,
      					useless_rows,nuseless_rows,next) ;
    }
/*
  Hand off the job of dealing with the fixed columns to a specialist.

  Note that there *cannot* be duplicates in this list or we'll get in trouble
  `unfixing' a column multiple times. The code above now adds a variable
  to fixed_cols only if it's not already fixed. If that ever changes,
  the disabled code (sort, unique) will need to be reenabled.
*/
    if (nfixed_cols) {
      if (false && nfixed_cols > 1) {
	std::sort(fixed_cols,fixed_cols+nfixed_cols) ;
	int *end = std::unique(fixed_cols,fixed_cols+nfixed_cols) ;
	nfixed_cols = static_cast<int>(end-fixed_cols) ;
      }
      next = remove_fixed_action::presolve(prob,fixed_cols,nfixed_cols,next) ;
    }
  } else {
    // delete arrays
    for (int i=0;i<nactions;i++) {
      deleteAction(actions[i].rowcols,int *) ;
      deleteAction(actions[i].bounds,double *) ;
    }
  }

  deleteAction(actions,action*) ;
  delete [] useless_rows ;
  delete [] fixed_cols ;

# if COIN_PRESOLVE_TUNING
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns =  prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving forcing_constraint_action::presolve: removed " << droppedRows
    << " rows, " << droppedColumns << " columns" ;
# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_)
    std::cout << " in " << (thisTime-prob->startTime_) << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
/*
  We're here to undo the bound changes that were put in place for forcing
  constraints.  This is a bit trickier than it appears.

  Assume we are working with constraint r.  The situation on arrival is
  that constraint r exists and is fully populated with fixed variables, all
  of which are nonbasic. Even though the constraint is tight, the logical
  s(r) is basic and the dual y(r) is zero.
  
  We may need to change that if a bound is relaxed to infinity on some
  variable x(t), making x(t)'s current nonbasic status untenable. We'll need
  to make s(r) nonbasic so that y(r) can be nonzero. Then we can make x(t)
  basic and use y(r) to force cbar(t) to zero. The code below will choose
  the variable x(t) whose reduced cost cbar(t) is most wrong and adjust y(r)
  to drive cbar(t) to zero using
     cbar(t) = c(t) - SUM{i\r} y(i) a(it) - y(r)a(rt)
     cbar(t) = cbar(t\r) - y(r)a(rt)
  Setting cbar(t) to zero,
     y(r) = cbar(t\r)/a(rt)

  We will need to scan row r, correcting cbar(j) for all x(j) entangled
  with the row. We may need to change the nonbasic status of x(j) if the
  adjustment causes cbar(j) to change sign.
*/
void forcing_constraint_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const action *const actions = actions_ ;
  const int nactions = nactions_ ;

  const double *colels = prob->colels_ ;
  const int *hrow = prob->hrow_ ;
  const CoinBigIndex *mcstrt = prob->mcstrt_ ;
  const int *hincol = prob->hincol_ ;
  const int *link = prob->link_ ;

  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;

  double *rcosts = prob->rcosts_ ;

  double *acts = prob->acts_ ;
  double *rowduals = prob->rowduals_ ;

  const double ztoldj = prob->ztoldj_ ;
  const double ztolzb = prob->ztolzb_ ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  const double *sol = prob->sol_ ;
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering forcing_constraint_action::postsolve, "
    << nactions << " constraints to process." << std::endl ;
# endif
  presolve_check_threads(prob) ;
  presolve_check_free_list(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# endif
/*
  Open a loop to process the actions. One action per constraint.
*/
  for (const action *f = &actions[nactions-1] ; actions <= f ; f--) {
    const int irow = f->row ;
    const int nlo = f->nlo ;
    const int nup = f->nup ;
    const int ninrow = nlo+nup ;
    const int *rowcols = f->rowcols ;
    const double *bounds = f->bounds ;

#   if PRESOLVE_DEBUG > 1
    std::cout
      << "  Restoring constraint " << irow << ", " << ninrow
      << " variables." << std::endl ;
#   endif

    PRESOLVEASSERT(prob->getRowStatus(irow) == CoinPrePostsolveMatrix::basic) ;
    PRESOLVEASSERT(rowduals[irow] == 0.0) ;
/*
  Process variables where the upper bound is relaxed.
    * If the variable is basic, we should leave the status unchanged. Relaxing
      the bound cannot make nonbasic status feasible.
    * The bound change may be a noop, in which nothing needs to be done.
    * Otherwise, the status should be set to NBLB.
*/
    bool dualfeas = true ;
    for (int k = 0 ; k < nlo ; k++) {
      const int jcol = rowcols[k] ;
      PRESOLVEASSERT(fabs(sol[jcol]-clo[jcol]) <= ztolzb) ;
      const double cbarj = rcosts[jcol] ;
      const double olduj = cup[jcol] ;
      const double newuj = bounds[k] ;
      const bool change = (fabs(newuj-olduj) > ztolzb) ;

#     if PRESOLVE_DEBUG > 2
      std::cout
        << "    x(" << jcol << ") " << prob->columnStatusString(jcol)
	<< " cbar = " << cbarj << ", lb = " << clo[jcol]
	<< ", ub = " << olduj << " -> " << newuj ;
#     endif

      if (change &&
          prob->getColumnStatus(jcol) != CoinPrePostsolveMatrix::basic) {
	prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atLowerBound) ;
	if (cbarj < -ztoldj || clo[jcol] <= -COIN_DBL_MAX) dualfeas = false ;
      }
      cup[jcol] = bounds[k] ;

#     if PRESOLVE_DEBUG > 2
      std::cout
        << " -> " << prob->columnStatusString(jcol) << "." << std::endl ;
#     endif
    }
/*
  Process variables where the lower bound is relaxed. The comments above
  apply.
*/
    for (int k = nlo ; k < ninrow ; k++) {
      const int jcol = rowcols[k] ;
      PRESOLVEASSERT(fabs(sol[jcol]-cup[jcol]) <= ztolzb) ;
      const double cbarj = rcosts[jcol] ;
      const double oldlj = clo[jcol] ;
      const double newlj = bounds[k] ;
      const bool change = (fabs(newlj-oldlj) > ztolzb) ;

#     if PRESOLVE_DEBUG > 2
      std::cout
        << "    x(" << jcol << ") " << prob->columnStatusString(jcol)
	<< " cbar = " << cbarj << ", ub = " << cup[jcol]
	<< ", lb = " << oldlj << " -> " << newlj ;
#     endif

      if (change &&
          prob->getColumnStatus(jcol) != CoinPrePostsolveMatrix::basic) {
	prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atUpperBound) ;
	if (cbarj > ztoldj || cup[jcol] >= COIN_DBL_MAX) dualfeas = false ;
      }
      clo[jcol] = bounds[k] ;

#     if PRESOLVE_DEBUG > 2
      std::cout
        << " -> " << prob->columnStatusString(jcol) << "." << std::endl ;
#     endif
    }
/*
  The reduced costs and status for the columns may or may not be ok for
  the relaxed column bounds.  If not, find the variable x<joow> most
  out-of-whack with respect to reduced cost and calculate the value of
  y<irow> required to reduce cbar<joow> to zero.
*/

    if (dualfeas == false) {
      int joow = -1 ;
      double yi = 0.0 ;
      for (int k = 0 ; k < ninrow ; k++) {
	int jcol = rowcols[k] ;
	CoinBigIndex kk = presolve_find_row2(irow,mcstrt[jcol],
					     hincol[jcol],hrow,link) ;
	const double &cbarj = rcosts[jcol] ;
	const CoinPrePostsolveMatrix::Status statj =
					prob->getColumnStatus(jcol) ;
	if ((cbarj < -ztoldj &&
	     statj != CoinPrePostsolveMatrix::atUpperBound) ||
	    (cbarj > ztoldj &&
	     statj != CoinPrePostsolveMatrix::atLowerBound)) {
	  double yi_j = cbarj/colels[kk] ;
	  if (fabs(yi_j) > fabs(yi)) {
	    joow = jcol ;
	    yi = yi_j ;
	  }
#         if PRESOLVE_DEBUG > 3
	  std::cout
	    << "      oow: x(" << jcol << ") "
	    << prob->columnStatusString(jcol) << " cbar " << cbarj << " aij "
	    << colels[kk] << " corr " << yi_j << "." << std::endl ;
#         endif
	}
      }
      assert(joow != -1) ;
/*
  Make x<joow> basic and set the row status according to whether we're
  tight at the lower or upper bound. Keep in mind the convention that a
  <= constraint has a slack 0 <= s <= infty, while a >= constraint has a
  surplus -infty <= s <= 0.
*/

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "    Adjusting row dual; x(" << joow
	<< ") " << prob->columnStatusString(joow) << " -> "
	<< statusName(CoinPrePostsolveMatrix::basic)
	<< ", y = 0.0 -> " << yi << "." << std::endl ;
#     endif

      prob->setColumnStatus(joow,CoinPrePostsolveMatrix::basic) ;
      if (acts[irow]-rlo[irow] < rup[irow]-acts[irow])
	prob->setRowStatus(irow,CoinPrePostsolveMatrix::atUpperBound) ;
      else
	prob->setRowStatus(irow,CoinPrePostsolveMatrix::atLowerBound) ;
      rowduals[irow] = yi ;

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "    Row status " << prob->rowStatusString(irow)
	<< ", lb = " << rlo[irow] << ", ax = " << acts[irow]
	<< ", ub = " << rup[irow] << "." << std::endl ;
#     endif
/*
  Now correct the reduced costs for other variables in the row. This may
  cause a reduced cost to change sign, in which case we need to change status.

  The code implicitly assumes that if it's necessary to change the status
  of a variable because the reduced cost has changed sign, then it will be
  possible to do it. I'm not sure I could prove that, however.
  -- lh, 121108 --
*/
      for (int k = 0 ; k < ninrow ; k++) {
	int jcol = rowcols[k] ;
	CoinBigIndex kk = presolve_find_row2(irow,mcstrt[jcol],
					     hincol[jcol],hrow,link) ;
	const double old_cbarj = rcosts[jcol] ;
	rcosts[jcol] -= yi*colels[kk] ;
	const double new_cbarj = rcosts[jcol] ;

	if ((old_cbarj < 0) != (new_cbarj < 0)) {
	  if (new_cbarj < -ztoldj && cup[jcol] < COIN_DBL_MAX)
	    prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atUpperBound) ;
	  else if (new_cbarj > ztoldj && clo[jcol] > -COIN_DBL_MAX)
	    prob->setColumnStatus(jcol,CoinPrePostsolveMatrix::atLowerBound) ;
	}

#       if PRESOLVE_DEBUG > 3
	const CoinPrePostsolveMatrix::Status statj =
						prob->getColumnStatus(jcol) ;
	std::cout
	  << "      corr: x(" << jcol << ") "
	  << prob->columnStatusString(jcol) << " cbar " << new_cbarj ;
	if ((new_cbarj < -ztoldj &&
	     statj != CoinPrePostsolveMatrix::atUpperBound) ||
	    (new_cbarj > ztoldj &&
	     statj != CoinPrePostsolveMatrix::atLowerBound) ||
	    (statj == CoinPrePostsolveMatrix::basic &&
	     fabs(new_cbarj) > ztoldj))
	  std::cout << " error!" << std::endl ;
	else
	  std::cout << "." << std::endl ;
#       endif

      }
    }
# if PRESOLVE_DEBUG > 0
  presolve_check_nbasic(prob) ;
# endif
  }

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_threads(prob) ;
  presolve_check_sol(prob,2,2,2) ;
  presolve_check_nbasic(prob) ;
# if PRESOLVE_DEBUG > 0
  std::cout << "Leaving forcing_constraint_action::postsolve." << std::endl ;
# endif
# endif

}
/*
  Original comments from 040916:

  This routine looks to be something of a work in progress.  Down in the
  bound propagation loop, why do we only work with variables with u_j =
  infty? The corresponding section of code for l_j = -infty is ifdef'd away.
  l<j> = -infty is uncommon; perhaps it's not worth the effort?

  Why exclude the code protected by PRESOLVE_TIGHTEN_DUALS? Why are we
  using ekkinf instead of PRESOLVE_INF?
*/
const CoinPresolveAction
  *remove_dual_action::presolve (CoinPresolveMatrix *prob,
				 const CoinPresolveAction *next)
{
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering remove_dual_action::presolve, " << prob->nrows_ 
    << "x" << prob->ncols_ << "." << std::endl ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob,2,1,1) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

  // 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_ ;
  const double *const colels = prob->colels_ ;
  const double *const cost = prob->cost_ ;

  // column type, bounds, solution, and status
  const unsigned char *const integerType = prob->integerType_ ;
  const double *const clo = prob->clo_ ;
  const double *const cup = prob->cup_ ;
  double *const csol = prob->sol_ ;
  unsigned char *&colstat = prob->colstat_ ;

  // 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_ ;
# if REMOVE_DUAL_ACTION_REPAIR_SOLN > 0
  const double *const rowels = prob->rowels_ ;
# endif

  // 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_) ;

/*
  Grab one of the preallocated scratch arrays to hold min and max values of
  row duals.
*/
  double *ymin	= prob->usefulRowDouble_ ;
  double *ymax	= ymin+nrows ;
/*
  Initialise row dual min/max. The defaults are +/- infty, but if we know
  that the logical has no upper (lower) bound, it can only be nonbasic at
  the other bound. Given minimisation, we can conclude:
    * <= constraint ==> [0,infty] ==> NBLB ==> cbar<i> > 0 ==> y<i> < 0
    * >= constraint ==> [-infty,0] ==> NBUB ==> cbar<i> < 0 ==> y<i> > 0
  Range constraints are not helpful here, the dual can be either sign. There's
  no calculation because we assume the objective coefficient c<i> = 0  and the
  coefficient a<ii> = 1.0, hence cbar<i> = -y<i>.
*/
  for (int i = 0; i < nrows; i++) {
    const bool no_lb = (rup[i] >= ekkinf) ;
    const bool no_ub = (rlo[i] <= -ekkinf) ;

    ymin[i] = ((no_lb && !no_ub)?0.0:(-PRESOLVE_INF)) ;
    ymax[i] = ((no_ub && !no_lb)?0.0:PRESOLVE_INF) ;
  }
/*
  We can do a similar calculation with singleton columns where the variable
  has only one finite bound, but we have to work a bit harder, as the cost
  coefficient c<j> is not necessarily 0.0 and a<ij> is not necessarily 1.0.

  cbar<j> = c<j> - y<i>a<ij>, hence y<i> = c<j>/a<ij> at cbar<j> = 0. The
  question is whether this is an upper or lower bound on y<i>.
    * x<j> NBLB ==> cbar<j> >= 0
      a<ij> > 0 ==> increasing y<i> decreases cbar<j> ==> upper bound 
      a<ij> < 0 ==> increasing y<i> increases cbar<j> ==> lower bound 
    * x<j> NBUB ==> cbar<j> <= 0
      a<ij> > 0 ==> increasing y<i> decreases cbar<j> ==> lower bound 
      a<ij> < 0 ==> increasing y<i> increases cbar<j> ==> upper bound 
  The condition below (simple test for equality to choose the bound) looks
  a bit odd, but a bit of boolean algebra should convince you it's correct.
  We have a bound; the only question is whether it's upper or lower.

  Skip integer variables; it's far too likely that we'll tighten infinite
  bounds elsewhere in presolve.

  NOTE: If bound propagation is applied to continuous variables, the same
  	hazard will apply.  -- lh, 110611 --
*/
  for (int j = 0 ; j < ncols ; j++) {
    if (integerType[j]) continue ;
    if (hincol[j] != 1) continue ;
    const bool no_ub = (cup[j] >= ekkinf) ;
    const bool no_lb = (clo[j] <= -ekkinf) ;
    if (no_ub != no_lb) {
      const int &i = hrow[mcstrt[j]] ;
      double aij = colels[mcstrt[j]] ;
      PRESOLVEASSERT(fabs(aij) > ZTOLDP) ;
      const double yzero = cost[j]/aij ;
      if ((aij > 0.0) == no_ub) {
	if (ymax[i] > yzero)
	  ymax[i] = yzero ;
      } else {
	if (ymin[i] < yzero)
	  ymin[i] = yzero ;
      }
    }
  }
  int nfixup_cols = 0 ;
  int nfixdown_cols = ncols ;
  // Grab another work array, sized to ncols_
  int *fix_cols	= prob->usefulColumnInt_ ;
# if PRESOLVE_TIGHTEN_DUALS > 0
  double *cbarmin = new double[ncols] ;
  double *cbarmax = new double[ncols] ;
# endif
/*
  Now we have (admittedly weak) bounds on the dual for each row. We can use
  these to calculate upper and lower bounds on cbar<j>. Open loops to
  take multiple passes over all columns.
*/
  int nPass = 0 ;
  while (nPass++ < 100) {
    int tightened = 0 ;
    for (int j = 0 ; j < ncols ; j++) {
      if (hincol[j] <= 0) continue ;
/*
  Calculate min cbar<j> and max cbar<j> for the column by calculating the
  contribution to c<j>-ya<j> using ymin<i> and ymax<i> from above.
*/
      const CoinBigIndex &kcs = mcstrt[j] ;
      const CoinBigIndex kce = kcs + hincol[j] ;
      // Number of infinite rows
      int nflagu = 0 ;
      int nflagl = 0 ;
      // Number of ordinary rows
      int nordu = 0 ;
      int nordl = 0 ;
      double cbarjmin = cost[j] ;
      double cbarjmax = cbarjmin ;
      for (CoinBigIndex k = kcs ; k < kce ; k++) {
	const int &i = hrow[k] ;
	const double &aij = colels[k] ;
	const double mindelta = aij*ymin[i] ;
	const double maxdelta = aij*ymax[i] ;
	
	if (aij > 0.0) {
	  if (ymin[i] >= -ekkinf2) {
	    cbarjmax -= mindelta ;
	    nordu++ ;
	  } else {
	    nflagu++ ;
	  }
	  if (ymax[i] <= ekkinf2) {
	    cbarjmin -= maxdelta ;
	    nordl++ ;
	  } else {
	    nflagl++ ;
	  }
	} else {
	  if (ymax[i] <= ekkinf2) {
	    cbarjmax -= maxdelta ;
	    nordu++ ;
	  } else {
	    nflagu++ ;
	  }
	  if (ymin[i] >= -ekkinf2) {
	    cbarjmin -= mindelta ;
	    nordl++ ;
	  } else {
	    nflagl++ ;
	  }
	}
      }
/*
  See if we can tighten bounds on a dual y<t>. See the comments at the head of
  the file for the linear algebra.

  At this point, I don't understand the restrictions on propagation. Neither
  are necessary. The net effect is to severely restrict the circumstances
  where we'll propagate. Requiring nflagu == 1 excludes the case where all
  duals have finite bounds (unlikely?). And why cbarjmax < -ztolcbarj?

  In any event, we're looking for the row that's contributing the infinity. If
  a<tj> > 0, the contribution is due to ymin<t> and we tighten ymax<t>;
  a<tj> < 0, ymax<t> and ymin<t>, respectively.

  The requirement cbarjmax < (ymax[i]*aij-ztolcbarj) ensures we don't propagate
  tiny changes. If we make a change, it will affect cbarjmin. Make the
  adjustment immediately.

  Continuous variables only.
*/
      if (!integerType[j]) {
	if (cup[j] > ekkinf) {
	  if (nflagu == 1 && cbarjmax < -ztolcbarj) {
	    for (CoinBigIndex k = kcs ; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double aij = colels[k] ;
	      if (aij > 0.0 && ymin[i] < -ekkinf2) {
		if (cbarjmax < (ymax[i]*aij-ztolcbarj)) {
		  const double newValue = cbarjmax/aij ;
		  if (ymax[i] > ekkinf2 && newValue <= ekkinf2) {
		    nflagl-- ;
		    cbarjmin -= aij*newValue ;
		  } else if (ymax[i] <= ekkinf2) {
		    cbarjmin -= aij*(newValue-ymax[i]) ;
		  }
#		  if PRESOLVE_DEBUG > 1
		  std::cout
		    << "NDUAL(infu/inf): u(" << j << ") = " << cup[j]
		    << ": max y(" << i << ") was " << ymax[i]
		    << " now " << newValue << "." << std::endl ;
#		  endif
		  ymax[i] = newValue ;
		  tightened++ ;
		}
	      } else if (aij < 0.0 && ymax[i] > ekkinf2) {
		if (cbarjmax < (ymin[i]*aij-ztolcbarj)) {
		  const double newValue = cbarjmax/aij ;
		  if (ymin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		    nflagl-- ;
		    cbarjmin -= aij*newValue ;
		  } else if (ymin[i] >= -ekkinf2) {
		    cbarjmin -= aij*(newValue-ymin[i]) ;
		  }
#		  if PRESOLVE_DEBUG > 1
		  std::cout
		    << "NDUAL(infu/inf): u(" << j << ") = " << cup[j]
		    << ": min y(" << i << ") was " << ymin[i]
		    << " now " << newValue << "." << std::endl ;
#		  endif
		  ymin[i] = newValue ;
		  tightened++ ;
		  // Huh? asymmetric 
		  // cbarjmin = 0.0 ;
		}
	      }
	    }
	  } else if (nflagl == 0 && nordl == 1 && cbarjmin < -ztolcbarj) {
/*
  This is a column singleton. Why are we doing this? It's not like changes
  to other y will affect this.
*/
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double aij = colels[k] ;
	      if (aij > 0.0) {
#		if PRESOLVE_DEBUG > 1
		std::cout
		  << "NDUAL(infu/sing): u(" << j << ") = " << cup[j]
		  << ": max y(" << i << ") was " << ymax[i]
		  << " now " << ymax[i]+cbarjmin/aij << "." << std::endl ;
#		endif
		ymax[i] += cbarjmin/aij ;
		cbarjmin = 0.0 ;
		tightened++ ;
	      } else if (aij < 0.0 ) {
#		if PRESOLVE_DEBUG > 1
		std::cout
		  << "NDUAL(infu/sing): u(" << j << ") = " << cup[j]
		  << ": min y(" << i << ") was " << ymin[i]
		  << " now " << ymin[i]+cbarjmin/aij << "." << std::endl ;
#		endif
		ymin[i] += cbarjmin/aij ;
		cbarjmin = 0.0 ;
		tightened++ ;
	      }
	    }
	  }
	}   // end u<j> = infty
#       if PROCESS_INFINITE_LB
/*
  Unclear why this section is commented out, except for the possibility that
  bounds of -infty < x < something are rare and it likely suffered fromm the
  same errors. Consider the likelihood that this whole block needs to be
  edited to sway min/max, & similar.
*/
	if (clo[j]<-ekkinf) {
	  // cbarj can not be positive
	  if (cbarjmin > ztolcbarj&&nflagl == 1) {
	    // We can make bound finite one way
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double coeff = colels[k] ;
	      
	      if (coeff < 0.0&&ymin[i] < -ekkinf2) {
		// ymax[i] has upper bound
		if (cbarjmin>ymax[i]*coeff+ztolcbarj) {
		  const double newValue = cbarjmin/coeff ;
		  // re-compute hi
		  if (ymax[i] > ekkinf2 && newValue <= ekkinf2) {
		    nflagu-- ;
		    cbarjmax -= coeff * newValue ;
		  } else if (ymax[i] <= ekkinf2) {
		    cbarjmax -= coeff * (newValue-ymax[i]) ;
		  }
		  ymax[i] = newValue ;
		  tightened++ ;
#		  if PRESOLVE_DEBUG > 1
		  printf("Col %d, row %d max pi now %g\n",j,i,ymax[i]) ;
#		  endif
		}
	      } else if (coeff > 0.0 && ymax[i] > ekkinf2) {
		// ymin[i] has lower bound
		if (cbarjmin>ymin[i]*coeff+ztolcbarj) {
		  const double newValue = cbarjmin/coeff ;
		  // re-compute lo
		  if (ymin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		    nflagu-- ;
		    cbarjmax -= coeff * newValue ;
		  } else if (ymin[i] >= -ekkinf2) {
		    cbarjmax -= coeff*(newValue-ymin[i]) ;
		  }
		  ymin[i] = newValue ;
		  tightened++ ;
#		  if PRESOLVE_DEBUG > 1
		  printf("Col %d, row %d min pi now %g\n",j,i,ymin[i]) ;
#		  endif
		}
	      }
	    }
	  } else if (nflagu == 0 && nordu == 1 && cbarjmax > ztolcbarj) {
	    // We may be able to tighten
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double coeff = colels[k] ;
	      
	      if (coeff < 0.0) {
		ymax[i] += cbarjmax/coeff ;
		cbarjmax =0.0 ;
		tightened++ ;
#		if PRESOLVE_DEBUG > 1
		printf("Col %d, row %d max pi now %g\n",j,i,ymax[i]) ;
#		endif
	      } else if (coeff > 0.0 ) {
		ymin[i] += cbarjmax/coeff ;
		cbarjmax =0.0 ;
		tightened++ ;
#		if PRESOLVE_DEBUG > 1
		printf("Col %d, row %d min pi now %g\n",j,i,ymin[i]) ;
#		endif
	      }
	    }
	  }
	}    // end l<j> < -infty
#       endif	// PROCESS_INFINITE_LB
      }
/*
  That's the end of propagation of bounds for dual variables.
*/
#     if PRESOLVE_TIGHTEN_DUALS > 0
      cbarmin[j] = (nflagl?(-PRESOLVE_INF):cbarjmin) ;
      cbarmax[j] = (nflagu?PRESOLVE_INF:cbarjmax) ;
#     endif
/*
  If cbarmin<j> > 0 (strict inequality) then x<j> NBLB at optimality. If l<j>
  is -infinity, notify the user and set the status to unbounded.
*/
      if (cbarjmin > ztolcbarj && nflagl == 0 && !prob->colProhibited2(j)) {
	if (clo[j] <= -ekkinf) {
	  CoinMessageHandler *msghdlr = prob->messageHandler() ;
	  msghdlr->message(COIN_PRESOLVE_COLUMNBOUNDB,prob->messages())
	    << j << CoinMessageEol ;
	  prob->status_ |= 2 ;
	  break ;
	} else {
	  fix_cols[--nfixdown_cols] = j ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout << "NDUAL(fix l): fix x(" << j << ")" ;
	  if (csol) std::cout << " = " << csol[j] ;
	  std::cout
	    << " at l(" << j << ") = " << clo[j] << "; cbar("
	    << j << ") > " << cbarjmin << "." << std::endl ;
#	  endif
	  if (csol) {
#           if REMOVE_DUAL_ACTION_REPAIR_SOLN > 0
/*
  Original comment: User may have given us feasible solution - move if simple

  Except it's not simple. The net result is that we end up with an excess of
  basic variables. Mark x<j> NBLB and let the client recalculate the solution
  after establishing the basis.
*/
	    if (csol[j]-clo[j] > 1.0e-7 && hincol[j] == 1) {
	      double value_j = colels[mcstrt[j]] ;
	      double distance_j = csol[j]-clo[j] ;
	      int row = hrow[mcstrt[j]] ;
	      // See if another column can take value
	      for (CoinBigIndex kk = mrstrt[row] ; kk < mrstrt[row]+hinrow[row] ; kk++) {
		const int k = hcol[kk] ;
		if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		  continue ;

		if (hincol[k] == 1 && k != j) {
		  const double value_k = rowels[kk] ;
		  double movement ;
		  if (value_k*value_j>0.0) {
		    // k needs to increase
		    double distance_k = cup[k]-csol[k] ;
		    movement = CoinMin((distance_j*value_j)/value_k,distance_k) ;
		  } else {
		    // k needs to decrease
		    double distance_k = clo[k]-csol[k] ;
		    movement = CoinMax((distance_j*value_j)/value_k,distance_k) ;
		  }
		  if (relEq(movement,0)) continue ;

		  csol[k] += movement ;
		  if (relEq(csol[k],clo[k]))
		  { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		  else
		  if (relEq(csol[k],cup[k]))
		  { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		  else
		  if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		  { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		  printf("NDUAL: x<%d> moved %g to %g; ",
			 k,movement,csol[k]) ;
		  printf("lb = %g, ub = %g, status now %s.\n",
			 clo[k],cup[k],columnStatusString(k)) ;
		  distance_j -= (movement*value_k)/value_j ;
		  csol[j] -= (movement*value_k)/value_j ;
		  if (distance_j<1.0e-7)
		    break ;
		}
	      }
	    }
#	    endif    // repair solution.

	    csol[j] = clo[j] ;
	    colstat[j] = CoinPrePostsolveMatrix::atLowerBound ;
	  }
	}
      } else if (cbarjmax < -ztolcbarj && nflagu == 0 &&
      		 !prob->colProhibited2(j)) {
/*
  If cbarmax<j> < 0 (strict inequality) then x<j> NBUB at optimality. If u<j>
  is infinity, notify the user and set the status to unbounded.
*/
	if (cup[j] >= ekkinf) {
	  CoinMessageHandler *msghdlr = prob->messageHandler() ;
	  msghdlr->message(COIN_PRESOLVE_COLUMNBOUNDA,prob->messages())
	    << j << CoinMessageEol ;
	  prob->status_ |= 2 ;
	  break ;
	} else {
	  fix_cols[nfixup_cols++] = j ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout << "NDUAL(fix u): fix x(" << j << ")" ;
	  if (csol) std::cout << " = " << csol[j] ;
	  std::cout
	    << " at u(" << j << ") = " << cup[j] << "; cbar("
	    << j << ") < " << cbarjmax << "." << std::endl ;
#	  endif
	  if (csol) {
#	    if 0
	    // See comments above for 'fix at lb'.
	    if (cup[j]-csol[j] > 1.0e-7 && hincol[j] == 1) {
	      double value_j = colels[mcstrt[j]] ;
	      double distance_j = csol[j]-cup[j] ;
	      int row = hrow[mcstrt[j]] ;
	      // See if another column can take value
	      for (CoinBigIndex kk = mrstrt[row] ; kk < mrstrt[row]+hinrow[row] ; kk++) {
		const int k = hcol[kk] ;
		if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		  continue ;

		if (hincol[k] == 1 && k != j) {
		  const double value_k = rowels[kk] ;
		  double movement ;
		  if (value_k*value_j<0.0) {
		    // k needs to increase
		    double distance_k = cup[k]-csol[k] ;
		    movement = CoinMin((distance_j*value_j)/value_k,distance_k) ;
		  } else {
		    // k needs to decrease
		    double distance_k = clo[k]-csol[k] ;
		    movement = CoinMax((distance_j*value_j)/value_k,distance_k) ;
		  }
		  if (relEq(movement,0)) continue ;

		  csol[k] += movement ;
		  if (relEq(csol[k],clo[k]))
		  { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		  else
		  if (relEq(csol[k],cup[k]))
		  { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		  else
		  if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		  { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		  printf("NDUAL: x<%d> moved %g to %g; ",
			 k,movement,csol[k]) ;
		  printf("lb = %g, ub = %g, status now %s.\n",
			 clo[k],cup[k],columnStatusString(k)) ;
		  distance_j -= (movement*value_k)/value_j ;
		  csol[j] -= (movement*value_k)/value_j ;
		  if (distance_j>-1.0e-7)
		    break ;
		}
	      }
	    }
#	    endif
	    csol[j] = cup[j] ;
	    colstat[j] = CoinPrePostsolveMatrix::atUpperBound ;
	  }
	}
      }    // end cbar<j> < 0
    }
/*
  That's the end of this walk through the columns.
*/
    // I don't know why I stopped doing this.
#   if PRESOLVE_TIGHTEN_DUALS > 0
    const double *rowels	= prob->rowels_ ;
    const int *hcol	= prob->hcol_ ;
    const CoinBigIndex *mrstrt	= prob->mrstrt_ ;
    int *hinrow	= prob->hinrow_ ;
    // tighten row dual bounds, as described on p. 229
    for (int i = 0; i < nrows; i++) {
      const bool no_ub = (rup[i] >= ekkinf) ;
      const bool no_lb = (rlo[i] <= -ekkinf) ;

      if ((no_ub ^ no_lb) == true) {
	const CoinBigIndex krs = mrstrt[i] ;
	const CoinBigIndex kre = krs + hinrow[i] ;
	const double rmax  = ymax[i] ;
	const double rmin  = ymin[i] ;

	// all row columns are non-empty
	for (CoinBigIndex k = krs ; k < kre ; k++) {
	  const double coeff = rowels[k] ;
	  const int icol = hcol[k] ;
	  const double cbarmax0 = cbarmax[icol] ;
	  const double cbarmin0 = cbarmin[icol] ;

	  if (no_ub) {
	    // cbarj must not be negative
	    if (coeff > ZTOLDP2 &&
	    	cbarjmax0 < PRESOLVE_INF && cup[icol] >= ekkinf) {
	      const double bnd = cbarjmax0 / coeff ;
	      if (rmax > bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MAX TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymax[i],bnd) ;
#		endif
		ymax[i] = rmax = bnd ;
		tightened ++; ;
	      }
	    } else if (coeff < -ZTOLDP2 &&
	    	       cbarjmax0 <PRESOLVE_INF && cup[icol] >= ekkinf) {
	      const double bnd = cbarjmax0 / coeff ;
	      if (rmin < bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MIN TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymin[i],bnd) ;
#		endif
		ymin[i] = rmin = bnd ;
		tightened ++; ;
	      }
	    }
	  } else {	// no_lb
	    // cbarj must not be positive
	    if (coeff > ZTOLDP2 &&
	    	cbarmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      const double bnd = cbarmin0 / coeff ;
	      if (rmin < bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MIN1 TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymin[i],bnd) ;
#		endif
		ymin[i] = rmin = bnd ;
		tightened ++; ;
	      }
	    } else if (coeff < -ZTOLDP2 &&
	    	       cbarmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      const double bnd = cbarmin0 / coeff ;
	      if (rmax > bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MAX TIGHT1[%d,%d]: %g --> %g\n",i,hrow[k],ymax[i],bnd) ;
#		endif
		ymax[i] = rmax = bnd ;
		tightened ++; ;
	      }
	    }
	  }
	}
      }
    }
#   endif    // PRESOLVE_TIGHTEN_DUALS
/*
  Is it productive to continue with another pass? Essentially, we need lots of
  tightening but no fixing. If we fixed any variables, break and process them.
*/
#   if PRESOLVE_DEBUG > 1
    std::cout
      << "NDUAL: pass " << nPass << ": fixed " << (ncols-nfixdown_cols)
      << " down, " << nfixup_cols << " up, tightened " << tightened
      << " duals." << std::endl ;
#   endif
    if (tightened < 100 || nfixdown_cols < ncols || nfixup_cols)
      break ;
  }
  assert (nfixup_cols <= nfixdown_cols) ;
/*
  Process columns fixed at upper bound.
*/
  if (nfixup_cols) {
#   if PRESOLVE_DEBUG > 1
    std::cout << "NDUAL(upper):" ;
    for (int k = 0 ; k < nfixup_cols ; k++)
      std::cout << " " << fix_cols[k] ;
    std::cout << "." << std::endl ;
#   endif
    next = make_fixed_action::presolve(prob,fix_cols,nfixup_cols,false,next) ;
  }
/*
  Process columns fixed at lower bound.
*/
  if (nfixdown_cols < ncols) {
    int *fixdown_cols = fix_cols+nfixdown_cols ; 
    nfixdown_cols = ncols-nfixdown_cols ;
#   if PRESOLVE_DEBUG > 1
    std::cout << "NDUAL(lower):" ;
    for (int k = 0 ; k < nfixdown_cols ; k++)
      std::cout << " " << fixdown_cols[k] ;
    std::cout << "." << std::endl ;
#   endif
    next = make_fixed_action::presolve(prob,fixdown_cols,
    				       nfixdown_cols,true,next) ;
  }
/*
  Now look for variables that, when moved in the favourable direction
  according to reduced cost, will naturally tighten an inequality to an
  equality. We can convert that inequality to an equality. See the comments
  at the head of the routine.

  Start with logicals. Open a loop and look for suitable rows. At the
  end of this loop, rows marked with +/-1 will be forced to equality by
  their logical. Rows marked with +/-2 are inequalities that (perhaps)
  can be forced to equality using architecturals. Rows marked with 0 are
  not suitable (range or nonbinding).

  Note that usefulRowInt_ is 3*nrows_; we'll use the second partition below.
*/
  int *canFix = prob->usefulRowInt_ ;
  for (int i = 0 ; i < nrows ; i++) {
    const bool no_rlb = (rlo[i] <= -ekkinf) ;
    const bool no_rub = (rup[i] >= ekkinf) ;
    canFix[i] = 0 ;
    if (no_rub && !no_rlb ) {
      if (ymin[i] > 0.0) 
	canFix[i] = -1 ;
      else
	canFix[i] = -2 ;
    } else if (no_rlb && !no_rub ) {
      if (ymax[i] < 0.0)
	canFix[i] = 1 ;
      else
	canFix[i] = 2 ;
    }
#   if PRESOLVE_DEBUG > 1
    if (abs(canFix[i]) == 1) {
      std::cout
        << "NDUAL(eq): candidate row (" << i << ") (" << rlo[i]
	<< "," << rup[i] << "), logical must be "
	<< ((canFix[i] == -1)?"NBUB":"NBLB") << "." << std::endl ;
    }
#   endif
  }
/*
  Can we do a similar trick with architectural variables? Here, we're looking
  for x<j> such that
  (1) Exactly one of l<j> or u<j> is infinite.
  (2) Moving x<j> towards the infinite bound can tighten exactly one
      constraint i to equality.  If x<j> is entangled with other constraints,
      moving x<j> towards the infinite bound will loosen those constraints.
  (3) Moving x<j> towards the infinite bound is a good idea according to the
      cost c<j> (note we don't have to consider reduced cost here).
  If we can find a suitable x<j>, constraint i can become an equality.

  This is yet another instance of bound propagation, but we're looking for
  a very specific pattern: A variable that can be increased arbitrarily
  in all rows it's entangled with, except for one, which bounds it. And
  we're going to push the variable so as to make that row an equality.
  But note what we're *not* doing: No actual comparison of finite bound
  values to the amount necessary to force an equality. So no worries about
  accuracy, the bane of bound propagation.

  Open a loop to scan columns. bindingUp and bindingDown indicate the result
  of the analysis; -1 says `possible', -2 is ruled out. Test first for
  condition (1). Column singletons are presumably handled elsewhere. Integer
  variables need not apply. If both bounds are finite, no need to look
  further.
*/
  for (int j = 0 ; j < ncols ; j++) {
    if (hincol[j] <= 1) continue ;
    if (integerType[j]) continue ;
    int bindingUp = -1 ;
    int bindingDown = -1 ;
    if (cup[j] < ekkinf)
      bindingUp = -2 ;
    if (clo[j] > -ekkinf)
      bindingDown = -2 ;
    if (bindingUp == -2 && bindingDown == -2) continue ;
/*
  Open a loop to walk the column and check for condition (2).

  The test for |canFix[i]| != 2 is a non-interference check. We don't want to
  mess with constraints where we've already decided to use the logical to
  force equality. Nor do we want to deal with range or nonbinding constraints.
*/
    const CoinBigIndex &kcs = mcstrt[j] ;
    const CoinBigIndex kce = kcs+hincol[j] ;
    for (CoinBigIndex k = kcs; k < kce; k++) {
      const int &i = hrow[k] ;
      if (abs(canFix[i]) != 2) {
	bindingUp = -2 ;
	bindingDown = -2 ;
	break ;
      }
      double aij = colels[k] ;
/*
  For a<ij> > 0 in a <= constraint (canFix = 2), the up direction is
  binding. For a >= constraint, it'll be the down direction. If the relevant
  binding code is still -1, set it to the index of the row. Similarly for
  a<ij> < 0.

  If this is the second or subsequent binding constraint in that direction,
  set binding[Up,Down] to -2 (we don't want to get into the business of
  calculating which constraint is actually binding).
*/
      if (aij > 0.0) {
	if (canFix[i] == 2) {
	  if (bindingUp == -1)
	    bindingUp = i ;
	  else
	    bindingUp = -2 ;
	} else {
	  if (bindingDown == -1)
	    bindingDown = i ;
	  else
	    bindingDown = -2 ;
	}
      } else {
	if (canFix[i] == 2) {
	  if (bindingDown == -1)
	    bindingDown = i ;
	  else
	    bindingDown = -2 ;
	} else {
	  if (bindingUp == -1)
	    bindingUp = i ;
	  else
	    bindingUp = -2 ;
	}
      }
    }
    if (bindingUp == -2 && bindingDown == -2) continue ;
/*
  If bindingUp > -2, then either no constraint provided a bound (-1) or
  there's a single constraint (0 <= i < m) that bounds x<j>.  If we have
  just one binding constraint, check that the reduced cost is favourable
  (c<j> <= 0 for x<j> NBUB at optimum for minimisation). If so, declare
  that we will force the row to equality (canFix[i] = +/-1). Note that we
  don't adjust the primal solution value for x<j>.

  If no constraint provided a bound, we might be headed for unboundedness,
  but leave that for some other code to determine.
*/
    double cj = cost[j] ;
    if (bindingUp > -2 && cj <= 0.0) {
      if (bindingUp >= 0) {
	canFix[bindingUp] /= 2 ;
#       if PRESOLVE_DEBUG > 1
	std::cout
	  << "NDUAL(eq): candidate row (" << bindingUp << ") ("
	  << rlo[bindingUp] << "," << rup[bindingUp] << "), "
	  << " increasing x(" << j << "), cbar = " << cj << "."
	  << std::endl ;
      } else {
          std::cout
	    << "NDUAL(eq): no binding upper bound for x(" << j
	    << "), cbar = " << cj << "." << std::endl ;
#        endif
      }
    } else if (bindingDown > -2 && cj >= 0.0) {
      if (bindingDown >= 0) {
	canFix[bindingDown] /= 2 ;
#       if PRESOLVE_DEBUG > 1
	std::cout
	  << "NDUAL(eq): candidate row (" << bindingDown << ") ("
	  << rlo[bindingDown] << "," << rup[bindingDown] << "), "
	  << " decreasing x(" << j << "), cbar = " << cj << "."
	  << std::endl ;
      } else {
          std::cout
	    << "NDUAL(eq): no binding lower bound for x(" << j
	    << "), cbar = " << cj << "." << std::endl ;
#        endif
      }
    }
  }
/*
  We have candidate rows. We've avoided scanning full rows until now,
  but there's one remaining hazard: if the row contains unfixed integer
  variables then we don't want to just pin the row to a fixed rhs; that
  might prevent us from achieving integrality. Scan canFix, count and
  record suitable rows (use the second partition of usefulRowInt_).
*/
# if PRESOLVE_DEBUG > 0
  int makeEqCandCnt = 0 ;
  for (int i = 0 ; i < nrows ; i++) {
    if (abs(canFix[i]) == 1) makeEqCandCnt++ ;
  }
# endif
  int makeEqCnt = nrows ;
  for (int i = 0 ; i < nrows ; i++) {
    if (abs(canFix[i]) == 1) {
      const CoinBigIndex &krs = mrstrt[i] ;
      const CoinBigIndex kre = krs+hinrow[i] ;
      for (CoinBigIndex k = krs ; k < kre ; k++) {
	const int j = hcol[k] ;
	if (cup[j] > clo[j] && (integerType[j]||prob->colProhibited2(j))) {
	  canFix[i] = 0 ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout
	    << "NDUAL(eq): cannot convert row " << i << " to equality; "
	    << "unfixed integer variable x(" << j << ")." << std::endl ;
#         endif
          break ;
	}
      }
      if (canFix[i] != 0) canFix[makeEqCnt++] = i ;
    }
  }
  makeEqCnt -= nrows ;
# if PRESOLVE_DEBUG > 0
  if ((makeEqCandCnt-makeEqCnt) > 0) {
    std::cout
      << "NDUAL(eq): rejected " << (makeEqCandCnt-makeEqCnt)
      << " rows due to unfixed integer variables." << std::endl ;
  }
# endif
/*
  If we've identified inequalities to convert, do the conversion, record
  the information needed to restore bounds in postsolve, and finally create
  the postsolve object.
*/
  if (makeEqCnt > 0) {
    action *bndRecords = new action[makeEqCnt] ;
    for (int k = 0 ; k < makeEqCnt ; k++) {
      const int &i = canFix[k+nrows] ;
#     if PRESOLVE_DEBUG > 1
      std::cout << "NDUAL(eq): forcing row " << i << " to equality;" ;
      if (canFix[i] == -1)
	std::cout << " dropping b = " << rup[i] << " to " << rlo[i] ;
      else
	std::cout << " raising blow = " << rlo[i] << " to " << rup[i] ;
      std::cout << "." << std::endl ;
#     endif
      action &bndRec = bndRecords[(k)] ;
      bndRec.rlo_ = rlo[i] ;
      bndRec.rup_ = rup[i] ;
      bndRec.ndx_ = i ;
      if (canFix[i] == 1) {
	rlo[i] = rup[i] ;
	prob->addRow(i) ;
      } else if (canFix[i] == -1) {
	rup[i] = rlo[i] ;
	prob->addRow(i) ;
      }
    }
    next = new remove_dual_action(makeEqCnt,bndRecords,next) ;
  }

# if PRESOLVE_TIGHTEN_DUALS > 0
  delete[] cbarmin ;
  delete[] cbarmax ;
# endif

# if COIN_PRESOLVE_TUNING > 0
  double thisTime = 0.0 ;
  if (prob->tuning_) thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob,2,1,1) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving remove_dual_action::presolve, dropped " << droppedRows
    << " rows, " << droppedColumns << " columns, forced "
    << makeEqCnt << " equalities" ;
# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_)
    std::cout << " in " << (thisTime-prob->startTime_) << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
Exemple #22
0
const CoinPresolveAction *drop_empty_rows_action::presolve(CoinPresolveMatrix *prob,
				       const CoinPresolveAction *next)
{
  int ncols	= prob->ncols_;
  CoinBigIndex *mcstrt	= prob->mcstrt_;
  int *hincol	= prob->hincol_;
  int *hrow	= prob->hrow_;

  int nrows	= prob->nrows_;
  // This is done after row copy needed
  //int *mrstrt	= prob->mrstrt_;
  int *hinrow	= prob->hinrow_;
  //int *hcol	= prob->hcol_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  unsigned char *rowstat	= prob->rowstat_;
  double *acts	= prob->acts_;
  int * originalRow  = prob->originalRow_;

  //presolvehlink *rlink = prob->rlink_;
  bool fixInfeasibility = (prob->presolveOptions_&16384)!=0;
  // Relax tolerance
  double tolerance = 10.0*prob->feasibilityTolerance_;


  int i;
  int nactions = 0;
  for (i=0; i<nrows; i++)
    if (hinrow[i] == 0)
      nactions++;

  if (nactions == 0)
    return next;
  else {
    action *actions 	= new action[nactions];
    int * rowmapping = new int [nrows];

    nactions = 0;
    int nrows2=0;
    for (i=0; i<nrows; i++) {
      if (hinrow[i] == 0) {
	action &e = actions[nactions];

#       if PRESOLVE_DEBUG
	if (nactions == 0)
	  printf("UNUSED ROWS:  ");
	else
	if (i < 100 && nactions%25 == 0)
	  printf("\n") ;
	else
	if (i >= 100 && i < 1000 && nactions%19 == 0)
	  printf("\n") ;
	else
	if (i >= 1000 && nactions%15 == 0)
	  printf("\n") ;
	printf("%d ", i);
#	endif

	nactions++;
	if (rlo[i] > 0.0 || rup[i] < 0.0) {
	  if ((rlo[i]<=tolerance &&
	       rup[i]>=-tolerance)||fixInfeasibility) {
	    rlo[i]=0.0;
	    rup[i]=0.0;
	  } else {
	    prob->status_|= 1;
	  prob->messageHandler()->message(COIN_PRESOLVE_ROWINFEAS,
					     prob->messages())
					       <<i
					       <<rlo[i]
					       <<rup[i]
					       <<CoinMessageEol;
	    break;
	  }
	}
	e.row	= i;
	e.rlo	= rlo[i];
	e.rup	= rup[i];
	rowmapping[i]=-1;

      } else {
	// move down - we want to preserve order
	rlo[nrows2]=rlo[i];
	rup[nrows2]=rup[i];
	originalRow[nrows2]=i;
	if (acts) {
	  acts[nrows2]=acts[i];
	  rowstat[nrows2]=rowstat[i];
	}
	rowmapping[i]=nrows2++;
      }
    }

    // remap matrix
    for (i=0;i<ncols;i++) {
      int j;
      for (j=mcstrt[i];j<mcstrt[i]+hincol[i];j++)
	hrow[j] = rowmapping[hrow[j]];
    }
    delete [] rowmapping;

    prob->nrows_ = nrows2;

#if	PRESOLVE_DEBUG
    presolve_check_nbasic(prob) ;
    if (nactions)
      printf("\ndropped %d rows\n", nactions);
#endif
    return (new drop_empty_rows_action(nactions, actions, next));
  }
}