void drop_zero_coefficients_action::postsolve(CoinPostsolveMatrix *prob) const
{
  const int nzeros	= nzeros_;
  const dropped_zero *const zeros = zeros_;

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

  for (const dropped_zero *z = &zeros[nzeros-1]; zeros<=z; z--) {
    int irow	= z->row;
    int jcol	= z->col;

    {
      CoinBigIndex k = free_list;
      assert(k >= 0 && k < prob->bulk0_) ;
      free_list = link[free_list];
      hrow[k] = irow;
      colels[k] = 0.0;
      link[k] = mcstrt[jcol];
      mcstrt[jcol] = k;
    }
    
    hincol[jcol]++;
  }

# if PRESOLVE_CONSISTENCY
  presolve_check_free_list(prob) ;
# endif

} 
/*
 * 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 ;
}
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 ;
}
/*
  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

}
/*
  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
}
void
CoinPostsolveMatrix::assignPresolveToPostsolve (CoinPresolveMatrix *&preObj)

{
/*
  Start with simple data --- allocated and current size.
*/
  ncols0_ = preObj->ncols0_ ;
  nrows0_ = preObj->nrows0_ ;
  nelems0_ = preObj->nelems0_ ;
  bulk0_ = preObj->bulk0_ ;

  ncols_ = preObj->ncols_ ;
  nrows_ = preObj->nrows_ ;
  nelems_ = preObj->nelems_ ;
/*
  Now bring over the column-major matrix and other problem data.
*/
  mcstrt_ = preObj->mcstrt_ ;
  preObj->mcstrt_ = 0 ;
  hincol_ = preObj->hincol_ ;
  preObj->hincol_ = 0 ;
  hrow_ = preObj->hrow_ ;
  preObj->hrow_ = 0 ;
  colels_ = preObj->colels_ ;
  preObj->colels_ = 0 ;

  cost_ = preObj->cost_ ;
  preObj->cost_ = 0 ;
  originalOffset_ = preObj->originalOffset_ ;
  clo_ = preObj->clo_ ;
  preObj->clo_ = 0 ;
  cup_ = preObj->cup_ ;
  preObj->cup_ = 0 ;
  rlo_ = preObj->rlo_ ;
  preObj->rlo_ = 0 ;
  rup_ = preObj->rup_ ;
  preObj->rup_ = 0 ;

  originalColumn_ = preObj->originalColumn_ ;
  preObj->originalColumn_ = 0 ;
  originalRow_ = preObj->originalRow_ ;
  preObj->originalRow_ = 0 ;

  ztolzb_ = preObj->ztolzb_ ;
  ztoldj_ = preObj->ztoldj_ ;
  maxmin_ = preObj->maxmin_ ;
/*
  Now the problem solution. Often this will be empty, but that's not a problem.
*/
  sol_ = preObj->sol_ ;
  preObj->sol_ = 0 ;
  rowduals_ = preObj->rowduals_ ;
  preObj->rowduals_ = 0 ;
  acts_ = preObj->acts_ ;
  preObj->acts_ = 0 ;
  rcosts_ = preObj->rcosts_ ;
  preObj->rcosts_ = 0 ;
  colstat_ = preObj->colstat_ ;
  preObj->colstat_ = 0 ;
  rowstat_ = preObj->rowstat_ ;
  preObj->rowstat_ = 0 ;
/*
  The CoinPostsolveMatrix comes with messages and a handler, but replace them
  with the versions from the CoinPresolveObject, in case they've been
  customized. Let preObj believe it's no longer responsible for the handler.
*/
  if (defaultHandler_ == true)
    delete handler_ ;
  handler_ = preObj->handler_ ;
  preObj->defaultHandler_ = false ;
  messages_ = preObj->messages_ ;
/*
  Initialise the postsolve portions of this object. Which amounts to setting
  up the thread links to match the column-major matrix representation. This
  would be trivial except that the presolve matrix is loosely packed. We can
  either compress the matrix, or record the existing free space pattern. Bet
  that the latter is more efficient. Remember that mcstrt_[ncols_] actually
  points to the end of the bulk storage area, so when we process the last
  column in the bulk storage area, we'll add the free space block at the end
  of bulk storage to the free list.

  We need to allow for a 0x0 matrix here --- a pathological case, but it slips
  in when (for example) confirming a solution in an ILP code.
*/
  free_list_ = NO_LINK ;
  maxlink_ = bulk0_ ;
  link_ = new CoinBigIndex [maxlink_] ;

  if (ncols_ > 0)
  { CoinBigIndex minkcs = -1 ;
    for (int j = 0 ; j < ncols_ ; j++)
    { CoinBigIndex kcs = mcstrt_[j] ;
      int lenj = hincol_[j] ;
      assert(lenj > 0) ;
      CoinBigIndex kce = kcs+lenj-1 ;
      CoinBigIndex k ;

      for (k = kcs ; k < kce ; k++)
      { link_[k] = k+1 ; }
      link_[k++] = NO_LINK ;

      if (preObj->clink_[j].pre == NO_LINK)
      { minkcs = kcs ; }
      int nxtj = preObj->clink_[j].suc ;
      assert(nxtj >= 0 && nxtj <= ncols_) ;
      CoinBigIndex nxtcs = mcstrt_[nxtj] ;
      for ( ; k < nxtcs ; k++)
      { link_[k] = free_list_ ;
	free_list_ = k ; } }

    assert(minkcs >= 0) ;
    if (minkcs > 0)
    { for (CoinBigIndex k = 0 ; k < minkcs ; k++)
      { link_[k] = free_list_ ;
	free_list_ = k ; } } }
  else
  { for (CoinBigIndex k = 0 ; k < maxlink_ ; k++)
    { link_[k] = free_list_ ;
      free_list_ = k ; } }
/*
  That's it, preObj can die now.
*/
  delete preObj ;
  preObj = 0 ;

# if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
/*
  These are used to track the action of postsolve transforms during debugging.
*/
  cdone_ = new char [ncols0_] ;
  CoinFillN(cdone_,ncols_,PRESENT_IN_REDUCED) ;
  CoinZeroN(cdone_+ncols_,ncols0_-ncols_) ;
  rdone_ = new char [nrows0_] ;
  CoinFillN(rdone_,nrows_,PRESENT_IN_REDUCED) ;
  CoinZeroN(rdone_+nrows_,nrows0_-nrows_) ;
# else
  cdone_ = 0 ;
  rdone_ = 0 ;
# endif

# if PRESOLVE_CONSISTENCY
  presolve_check_free_list(this,true) ;
  presolve_check_threads(this) ;
# endif

  return ; }
void isolated_constraint_action::postsolve(CoinPostsolveMatrix *prob) const
{
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *link		= prob->link_;
  int *hincol		= prob->hincol_;
  
  double *rowduals	= prob->rowduals_;
  double *rowacts	= prob->acts_;
  double *sol		= prob->sol_;

  CoinBigIndex &free_list		= prob->free_list_;


  // hides fields
  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double rowact = 0.0;

  int irow  = this->row_;

  rup[irow] = this->rup_;
  rlo[irow] = this->rlo_;
  int k;

  for (k=0; k<this->ninrow_; k++) {
    int jcol = this->rowcols_[k];

    sol[jcol] = 0.0;	// ONLY ACCEPTED SUCH CONSTRAINTS

    CoinBigIndex kk = free_list;
    assert(kk >= 0 && kk < prob->bulk0_) ;
    free_list = link[free_list];

    mcstrt[jcol] = kk;

    //rowact += rowels[k] * sol[jcol];

    colels[kk] = this->rowels_[k];
    hrow[kk]   = irow;
    link[kk] = NO_LINK ;

    hincol[jcol] = 1;
  }

# if PRESOLVE_CONSISTENCY
  presolve_check_free_list(prob) ;
# endif

  // ???
  prob->setRowStatus(irow,CoinPrePostsolveMatrix::basic);
    rowduals[irow] = 0.0;

  rowacts[irow] = rowact;

  // leave until desctructor
  //  deleteAction(rowcols_,int *);
  //  deleteAction(rowels_,double *);
  //  deleteAction(costs_,double *);
}
void tripleton_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 *rcosts	= prob->rcosts_;

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

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

  const double maxmin	= prob->maxmin_;

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

  CoinBigIndex &free_list = prob->free_list_;

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

  // Space for accumulating two columns
  int nrows = prob->nrows_;
  int * index1 = new int[nrows];
  double * element1 = new double[nrows];
  memset(element1,0,nrows*sizeof(double));
  int * index2 = new int[nrows];
  double * element2 = new double[nrows];
  memset(element2,0,nrows*sizeof(double));

  for (const action *f = &actions[nactions-1]; actions<=f; f--) {
    int irow = f->row;

    // probably don't need this
    double ylo0 = f->cloy;
    double yup0 = f->cupy;

    double coeffx = f->coeffx;
    double coeffy = f->coeffy;
    double coeffz = f->coeffz;
    int jcolx = f->icolx;
    int jcoly = f->icoly;
    int jcolz = f->icolz;

    // needed?
    double rhs = f->rlo;

    /* the column was in the reduced problem */
    PRESOLVEASSERT(cdone[jcolx] && rdone[irow]==DROP_ROW&&cdone[jcolz]);
    PRESOLVEASSERT(cdone[jcoly]==DROP_COL);

    // probably don't need this
    rlo[irow] = f->rlo;
    rup[irow] = f->rup;

    // probably don't need this
    clo[jcoly] = ylo0;
    cup[jcoly] = yup0;

    dcost[jcoly] = f->costy;
    dcost[jcolx] += f->costy*coeffx/coeffy;
    dcost[jcolz] += f->costy*coeffz/coeffy;

    // this is why we want coeffx < coeffy (55)
    sol[jcoly] = (rhs - coeffx * sol[jcolx] - coeffz * sol[jcolz]) / coeffy;
	  
    // since this row is fixed 
    acts[irow] = rhs;

    // acts[irow] always ok, since slack is fixed
    if (rowstat)
      prob->setRowStatus(irow,CoinPrePostsolveMatrix::atLowerBound);


    // CLAIM:
    // if the new pi value is chosen to keep the reduced cost
    // of col x at its prior value, then the reduced cost of
    // col y will be 0.
    
    // also have to update row activities and bounds for rows affected by jcoly
    //
    // sol[jcolx] was found for coeffx that
    // was += colels[kcoly] * coeff_factor;
    // where coeff_factor == -coeffx / coeffy
    //
    // its contribution to activity was
    // (colels[kcolx] + colels[kcoly] * coeff_factor) * sol[jcolx]	(1)
    //
    // After adjustment, the two columns contribute:
    // colels[kcoly] * sol[jcoly] + colels[kcolx] * sol[jcolx]
    // == colels[kcoly] * ((rhs - coeffx * sol[jcolx]) / coeffy) + colels[kcolx] * sol[jcolx]
    // == colels[kcoly] * rhs/coeffy + colels[kcoly] * coeff_factor * sol[jcolx] + colels[kcolx] * sol[jcolx]
    // colels[kcoly] * rhs/coeffy + the expression (1)
    //
    // therefore, we must increase the row bounds by colels[kcoly] * rhs/coeffy,
    // which is similar to the bias
    double djy = maxmin * dcost[jcoly];
    double djx = maxmin * dcost[jcolx];
    double djz = maxmin * dcost[jcolz];
    double bounds_factor = rhs/coeffy;
    // need to reconstruct x and z
    double multiplier1 = coeffx/coeffy;
    double multiplier2 = coeffz/coeffy;
    int * indy = reinterpret_cast<int *>(f->colel+f->ncoly);
    int ystart = NO_LINK;
    int nX=0,nZ=0;
    int i,iRow;
    for (i=0; i<f->ncoly; ++i) {
      int iRow = indy[i];
      double yValue = f->colel[i];
      CoinBigIndex k = free_list;
      assert(k >= 0 && k < prob->bulk0_) ;
      free_list = link[free_list];
      if (iRow != irow) {
	// are these tests always true???

	// undo elim_tripleton(1)
	if (-PRESOLVE_INF < rlo[iRow])
	  rlo[iRow] += yValue * bounds_factor;

	// undo elim_tripleton(2)
	if (rup[iRow] < PRESOLVE_INF)
	  rup[iRow] += yValue * bounds_factor;

	acts[iRow] += yValue * bounds_factor;

	djy -= rowduals[iRow] * yValue;
      } 
      
      hrow[k] = iRow;
      PRESOLVEASSERT(rdone[hrow[k]] || hrow[k] == irow);
      colels[k] = yValue;
      link[k] = ystart;
      ystart = k;
      element1[iRow]=yValue*multiplier1;
      index1[nX++]=iRow;
      element2[iRow]=yValue*multiplier2;
      index2[nZ++]=iRow;
    }
#   if PRESOLVE_CONSISTENCY
    presolve_check_free_list(prob) ;
#   endif
    mcstrt[jcoly] = ystart;
    hincol[jcoly] = f->ncoly;
    // find the tail
    CoinBigIndex k=mcstrt[jcolx];
    CoinBigIndex last = NO_LINK;
    int numberInColumn = hincol[jcolx];
    int numberToDo=numberInColumn;
    for (i=0; i<numberToDo; ++i) {
      iRow = hrow[k];
      assert (iRow>=0&&iRow<nrows);
      double value = colels[k]+element1[iRow];
      element1[iRow]=0.0;
      if (fabs(value)>=1.0e-15) {
	colels[k]=value;
	last=k;
	k = link[k];
	if (iRow != irow) 
	  djx -= rowduals[iRow] * value;
      } else {
	numberInColumn--;
	// add to free list
	int nextk = link[k];
	link[k]=free_list;
	free_list=k;
	assert (k>=0);
	k=nextk;
	if (last!=NO_LINK)
	  link[last]=k;
	else
	  mcstrt[jcolx]=k;
      }
    }
    for (i=0;i<nX;i++) {
      int iRow = index1[i];
      double xValue = element1[iRow];
      element1[iRow]=0.0;
      if (fabs(xValue)>=1.0e-15) {
	if (iRow != irow)
	  djx -= rowduals[iRow] * xValue;
	numberInColumn++;
	CoinBigIndex k = free_list;
	assert(k >= 0 && k < prob->bulk0_) ;
	free_list = link[free_list];
	hrow[k] = iRow;
	PRESOLVEASSERT(rdone[hrow[k]] || hrow[k] == irow);
	colels[k] = xValue;
	if (last!=NO_LINK)
	  link[last]=k;
	else
	  mcstrt[jcolx]=k;
	last = k;
      }
    }
#   if PRESOLVE_CONSISTENCY
    presolve_check_free_list(prob) ;
#   endif
    link[last]=NO_LINK;
    assert(numberInColumn);
    hincol[jcolx] = numberInColumn;
    // find the tail
    k=mcstrt[jcolz];
    last = NO_LINK;
    numberInColumn = hincol[jcolz];
    numberToDo=numberInColumn;
    for (i=0; i<numberToDo; ++i) {
      iRow = hrow[k];
      assert (iRow>=0&&iRow<nrows);
      double value = colels[k]+element2[iRow];
      element2[iRow]=0.0;
      if (fabs(value)>=1.0e-15) {
	colels[k]=value;
	last=k;
	k = link[k];
	if (iRow != irow) 
	  djz -= rowduals[iRow] * value;
      } else {
	numberInColumn--;
	// add to free list
	int nextk = link[k];
	assert(free_list>=0);
	link[k]=free_list;
	free_list=k;
	assert (k>=0);
	k=nextk;
	if (last!=NO_LINK)
	  link[last]=k;
	else
	  mcstrt[jcolz]=k;
      }
    }
    for (i=0;i<nZ;i++) {
      int iRow = index2[i];
      double zValue = element2[iRow];
      element2[iRow]=0.0;
      if (fabs(zValue)>=1.0e-15) {
	if (iRow != irow)
	  djz -= rowduals[iRow] * zValue;
	numberInColumn++;
	CoinBigIndex k = free_list;
	assert(k >= 0 && k < prob->bulk0_) ;
	free_list = link[free_list];
	hrow[k] = iRow;
	PRESOLVEASSERT(rdone[hrow[k]] || hrow[k] == irow);
	colels[k] = zValue;
	if (last!=NO_LINK)
	  link[last]=k;
	else
	  mcstrt[jcolz]=k;
	last = k;
      }
    }
#   if PRESOLVE_CONSISTENCY
    presolve_check_free_list(prob) ;
#   endif
    link[last]=NO_LINK;
    assert(numberInColumn);
    hincol[jcolz] = numberInColumn;
    
    
    
    // The only problem with keeping the reduced costs the way they were
    // was that the variable's bound may have moved, requiring it
    // to become basic.
    //printf("djs x - %g (%g), y - %g (%g)\n",djx,coeffx,djy,coeffy);
    if (colstat) {
      if (prob->columnIsBasic(jcolx) ||
	  (fabs(clo[jcolx] - sol[jcolx]) < ztolzb && rcosts[jcolx] >= -ztoldj) ||
	  (fabs(cup[jcolx] - sol[jcolx]) < ztolzb && rcosts[jcolx] <= ztoldj) ||
	  (prob->getColumnStatus(jcolx) ==CoinPrePostsolveMatrix::isFree&&
           fabs(rcosts[jcolx]) <= ztoldj)) {
	// colx or y is fine as it is - make coly basic

	prob->setColumnStatus(jcoly,CoinPrePostsolveMatrix::basic);
	// 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[jcolx] = djx - rowduals[irow] * coeffx;
#       if PRESOLVE_DEBUG
	if (prob->columnIsBasic(jcolx)&&fabs(rcosts[jcolx])>1.0e-5)
	  printf("bad dj %d %g\n",jcolx,rcosts[jcolx]);
#       endif
	rcosts[jcolz] = djz - rowduals[irow] * coeffz;
	//if (prob->columnIsBasic(jcolz))
	//assert (fabs(rcosts[jcolz])<1.0e-5);
	rcosts[jcoly] = 0.0;
      } else {
	prob->setColumnStatus(jcolx,CoinPrePostsolveMatrix::basic);
	prob->setColumnStatusUsingValue(jcoly);

	// change rowduals[jcolx] enough to cancel out rcosts[jcolx]
	rowduals[irow] = djx / coeffx;
	rcosts[jcolx] = 0.0;
	// change rowduals[jcolx] enough to cancel out rcosts[jcolx]
	//rowduals[irow] = djz / coeffz;
	//rcosts[jcolz] = 0.0;
	rcosts[jcolz] = djz - rowduals[irow] * coeffz;
	rcosts[jcoly] = djy - rowduals[irow] * coeffy;
      }
    } 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;
    }
    
    // DEBUG CHECK
#   if PRESOLVE_DEBUG
    {
      CoinBigIndex k = mcstrt[jcolx];
      int nx = hincol[jcolx];
      double dj = maxmin * dcost[jcolx];
      
      for (int i=0; i<nx; ++i) {
	int row = hrow[k];
	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];
      int ny = hincol[jcoly];
      double dj = maxmin * dcost[jcoly];
      
      for (int i=0; i<ny; ++i) {
	int row = hrow[k];
	double coeff = colels[k];
	k = link[k];

	dj -= rowduals[row] * coeff;
	//printf("b %d coeff %g dual %g dj %g\n",
	// row,coeff,rowduals[row],dj);
      }
      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;
      //exit(0);
    }
#   endif
    
#   if PRESOLVE_DEBUG || PRESOLVE_CONSISTENCY
    cdone[jcoly] = TRIPLETON;
    rdone[irow] = TRIPLETON;
#   endif
  }
  delete [] index1;
  delete [] element1;
  delete [] index2;
  delete [] element2;

# if PRESOLVE_CONSISTENCY
  presolve_check_threads(prob) ;
# endif

}
/*
  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 ;
}