1 
2 /* -------------------------------------------------------------------------
3    Presolve routines for lp_solve v5.0+
4    -------------------------------------------------------------------------
5     Author:        Kjell Eikland
6     Contact:       kjell.eikland@broadpark.no
7     License terms: LGPL.
8 
9     Requires:      lp_lib.h, lp_presolve, lp_crash.h, lp_scale.h, lp_report.h
10 
11     Release notes:
12     v1.0.0  1 January 2003      Initial crude version used with lp_solve v4.
13     v5.0.0  1 January 2004      Significantly expanded and repackaged
14                                 presolve routines for lp_solve v5 release.
15     v5.0.1  1 April   2004      Added reference to new crash module
16     v5.1.0  20 August 2004      Reworked infeasibility detection.
17                                 Added encapsulation of presolve undo logic.
18     v5.1.1  10 September 2004   Added variable bound tightening based on
19                                 full-constraint information, as well as
20                                 variable fixing by duality.
21     v5.2.0  1 January 2005      Fixes to bound fixing handling.
22                                 Added fast batch compression after presolve.
23                                 Restructured calls by adding presolve wrapper.
24                                 Major optimization of identification logic
25                                   along with bug fixes.
26                                 Enabled storage of eliminated matrix data.
27                                 Added function to report on constraint classes.
28     v5.5.0  1 June 2005         Added implied slack presolve, restructured
29                                 looping logic to be more modular, and made
30                                 active row/column selection logic faster.
31     v5.5.1  18 June 2005        Finished sparsity-enhancing logic and added
32                                 initial version of column aggregation code.
33    ------------------------------------------------------------------------- */
34 
35 #include <string.h>
36 #include "commonlib.h"
37 #include "lp_lib.h"
38 #include "lp_presolve.h"
39 #include "lp_crash.h"
40 #include "lp_scale.h"
41 #include "lp_report.h"
42 
43 #ifdef FORTIFY
44 # include "lp_fortify.h"
45 #endif
46 
47 
48 #define presolve_setstatus(one, two)  presolve_setstatusex(one, two, __LINE__, __FILE__)
presolve_setstatusex(presolverec * psdata,int status,int lineno,char * filename)49 STATIC int presolve_setstatusex(presolverec *psdata, int status, int lineno, char *filename)
50 {
51   if((status == INFEASIBLE) || (status == UNBOUNDED)) {
52     report(psdata->lp,
53 #ifdef Paranoia
54            NORMAL,
55 #else
56            DETAILED,
57 #endif
58            "presolve_setstatus: Status set to '%s' on code line %d, file '%s'\n",
59            (status == INFEASIBLE ? "INFEASIBLE" : "UNBOUNDED"), lineno, (filename == NULL ? "Unknown" : filename));
60   }
61   return( status );
62 }
63 
presolve_statuscheck(presolverec * psdata,int * status)64 STATIC MYBOOL presolve_statuscheck(presolverec *psdata, int *status)
65 {
66   if(*status == RUNNING) {
67     lprec *lp = psdata->lp;
68     if(!mat_validate(lp->matA))
69       *status = MATRIXERROR;
70     else if(userabort(lp, -1))
71       *status = lp->spx_status;
72   }
73   return( (MYBOOL) (*status == RUNNING) );
74 }
75 
presolve_createUndo(lprec * lp)76 STATIC MYBOOL presolve_createUndo(lprec *lp)
77 {
78   if(lp->presolve_undo != NULL)
79     presolve_freeUndo(lp);
80   lp->presolve_undo = (presolveundorec *) calloc(1, sizeof(presolveundorec));
81   lp->presolve_undo->lp = lp;
82   if(lp->presolve_undo == NULL)
83     return( FALSE );
84   return( TRUE );
85 }
inc_presolve_space(lprec * lp,int delta,MYBOOL isrows)86 STATIC MYBOOL inc_presolve_space(lprec *lp, int delta, MYBOOL isrows)
87 {
88   int i, ii,
89       oldrowcolalloc, rowcolsum, oldrowalloc, oldcolalloc;
90   presolveundorec *psundo = lp->presolve_undo;
91 
92   if(psundo == NULL) {
93     presolve_createUndo(lp);
94     psundo = lp->presolve_undo;
95   }
96 
97   /* Set constants */
98   oldrowalloc = lp->rows_alloc-delta;
99   oldcolalloc = lp->columns_alloc-delta;
100   oldrowcolalloc = lp->sum_alloc-delta;
101   rowcolsum = lp->sum_alloc + 1;
102 
103   /* Reallocate lp memory */
104   if(isrows)
105     allocREAL(lp, &psundo->fixed_rhs,   lp->rows_alloc+1, AUTOMATIC);
106   else
107     allocREAL(lp, &psundo->fixed_obj,   lp->columns_alloc+1, AUTOMATIC);
108   allocINT(lp,  &psundo->var_to_orig, rowcolsum, AUTOMATIC);
109   allocINT(lp,  &psundo->orig_to_var, rowcolsum, AUTOMATIC);
110 
111   /* Fill in default values, where appropriate */
112   if(isrows)
113     ii = oldrowalloc+1;
114   else
115     ii = oldcolalloc+1;
116   for(i = oldrowcolalloc+1; i < rowcolsum; i++, ii++) {
117     psundo->var_to_orig[i] = 0;
118     psundo->orig_to_var[i] = 0;
119     if(isrows)
120       psundo->fixed_rhs[ii] = 0;
121     else
122       psundo->fixed_obj[ii] = 0;
123   }
124 
125   return(TRUE);
126 }
presolve_setOrig(lprec * lp,int orig_rows,int orig_cols)127 STATIC MYBOOL presolve_setOrig(lprec *lp, int orig_rows, int orig_cols)
128 {
129   presolveundorec *psundo = lp->presolve_undo;
130 
131   if(psundo == NULL)
132     return( FALSE );
133   psundo->orig_rows = orig_rows;
134   psundo->orig_columns = orig_cols;
135   psundo->orig_sum = orig_rows + orig_cols;
136   if(lp->wasPresolved)
137     presolve_fillUndo(lp, orig_rows, orig_cols, FALSE);
138   return( TRUE );
139 }
presolve_fillUndo(lprec * lp,int orig_rows,int orig_cols,MYBOOL setOrig)140 STATIC MYBOOL presolve_fillUndo(lprec *lp, int orig_rows, int orig_cols, MYBOOL setOrig)
141 {
142   int i;
143   presolveundorec *psundo = lp->presolve_undo;
144 
145   for(i = 0; i <= orig_rows; i++) {
146     psundo->var_to_orig[i] = i;
147     psundo->orig_to_var[i] = i;
148     psundo->fixed_rhs[i]   = 0;
149   }
150   for(i = 1; i <= orig_cols; i++) {
151     psundo->var_to_orig[orig_rows + i] = i;
152     psundo->orig_to_var[orig_rows + i] = i;
153     psundo->fixed_obj[i] = 0;
154   }
155   if(setOrig)
156     presolve_setOrig(lp, orig_rows, orig_cols);
157 
158   return( TRUE );
159 }
presolve_rebuildUndo(lprec * lp,MYBOOL isprimal)160 STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal)
161 {
162   int             ik, ie, ix, j, k, *colnrDep;
163   REAL             hold, *value, *solution, *slacks;
164   presolveundorec *psdata = lp->presolve_undo;
165   MATrec          *mat = NULL;
166 
167   /* Point to and initialize undo structure at first call */
168   if(isprimal) {
169     if(psdata->primalundo != NULL)
170       mat = psdata->primalundo->tracker;
171     solution = lp->full_solution + lp->presolve_undo->orig_rows;
172     slacks   = lp->full_solution;
173   }
174   else {
175     if(psdata->dualundo != NULL)
176       mat = psdata->dualundo->tracker;
177     solution = lp->full_duals;
178     slacks   = lp->full_duals + lp->presolve_undo->orig_rows;
179   }
180   if(mat == NULL)
181     return( FALSE );
182 
183   /* Loop backward over the undo chain */
184   for(j = mat->col_tag[0]; j > 0; j--) {
185     ix = mat->col_tag[j];
186     ik = mat->col_end[j-1];
187     ie = mat->col_end[j];
188     colnrDep = &COL_MAT_ROWNR(ik);
189     value    = &COL_MAT_VALUE(ik);
190     hold = 0;
191     k = 0;
192     for(; ik < ie; ik++, colnrDep += matRowColStep, value += matValueStep) {
193 
194       /* Constant term */
195       if(*colnrDep == 0)
196         hold += *value;
197 
198       /* Special case with dependence on a slack variable */
199       else if(isprimal && (*colnrDep > lp->presolve_undo->orig_columns)) {
200         k = (*colnrDep) - lp->presolve_undo->orig_columns;
201         hold -= (*value) * slacks[k];
202         slacks[k] = 0;
203       }
204       else if(!isprimal && (*colnrDep > lp->presolve_undo->orig_rows)) {
205         k = (*colnrDep) - lp->presolve_undo->orig_rows;
206         hold -= (*value) * slacks[k];
207         slacks[k] = 0;
208       }
209 
210       /* Dependence on other user variable */
211       else
212         hold -= (*value) * solution[*colnrDep];
213 
214       *value = 0;
215     }
216     if(fabs(hold) > lp->epsvalue)
217       solution[ix] = hold;
218   }
219 
220   return( TRUE );
221 }
presolve_freeUndo(lprec * lp)222 STATIC MYBOOL presolve_freeUndo(lprec *lp)
223 {
224   presolveundorec *psundo = lp->presolve_undo;
225 
226   if(psundo == NULL)
227     return( FALSE );
228   FREE(psundo->orig_to_var);
229   FREE(psundo->var_to_orig);
230   FREE(psundo->fixed_rhs);
231   FREE(psundo->fixed_obj);
232   if(psundo->deletedA != NULL)
233     freeUndoLadder(&(psundo->deletedA));
234   if(psundo->primalundo != NULL)
235     freeUndoLadder(&(psundo->primalundo));
236   if(psundo->dualundo != NULL)
237     freeUndoLadder(&(psundo->dualundo));
238   FREE(lp->presolve_undo);
239   return( TRUE );
240 }
241 
presolve_storeDualUndo(presolverec * psdata,int rownr,int colnr)242 STATIC void presolve_storeDualUndo(presolverec *psdata, int rownr, int colnr)
243 {
244   lprec    *lp = psdata->lp;
245   MYBOOL   firstdone = FALSE;
246   int      ix, iix, item;
247   REAL     Aij = get_mat(lp, rownr, colnr);
248   MATrec   *mat = lp->matA;
249 
250   if(presolve_collength(psdata, colnr) == 0)
251     return;
252 
253   /* Add undo information for the dual of the deleted constraint */
254   item = 0;
255   for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0;
256       ix = presolve_nextrow(psdata, colnr, &item)) {
257     iix = COL_MAT_ROWNR(ix);
258     if(iix == rownr)
259       continue;
260     if(!firstdone)
261       firstdone = addUndoPresolve(lp, FALSE, rownr, get_mat(lp, 0, colnr)/Aij,
262                                                     get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix);
263     else
264       appendUndoPresolve(lp, FALSE, get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix);
265   }
266 }
267 
268 /* ----------------------------------------------------------------------------- */
269 /* Presolve debugging routines                                                   */
270 /* ----------------------------------------------------------------------------- */
presolve_SOScheck(presolverec * psdata)271 STATIC MYBOOL presolve_SOScheck(presolverec *psdata)
272 {
273   MYBOOL status = TRUE;
274   lprec  *lp = psdata->lp;
275   int    *list, i, j, n, k, nk, colnr, nSOS = SOS_count(lp), nerr = 0;
276   SOSrec *SOS;
277 
278   if(nSOS == 0)
279     return( status );
280 
281   /* For each SOS and each member check validity */
282   for(i = 1; i<= nSOS; i++) {
283     SOS = lp->SOS->sos_list[i-1];
284     list = SOS->members;
285     n = list[0];
286     for(j = 1; j<= n; j++) {
287       colnr = list[j];
288       /* Check valid range */
289       if((colnr < 1) || (colnr > lp->columns)) {
290         nerr++;
291         report(lp, IMPORTANT, "presolve_SOScheck: A - Column index %d is outside of valid range\n",
292                               colnr);
293       }
294       /* Check for deletion */
295       if(!isActiveLink(psdata->cols->varmap, colnr)) {
296         nerr++;
297         report(lp, IMPORTANT, "presolve_SOScheck: B - Column index %d has been marked for deletion\n",
298                               colnr);
299       }
300       /* Check if sorted member array is Ok */
301       if(SOS_member_index(lp->SOS, i, colnr) != j) {
302         nerr++;
303         report(lp, IMPORTANT, "presolve_SOScheck: C - Column index %d not found in fast search array\n",
304                               colnr);
305       }
306       /* Check for variable membership in this SOS record of the sparse storage */
307       k = lp->SOS->memberpos[colnr-1];
308       nk = lp->SOS->memberpos[colnr];
309       while((k < nk) && (lp->SOS->membership[k] != i))
310         k++;
311       if(k >= nk) {
312         nerr++;
313         report(lp, IMPORTANT, "presolve_SOScheck: D - Column index %d was not found in sparse array\n",
314                               colnr);
315       }
316     }
317   }
318 
319   /* Check that all members in the sparse array can be validated as SOS members */
320   for(colnr = 1; colnr <= lp->columns; colnr++) {
321     k = lp->SOS->memberpos[colnr-1];
322     nk = lp->SOS->memberpos[colnr];
323     for(; k < nk; k++) {
324       if(!SOS_is_member(lp->SOS, lp->SOS->membership[k], colnr)) {
325         nerr++;
326         report(lp, IMPORTANT, "presolve_SOScheck: E - Sparse array did not indicate column index %d as member of SOS %d\n",
327                               colnr, lp->SOS->membership[k]);
328       }
329     }
330   }
331   status = (MYBOOL) (nerr == 0);
332   if(!status)
333     report(lp, IMPORTANT, "presolve_SOScheck: There were %d errors\n",
334                            nerr);
335 
336 
337   return( status );
338 }
339 
340 /* ----------------------------------------------------------------------------- */
341 /* Presolve routines for tightening the model                                    */
342 /* ----------------------------------------------------------------------------- */
343 
presolve_roundrhs(lprec * lp,REAL value,MYBOOL isGE)344 INLINE REAL presolve_roundrhs(lprec *lp, REAL value, MYBOOL isGE)
345 {
346 #ifdef DoPresolveRounding
347   REAL eps = PRESOLVE_EPSVALUE*1000,
348   /* REAL eps = PRESOLVE_EPSVALUE*pow(10.0,MAX(0,log10(1+fabs(value)))), */
349   testout = my_precision(value, eps);
350 #if 1
351   if(my_chsign(isGE, value-testout) < 0)
352     value = testout;
353 #elif 0
354   if(my_chsign(isGE, value-testout) < 0)
355     value = testout;
356   else if(value != testout)
357     value += my_chsign(isGE, (value-testout)/2);
358     /* value = testout + my_chsign(isGE, (value-testout)/2); */
359 #else
360   if(testout != value)
361     value += my_chsign(isGE, eps*1000);              /* BASE OPTION */
362 #endif
363 
364 #endif
365   return( value );
366 }
367 
presolve_roundval(lprec * lp,REAL value)368 INLINE REAL presolve_roundval(lprec *lp, REAL value)
369 {
370 #ifdef DoPresolveRounding
371   /* value = my_precision(value, PRESOLVE_EPSVALUE*MAX(1,log10(1+fabs(value)))); */
372   value = my_precision(value, PRESOLVE_EPSVALUE);    /* BASE OPTION */
373 #endif
374   return( value );
375 }
376 
presolve_mustupdate(lprec * lp,int colnr)377 INLINE MYBOOL presolve_mustupdate(lprec *lp, int colnr)
378 {
379 #if 0
380   return( my_infinite(lp, get_lowbo(lp, colnr)) ||
381           my_infinite(lp, get_upbo(lp, colnr)) );
382 #else
383   return( my_infinite(lp, lp->orig_lowbo[lp->rows+colnr]) ||
384           my_infinite(lp, lp->orig_upbo[lp->rows+colnr]) );
385 #endif
386 }
387 
presolve_sumplumin(lprec * lp,int item,psrec * ps,MYBOOL doUpper)388 INLINE REAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper)
389 {
390   REAL *plu = (doUpper ? ps->pluupper : ps->plulower),
391        *neg = (doUpper ? ps->negupper : ps->neglower);
392 
393   if(fabs(plu[item]) >= lp->infinite)
394     return( plu[item] );
395   else if(fabs(neg[item]) >= lp->infinite)
396     return( neg[item] );
397   else
398     return( plu[item]+neg[item] );
399 }
400 
presolve_range(lprec * lp,int rownr,psrec * ps,REAL * loValue,REAL * hiValue)401 INLINE void presolve_range(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue)
402 {
403   *loValue = presolve_sumplumin(lp, rownr,   ps, FALSE);
404   *hiValue = presolve_sumplumin(lp, rownr,   ps, TRUE);
405 }
406 
presolve_rangeorig(lprec * lp,int rownr,psrec * ps,REAL * loValue,REAL * hiValue,REAL delta)407 STATIC void presolve_rangeorig(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue, REAL delta)
408 {
409   delta = my_chsign(is_chsign(lp, rownr), lp->presolve_undo->fixed_rhs[rownr] + delta);
410   *loValue = presolve_sumplumin(lp, rownr,   ps, FALSE) + delta;
411   *hiValue = presolve_sumplumin(lp, rownr,   ps, TRUE) + delta;
412 }
413 
presolve_rowfeasible(presolverec * psdata,int rownr,MYBOOL userowmap)414 STATIC MYBOOL presolve_rowfeasible(presolverec *psdata, int rownr, MYBOOL userowmap)
415 {
416   lprec    *lp = psdata->lp;
417   MYBOOL   status = TRUE;
418   int      contype, origrownr = rownr;
419   REAL     LHS, RHS, value;
420 
421   /* Optionally loop across all active rows in the provided map (debugging) */
422   if(userowmap)
423     rownr = firstActiveLink(psdata->rows->varmap);
424 
425   /* Now do once for ingoing rownr or loop across rowmap */
426   while((status == TRUE) && (rownr != 0)) {
427 
428     /* Check the lower bound */
429     value = presolve_sumplumin(lp, rownr, psdata->rows, TRUE);
430     LHS = get_rh_lower(lp, rownr);
431     if(value < LHS-lp->epssolution) {
432       contype = get_constr_type(lp, rownr);
433       report(lp, NORMAL, "presolve_rowfeasible: Lower bound infeasibility in %s row %s (%g << %g)\n",
434                           get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, LHS);
435       if(rownr != origrownr)
436       report(lp, NORMAL, "        ...           Input row base used for testing was %s\n",
437                                                     get_row_name(lp, origrownr));
438       status = FALSE;
439     }
440 
441     /* Check the upper bound */
442     value = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
443     RHS = get_rh_upper(lp, rownr);
444     if(value > RHS+lp->epssolution) {
445       contype = get_constr_type(lp, rownr);
446       report(lp, NORMAL, "presolve_rowfeasible: Upper bound infeasibility in %s row %s (%g >> %g)\n",
447                           get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, RHS);
448       status = FALSE;
449     }
450     if(userowmap)
451       rownr = nextActiveLink(psdata->rows->varmap, rownr);
452     else
453       rownr = 0;
454   }
455   return( status );
456 }
457 
presolve_debugmap(presolverec * psdata,char * caption)458 STATIC MYBOOL presolve_debugmap(presolverec *psdata, char *caption)
459 {
460   lprec *lp = psdata->lp;
461   MATrec *mat = lp->matA;
462   int    colnr, ix, ie, nx, jx, je, *cols, *rows, n;
463   int    nz = mat->col_end[lp->columns] - 1;
464   MYBOOL status = FALSE;
465 
466   for(colnr = 1; colnr <= lp->columns; colnr++) {
467     rows = psdata->cols->next[colnr];
468     if(!isActiveLink(psdata->cols->varmap, colnr)) {
469       if(rows != NULL) {
470         report(lp, SEVERE, "presolve_debugmap: Inactive column %d is non-empty\n",
471                            colnr);
472         goto Done;
473       }
474       else
475         continue;
476     }
477     if(rows == NULL)
478       report(lp, SEVERE, "presolve_debugmap: Active column %d is empty\n",
479                          colnr);
480     je = *rows;
481     rows++;
482     for(jx = 1; jx <= je; jx++, rows++) {
483       if((*rows < 0) || (*rows > nz)) {
484         report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d out of range (index %d<=%d)\n",
485                            *rows, colnr, jx, je);
486         goto Done;
487       }
488       cols = psdata->rows->next[COL_MAT_ROWNR(*rows)];
489       ie = cols[0];
490       n = 0;
491       for(ix = 1; ix <= ie; ix++) {
492         nx = cols[ix];
493         if((nx < 0) || (nx > nz)) {
494           report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d to row %d out of range\n",
495                              nx, colnr, jx);
496           goto Done;
497         }
498       }
499     }
500   }
501   status = TRUE;
502 Done:
503   if(!status && (caption != NULL))
504     report(lp, SEVERE, "...caller was '%s'\n", caption);
505   return( status );
506 }
507 
presolve_validate(presolverec * psdata,MYBOOL forceupdate)508 STATIC MYBOOL presolve_validate(presolverec *psdata, MYBOOL forceupdate)
509 {
510   int    i, ie, j, je, k, rownr, *items;
511   REAL   upbound, lobound, value;
512   lprec  *lp = psdata->lp;
513   MATrec *mat = lp->matA;
514   MYBOOL status = mat->row_end_valid && !forceupdate;
515 
516   if(status)
517     return( status );
518   else if(!mat->row_end_valid)
519     status = mat_validate(mat);
520   else
521     status = forceupdate;
522   if(status) {
523 
524     /* First update rows... */
525     for(i = 1; i <= lp->rows; i++) {
526 
527       psdata->rows->plucount[i] = 0;
528       psdata->rows->negcount[i] = 0;
529       psdata->rows->pluneg[i]   = 0;
530 
531       if(!isActiveLink(psdata->rows->varmap, i)) {
532         FREE(psdata->rows->next[i]);
533       }
534       else {
535         /* Create next column pointers by row */
536         k = mat_rowlength(mat, i);
537         allocINT(lp, &(psdata->rows->next[i]), k+1, AUTOMATIC);
538         items = psdata->rows->next[i];
539         je = mat->row_end[i];
540         k = 0;
541         for(j = mat->row_end[i-1]; j < je; j++)
542           if(isActiveLink(psdata->cols->varmap, ROW_MAT_COLNR(j))) {
543             k++;
544             items[k] = j;
545           }
546         items[0] = k;
547       }
548     }
549 
550     /* ...then update columns */
551     for(j = 1; j <= lp->columns; j++) {
552 
553       psdata->cols->plucount[j] = 0;
554       psdata->cols->negcount[j] = 0;
555       psdata->cols->pluneg[j]   = 0;
556 
557       if(!isActiveLink(psdata->cols->varmap, j)) {
558         FREE(psdata->cols->next[j]);
559       }
560       else {
561         upbound = get_upbo(lp, j);
562         lobound = get_lowbo(lp, j);
563         if(is_semicont(lp, j) && (upbound > lobound)) {
564           if(lobound > 0)
565             lobound = 0;
566           else if(upbound < 0)
567             upbound = 0;
568         }
569 
570         /* Create next row pointers by column */
571         k = mat_collength(mat, j);
572         allocINT(lp, &(psdata->cols->next[j]), k+1, AUTOMATIC);
573         items = psdata->cols->next[j];
574         ie = mat->col_end[j];
575         k = 0;
576         for(i = mat->col_end[j-1]; i < ie; i++) {
577           rownr = COL_MAT_ROWNR(i);
578           if(isActiveLink(psdata->rows->varmap, rownr)) {
579             k++;
580             items[k] = i;
581 
582             /* Cumulate counts */
583             value = COL_MAT_VALUE(i);
584             if(my_chsign(is_chsign(lp, rownr), value) > 0) {
585               psdata->rows->plucount[rownr]++;
586               psdata->cols->plucount[j]++;
587             }
588             else {
589               psdata->rows->negcount[rownr]++;
590               psdata->cols->negcount[j]++;
591             }
592             if((lobound < 0) && (upbound >= 0)) {
593               psdata->rows->pluneg[rownr]++;
594               psdata->cols->pluneg[j]++;
595             }
596           }
597         }
598         items[0] = k;
599       }
600     }
601 #ifdef Paranoia
602     presolve_debugmap(psdata, "presolve_validate");
603 #endif
604   }
605   return( status );
606 }
607 
presolve_rowtallies(presolverec * psdata,int rownr,int * plu,int * neg,int * pluneg)608 STATIC MYBOOL presolve_rowtallies(presolverec *psdata, int rownr, int *plu, int *neg, int *pluneg)
609 {
610   REAL   value;
611   lprec  *lp = psdata->lp;
612   MATrec *mat = lp->matA;
613   int    ix, jx, ib = 0;
614   MYBOOL chsign = is_chsign(lp, rownr);
615 
616   /* Initialize */
617   *plu = 0;
618   *neg = 0;
619   *pluneg = 0;
620 
621   /* Loop over still active row members */
622   for(ix = presolve_nextcol(psdata, rownr, &ib); ix >= 0; ix = presolve_nextcol(psdata, rownr, &ib)) {
623 
624     /* Get matrix column and value */
625     jx    = ROW_MAT_COLNR(ix);
626     value = ROW_MAT_VALUE(ix);
627 
628     /* Cumulate counts */
629     if(my_chsign(chsign, value) > 0)
630       (*plu)++;
631     else
632       (*neg)++;
633     if((get_lowbo(lp, jx) < 0) && (get_upbo(lp, jx) >= 0))
634       (*pluneg)++;
635   }
636   return( TRUE );
637 }
presolve_debugrowtallies(presolverec * psdata)638 STATIC MYBOOL presolve_debugrowtallies(presolverec *psdata)
639 {
640   lprec  *lp = psdata->lp;
641   int    i, plu, neg, pluneg, nerr = 0;
642 
643   for(i = 1; i <= lp->rows; i++)
644     if(isActiveLink(psdata->rows->varmap, i) &&
645        presolve_rowtallies(psdata, i, &plu, &neg, &pluneg)) {
646       if((psdata->rows->plucount[i] != plu) ||
647          (psdata->rows->negcount[i] != neg) ||
648          (psdata->rows->pluneg[i] != pluneg)) {
649         nerr++;
650         report(lp, SEVERE, "presolve_debugrowtallies: Detected inconsistent count for row %d\n", i);
651       }
652     }
653   return( (MYBOOL) (nerr == 0) );
654 }
655 
presolve_debugcheck(lprec * lp,LLrec * rowmap,LLrec * colmap)656 STATIC int presolve_debugcheck(lprec *lp, LLrec *rowmap, LLrec *colmap)
657 {
658   int i, j, errc = 0;
659 
660   /* Validate constraint bounds */
661   for(i = 1; i < lp->rows; i++) {
662     if((rowmap != NULL) && !isActiveLink(rowmap, i))
663       continue;
664     /* Check if we have a negative range */
665     if(lp->orig_upbo[i] < 0) {
666       errc++;
667       report(lp, SEVERE, "presolve_debugcheck: Detected negative range %g for row %d\n",
668                          lp->orig_upbo[i], i);
669     }
670   }
671   /* Validate variables */
672   for(j = 1; j < lp->columns; j++) {
673     if((colmap != NULL) && !isActiveLink(colmap, j))
674       continue;
675     i = lp->rows+j;
676     /* Check if we have infeasible  bounds */
677     if(lp->orig_lowbo[i] > lp->orig_upbo[i]) {
678       errc++;
679       report(lp, SEVERE, "presolve_debugcheck: Detected UB < LB for column %d\n",
680                          j);
681     }
682   }
683   /* Return total number of errors */
684   return( errc );
685 }
686 
presolve_candeletevar(presolverec * psdata,int colnr)687 STATIC MYBOOL presolve_candeletevar(presolverec *psdata, int colnr)
688 {
689   lprec    *lp = psdata->lp;
690   int      usecount = SOS_memberships(lp->SOS, colnr);
691 
692   return( (MYBOOL) ((lp->SOS == NULL) || (usecount == 0) ||
693                     (/*is_presolve(lp, PRESOLVE_SOS) &&*/
694                      (((lp->SOS->sos1_count == lp->SOS->sos_count)) ||
695                       (usecount == SOS_is_member_of_type(lp->SOS, colnr, SOS1))))) );
696 }
697 
presolve_rowlengthex(presolverec * psdata,int rownr)698 STATIC int presolve_rowlengthex(presolverec *psdata, int rownr)
699 {
700   int j1 = psdata->rows->plucount[rownr] + psdata->rows->negcount[rownr];
701 #ifdef Paranoia
702   int j2 = presolve_rowlength(psdata, rownr);
703 
704   if(j1 != j2) {
705     report(psdata->lp, SEVERE, "presolve_rowlengthex: Expected row length %d, but found %d in row %s\n",
706                                 j2, j1, get_row_name(psdata->lp, rownr));
707     j1 = -j1;
708   }
709 #endif
710 
711   return( j1 );
712 }
presolve_rowlengthdebug(presolverec * psdata)713 STATIC int presolve_rowlengthdebug(presolverec *psdata)
714 {
715   int rownr, n = 0;
716 
717   for(rownr = firstActiveLink(psdata->rows->varmap); rownr != 0;
718     rownr = nextActiveLink(psdata->rows->varmap, rownr))
719     n += presolve_rowlengthex(psdata, rownr);
720   return( n );
721 }
722 
presolve_nextrecord(psrec * ps,int recnr,int * previtem)723 INLINE int presolve_nextrecord(psrec *ps, int recnr, int *previtem)
724 {
725   int *nzlist = ps->next[recnr], nzcount = nzlist[0], status = -1;
726 
727   /* Check if we simply wish the last active column */
728   if(previtem == NULL) {
729     if(nzlist != NULL)
730       status = nzlist[*nzlist];
731     return( status );
732   }
733 
734   /* Step to next */
735 #ifdef Paranoia
736   else if((*previtem < 0) || (*previtem > nzcount))
737     return( status );
738 #endif
739   (*previtem)++;
740 
741   /* Set the return values */
742   if(*previtem > nzcount)
743     (*previtem) = 0;
744   else
745     status = nzlist[*previtem];
746 
747   return( status );
748 }
presolve_nextcol(presolverec * psdata,int rownr,int * previtem)749 INLINE int presolve_nextcol(presolverec *psdata, int rownr, int *previtem)
750 /* Find the first active (non-eliminated) nonzero column in rownr after prevcol */
751 {
752   return( presolve_nextrecord(psdata->rows, rownr, previtem) );
753 }
presolve_lastcol(presolverec * psdata,int rownr)754 INLINE int presolve_lastcol(presolverec *psdata, int rownr)
755 {
756   return( presolve_nextrecord(psdata->rows, rownr, NULL) );
757 }
presolve_nextrow(presolverec * psdata,int colnr,int * previtem)758 INLINE int presolve_nextrow(presolverec *psdata, int colnr, int *previtem)
759 /* Find the first active (non-eliminated) nonzero row in colnr after prevrow */
760 {
761   return( presolve_nextrecord(psdata->cols, colnr, previtem) );
762 }
presolve_lastrow(presolverec * psdata,int colnr)763 INLINE int presolve_lastrow(presolverec *psdata, int colnr)
764 {
765   return( presolve_nextrecord(psdata->cols, colnr, NULL) );
766 }
767 
presolve_adjustrhs(presolverec * psdata,int rownr,REAL fixdelta,REAL epsvalue)768 INLINE void presolve_adjustrhs(presolverec *psdata, int rownr, REAL fixdelta, REAL epsvalue)
769 {
770   lprec *lp = psdata->lp;
771 
772   lp->orig_rhs[rownr] -= fixdelta;
773   if(epsvalue > 0)
774 #if 1
775     my_roundzero(lp->orig_rhs[rownr], epsvalue);
776 #else
777     lp->orig_rhs[rownr] = presolve_roundrhs(lp, lp->orig_rhs[rownr], FALSE);
778 #endif
779   lp->presolve_undo->fixed_rhs[rownr] += fixdelta;
780 }
781 
presolve_shrink(presolverec * psdata,int * nConRemove,int * nVarRemove)782 STATIC int presolve_shrink(presolverec *psdata, int *nConRemove, int *nVarRemove)
783 {
784   SOSgroup *SOS = psdata->lp->SOS;
785   int     status = RUNNING, countR = 0, countC = 0,
786           i, ix, n, *list;
787   REAL    fixValue;
788 
789   /* Remove empty rows */
790   list = psdata->rows->empty;
791   if(list != NULL) {
792     n = list[0];
793     for(i = 1; i <= n; i++)
794       if(isActiveLink(psdata->rows->varmap, list[i])) {
795         presolve_rowremove(psdata, list[i], FALSE);
796         countR++;
797       }
798     if(nConRemove != NULL)
799       (*nConRemove) += countR;
800     list[0] = 0;
801   }
802 
803   /* Fix and remove empty columns (unless they are in a SOS) */
804   list = psdata->cols->empty;
805   if(list != NULL) {
806     n = list[0];
807     for(i = 1; i <= n; i++) {
808       ix = list[i];
809       if(isActiveLink(psdata->cols->varmap, ix)) {
810         if(presolve_colfixdual(psdata, ix, &fixValue, &status)) {
811           if(!presolve_colfix(psdata, ix, fixValue, TRUE, nVarRemove)) {
812             status = presolve_setstatus(psdata, INFEASIBLE);
813             break;
814           }
815           presolve_colremove(psdata, ix, FALSE);
816           countC++;
817         }
818         else if(SOS_is_member(SOS, 0, ix))
819           report(psdata->lp, DETAILED, "presolve_shrink: Empty column %d is member of a SOS\n", ix);
820       }
821     }
822     list[0] = 0;
823   }
824 
825   return( status );
826 }
827 
presolve_rowremove(presolverec * psdata,int rownr,MYBOOL allowcoldelete)828 STATIC void presolve_rowremove(presolverec *psdata, int rownr, MYBOOL allowcoldelete)
829 {
830   lprec    *lp = psdata->lp;
831   MATrec   *mat = lp->matA;
832   int      ix, ie, nx, jx, je, *cols, *rows, n, colnr;
833 
834 #ifdef Paranoia
835   if((rownr < 1) || (rownr > lp->rows))
836     report(lp, SEVERE, "presolve_rowremove: Row %d out of range\n", rownr);
837 #endif
838 
839   /* Remove this row for each column that is active in the row */
840   cols = psdata->rows->next[rownr];
841   ie = *cols;
842   cols++;
843   for(ix = 1; ix <= ie; ix++, cols++) {
844     n = 0;
845     colnr = ROW_MAT_COLNR(*cols);
846     rows = psdata->cols->next[colnr];
847     je = rows[0];
848     /* See if we can narrow the search window */
849     jx = je / 2;
850     if((jx > 5) && (rownr >= COL_MAT_ROWNR(rows[jx])))
851       n = jx-1;
852     else
853       jx = 1;
854     /* Do the compression loop */
855     for(; jx <= je; jx++) {
856       nx = rows[jx];
857       if(COL_MAT_ROWNR(nx) != rownr) {
858         n++;
859         rows[n] = nx;
860       }
861     }
862     rows[0] = n;
863 
864     /* Make sure we delete columns that have become empty */
865 #if 1
866     if((n == 0) && allowcoldelete) {
867       int *list = psdata->cols->empty;
868       n = ++list[0];
869       list[n] = colnr;
870     }
871 #endif
872 
873   }
874   FREE(psdata->rows->next[rownr]);
875 
876   removeLink(psdata->rows->varmap, rownr);
877   switch(get_constr_type(lp, rownr)) {
878     case LE: removeLink(psdata->LTmap, rownr);
879               break;
880     case EQ: removeLink(psdata->EQmap, rownr);
881               break;
882   }
883   if(isActiveLink(psdata->INTmap, rownr))
884     removeLink(psdata->INTmap, rownr);
885 }
886 
presolve_colremove(presolverec * psdata,int colnr,MYBOOL allowrowdelete)887 STATIC int presolve_colremove(presolverec *psdata, int colnr, MYBOOL allowrowdelete)
888 {
889   lprec    *lp = psdata->lp;
890 
891 #ifdef Paranoia
892   if((colnr < 1) || (colnr > lp->columns))
893     report(lp, SEVERE, "presolve_colremove: Column %d out of range\n", colnr);
894   if(!isActiveLink(psdata->cols->varmap, colnr) || !presolve_candeletevar(psdata, colnr))
895     colnr = -1;
896   else
897 #endif
898   {
899     MATrec *mat = lp->matA;
900     int    ix, ie, nx, jx, je, *cols, *rows, n, rownr;
901 
902     /* Remove this column for each row that is active in the column */
903     rows = psdata->cols->next[colnr];
904     je = *rows;
905     rows++;
906     for(jx = 1; jx <= je; jx++, rows++) {
907       n = 0;
908       rownr = COL_MAT_ROWNR(*rows);
909       cols = psdata->rows->next[rownr];
910       ie = cols[0];
911       /* See if we can narrow the search window */
912       ix = ie / 2;
913       if((ix > 5) && (colnr >= ROW_MAT_COLNR(cols[ix])))
914         n = ix-1;
915       else
916         ix = 1;
917       /* Do the compression loop */
918       for(; ix <= ie; ix++) {
919         nx = cols[ix];
920         if(ROW_MAT_COLNR(nx) != colnr) {
921           n++;
922           cols[n] = nx;
923         }
924       }
925       cols[0] = n;
926 
927       /* Make sure we delete rows that become empty */
928 #if 1
929       if((n == 0) && allowrowdelete) {
930         int *list = psdata->rows->empty;
931         n = ++list[0];
932         list[n] = rownr;
933       }
934 #endif
935 
936     }
937     FREE(psdata->cols->next[colnr]);
938 
939     /* Update other counts */
940     if(SOS_is_member(lp->SOS, 0, colnr)) {
941       if(lp->sos_priority != NULL) {
942         lp->sos_vars--;
943         if(is_int(lp, colnr))
944           lp->sos_ints--;
945       }
946       SOS_member_delete(lp->SOS, 0, colnr);
947       clean_SOSgroup(lp->SOS, TRUE);
948       if(SOS_count(lp) == 0)
949         free_SOSgroup(&(lp->SOS));
950     }
951 
952     /* Finally remove the column from the active column list */
953     colnr = removeLink(psdata->cols->varmap, colnr);
954   }
955   return( colnr );
956 }
957 
presolve_redundantSOS(presolverec * psdata,int * nb,int * nSum)958 STATIC int presolve_redundantSOS(presolverec *psdata, int *nb, int *nSum)
959 {
960   lprec    *lp = psdata->lp;
961   int      i, ii, k, kk, j, nrows = lp->rows, *fixed = NULL,
962            iBoundTighten = 0, status = RUNNING;
963   SOSrec   *SOS;
964 
965   /* Is there anything to do? */
966   i = ii = SOS_count(lp);
967   if(ii == 0)
968     return( status );
969 
970   /* Allocate working member list */
971   if(!allocINT(lp, &fixed, lp->columns+1, FALSE) )
972     return( lp->spx_status );
973 
974   /* Check if we have SOS'es that are already satisfied or fixable/satisfiable */
975   while(i > 0) {
976     SOS = lp->SOS->sos_list[i-1];
977     kk = SOS->members[0];
978     fixed[0] = 0;
979     for(k = 1; k <= kk; k++) {
980       j = SOS->members[k];
981       if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j)) {
982         fixed[++fixed[0]] = k;
983         /* Abort if we have identified SOS infeasibility */
984         if(fixed[0] > SOS->type) {
985           status = presolve_setstatus(psdata, INFEASIBLE);
986           goto Done;
987         }
988       }
989     }
990     /* If there were an exact number of non-zero SOS members, check their sequentiality */
991     if(fixed[0] == SOS->type) {
992       /* Check sequentiality of members with non-zero lower bounds */
993       for(k = 2; k <= fixed[0]; k++) {
994         if(fixed[k] != fixed[k-1]+1) {
995           status = presolve_setstatus(psdata, INFEASIBLE);
996           goto Done;
997         }
998       }
999       /* Fix other member variables to zero, if necessary */
1000       for(k = kk; k > 0; k--) {
1001         j = SOS->members[k];
1002         if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j))
1003           continue;
1004         if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) {
1005           status = presolve_setstatus(psdata, INFEASIBLE);
1006           goto Done;
1007         }
1008       }
1009       /* Remove the SOS */
1010       delete_SOSrec(lp->SOS, i /* , FALSE */);
1011     }
1012     /* Otherwise, try to fix variables outside the SOS type window */
1013     else if(fixed[0] > 0) {
1014       for(k = kk; k > 0; k--) {
1015         if((k > fixed[fixed[0]]-SOS->type) && /* After leading entries   */
1016            (k < fixed[1]+SOS->type))          /* Before trailing entries */
1017           continue;
1018         j = SOS->members[k];
1019         SOS_member_delete(lp->SOS, i, j);
1020         /* if(get_upbo(lp, j) - get_lowbo(lp, j) < lp->epsprimal) */
1021         if(is_fixedvar(lp, nrows+j))
1022           continue;
1023         if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) {
1024           status = presolve_setstatus(psdata, INFEASIBLE);
1025           goto Done;
1026         }
1027       }
1028     }
1029     i--;
1030   }
1031 
1032   /* Update the sparse member map if there were SOS deletions;
1033      Remember that delete_SOSrec() above specified deferred updating! */
1034   i = SOS_count(lp);
1035   if((i < ii) || (iBoundTighten > 0)) {
1036     SOS_member_updatemap(lp->SOS);
1037   }
1038 
1039   /* Update tag orders */
1040   for(; i > 0; i--)
1041     lp->SOS->sos_list[i-1]->tagorder = i;
1042 
1043 Done:
1044   FREE(fixed);
1045   (*nb) += iBoundTighten;
1046   (*nSum) += iBoundTighten;
1047 
1048   return( status );
1049 }
1050 
presolve_fixSOS1(presolverec * psdata,int colnr,REAL fixvalue,int * nr,int * nv)1051 STATIC MYBOOL presolve_fixSOS1(presolverec *psdata, int colnr, REAL fixvalue, int *nr, int *nv)
1052 {
1053   lprec    *lp = psdata->lp;
1054   int      i, k, j;
1055   SOSrec   *SOS;
1056   REAL     newvalue;
1057   MYBOOL   *fixed = NULL, status = FALSE;
1058 
1059   /* Allocate working member list */
1060   if(!allocMYBOOL(lp, &fixed, lp->columns+1, TRUE) )
1061     return(FALSE);
1062 
1063   /* Fix variables in SOS's where colnr is a member */
1064   i = SOS_count(lp);
1065   while(i > 0) {
1066     /* Set next SOS target (note that colnr has been tested earlier as not being a member of a higher order SOS) */
1067     SOS = lp->SOS->sos_list[i-1];
1068     if(SOS_is_member(lp->SOS, i, colnr)) {
1069       for(k = SOS->members[0]; k > 0; k--) {
1070         j = SOS->members[k];
1071         if(fixed[j])
1072           continue;
1073         if(j == colnr) {
1074           fixed[j] = TRUE;
1075           newvalue = fixvalue;
1076         }
1077         else {
1078           fixed[j] = AUTOMATIC;
1079           newvalue = 0.0;
1080         }
1081         /* If it is a member of a higher order SOS then just change bounds */
1082         if(!presolve_candeletevar(psdata, j)) {
1083           set_bounds(lp, j, newvalue, newvalue);
1084           fixed[j] = TRUE | AUTOMATIC;
1085           psdata->forceupdate = TRUE;
1086         }
1087         /* Otherwise fix it in preparation for removal */
1088         else if(!presolve_colfix(psdata, j, newvalue, TRUE, nv))
1089           goto Done;
1090       }
1091     }
1092     i--;
1093   }
1094 
1095   /* Delete SOS'es or SOS member variables where we can */
1096   k = i = SOS_count(lp);
1097   while(i > 0) {
1098     /* Set next SOS target */
1099     SOS = lp->SOS->sos_list[i-1];
1100     if(SOS_is_member(lp->SOS, i, colnr)) {
1101       /* Always delete SOS1's */
1102       if(SOS->type == SOS1)
1103         delete_SOSrec(lp->SOS, i /* , FALSE */);
1104       /* Only delete leading or trailing SOS members in higher-order SOS'es that are fixed at 0;
1105         (note that this section of the code will never be called in the current setup) */
1106       else {
1107         /* First the leading entries... */
1108         for(j = 1; j <= SOS->members[0]; j++) {
1109           if(fixed[SOS->members[j]] == AUTOMATIC)
1110             SOS_member_delete(lp->SOS, i, SOS->members[j]);
1111         }
1112         /* ...then trailing entries */
1113         for(j = SOS->members[0]; j > 0; j--) {
1114           if(fixed[SOS->members[j]] == AUTOMATIC)
1115             SOS_member_delete(lp->SOS, i, SOS->members[j]);
1116         }
1117       }
1118     }
1119     i--;
1120   }
1121 
1122   /* Update the sparse member map if there were SOS deletions; delete_SOSrec() above
1123      specified deferred updating */
1124   i = SOS_count(lp);
1125   if(i < k)
1126     SOS_member_updatemap(lp->SOS);
1127 
1128   /* Delete the variables that have been fixed */
1129   k = 0;
1130   for(j = lp->columns; j > 0; j--) {
1131     if((fixed[j] == TRUE) || (fixed[j] == AUTOMATIC)) {
1132        presolve_colremove(psdata, j, TRUE);
1133        k++;
1134     }
1135   }
1136 
1137   /* Update tag orders */
1138   i = SOS_count(lp);
1139   for(; i > 0; i--)
1140     lp->SOS->sos_list[i-1]->tagorder = i;
1141 
1142   status = TRUE;
1143 
1144 Done:
1145   FREE(fixed);
1146   return( status );
1147 }
1148 
presolve_setEQ(presolverec * psdata,int rownr)1149 STATIC void presolve_setEQ(presolverec *psdata, int rownr)
1150 {
1151   lprec *lp = psdata->lp;
1152 
1153   if(is_constr_type(lp, rownr, LE))
1154      removeLink(psdata->LTmap, rownr);
1155    setLink(psdata->EQmap, rownr);
1156    set_constr_type(lp, rownr, EQ);
1157    psdata->dv_lobo[rownr] = -lp->infinite;
1158    psdata->dv_upbo[rownr] = lp->infinite;
1159 }
1160 
presolve_singletonbounds(presolverec * psdata,int rownr,int colnr,REAL * lobound,REAL * upbound,REAL * aval)1161 STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr, REAL *lobound, REAL *upbound, REAL *aval)
1162 {
1163   lprec  *lp = psdata->lp;
1164   REAL   coeff_a, epsvalue = psdata->epsvalue;
1165   MYBOOL isneg;
1166 
1167   /* Compute row singleton variable range */
1168   if(is_constr_type(lp, rownr, EQ) && (fabs(*lobound) < epsvalue))
1169     *lobound = *upbound = 0;
1170   else {
1171     if(aval == NULL)
1172       coeff_a = get_mat(lp, rownr, colnr);
1173     else
1174       coeff_a = *aval;
1175     isneg = (MYBOOL) (coeff_a < 0);
1176     if(*lobound > -lp->infinite)
1177       *lobound /= coeff_a;
1178     else if(isneg)
1179       *lobound = -(*lobound);
1180     if(*upbound < lp->infinite)
1181       *upbound /= coeff_a;
1182     else if(isneg)
1183       *upbound = -(*upbound);
1184     if(isneg)
1185       swapREAL(lobound, upbound);
1186   }
1187 
1188   /* Check against bound - handle SC variables specially */
1189   if(is_semicont(lp, colnr)) {
1190     coeff_a = get_lowbo(lp, colnr);
1191     if(coeff_a > 0) {
1192       SETMAX(*lobound, 0.0);
1193       SETMIN(*upbound, get_upbo(lp, colnr));
1194     }
1195     else {
1196       coeff_a = get_upbo(lp, colnr);
1197       if(coeff_a > 0) {
1198         SETMAX(*lobound, get_lowbo(lp, colnr));
1199         SETMIN(*upbound, 0.0);
1200       }
1201     }
1202   }
1203   else {
1204     SETMAX(*lobound, get_lowbo(lp, colnr));
1205     SETMIN(*upbound, get_upbo(lp, colnr));
1206   }
1207 
1208   /* Return with consistency status */
1209 #ifdef DoPresolveRelativeTest
1210   isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue);
1211 #else
1212   isneg = (MYBOOL) (*upbound >= *lobound - epsvalue);
1213 #endif
1214   if(!isneg) {
1215     /* Attempt bound-related error correction */
1216     if(fabs(my_reldiff(*lobound, get_upbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue)
1217       *lobound = get_upbo(lp, colnr);
1218     else if(fabs(my_reldiff(*upbound, get_lowbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue)
1219       *upbound = get_lowbo(lp, colnr);
1220 #ifdef DoPresolveRelativeTest
1221     isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue);
1222 #else
1223     isneg = (MYBOOL) (*upbound >= *lobound - epsvalue);
1224 #endif
1225     if(!isneg)
1226       report(lp, NORMAL, "presolve_singletonbounds: Singleton variable %s in row %s infeasibility (%g << %g)\n",
1227                          get_col_name(lp, colnr), get_row_name(lp, rownr), *lobound, *upbound);
1228   }
1229   return( isneg );
1230 }
1231 
presolve_altsingletonvalid(presolverec * psdata,int rownr,int colnr,REAL reflotest,REAL refuptest)1232 STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int colnr, REAL reflotest, REAL refuptest)
1233 {
1234   lprec *lp = psdata->lp;
1235   REAL  coeff_bl, coeff_bu, epsvalue = psdata->epsvalue;
1236 
1237   coeff_bl = get_rh_lower(lp, rownr);
1238   coeff_bu = get_rh_upper(lp, rownr);
1239 
1240   /* Check base data validity */
1241 #ifdef DoPresolveRelativeTest
1242   if((my_reldiff(refuptest, reflotest) < -epsvalue) ||
1243 #else
1244   if((reflotest > refuptest + epsvalue) ||
1245 #endif
1246      !presolve_singletonbounds(psdata, rownr, colnr, &coeff_bl, &coeff_bu, NULL))
1247     return( FALSE );
1248 
1249   /* Base data is Ok, now check against against each other */
1250   epsvalue = MAX(reflotest-coeff_bu, coeff_bl-refuptest) / epsvalue;
1251   if(epsvalue > PRESOLVE_BOUNDSLACK) {
1252     report(lp, NORMAL, "presolve_altsingletonvalid: Singleton variable %s in row %s infeasible (%g)\n",
1253                        get_col_name(lp, colnr), get_row_name(lp, rownr), MAX(reflotest-coeff_bu, coeff_bl-refuptest));
1254     return( FALSE );
1255   }
1256   else
1257     return( TRUE );
1258 }
1259 
presolve_multibounds(presolverec * psdata,int rownr,int colnr,REAL * lobound,REAL * upbound,REAL * aval,MYBOOL * rowbinds)1260 STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr,
1261                                    REAL *lobound, REAL *upbound, REAL *aval, MYBOOL *rowbinds)
1262 {
1263   lprec    *lp = psdata->lp;
1264   MYBOOL   rowbindsvar = FALSE, status = FALSE;
1265   REAL     coeff_a, LHS, RHS, netX, Xupper, Xlower, epsvalue = psdata->epsvalue;
1266 
1267   /* Get variable bounds for netting */
1268   LHS = *lobound;
1269   RHS = *upbound;
1270   Xlower = get_lowbo(lp, colnr);
1271   Xupper = get_upbo(lp, colnr);
1272 
1273   /* Identify opportunity for bound tightening */
1274   if(aval == NULL)
1275     coeff_a = get_mat(lp, rownr, colnr);
1276   else
1277     coeff_a = *aval;
1278 
1279   netX = presolve_sumplumin(lp, rownr, psdata->rows, TRUE);
1280   if(!my_infinite(lp, LHS) && !my_infinite(lp, netX)) {
1281     if(coeff_a > 0) {
1282       LHS -= netX-coeff_a*Xupper;
1283       LHS /= coeff_a;
1284       if(LHS > Xlower + epsvalue) {
1285         Xlower = presolve_roundrhs(lp, LHS, TRUE);
1286         status = TRUE;
1287       }
1288       else if(LHS > Xlower - epsvalue)
1289         rowbindsvar = TRUE;
1290     }
1291     else {
1292       LHS -= netX-coeff_a*Xlower;
1293       LHS /= coeff_a;
1294       if(LHS < Xupper - epsvalue) {
1295         Xupper = presolve_roundrhs(lp, LHS, FALSE);
1296         status = AUTOMATIC;
1297       }
1298       else if(LHS < Xupper + epsvalue)
1299         rowbindsvar = AUTOMATIC;
1300     }
1301   }
1302 
1303   netX = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
1304   if(!my_infinite(lp, RHS) && !my_infinite(lp, netX)) {
1305     if(coeff_a < 0) {
1306       if(!my_infinite(lp, Xupper)) {
1307         RHS -= netX-coeff_a*Xupper;
1308         RHS /= coeff_a;
1309         if(RHS > Xlower + epsvalue) {
1310           Xlower = presolve_roundrhs(lp, RHS, TRUE);
1311           status |= TRUE;
1312         }
1313         else if(RHS > Xlower - epsvalue)
1314           rowbindsvar |= TRUE;
1315       }
1316     }
1317     else if(!my_infinite(lp, Xlower)) {
1318       RHS -= netX-coeff_a*Xlower;
1319       RHS /= coeff_a;
1320       if(RHS < Xupper - epsvalue) {
1321         Xupper = presolve_roundrhs(lp, RHS, FALSE);
1322         status |= AUTOMATIC;
1323       }
1324       else if(RHS < Xupper + epsvalue)
1325         rowbindsvar |= AUTOMATIC;
1326     }
1327   }
1328 
1329   *lobound = Xlower;
1330   *upbound = Xupper;
1331   if(rowbinds != NULL)
1332     *rowbinds = rowbindsvar;
1333 
1334   return(status);
1335 }
1336 
isnz_origobj(lprec * lp,int colnr)1337 STATIC MYBOOL isnz_origobj(lprec *lp, int colnr)
1338 {
1339   return( (MYBOOL) (lp->orig_obj[colnr] != 0) );
1340 }
1341 
presolve_testrow(presolverec * psdata,int lastrow)1342 STATIC MYBOOL presolve_testrow(presolverec *psdata, int lastrow)
1343 {
1344   if(psdata->forceupdate) {
1345     presolve_updatesums(psdata);
1346     psdata->forceupdate = FALSE;
1347   }
1348   if(!presolve_rowfeasible(psdata, 0, TRUE))
1349     return( FALSE );
1350   else
1351     return( TRUE );
1352 }
1353 
presolve_coltighten(presolverec * psdata,int colnr,REAL LOnew,REAL UPnew,int * count)1354 STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, REAL LOnew, REAL UPnew, int *count)
1355 {
1356   lprec    *lp = psdata->lp;
1357   int      elmnr, elmend, k, oldcount = 0, newcount = 0, deltainf;
1358   REAL     LOold, UPold, Value, margin = psdata->epsvalue;
1359   MATrec   *mat = lp->matA;
1360   REAL     *value;
1361   int      *rownr;
1362 
1363   /* Attempt correction of marginally equal, but inconsistent input values */
1364   Value = UPnew - LOnew;
1365   if((Value <= -margin) && (Value > -lp->epsprimal)) {
1366     if(fabs(fmod(UPnew, 1.0)) < margin)
1367       LOnew = UPnew;
1368     else
1369       UPnew = LOnew;
1370   }
1371 
1372   /* Check if there is anything to do */
1373   LOold = get_lowbo(lp, colnr);
1374   UPold = get_upbo(lp, colnr);
1375 #ifdef Paranoia
1376   if(((LOold > LOnew) && !is_semicont(lp, colnr)) || (UPold < UPnew)) {
1377     report(lp, SEVERE, "presolve_coltighten: Inconsistent new bounds requested for column %d\n", colnr);
1378     return( FALSE );
1379   }
1380 #endif
1381   if(count != NULL)
1382     newcount = *count;
1383   oldcount = newcount;
1384 
1385   /* Modify inf-count */
1386   deltainf = 0;
1387   if((UPold < lp->infinite) || (LOold > -lp->infinite))
1388     deltainf -= 1;
1389   if((UPnew < lp->infinite) || (LOnew > -lp->infinite))
1390     deltainf += 1;
1391   if(isnz_origobj(lp, colnr))
1392     psdata->rows->infcount[0] += deltainf;
1393   elmnr = mat->col_end[colnr-1];
1394   elmend = mat->col_end[colnr];
1395   rownr = &COL_MAT_ROWNR(elmnr);
1396   for(; elmnr < elmend; elmnr++, rownr += matRowColStep) {
1397     k = *rownr;
1398     if(isActiveLink(psdata->rows->varmap, k))
1399       psdata->rows->infcount[k] += deltainf;
1400   }
1401 
1402   /* Look for opportunity to tighten upper variable bound */
1403   if((UPnew < lp->infinite) && (UPnew+margin < UPold)) {
1404     if(is_int(lp, colnr))
1405       UPnew = floor(UPnew+margin);
1406     if(UPold < lp->infinite) {
1407       /* First do OF */
1408       k = 0;
1409       Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]);
1410       if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite))
1411         psdata->rows->pluupper[k] += (UPnew-UPold)*Value;
1412       else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite))
1413         psdata->rows->negupper[k] += (LOnew-LOold)*Value;
1414       psdata->rows->infcount[k] += deltainf;
1415 
1416       /* Then scan the constraint rows */
1417       elmnr = mat->col_end[colnr-1];
1418       elmend = mat->col_end[colnr];
1419       rownr = &COL_MAT_ROWNR(elmnr);
1420       value = &COL_MAT_VALUE(elmnr);
1421       for(; elmnr < elmend;
1422           elmnr++, rownr += matRowColStep, value += matValueStep) {
1423         k = *rownr;
1424         if(!isActiveLink(psdata->rows->varmap, k))
1425           continue;
1426         Value = my_chsign(is_chsign(lp, k), *value);
1427         if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite))
1428           psdata->rows->pluupper[k] += (UPnew-UPold)*Value;
1429         else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite))
1430           psdata->rows->negupper[k] += (LOnew-LOold)*Value;
1431       }
1432     }
1433     else
1434       psdata->forceupdate = TRUE;
1435     if(UPnew < UPold) {
1436       UPold = UPnew;
1437       newcount++;
1438     }
1439   }
1440 
1441   /* Look for opportunity to tighten lower variable bound */
1442   if((LOnew > -lp->infinite) && (LOnew-margin > LOold)) {
1443     if(is_int(lp, colnr))
1444        LOnew = ceil(LOnew-margin);
1445     if(LOold > -lp->infinite) {
1446       /* First do OF */
1447       k = 0;
1448       Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]);
1449       if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite))
1450         psdata->rows->plulower[k] += (LOnew-LOold)*Value;
1451       else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite))
1452         psdata->rows->neglower[k] += (UPnew-UPold)*Value;
1453 
1454       /* Then scan the constraint rows */
1455       elmnr = mat->col_end[colnr-1];
1456       elmend = mat->col_end[colnr];
1457       rownr = &COL_MAT_ROWNR(elmnr);
1458       value = &COL_MAT_VALUE(elmnr);
1459       for(; elmnr < elmend;
1460           elmnr++, rownr += matRowColStep, value += matValueStep) {
1461         k = *rownr;
1462         if(!isActiveLink(psdata->rows->varmap, k))
1463           continue;
1464         Value = my_chsign(is_chsign(lp, k), *value);
1465         if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite))
1466           psdata->rows->plulower[k] += (LOnew-LOold)*Value;
1467         else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite))
1468           psdata->rows->neglower[k] += (UPnew-UPold)*Value;
1469       }
1470     }
1471     else
1472       psdata->forceupdate = TRUE;
1473     if(LOnew > LOold) {
1474       LOold = LOnew;
1475       newcount++;
1476     }
1477   }
1478 
1479   /* Now set the new variable bounds, if they are tighter */
1480   if(newcount > oldcount) {
1481     UPnew = presolve_roundval(lp, UPnew);
1482     LOnew = presolve_roundval(lp, LOnew);
1483     if(LOnew > UPnew) {
1484       if(LOnew-UPnew < margin) {
1485         LOnew = UPnew;
1486       }
1487       else {
1488         report(lp, NORMAL, "presolve_coltighten: Found column %s with LB %g > UB %g\n",
1489                             get_col_name(lp, colnr), LOnew, UPnew);
1490         return( FALSE );
1491       }
1492     }
1493     if(lp->spx_trace || (lp->verbose > DETAILED))
1494       report(lp, NORMAL, "presolve_coltighten: Replaced bounds on column %s to [%g ... %g]\n",
1495                          get_col_name(lp, colnr), LOnew, UPnew);
1496     set_bounds(lp, colnr, LOnew, UPnew);
1497   }
1498   if(count != NULL)
1499     *count = newcount;
1500 
1501   return( TRUE );
1502 }
1503 
presolve_rowtighten(presolverec * psdata,int rownr,int * tally,MYBOOL intsonly)1504 STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOOL intsonly)
1505 {
1506   lprec  *lp = psdata->lp;
1507   MYBOOL rowbinds;
1508   int    item = 0, jx, jjx, ix, idxn = 0, *idxbound = NULL, status = RUNNING;
1509   REAL   *newbound = NULL, RHlo = get_rh_lower(lp, rownr), RHup = get_rh_upper(lp, rownr),
1510          VARlo, VARup, Aval;
1511   MATrec *mat = lp->matA;
1512 
1513   jx = presolve_rowlength(psdata, rownr);
1514   allocREAL(lp, &newbound, 2*jx, TRUE);
1515   allocINT (lp, &idxbound, 2*jx, TRUE);
1516 
1517   /* Identify bound tightening for each active variable in the constraint */
1518   for(jx = presolve_nextcol(psdata, rownr, &item); jx >= 0;
1519       jx = presolve_nextcol(psdata, rownr, &item)) {
1520     jjx = ROW_MAT_COLNR(jx);
1521     Aval = ROW_MAT_VALUE(jx);
1522     Aval = my_chsign(rownr, Aval);
1523 
1524     VARlo = RHlo;
1525     VARup = RHup;
1526     presolve_multibounds(psdata, rownr,jjx, &VARlo, &VARup, &Aval, &rowbinds);
1527     if(rowbinds & TRUE) {
1528       idxbound[idxn] = -jjx;
1529       newbound[idxn] = VARlo;
1530       idxn++;
1531     }
1532     if(rowbinds & AUTOMATIC) {
1533       idxbound[idxn] = jjx;
1534       newbound[idxn] = VARup;
1535       idxn++;
1536     }
1537   }
1538 
1539   /* Loop over the bounds identified for tightening and perform update */
1540   ix = 0;
1541   while(ix < idxn) {
1542     jjx = idxbound[ix];
1543     jx = abs(jjx);
1544 
1545     /* Skip free variables and non-ints, if specified */
1546     if(is_unbounded(lp, jx) ||
1547        (intsonly && !is_int(lp, jx)))
1548       continue;
1549 
1550     VARlo = get_lowbo(lp, jx);
1551     VARup = get_upbo(lp, jx);
1552     /* while((ix < idxn) && (jx == abs(jjx))) { */
1553     while((ix < idxn) && (jx == abs((jjx = idxbound[ix])))) {
1554       if(jjx < 0)
1555         VARlo = newbound[ix];
1556       else
1557         VARup = newbound[ix];
1558       ix++;
1559     }
1560     if(!presolve_coltighten(psdata, jx, VARlo, VARup, tally)) {
1561       status = presolve_setstatus(psdata, INFEASIBLE);
1562       break;
1563     }
1564   }
1565 
1566   FREE(newbound);
1567   FREE(idxbound);
1568 
1569   return(status);
1570 }
1571 
set_dv_bounds(presolverec * psdata,int rownr,REAL lowbo,REAL upbo)1572 STATIC void set_dv_bounds(presolverec *psdata, int rownr, REAL lowbo, REAL upbo)
1573 {
1574   psdata->dv_lobo[rownr] = lowbo;
1575   psdata->dv_upbo[rownr] = upbo;
1576 }
get_dv_lower(presolverec * psdata,int rownr)1577 STATIC REAL get_dv_lower(presolverec *psdata, int rownr)
1578 {
1579   return( psdata->dv_lobo[rownr] );
1580 }
1581 
get_dv_upper(presolverec * psdata,int rownr)1582 STATIC REAL get_dv_upper(presolverec *psdata, int rownr)
1583 {
1584   return( psdata->dv_upbo[rownr] );
1585 }
1586 
presolve_rowfix(presolverec * psdata,int rownr,REAL newvalue,MYBOOL remove,int * tally)1587 STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, REAL newvalue, MYBOOL remove, int *tally)
1588 {
1589   lprec    *lp = psdata->lp;
1590   int      i, ix, ie;
1591   MYBOOL   isneg, lofinite, upfinite, doupdate = FALSE, chsign = is_chsign(lp, rownr);
1592   REAL     lobound, upbound, lovalue, upvalue,
1593            Value, fixvalue, fixprod, mult;
1594   MATrec   *mat = lp->matA;
1595   psrec    *ps = psdata->cols;
1596 
1597   /* Set "fixed" value in case we are deleting a variable */
1598   upbound = get_dv_upper(psdata, rownr);
1599   lobound = get_dv_lower(psdata, rownr);
1600   if(remove) {
1601     if(upbound-lobound < psdata->epsvalue) {
1602       if((newvalue > lobound) && (newvalue < upbound))
1603         fixvalue = newvalue;
1604       else
1605         fixvalue = lobound;
1606     }
1607     else {
1608       if(my_infinite(lp, newvalue) && (get_rh(lp, rownr) == 0))
1609         fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound));
1610       else
1611         fixvalue = newvalue;
1612     }
1613     set_dv_bounds(psdata, rownr, fixvalue, fixvalue);
1614     if(fixvalue != 0)
1615       addUndoPresolve(lp, FALSE, rownr, fixvalue, 0, 0);
1616     mult = -1;
1617   }
1618   else {
1619     mult = 1;
1620     fixvalue = 0;
1621   }
1622 
1623   /* Loop over rows to update statistics */
1624   ix = mat->row_end[rownr - 1];
1625   ie = mat->row_end[rownr];
1626   for(; ix < ie; ix++) {
1627 
1628    /* Retrieve row data and adjust RHS if we are deleting a variable */
1629     i     = ROW_MAT_COLNR(ix);
1630     Value = ROW_MAT_VALUE(ix);
1631     if(Value == 0)
1632       continue;
1633 
1634     if(remove && (fixvalue != 0)) {
1635       fixprod = Value*fixvalue;
1636       lp->orig_obj[i] -= fixprod;
1637       my_roundzero(lp->orig_obj[i], psdata->epsvalue);
1638       lp->presolve_undo->fixed_obj[i] += fixprod;
1639     }
1640 
1641    /* Prepare for further processing */
1642     Value = my_chsign(chsign, Value);
1643     isneg = (MYBOOL) (Value < 0);
1644 
1645    /* Reduce row variable counts if we are removing the variable */
1646     if(!isActiveLink(ps->varmap, i))
1647       continue;
1648     if(remove) {
1649       if(isneg) {
1650         ps->negcount[i]--;
1651       }
1652       else {
1653         ps->plucount[i]--;
1654       }
1655       if((lobound < 0) && (upbound >= 0)) {
1656         ps->pluneg[i]--;
1657       }
1658     }
1659 
1660    /* Compute associated constraint contribution values */
1661     upfinite = (MYBOOL) (upbound < lp->infinite);
1662     lofinite = (MYBOOL) (lobound > -lp->infinite);
1663     if(upfinite || lofinite) {
1664       if(remove)
1665         ps->infcount[i]--;
1666       else
1667         ps->infcount[i]++;
1668     }
1669     upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite));
1670     lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite));
1671 
1672    /* Cumulate effective upper column bound (only bother with non-finite bound) */
1673     if(isneg) {
1674       if((ps->negupper[i] < lp->infinite) && lofinite) {
1675         ps->negupper[i] += mult*lovalue;
1676         ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE);
1677       }
1678       else if(remove && !lofinite)
1679         doupdate = TRUE;
1680       else
1681         ps->negupper[i] = lp->infinite;
1682     }
1683     else {
1684       if((ps->pluupper[i] < lp->infinite) && upfinite) {
1685         ps->pluupper[i] += mult*upvalue;
1686         ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE);
1687       }
1688       else if(remove && !upfinite)
1689         doupdate = TRUE;
1690       else
1691         ps->pluupper[i] = lp->infinite;
1692     }
1693 
1694    /* Cumulate effective lower column bound (only bother with non-finite bound) */
1695     if(isneg) {
1696       if((ps->neglower[i] > -lp->infinite) && upfinite) {
1697         ps->neglower[i] += mult*upvalue;
1698         ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE);
1699       }
1700       else if(remove && !upfinite)
1701         doupdate = TRUE;
1702       else
1703         ps->neglower[i] = -lp->infinite;
1704     }
1705     else {
1706       if((ps->plulower[i] > -lp->infinite) && lofinite) {
1707         ps->plulower[i] += mult*lovalue;
1708         ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE);
1709       }
1710       else if(remove && !lofinite)
1711         doupdate = TRUE;
1712       else
1713         ps->plulower[i] = -lp->infinite;
1714     }
1715 
1716    /* Validate consistency of eliminated singleton */
1717     if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) {
1718       presolve_range(lp, i, ps, &lovalue, &upvalue);
1719       Value = get_mat(lp, 0, i);
1720       if((upvalue < Value) ||
1721          (lovalue > Value)) {
1722         report(lp, IMPORTANT, "presolve: Row %s (%g << %g) infeasibility in column %s (OF=%g)\n",
1723                               get_row_name(lp, rownr), lovalue, upvalue, get_col_name(lp, i), Value);
1724         return( FALSE );
1725       }
1726     }
1727   }
1728   if(remove) {
1729     psdata->forceupdate |= doupdate;
1730     if(tally != NULL)
1731       (*tally)++;
1732   }
1733   return( TRUE );
1734 }
1735 
1736 
presolve_colsingleton(presolverec * psdata,int i,int j,int * count)1737 STATIC int presolve_colsingleton(presolverec *psdata, int i, int j, int *count)
1738 {
1739   lprec    *lp = psdata->lp;
1740   REAL     RHlow, RHup, LObound, UPbound, Value;
1741 
1742 #ifdef Paranoia
1743   if(!isActiveLink(psdata->cols->varmap, j))
1744     report(lp, SEVERE, "presolve_colsingleton: Nothing to do, column %d was eliminated earlier\n",
1745                        j);
1746 #endif
1747 
1748   Value = get_mat(lp,i,j);
1749   if(Value == 0)
1750     return( RUNNING );
1751 
1752   /* Initialize and identify semicontinuous variable */
1753   LObound = get_lowbo(lp, j);
1754   UPbound = get_upbo(lp, j);
1755   if(is_semicont(lp, j) && (UPbound > LObound)) {
1756     if(LObound > 0)
1757       LObound = 0;
1758     else if(UPbound < 0)
1759       UPbound = 0;
1760   }
1761 
1762   /* Get singleton variable bounds */
1763   RHlow = get_rh_lower(lp, i);
1764   RHup  = get_rh_upper(lp, i);
1765   if(!presolve_singletonbounds(psdata, i,j, &RHlow, &RHup, &Value))
1766     return( presolve_setstatus(psdata, INFEASIBLE) );
1767 
1768   if(presolve_coltighten(psdata, j, RHlow, RHup, count))
1769     return( RUNNING );
1770   else
1771     return( presolve_setstatus(psdata, INFEASIBLE) );
1772 }
1773 
presolve_colfix(presolverec * psdata,int colnr,REAL newvalue,MYBOOL remove,int * tally)1774 STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, REAL newvalue, MYBOOL remove, int *tally)
1775 {
1776   lprec    *lp = psdata->lp;
1777   int      i, ix, ie;
1778   MYBOOL   isneg, lofinite, upfinite, doupdate = FALSE, doOF = TRUE;
1779   REAL     lobound, upbound, lovalue, upvalue,
1780            Value, fixvalue, mult;
1781   MATrec   *mat = lp->matA;
1782   psrec    *ps = psdata->rows;
1783   REAL     *value;
1784   int      *rownr;
1785 
1786   /* Set "fixed" value in case we are deleting a variable */
1787   upbound = get_upbo(lp, colnr);
1788   lobound = get_lowbo(lp, colnr);
1789   if(remove) {
1790     if(upbound-lobound < psdata->epsvalue) {
1791       if((newvalue > lobound) && (newvalue < upbound))
1792         fixvalue = newvalue;
1793       else
1794         fixvalue = lobound;
1795     }
1796     else {
1797       if(my_infinite(lp, newvalue) && (get_mat(lp, 0, colnr) == 0))
1798         fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound));
1799       else
1800         fixvalue = newvalue;
1801     }
1802 #if 1 /* Fast normal version */
1803     set_bounds(lp, colnr, fixvalue, fixvalue);
1804 #else /* Slower version that can be used for debugging/control purposes */
1805     presolve_coltighten(psdata, colnr, fixvalue, fixvalue, NULL);
1806     lobound = fixvalue;
1807     upbound = fixvalue;
1808 #endif
1809     if(fixvalue != 0)
1810       addUndoPresolve(lp, TRUE, colnr, fixvalue, 0, 0);
1811     mult = -1;
1812   }
1813   else {
1814     mult = 1;
1815     fixvalue = 0;
1816   }
1817 
1818   /* Adjust semi-continuous variable bounds to zero-base */
1819   if(is_semicont(lp, colnr) && (upbound > lobound)) {
1820     if(lobound > 0)
1821       lobound = 0;
1822     else if(upbound < 0)
1823       upbound = 0;
1824   }
1825 
1826   /* Loop over rows to update statistics */
1827   ix = mat->col_end[colnr - 1];
1828   ie = mat->col_end[colnr];
1829   rownr = &COL_MAT_ROWNR(ix);
1830   value = &COL_MAT_VALUE(ix);
1831   for(; doOF || (ix < ie);
1832       ix++, rownr += matRowColStep, value += matValueStep) {
1833 
1834    /* Retrieve row data and adjust RHS if we are deleting a variable */
1835 Restart:
1836     if(doOF) {
1837       i = 0;
1838       Value = lp->orig_obj[colnr];
1839     }
1840     else {
1841       i = *rownr;
1842       Value = *value;
1843       if(!isActiveLink(ps->varmap, i))
1844         continue;
1845     }
1846     if(Value == 0)
1847       goto BlockEnd;
1848 
1849     if(remove && (fixvalue != 0))
1850       presolve_adjustrhs(psdata, i, Value*fixvalue, psdata->epsvalue);
1851 
1852    /* Prepare for further processing */
1853     Value = my_chsign(is_chsign(lp, i), Value);
1854     isneg = (MYBOOL) (Value < 0);
1855 
1856    /* Reduce row variable counts if we are removing the variable */
1857     if(remove == TRUE) {
1858       if(isneg) {
1859         ps->negcount[i]--;
1860       }
1861       else {
1862         ps->plucount[i]--;
1863       }
1864       if((lobound < 0) && (upbound >= 0)) {
1865         ps->pluneg[i]--;
1866       }
1867     }
1868 
1869    /* Compute associated constraint contribution values */
1870     upfinite = (MYBOOL) (upbound < lp->infinite);
1871     lofinite = (MYBOOL) (lobound > -lp->infinite);
1872     if(upfinite || lofinite) {
1873       if(remove)
1874         ps->infcount[i]--;
1875       else
1876         ps->infcount[i]++;
1877     }
1878     upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite));
1879     lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite));
1880 
1881    /* Cumulate effective upper row bound (only bother with non-finite bound) */
1882     if(isneg) {
1883       if((ps->negupper[i] < lp->infinite) && lofinite) {
1884         ps->negupper[i] += mult*lovalue;
1885         ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE);
1886       }
1887       else if(remove && !lofinite)
1888         doupdate = TRUE;
1889       else
1890         ps->negupper[i] = lp->infinite;
1891     }
1892     else {
1893       if((ps->pluupper[i] < lp->infinite) && upfinite) {
1894         ps->pluupper[i] += mult*upvalue;
1895         ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE);
1896       }
1897       else if(remove && !upfinite)
1898         doupdate = TRUE;
1899       else
1900         ps->pluupper[i] = lp->infinite;
1901     }
1902 
1903    /* Cumulate effective lower row bound (only bother with non-finite bound) */
1904     if(isneg) {
1905       if((ps->neglower[i] > -lp->infinite) && upfinite) {
1906         ps->neglower[i] += mult*upvalue;
1907         ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE);
1908       }
1909       else if(remove && !upfinite)
1910         doupdate = TRUE;
1911       else
1912         ps->neglower[i] = -lp->infinite;
1913     }
1914     else {
1915       if((ps->plulower[i] > -lp->infinite) && lofinite) {
1916         ps->plulower[i] += mult*lovalue;
1917         ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE);
1918       }
1919       else if(remove && !lofinite)
1920         doupdate = TRUE;
1921       else
1922         ps->plulower[i] = -lp->infinite;
1923     }
1924 
1925    /* Validate consistency of eliminated singleton */
1926     if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) {
1927       if(i == 0) {
1928         lovalue = get_rh_lower(lp, i);
1929         upvalue = get_rh_upper(lp, i);
1930         report(lp, DETAILED, "presolve_colfix: Objective determined by presolve as %18g\n",
1931                              (is_maxim(lp) ? upvalue : lovalue));
1932       }
1933       else {
1934         presolve_range(lp, i, ps, &lovalue, &upvalue);
1935 #if 1
1936         Value = 0;
1937 #else
1938         Value = MAX(fabs(upvalue), fabs(lovalue));
1939         Value = psdata->epsvalue * MAX(1, Value);
1940 #endif
1941         if((upvalue < get_rh_lower(lp, i)-Value) ||
1942            (lovalue > get_rh_upper(lp, i)+Value)) {
1943           report(lp, NORMAL, "presolve_colfix: Variable %s (%g << %g) infeasibility in row %s (%g << %g)\n",
1944                               get_col_name(lp, colnr), lovalue, upvalue,
1945                               get_row_name(lp, i), get_rh_lower(lp,i), get_rh_upper(lp, i));
1946           return( FALSE );
1947         }
1948       }
1949     }
1950 BlockEnd:
1951     if(doOF) {
1952       doOF = FALSE;
1953       if(ix < ie)
1954         goto Restart;
1955     }
1956 
1957   }
1958   if(remove) {
1959     psdata->forceupdate |= doupdate;
1960     if(tally != NULL)
1961       (*tally)++;
1962   }
1963   return( TRUE );
1964 }
1965 
1966 /* Delete the columns of the specified row, but make sure we don't delete SOS variables.
1967    Note that we cannot use presolve_nextcol() here, since the variables are deleted. */
presolve_rowfixzero(presolverec * psdata,int rownr,int * nv)1968 STATIC int presolve_rowfixzero(presolverec *psdata, int rownr, int *nv)
1969 {
1970   lprec  *lp = psdata->lp;
1971   MATrec *mat = lp->matA;
1972   int    ix, jx, ib = mat->row_end[rownr-1];
1973   for(ix = mat->row_end[rownr]-1; ix >= ib; ix--) {
1974     jx = ROW_MAT_COLNR(ix);
1975     if(isActiveLink(psdata->cols->varmap, jx)) {
1976       if(!presolve_colfix(psdata, jx, 0.0, TRUE, nv))
1977         return( presolve_setstatus(psdata, INFEASIBLE) );
1978       if(presolve_candeletevar(psdata, jx))
1979         presolve_colremove(psdata, jx, TRUE);
1980     }
1981   }
1982 #ifdef xxParanoia
1983   if(!presolve_debugrowtallies(psdata))
1984     return( INFEASIBLE );
1985 #endif
1986   return( RUNNING );
1987 }
1988 
1989 /* Function to find if a variable can be fixed based on considering the dual */
presolve_colfixdual(presolverec * psdata,int colnr,REAL * fixValue,int * status)1990 STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, REAL *fixValue, int *status)
1991 {
1992   lprec   *lp = psdata->lp;
1993   MYBOOL  hasOF, isMI, isDualFREE = TRUE;
1994   int     i, ix, ie, *rownr, signOF;
1995   REAL    *value, loX, upX, eps = psdata->epsvalue;
1996   MATrec  *mat = lp->matA;
1997 
1998   /* First check basic variable range */
1999   loX = get_lowbo(lp, colnr);
2000   upX = get_upbo(lp, colnr);
2001   if(((loX < 0) && (upX > 0)) ||
2002      (fabs(upX-loX) < lp->epsvalue) ||
2003      SOS_is_member_of_type(lp->SOS, colnr, SOSn))
2004     return( FALSE );
2005   isMI = (MYBOOL) (upX <= 0);
2006 
2007   /* Retrieve OF (standard form assuming maximization) */
2008   ix = mat->col_end[colnr - 1];
2009   ie = mat->col_end[colnr];
2010   rownr = &COL_MAT_ROWNR(ix);
2011   value = &COL_MAT_VALUE(ix);
2012   hasOF = isnz_origobj(lp, colnr);
2013   if(hasOF)
2014     signOF = my_sign(lp->orig_obj[colnr]);
2015   else
2016     signOF = 0;
2017 
2018   /* Loop over all constraints involving active variable (standard form with LE constraints)*/
2019   for(; (ix < ie) && isDualFREE;
2020       ix++, rownr += matRowColStep, value += matValueStep) {
2021     i = *rownr;
2022     if(!isActiveLink(psdata->rows->varmap, i))
2023       continue;
2024     if(presolve_rowlength(psdata, i) == 1) {
2025       REAL val = my_chsign(is_chsign(lp, i), *value),
2026            loR = get_rh_lower(lp, i),
2027            upR = get_rh_upper(lp, i);
2028       if(!presolve_singletonbounds(psdata, i, colnr, &loR, &upR, &val)) {
2029         *status = presolve_setstatus(psdata, INFEASIBLE);
2030         return( FALSE );
2031       }
2032       if(loR > loX + psdata->epsvalue)
2033         loX = presolve_roundrhs(lp, loR, TRUE);
2034       if(upR < upX - psdata->epsvalue)
2035         upX = presolve_roundrhs(lp, upR, FALSE);
2036       continue;
2037     }
2038     else
2039       isDualFREE = my_infinite(lp, get_rh_range(lp, i)) ||                                          /* Explicitly free */
2040                    ((presolve_sumplumin(lp, i, psdata->rows, TRUE)-eps <= get_rh_upper(lp, i)) &&   /* Implicitly free */
2041                     (presolve_sumplumin(lp, i, psdata->rows, FALSE)+eps >= get_rh_lower(lp, i)));
2042     if(isDualFREE) {
2043       if(signOF == 0)  /* Test on the basis of identical signs in the constraints */
2044         signOF = my_sign(*value);
2045       else             /* Test on the basis of constraint sign equal to OF sign */
2046         isDualFREE = (MYBOOL) (signOF == my_sign(*value));
2047     }
2048   }
2049 
2050   /* Set fixing value if we were successful */
2051   if(isDualFREE) {
2052     if(signOF == 0) {
2053       SETMAX(loX, 0);
2054       *fixValue = MIN(loX, upX);
2055     }
2056     else if(signOF > 0) {
2057       if(my_infinite(lp, loX))
2058         isDualFREE = FALSE;
2059       else {
2060         if(is_int(lp, colnr))
2061           *fixValue = ceil(loX-PRESOLVE_EPSVALUE);
2062         else
2063           *fixValue = loX;
2064       }
2065     }
2066     else {
2067       if(my_infinite(lp, upX))
2068         isDualFREE = FALSE;
2069       else {
2070         if(is_int(lp, colnr) && (upX != 0))
2071           *fixValue = floor(upX+PRESOLVE_EPSVALUE);
2072         else
2073           *fixValue = upX;
2074       }
2075     }
2076     if((*fixValue != 0) && SOS_is_member(lp->SOS, 0, colnr))
2077       return( FALSE );
2078 
2079   }
2080 
2081   return( isDualFREE );
2082 }
2083 
2084 #if 0
2085 STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, REAL *fixvalue)
2086 {
2087   lprec    *lp = psdata->lp;
2088   int      i, ix, item;
2089   REAL     loLim, absvalue, epsvalue = psdata->epsvalue;
2090   MATrec   *mat = lp->matA;
2091   MYBOOL   chsign, canfix = FALSE;
2092 
2093   if(!is_binary(lp, colnr))
2094     return( canfix );
2095 
2096   /* Loop over all active rows to search for fixing opportunity */
2097   item = 0;
2098   for(ix = presolve_nextrow(psdata, colnr, &item);
2099       (ix >= 0) && !canfix;
2100       ix = presolve_nextrow(psdata, colnr, &item)) {
2101     i = COL_MAT_ROWNR(ix);
2102     *fixvalue = COL_MAT_VALUE(ix);
2103     chsign = is_chsign(lp, i);
2104 
2105     /* First check the lower bound of the normalized constraint */
2106     loLim = presolve_sumplumin(lp, i, psdata->rows, chsign);
2107     loLim = my_chsign(chsign, loLim);
2108     absvalue = fabs(*fixvalue);
2109     canfix = (MYBOOL) ((loLim + absvalue > lp->orig_rhs[i]+epsvalue*MAX(1, absvalue)));
2110 
2111     /* If we were unsuccessful in fixing above, try the upper bound
2112        of the normalized constraint - if it is finite */
2113     if(!canfix && !my_infinite(lp, get_rh_range(lp, i))) {
2114       loLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign);
2115       loLim = my_chsign(!chsign, loLim);
2116       *fixvalue = -(*fixvalue);
2117       canfix = (MYBOOL) ((loLim + absvalue > get_rh_range(lp, i)-lp->orig_rhs[i]+epsvalue*MAX(1, absvalue)));
2118     }
2119   }
2120 
2121   /* Check if we were successful in identifying fixing opportunity */
2122   if(canfix) {
2123     if(*fixvalue < 0)
2124       *fixvalue = 1;
2125     else
2126       *fixvalue = 0;
2127   }
2128   return( canfix );
2129 }
2130 #else
presolve_probefix01(presolverec * psdata,int colnr,REAL * fixvalue)2131 STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, REAL *fixvalue)
2132 {
2133   lprec    *lp = psdata->lp;
2134   int      i, ix, item;
2135   REAL     loLim, upLim, range, absvalue, epsvalue = psdata->epsvalue, tolgap;
2136   MATrec   *mat = lp->matA;
2137   MYBOOL   chsign, status = FALSE;
2138 
2139   if(!is_binary(lp, colnr))
2140     return( status );
2141 
2142   /* Loop over all active rows to search for fixing opportunity.  The logic is that if a
2143      constraint gets violated by setting a variable at one of its bounds, then it can be
2144      fixed at its opposite bound. */
2145   item = 0;
2146 
2147   for(ix = presolve_nextrow(psdata, colnr, &item); (ix >= 0); ix = presolve_nextrow(psdata, colnr, &item)) {
2148     i = COL_MAT_ROWNR(ix);
2149     *fixvalue = COL_MAT_VALUE(ix);
2150     absvalue = fabs(*fixvalue);
2151     SETMIN(absvalue, 100);
2152     tolgap = epsvalue*MAX(1, absvalue);
2153     chsign = is_chsign(lp, i);
2154 
2155     /* Get the constraint value limits based on variable bounds, normalized to LE constraint */
2156     loLim = presolve_sumplumin(lp, i, psdata->rows, FALSE);
2157     upLim = presolve_sumplumin(lp, i, psdata->rows, TRUE);
2158     if(chsign) {
2159       loLim = my_chsign(chsign, loLim);
2160       upLim = my_chsign(chsign, upLim);
2161       swapREAL(&loLim, &upLim);
2162     }
2163 
2164     /* Check the upper constraint bound for possible violation if the value were to be fixed at 1 */
2165     if(loLim + *fixvalue > lp->orig_rhs[i]+tolgap) {
2166       if(*fixvalue < 0)
2167         presolve_setstatus(psdata, INFEASIBLE);
2168       *fixvalue = 0;
2169       break;
2170     }
2171 
2172     /* Check the lower constraint bound for possible violation if the value were to be fixed at 1 */
2173     range = get_rh_range(lp, i);
2174     if(!my_infinite(lp, range) &&
2175        (upLim + *fixvalue < lp->orig_rhs[i]-range-tolgap)) {
2176       if(*fixvalue > 0)
2177         presolve_setstatus(psdata, INFEASIBLE);
2178       *fixvalue = 0;
2179       break;
2180     }
2181 
2182     /* Check if we have to fix the value at 1 to avoid constraint infeasibility */
2183     if(psdata->rows->infcount[i] >= 1)
2184       continue;
2185     if(((*fixvalue < 0) && (upLim + *fixvalue >= loLim-tolgap) && (upLim > lp->orig_rhs[i]+tolgap)) ||
2186        ((*fixvalue > 0) && (loLim + *fixvalue <= upLim+tolgap) && (loLim < lp->orig_rhs[i]-range-tolgap) && !my_infinite(lp, range))) {
2187       *fixvalue = 1;
2188       break;
2189     }
2190   }
2191   status = (MYBOOL) (ix >= 0);
2192 
2193   /* Returns TRUE if fixing opportunity was identified */
2194   return( status );
2195 }
2196 #endif
2197 
presolve_probetighten01(presolverec * psdata,int colnr)2198 STATIC int presolve_probetighten01(presolverec *psdata, int colnr)
2199 {
2200   lprec    *lp = psdata->lp;
2201   MYBOOL   chsign;
2202   int      i, ix, item, n = 0;
2203   REAL     upLim, value, absvalue, epsvalue = psdata->epsvalue;
2204   MATrec   *mat = lp->matA;
2205 
2206 #if 0 /* Handled in calling routine */
2207   if(!is_binary(lp, colnr))
2208     return( n );
2209 #endif
2210 
2211   /* Loop over all active rows and do coefficient tightening for qualifying constraints */
2212   item = 0;
2213   for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0;
2214       ix = presolve_nextrow(psdata, colnr, &item)) {
2215     i = COL_MAT_ROWNR(ix);
2216     value = COL_MAT_VALUE(ix);
2217     chsign = is_chsign(lp, i);
2218     upLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign);
2219     upLim = my_chsign(chsign, upLim);
2220 
2221     /* Does this constraint qualify for coefficient tightening? */
2222     absvalue = fabs(value);
2223     if(upLim - absvalue < lp->orig_rhs[i]-epsvalue*MAX(1, absvalue)) {
2224       REAL delta = lp->orig_rhs[i] - upLim;
2225       lp->orig_rhs[i] = upLim;
2226       upLim = value - my_chsign(value < 0, delta);
2227       COL_MAT_VALUE(ix) = upLim;
2228       if(my_sign(value) != my_sign(upLim)) {
2229         if(chsign) {
2230           psdata->rows->negcount[i]--;
2231           psdata->rows->plucount[i]++;
2232         }
2233         else {
2234           psdata->rows->negcount[i]++;
2235           psdata->rows->plucount[i]--;
2236         }
2237       }
2238       n++;
2239     }
2240   }
2241   return( n );
2242 }
2243 
presolve_mergerows(presolverec * psdata,int * nRows,int * nSum)2244 STATIC int presolve_mergerows(presolverec *psdata, int *nRows, int *nSum)
2245 {
2246   lprec *lp = psdata->lp;
2247   MYBOOL candelete;
2248   int    status = RUNNING, item1, item2,
2249          firstix, RT1, RT2, i, ix, iix, j, jjx, n = 0;
2250   REAL   Value1, Value2, bound;
2251   MATrec *mat = lp->matA;
2252 
2253   for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) {
2254 
2255     /* First scan for rows with identical row lengths */
2256     ix = prevActiveLink(psdata->rows->varmap, i);
2257     if(ix == 0)
2258       break;
2259 
2260     /* Don't bother about empty rows or row singletons, since they are
2261        handled by PRESOLVE_ROWS */
2262     j = presolve_rowlength(psdata, i);
2263     if(j <= 1) {
2264       i = ix;
2265       continue;
2266     }
2267 
2268 #if 0
2269     /* Enable this to scan all rows back */
2270     RT2 = lp->rows;
2271 
2272     /* Check abort since this section can be pretty "expensive" */
2273     if(!presolve_statuscheck(psdata, &status))
2274       return( status );
2275 #else
2276     RT2 = 2+1;
2277 #endif
2278     firstix = ix;
2279     for(RT1 = 0; (ix > 0) && (RT1 < RT2) && (status == RUNNING);
2280         ix = prevActiveLink(psdata->rows->varmap, ix), RT1++)  {
2281       candelete = FALSE;
2282       if(presolve_rowlength(psdata, ix) != j)
2283         continue;
2284 
2285       /* Check if the beginning columns are identical; if not, continue */
2286       item1 = 0;
2287       iix = presolve_nextcol(psdata, ix, &item1);
2288       item2 = 0;
2289       jjx = presolve_nextcol(psdata, i,  &item2);
2290 
2291       if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx))
2292         continue;
2293 
2294       /* We have a candidate row; check if the entries have a fixed non-zero ratio */
2295       Value1 = get_mat_byindex(lp, iix, TRUE, FALSE);
2296       Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE);
2297       bound = Value1 / Value2;
2298       Value1 = bound;
2299 
2300       /* Loop over remaining entries */
2301       jjx = presolve_nextcol(psdata, i, &item2);
2302       for(; (jjx >= 0) && (Value1 == bound);
2303           jjx = presolve_nextcol(psdata, i, &item2)) {
2304         iix = presolve_nextcol(psdata, ix, &item1);
2305         if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx))
2306           break;
2307         Value1 = get_mat_byindex(lp, iix, TRUE, FALSE);
2308         Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE);
2309 
2310         /* If the ratio is different from the reference value we have a mismatch */
2311         Value1 = Value1 / Value2;
2312         if(bound == lp->infinite)
2313           bound = Value1;
2314         else if(fabs(Value1 - bound) > psdata->epsvalue)
2315           break;
2316       }
2317 
2318       /* Check if we found a match (we traversed all active columns without a break) */
2319       if(jjx < 0) {
2320 
2321         /* Get main reference values */
2322         Value1 = lp->orig_rhs[ix];
2323         Value2 = lp->orig_rhs[i] * bound;
2324 
2325         /* First check for inconsistent equalities */
2326         if((fabs(Value1 - Value2) > psdata->epsvalue) &&
2327            ((get_constr_type(lp, ix) == EQ) && (get_constr_type(lp, i) == EQ))) {
2328           report(lp, NORMAL, "presolve_mergerows: Inconsistent equalities %d and %d found\n",
2329                              ix, i);
2330           status = presolve_setstatus(psdata, INFEASIBLE);
2331         }
2332 
2333         else {
2334 
2335           /* Update lower and upper bounds */
2336           if(is_chsign(lp, i) != is_chsign(lp, ix))
2337             bound = -bound;
2338 
2339           Value1 = get_rh_lower(lp, i);
2340           if(Value1 <= -lp->infinite)
2341             Value1 *= my_sign(bound);
2342           else
2343             Value1 *= bound;
2344           my_roundzero(Value1, lp->epsdual);      /* Extra rounding tolerance *** */
2345 
2346           Value2 = get_rh_upper(lp, i);
2347           if(Value2 >= lp->infinite)
2348             Value2 *= my_sign(bound);
2349           else
2350             Value2 *= bound;
2351           my_roundzero(Value2, lp->epsdual);      /* Extra rounding tolerance *** */
2352 
2353           if((bound < 0))
2354             swapREAL(&Value1, &Value2);
2355 
2356           bound = get_rh_lower(lp, ix);
2357           if(Value1 > bound + psdata->epsvalue)
2358             set_rh_lower(lp, ix, Value1);
2359           else
2360             Value1 = bound;
2361           bound = get_rh_upper(lp, ix);
2362           if(Value2 < bound - psdata->epsvalue)
2363             set_rh_upper(lp, ix, Value2);
2364           else
2365             Value2 = bound;
2366 
2367           /* Check results and make equality if appropriate */
2368           if(fabs(Value2-Value1) < psdata->epsvalue)
2369             presolve_setEQ(psdata, ix);
2370           else if(Value2 < Value1) {
2371             status = presolve_setstatus(psdata, INFEASIBLE);
2372           }
2373 
2374           /* Verify if we can continue */
2375           candelete = (MYBOOL) (status == RUNNING);
2376           if(!candelete) {
2377             report(lp, NORMAL, "presolve: Range infeasibility found involving rows %s and %s\n",
2378                                 get_row_name(lp, ix), get_row_name(lp, i));
2379           }
2380         }
2381       }
2382       /* Perform i-row deletion if authorized */
2383       if(candelete) {
2384         presolve_rowremove(psdata, i, TRUE);
2385         n++;
2386         break;
2387       }
2388     }
2389     i = firstix;
2390   }
2391   (*nRows) += n;
2392   (*nSum)  += n;
2393 
2394   return( status );
2395 }
2396 
presolve_reduceGCD(presolverec * psdata,int * nn,int * nb,int * nsum)2397 STATIC MYBOOL presolve_reduceGCD(presolverec *psdata, int *nn, int *nb, int *nsum)
2398 {
2399   lprec    *lp = psdata->lp;
2400   MYBOOL   status = TRUE;
2401   int      i, jx, je, in = 0, ib = 0;
2402   LLONG    GCDvalue;
2403   REAL     *Avalue, Rvalue, epsvalue = psdata->epsvalue;
2404   MATrec   *mat = lp->matA;
2405 
2406   for(i = firstActiveLink(psdata->INTmap); i != 0; i = nextActiveLink(psdata->INTmap, i)) {
2407 
2408     /* Obtain the row GCD */
2409     jx = mat->row_end[i - 1];
2410     je = mat->row_end[i];
2411     Rvalue = ROW_MAT_VALUE(jx);
2412     GCDvalue = abs((int) Rvalue);
2413     jx++;
2414     if(jx < je)
2415     for(; (jx < je) && (GCDvalue > 1); jx++) {
2416       Rvalue = fabs(ROW_MAT_VALUE(jx));
2417       GCDvalue = gcd((LLONG) Rvalue, GCDvalue, NULL, NULL);
2418     }
2419 
2420     /* Reduce the coefficients, if possible */
2421     if(GCDvalue > 1) {
2422       jx = mat->row_end[i - 1];
2423       je = mat->row_end[i];
2424       for(; jx < je; jx++) {
2425         Avalue = &ROW_MAT_VALUE(jx);
2426         *Avalue /= GCDvalue;
2427         in++;
2428       }
2429       Rvalue = (lp->orig_rhs[i] / GCDvalue) + epsvalue;
2430       lp->orig_rhs[i] = floor(Rvalue);
2431       Rvalue = fabs(lp->orig_rhs[i]-Rvalue);
2432       if(is_constr_type(lp, i, EQ) && (Rvalue > epsvalue)) {
2433         report(lp, NORMAL, "presolve_reduceGCD: Infeasible equality constraint %d\n", i);
2434         status = FALSE;
2435         break;
2436       }
2437       if(!my_infinite(lp, lp->orig_upbo[i]))
2438         lp->orig_upbo[i] = floor(lp->orig_upbo[i] / GCDvalue);
2439       ib++;
2440     }
2441   }
2442   if(status && (in > 0))
2443     report(lp, DETAILED, "presolve_reduceGCD: Did %d constraint coefficient reductions.\n", in);
2444 
2445   (*nn)   += in;
2446   (*nb)   += ib;
2447   (*nsum) += in + ib;
2448 
2449   return( status );
2450 }
2451 
presolve_knapsack(presolverec * psdata,int * nn)2452 STATIC int presolve_knapsack(presolverec *psdata, int *nn)
2453 {
2454   lprec *lp = psdata->lp;
2455   int    m, n, i, ix, j, jx, colnr, *rownr = NULL,
2456          status = RUNNING;
2457   REAL   *colOF = lp->orig_obj, value, *ratio = NULL;
2458   LLrec  *map = psdata->EQmap;
2459   MATrec *mat = lp->matA;
2460 
2461   /* Check if it is worth trying */
2462   m = mat->row_end[0];
2463   if((map->count == 0) || (m < 2))
2464     return( status );
2465 
2466   /* Get the OF row */
2467   allocINT(lp, &rownr,  map->count+1, FALSE);
2468   allocREAL(lp, &ratio, map->count+1, FALSE);
2469 
2470   /* Loop over each row trying to find equal entries in the OF */
2471   rownr[0] = 0;
2472   for(i = firstActiveLink(map); i != 0; i = nextActiveLink(map, i)) {
2473     if(get_rh(lp, i) <= 0)
2474       continue;
2475     jx = mat->row_end[i];
2476     n = 0;
2477     for(j = mat->row_end[i-1]; j  < jx; j++, n++) {
2478       colnr = ROW_MAT_COLNR(j);
2479       value = ROW_MAT_VALUE(j);
2480       if(colOF[colnr] == 0)
2481         break;
2482       if(n == 0) {
2483         ratio[0] = colOF[colnr] / value;
2484       }
2485       else if(fabs(value * ratio[0] - colOF[colnr]) > psdata->epsvalue) {
2486         n = -1;
2487         break;
2488       }
2489     }
2490     /* Register row if we were successful (and row long enough) */
2491     if(n >= 2) {
2492       ix = ++rownr[0];
2493       rownr[ix] = i;
2494       ratio[ix] = ratio[0];
2495     }
2496   }
2497   n = rownr[0];
2498   if(n == 0)
2499     goto Finish;
2500 
2501   /* Process the identified rows, eliminating the OF value */
2502   for(ix = 1; ix <= n; ix++) {
2503     i = rownr[ix];
2504     jx = mat->row_end[i];
2505     for(j = mat->row_end[i-1]; j  < jx; j++) {
2506       colnr = ROW_MAT_COLNR(j);
2507       colOF[colnr] = 0;
2508     }
2509   }
2510 
2511   /* Update key mapper structures */
2512   j = lp->columns;
2513   psdata->cols->varmap = cloneLink(psdata->cols->varmap, j+n, TRUE);
2514   psdata->forceupdate = TRUE;
2515 
2516   /* Finally, add helper columns */
2517   for(ix = 1; ix <= n; ix++) {
2518     i = rownr[ix];
2519     rownr[0] = 0;
2520     colOF[0] = my_chsign(is_maxim(lp), ratio[ix]);
2521     rownr[1] = i;
2522     colOF[1] = -1;
2523     value = get_rh(lp, i);
2524 /*    j = get_constr_type(lp, i); */
2525     add_columnex(lp, 2, colOF, rownr);
2526     set_bounds(lp, lp->columns, value, value);
2527 /*    presolve_setEQ(psdata, i); */
2528     set_rh(lp, i, 0);
2529     appendLink(psdata->cols->varmap, j+ix);
2530   }
2531   presolve_validate(psdata, TRUE);
2532 
2533   /* Clean up before returning */
2534 Finish:
2535   FREE(rownr);
2536   FREE(ratio);
2537   (*nn) += n;
2538 
2539   return( status );
2540 }
2541 
presolve_invalideq2(lprec * lp,presolverec * psdata)2542 STATIC MYBOOL presolve_invalideq2(lprec *lp, presolverec *psdata)
2543 {
2544   int    jx, jjx, i = 0, item;
2545   MATrec *mat = lp->matA;
2546   MYBOOL error = FALSE;
2547 
2548   do {
2549 
2550     if(i == 0)
2551       i = firstActiveLink(psdata->EQmap);
2552     else
2553       i = nextActiveLink(psdata->EQmap, i);
2554     if(i == 0)
2555       return( error );
2556 
2557     /* Get the row index of the first 2-element equality */
2558     for(; i > 0; i = nextActiveLink(psdata->EQmap, i))
2559       if(presolve_rowlength(psdata, i) == 2)
2560         break;
2561     if(i == 0)
2562       return( error );
2563 
2564     /* Get the first column */
2565     item = 0;
2566     jx  = presolve_nextcol(psdata, i, &item);
2567     if(jx < 0)
2568       error = TRUE;
2569     jx  = ROW_MAT_COLNR(jx);
2570 
2571     /* Get the second column */
2572     jjx = presolve_nextcol(psdata, i, &item);
2573     if(jjx < 0)
2574       error = AUTOMATIC;
2575   } while(!error);
2576 
2577   return( error );
2578 }
2579 
2580 /* Callback to obtain the non-zero rows of equality constraints */
presolve_getcolumnEQ(lprec * lp,int colnr,REAL nzvalues[],int nzrows[],int mapin[])2581 int BFP_CALLMODEL presolve_getcolumnEQ(lprec *lp, int colnr, REAL nzvalues[], int nzrows[], int mapin[])
2582 {
2583   int    i, ib, ie, nn = 0;
2584   MATrec *mat = lp->matA;
2585 
2586   ib = mat->col_end[colnr-1];
2587   ie = mat->col_end[colnr];
2588   for(; ib < ie; ib++) {
2589     i = COL_MAT_ROWNR(ib);
2590     if(!is_constr_type(lp, i, EQ) ||  /* It has to be an equality constraint         */
2591        (mapin[i] == 0))               /* And it should not already have been deleted */
2592       continue;
2593     if(nzvalues != NULL) {
2594       nzrows[nn] = mapin[i];
2595       nzvalues[nn] = COL_MAT_VALUE(ib);
2596     }
2597     nn++;
2598   }
2599   return( nn );
2600 }
presolve_singularities(presolverec * psdata,int * nn,int * nr,int * nv,int * nSum)2601 STATIC int presolve_singularities(presolverec *psdata, int *nn, int *nr, int *nv, int *nSum)
2602 {
2603   lprec *lp = psdata->lp;
2604   int i, j, n, *rmapin = NULL, *rmapout = NULL, *cmapout = NULL;
2605 
2606   if(lp->bfp_findredundant(lp, 0, NULL, NULL, NULL) == 0)
2607     return( 0 );
2608 
2609   /* Create condensed row map */
2610   allocINT(lp, &rmapin, lp->rows+1, TRUE);
2611   allocINT(lp, &rmapout, psdata->EQmap->count+1, FALSE);
2612   allocINT(lp, &cmapout, lp->columns+1, FALSE);
2613   n = 0;
2614   for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) {
2615     n++;
2616     rmapout[n] = i;
2617     rmapin[i]  = n;
2618   }
2619   rmapout[0] = n;
2620   n = 0;
2621   for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i)) {
2622     n++;
2623     cmapout[n]  = i;
2624   }
2625   cmapout[0] = n;
2626 
2627   /* Do the rank-revealing factorization */
2628   n = lp->bfp_findredundant(lp, psdata->EQmap->count, presolve_getcolumnEQ, rmapin, cmapout);
2629 
2630   /* Delete the redundant rows */
2631   for(i = 1; i <= n; i++) {
2632     j = rmapin[i];
2633     j = rmapout[j];
2634     presolve_rowremove(psdata, j, TRUE);
2635   }
2636   (*nn)   += n;
2637   (*nr)   += n;
2638   (*nSum) += n;
2639 
2640   /* Clean up */
2641   FREE(rmapout);
2642   FREE(rmapin);
2643   FREE(cmapout);
2644 
2645   return( n );
2646 }
2647 
presolve_elimeq2(presolverec * psdata,int * nn,int * nr,int * nc,int * nSum)2648 STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int *nSum)
2649 {
2650   lprec     *lp = psdata->lp;
2651   int       n, i, jx, jjx, k, item, *plucount, *negcount, colplu, colneg,
2652             iCoeffChanged = 0, iRowsRemoved = 0, iVarsFixed = 0, nrows = lp->rows,
2653             status = RUNNING, *colindex = NULL;
2654   MYBOOL    freshupdate;
2655   REAL      Coeff1, Coeff2, Value1, Value2, lobound, upbound, bound, test, product,
2656             *colvalue = NULL, *delvalue = NULL, *colitem;
2657   MATrec    *mat = lp->matA, *rev = NULL;
2658   DeltaVrec *DV = NULL;
2659   LLrec     *EQ2 = NULL;
2660 
2661   /* See if there is anything to do */
2662   if(psdata->EQmap->count == 0) {
2663     (*nSum) = 0;
2664     return( status );
2665   }
2666 
2667   /* Tally counts */
2668   createLink(lp->rows, &EQ2, NULL);
2669   if((EQ2 == NULL) || !allocREAL(lp, &colvalue, nrows+1, FALSE) ||
2670                       !allocREAL(lp, &delvalue, nrows+1, FALSE))
2671     goto Finish;
2672   for(i = firstActiveLink(psdata->EQmap); i > 0; i = nextActiveLink(psdata->EQmap, i)) {
2673     if(presolve_rowlength(psdata, i) == 2)
2674       appendLink(EQ2, i);
2675   }
2676   if(EQ2->count == 0)
2677     goto Finish;
2678   n = 0;
2679 
2680   /* Do the elimination loop for all identified 2-element equalities */
2681   for(i = firstActiveLink(EQ2); i > 0; i = nextActiveLink(EQ2, i)) {
2682 
2683     /* Check if the constraint has been modified by a previous elimination */
2684     if(presolve_rowlength(psdata, i) != 2)
2685       continue;
2686 
2687     /* Get the column indeces of NZ-values of the "pivot" row */
2688     item = 0;
2689     jx  = presolve_nextcol(psdata, i, &item);   /* Eliminated variable coefficient    b */
2690 #ifdef Paranoia
2691     if(jx < 0)
2692       report(lp, SEVERE, "presolve_elimeq2: No qualifying %dst column was found in row %s (ostensible length %d)\n",
2693                          1, get_row_name(lp, i), presolve_rowlength(psdata, i));
2694 #endif
2695     Coeff2 = ROW_MAT_VALUE(jx);
2696     jx  = ROW_MAT_COLNR(jx);
2697     jjx = presolve_nextcol(psdata, i, &item);  /* Non-eliminated variable coefficient a */
2698 #ifdef Paranoia
2699     if(jjx < 0)
2700       report(lp, SEVERE, "presolve_elimeq2: No qualifying %dnd column was found in row %s (ostensible length %d)\n",
2701                           2, get_row_name(lp, i), presolve_rowlength(psdata, i));
2702 #endif
2703     Coeff1 = ROW_MAT_VALUE(jjx);
2704     jjx = ROW_MAT_COLNR(jjx);
2705 
2706     /* Check if at least one of the coefficients is large enough to preserve stability;
2707        use opposing maximum column values for stability testing. */
2708     if((fabs(Coeff1) < psdata->epspivot*mat->colmax[jx]) &&
2709        ((fabs(Coeff1) != 1) && (fabs(Coeff2) != 1)) &&
2710        (fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx]))
2711       continue;
2712 
2713     /* Cannot eliminate a variable if both are SOS members or SC variables */
2714     if((is_semicont(lp, jx) && is_semicont(lp, jjx)) ||
2715         (SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx)))
2716       continue;
2717 
2718     /* First check if we are allowed to swap; set swap "blockers" */
2719     k = 0;
2720     if(!is_int(lp, jx) && is_int(lp, jjx))
2721       k += 1;
2722     else if(!is_semicont(lp, jx) && is_semicont(lp, jjx))
2723       k += 2;
2724     else if(!SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx))
2725       k += 4;
2726 
2727     /* If there were no blockers, determine if we MUST swap the variable to be eliminated */
2728     if(k == 0) {
2729       if(is_int(lp, jx) && !is_int(lp, jjx))
2730         k += 8;
2731       else if(is_semicont(lp, jx) && !is_semicont(lp, jjx))
2732         k += 16;
2733       else if(SOS_is_member(lp->SOS, 0, jx) && !SOS_is_member(lp->SOS, 0, jjx))
2734         k += 32;
2735 
2736       /* If we are not forced to swap, decide if it otherwise makes sense - high order */
2737       if(k == 0) {
2738         if((fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx]) &&
2739            (fabs(Coeff1) > psdata->epspivot*mat->colmax[jx]))
2740           k += 64;
2741         else if(presolve_collength(psdata, jx) > presolve_collength(psdata, jjx))
2742           k += 128;
2743       }
2744 
2745       /* If we are not forced to swap, decide if it otherwise makes sense - low order */
2746       if(k == 0) {
2747         Value2 = Coeff1/Coeff2;
2748 #ifdef DualFeasibilityLogicEQ2
2749         if((Value2*lp->orig_obj[jx] < 0) &&
2750           (Value2*lp->orig_obj[jjx] > 0))                     /* Seek increased dual feasibility */
2751           k += 256;
2752 #endif
2753 #ifdef DivisorIntegralityLogicEQ2
2754         if((fabs(modf(Coeff2, &Value2)) >= lp->epsvalue) &&    /* Seek integrality of result */
2755            (fabs(modf(Coeff1, &Value2)) < lp->epsvalue))
2756           k += 512;
2757         else if((fabs(fabs(Coeff2)-1) >= lp->epsvalue) &&    /* Seek integrality of divisor */
2758                  (fabs(fabs(Coeff1)-1) < lp->epsvalue))
2759           k += 1024;
2760 #endif
2761       }
2762 
2763     }
2764     else
2765       k = 0;
2766 
2767     /* Perform variable index swap if indicated */
2768     if(k != 0) {
2769       swapINT(&jx, &jjx);
2770       swapREAL(&Coeff1, &Coeff2);
2771     }
2772 
2773     Value1 = lp->orig_rhs[i]/Coeff2; /* Delta constant term */
2774     Value2 = Coeff1/Coeff2;          /* Delta variable term */
2775     upbound = lp->orig_upbo[lp->rows+jx];
2776     lobound = lp->orig_lowbo[lp->rows+jx];
2777     if(lp->spx_trace) {
2778       report(lp, DETAILED, "Row %3d : Elim %g %s - %d\n", i, Coeff2, get_col_name(lp, jx), jx);
2779       report(lp, DETAILED, "          Keep %g %s - %d\n",    Coeff1, get_col_name(lp, jjx), jjx);
2780     }
2781 
2782     /* Get the coefficient vectors of the independent (jjx) and dependent (jx) columns;
2783       the dependent column will be deleted and reconstructed during postsolve. */
2784     freshupdate = (MYBOOL) ((colindex == NULL) || (colindex[jjx] == 0));
2785     if(freshupdate)
2786       mat_expandcolumn(mat, jjx, colvalue, NULL, TRUE);
2787     else
2788       mat_expandcolumn(rev, colindex[jjx], colvalue, NULL, FALSE);
2789     if((colindex == NULL) || (colindex[jx] == 0))
2790       mat_expandcolumn(mat, jx, delvalue, NULL, TRUE);
2791     else
2792       mat_expandcolumn(rev, colindex[jx], delvalue, NULL, FALSE);
2793 
2794     /* Add variable reconstruction information */
2795     addUndoPresolve(lp, TRUE, jx, Value1, Value2, jjx);
2796 
2797     /* If possible, tighten the bounds of the uneliminated variable based
2798        on the bounds of the eliminated variable. Also handle roundings
2799        and attempt precision management. */
2800     bound = lobound;
2801     k = nrows+jjx;
2802     if(bound > -lp->infinite) {
2803       bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1;
2804       if(Value2 > 0) {
2805         test = lp->orig_upbo[k];
2806         if(bound < test - psdata->epsvalue) {
2807           if(is_int(lp, jjx))
2808             lp->orig_upbo[k] = floor(bound + lp->epsint);
2809           else
2810             lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE);
2811         }
2812       }
2813       else {
2814         test = lp->orig_lowbo[k];
2815         if(bound > test + psdata->epsvalue) {
2816           if(is_int(lp, jjx))
2817             lp->orig_lowbo[k] = ceil(bound - lp->epsint);
2818           else
2819             lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE);
2820         }
2821       }
2822     }
2823     bound = upbound;
2824     if(bound < lp->infinite) {
2825       bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1;
2826       if(Value2 < 0) {
2827         test = lp->orig_upbo[k];
2828         if(bound < test - psdata->epsvalue) {
2829           if(is_int(lp, jjx))
2830             lp->orig_upbo[k] = floor(bound + lp->epsint);
2831           else
2832             lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE);
2833         }
2834       }
2835       else {
2836         test = lp->orig_lowbo[k];
2837         if(bound > test + psdata->epsvalue) {
2838           if(is_int(lp, jjx))
2839             lp->orig_lowbo[k] = ceil(bound - lp->epsint);
2840           else
2841             lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE);
2842         }
2843       }
2844     }
2845 
2846 #ifdef Eq2Reldiff
2847     test = 2*lp->epsvalue;
2848 #else
2849     test = psdata->epsvalue;
2850 #endif
2851     if(/*(lp->orig_upbo[k] < lp->orig_lowbo[k]) ||*/
2852 #ifdef Eq2Reldiff
2853        (fabs(my_reldiff(lp->orig_upbo[k],lp->orig_lowbo[k])) < test)) {
2854 #else
2855        (fabs(lp->orig_upbo[k] - lp->orig_lowbo[k]) < test)) {
2856 #endif
2857       my_roundzero(lp->orig_lowbo[k], test);
2858       lp->orig_upbo[k] = lp->orig_lowbo[k];
2859     }
2860     else {
2861       my_roundzero(lp->orig_upbo[k], test);
2862       my_roundzero(lp->orig_lowbo[k], test);
2863     }
2864 
2865     if(/*(upbound < lobound) ||*/
2866 #ifdef Eq2Reldiff
2867        (fabs(my_reldiff(upbound, lobound)) < test)) {
2868 #else
2869        (fabs(upbound - lobound) < test)) {
2870 #endif
2871       my_roundzero(lobound, test);
2872       lp->orig_upbo[nrows+jx] = lobound;
2873       upbound = lobound;
2874     }
2875 
2876     /* Loop over the non-zero rows of the column (jx) to be eliminated;
2877       substitute jx-variable by updating rhs and jjx coefficients */
2878     colitem = colvalue;
2879     plucount = psdata->rows->plucount;
2880     negcount = psdata->rows->negcount;
2881     colplu = 0;
2882     colneg = 0;
2883     /* Count of non-zeros in the independent column jjx */
2884     item = presolve_collength(psdata, jjx) - 1;
2885     if(isnz_origobj(lp, jjx))
2886       item++;
2887     for(k = 0; k <= nrows; k++, colitem++) {
2888 
2889       bound = delvalue[k];
2890       if((k == i) || (bound == 0) ||
2891          ((k > 0) && !isActiveLink(psdata->rows->varmap, k)))
2892         continue;
2893 
2894       /* Do constraint and nz-count updates for the substituted variable */
2895       product = bound*Value1;
2896 
2897       /* "Raw"/unsigned data */
2898       presolve_adjustrhs(psdata, k, my_chsign(is_chsign(lp, k), product), test);
2899 
2900       /* Change back to signed part */
2901       if(*colitem != 0) {
2902         if(*colitem > 0) {
2903           colplu--;
2904           plucount[k]--;
2905         }
2906         else {
2907           colneg--;
2908           negcount[k]--;
2909         }
2910         if((lobound < 0) && (upbound >= 0)) {
2911           psdata->cols->pluneg[jjx]--;
2912           psdata->rows->pluneg[k]--;
2913         }
2914         item--;
2915       }
2916       (*colitem) -= bound*Value2;
2917       iCoeffChanged++;
2918 
2919       /* Update counts */
2920       if(fabs(*colitem) >= mat->epsvalue) {
2921         if(*colitem > 0) {
2922           colplu++;
2923           plucount[k]++;
2924         }
2925         else {
2926           colneg++;
2927           negcount[k]++;
2928         }
2929         if((lobound < 0) && (upbound >= 0)) {
2930           psdata->cols->pluneg[jjx]++;
2931           psdata->rows->pluneg[k]++;
2932         }
2933         item++;
2934       }
2935       else {
2936         *colitem = 0;
2937       }
2938 
2939       /* Also reduce count if the row contains the deleted variable */
2940       if(bound > 0)
2941         plucount[k]--;
2942       else
2943         negcount[k]--;
2944     }
2945     psdata->cols->plucount[jjx] += colplu;
2946     psdata->cols->negcount[jjx] += colneg;
2947 
2948     /* Save the new column */
2949     if(rev == NULL) {
2950       DV = createUndoLadder(lp, nrows, lp->columns / RESIZEFACTOR);
2951       rev = DV->tracker;
2952       rev->epsvalue = mat->epsvalue;
2953       allocINT(lp, &(rev->col_tag), mat->columns_alloc+1, FALSE);
2954       allocINT(lp, &colindex, lp->columns+1, TRUE);
2955       rev->col_tag[0] = 0;
2956     }
2957     n = rev->col_tag[0] = incrementUndoLadder(DV);
2958     mat_setcol(rev, n, 0, colvalue, NULL, FALSE, FALSE);
2959     rev->col_tag[n] = jjx;
2960 
2961     /* Save index to updated vector, but specially handle case where we have
2962       the same independent variable for multiple equations! */
2963     if(!freshupdate)
2964       rev->col_tag[colindex[jjx]] *= -1;
2965     colindex[jjx] = n;
2966 
2967     /* Delete the column dependent variable */
2968     jx = presolve_colremove(psdata, jx, FALSE);
2969     iVarsFixed++;
2970 
2971     /* Check if we have been lucky enough to have eliminated the independent
2972        variable via substitution of the dependent variable */
2973     if(item == 0) {
2974 #ifdef Paranoia
2975       report(lp, DETAILED, "presolve_elimeq2: Was able to remove variables %d and %d in row %s\n",
2976                          jx, jjx, get_row_name(lp, i));
2977 #endif
2978       if(presolve_colfix(psdata, jjx, 0.0, TRUE, nc))
2979         jjx = presolve_colremove(psdata, jjx, FALSE);
2980     }
2981 
2982     /* Delete the row */
2983     presolve_rowremove(psdata, i, FALSE);
2984     iRowsRemoved++;
2985   }
2986 
2987   /* Perform the column updates collected above */
2988   if(n > 0) {
2989     mat_mapreplace(mat, psdata->rows->varmap, psdata->cols->varmap, rev);
2990     presolve_validate(psdata, TRUE);
2991 #ifdef PresolveForceUpdateMax
2992     mat_computemax(mat /* , FALSE */);
2993 #endif
2994     psdata->forceupdate = TRUE;
2995   }
2996 
2997   /* Free work arrays */
2998 Finish:
2999   if(DV != NULL)
3000     freeUndoLadder(&DV);
3001   freeLink(&EQ2);
3002   FREE(colvalue);
3003   FREE(delvalue);
3004   FREE(colindex);
3005 
3006   /* Update counters */
3007   (*nn)   += iCoeffChanged;
3008   (*nr)   += iRowsRemoved;
3009   (*nc)   += iVarsFixed;
3010   (*nSum) += iCoeffChanged + iRowsRemoved + iVarsFixed;
3011 
3012   return( status );
3013 }
3014 
3015 STATIC MYBOOL presolve_impliedfree(lprec *lp, presolverec *psdata, int colnr)
3016 {
3017   int    i, ix, ie;
3018   REAL   Tlower, Tupper;
3019   MYBOOL status, rowbinds, isfree = FALSE;
3020   MATrec *mat = lp->matA;
3021 
3022   if(my_infinite(lp, get_lowbo(lp, colnr)) && my_infinite(lp, get_upbo(lp, colnr)))
3023     return( TRUE );
3024 
3025   ie = mat->col_end[colnr];
3026   for(ix = mat->col_end[colnr-1]; (isfree != (TRUE | AUTOMATIC)) && (ix < ie); ix++) {
3027     i = COL_MAT_ROWNR(ix);
3028     if(!isActiveLink(psdata->rows->varmap, i))
3029       continue;
3030     Tlower = get_rh_lower(lp, i);
3031     Tupper = get_rh_upper(lp, i);
3032     status = presolve_multibounds(psdata, i, colnr, &Tlower, &Tupper, NULL, &rowbinds);
3033     isfree = isfree | status | rowbinds;
3034   }
3035 
3036   return( (MYBOOL) (isfree == (TRUE | AUTOMATIC)) );
3037 }
3038 
3039 STATIC MYBOOL presolve_impliedcolfix(presolverec *psdata, int rownr, int colnr, MYBOOL isfree)
3040 {
3041   lprec    *lp = psdata->lp;
3042   MYBOOL   signflip, undoadded = FALSE;
3043   MATrec   *mat = lp->matA;
3044   int      jx, i, ib, ie = mat->row_end[rownr];
3045   REAL     varLo = 0, varHi = 0, varRange, conRange = 0, matValue = 0, dual, RHS = lp->orig_rhs[rownr],
3046            pivot, matAij = mat_getitem(mat, rownr, colnr), *vecOF = lp->orig_obj;
3047 
3048   /* We cannot have semi-continuous or non-qualifying integers */
3049   if(is_semicont(lp, colnr) || is_SOS_var(lp, colnr))
3050     return( FALSE );
3051   if(is_int(lp, colnr)) {
3052     if(!isActiveLink(psdata->INTmap, rownr) || !is_presolve(lp, PRESOLVE_KNAPSACK))
3053       return( FALSE );
3054     /* colnr must have a coefficient equal to the smallest in the row */
3055     varRange = lp->infinite;
3056     i = 0;
3057     pivot = 0;
3058     for(ib = presolve_nextcol(psdata, rownr, &i); i != 0; ib = presolve_nextcol(psdata, rownr, &i)) {
3059       jx = ROW_MAT_COLNR(ib);
3060       dual = fabs(ROW_MAT_VALUE(ib));
3061       /* Check if we have the target column and save the pivot value */
3062       if(jx == colnr) {
3063         /* Always accept unit coefficient */
3064         if(fabs(dual - 1) < psdata->epsvalue)
3065           break;
3066         pivot = dual;
3067         /* Otherwise continue scan */
3068       }
3069       /* Cannot accept case where result can be fractional */
3070       else if((pivot > dual + psdata->epsvalue) ||
3071                ((pivot > 0) && (fabs(fmod(dual, pivot)) > psdata->epsvalue)))
3072         return( FALSE );
3073     }
3074   }
3075 
3076   /* Ascertain that the pivot value is large enough to preserve stability */
3077   pivot = matAij;
3078   if(fabs(pivot) < psdata->epspivot*mat->colmax[colnr])
3079     return( FALSE );
3080 
3081   /* Must ascertain that the row variables are not SOS'es; this is because
3082      the eliminated variable will be a function of another variable. */
3083   if(SOS_count(lp) > 0) {
3084     for(ib = mat->row_end[rownr-1]; ib < ie; ib++)
3085       if(SOS_is_member(lp->SOS, 0, ROW_MAT_COLNR(ib)))
3086         return( FALSE );
3087   }
3088 
3089   /* Calculate the dual value */
3090   dual = vecOF[colnr]/pivot;
3091 
3092   /* Here we have free variable in an equality constraint; this means we can
3093      can adjust the OF for the deleted variable and also delete the constraint. */
3094   if(isfree && is_constr_type(lp, rownr, EQ)) {
3095     matValue = RHS/pivot;
3096     if(matValue != 0)
3097       undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0);
3098   }
3099 
3100   else {
3101 
3102     /* IMPLIEDFREE: For simplicity, ensure that we can keep the slack based at 0,
3103                    and not its upper bound. Effectively, we consider the constraint
3104                    an equality, using the information of the sign of the dual.
3105        IMPLIEDSLK: Since we already have an equality constraint, we wish to make sure
3106                    that the ensuing inequality constraint will have an RHS that is
3107                    non-infinite. */
3108     if(isfree) {
3109       SETMIN(RHS, presolve_sumplumin(lp, rownr, psdata->rows, TRUE));
3110       matValue = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
3111       conRange = get_rh_lower(lp, rownr);
3112       conRange = RHS - MAX(matValue, conRange);
3113       signflip = (MYBOOL) ((dual > 0) &&
3114                            !my_infinite(lp, conRange));
3115     }
3116     else {
3117       varLo = get_lowbo(lp, colnr);
3118       varLo *= (my_infinite(lp, varLo) ? my_sign(pivot) : pivot);
3119       varHi = get_upbo(lp, colnr);
3120       varHi *= (my_infinite(lp, varHi) ? my_sign(pivot) : pivot);
3121       if(pivot < 0)
3122         swapREAL(&varHi, &varLo);
3123       signflip = my_infinite(lp, varLo);
3124     }
3125     if(signflip) {
3126       mat_multrow(mat, rownr, -1);
3127       RHS -= conRange;
3128       RHS = -RHS;
3129       lp->orig_rhs[rownr] = RHS;
3130       pivot = -pivot;
3131       dual  = -dual;
3132       if(!isfree) {
3133         varLo = -varLo;
3134         varHi = -varHi;
3135         swapREAL(&varHi, &varLo);
3136       }
3137     }
3138     matValue = RHS/pivot;
3139 
3140     /* Prepare for deleting free or implied free variable in inequality constraint.
3141        Different strategies need to be used:
3142 
3143        ACTUAL:  Find the proper constraint bound and store undo information for
3144                 recovering the value of the implied free variable.  The constraint
3145                 is then deleted.  We have to adjust the objective function if the
3146                 OF coefficient for the implied free variable is non-zero.
3147        IMPLIED: Convert the constraint to an inequality at the proper bound.
3148                 For given models, the new equality constraint can later provide
3149                 an implied slack, which means that a further variable is eliminated,
3150                 and the constraint again becomes an inequality constraint.
3151 
3152       Note that this version only implements the ACTUAL mode */
3153     if(isfree) {
3154       /* Add undo information connecting the deleted variable to the RHS */
3155       if(matValue != 0)
3156         undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0);
3157       /* Add undo information for the dual of the deleted constraint */
3158       if(dual != 0)
3159         addUndoPresolve(lp, FALSE, rownr, dual, 0.0, 0);
3160     }
3161 
3162     /* Prepare for deleting implied slack variable.  The following two cases are
3163       handled:
3164 
3165       1. Equality constraint: Convert the constraint to an inequality constraint
3166                               that is possibly ranged
3167       2. Other constraints:   Expand existing slack variable / constraint
3168                               range, if required. */
3169     else {
3170       if(my_infinite(lp, varHi))
3171         varRange = lp->infinite;
3172 #ifdef Paranoia
3173       else if(my_infinite(lp, varLo)) {
3174         report(lp, SEVERE, "presolve_impliedcolfix: Negative infinite limit for variable %d\n", colnr);
3175         varRange = lp->infinite;
3176       }
3177 #endif
3178       else
3179         varRange = my_precision(fabs(varHi - varLo) + lp->epsvalue, psdata->epsvalue);
3180       presolve_adjustrhs(psdata, rownr, varLo, psdata->epsvalue);
3181 
3182       /* Handle case 1 of an equality constraint */
3183       if(is_constr_type(lp, rownr, EQ)) {
3184         /* Make sure we actually have a ranged constraint */
3185         if(varRange > 0) {
3186           set_constr_type(lp, rownr, LE);
3187           if(!my_infinite(lp, varRange))
3188             lp->orig_upbo[rownr] = varRange;
3189           setLink(psdata->LTmap, rownr);
3190           removeLink(psdata->EQmap, rownr);
3191         }
3192       }
3193       /* Handle case 2 of an inequality constraint (UNDER CONSTRUCTION!)*/
3194       else {
3195         if(!my_infinite(lp, lp->orig_upbo[rownr])) {
3196           if(my_infinite(lp, varRange))
3197             lp->orig_upbo[rownr] = lp->infinite;
3198           else
3199             lp->orig_upbo[rownr] += varHi - varLo;
3200         }
3201       }
3202       /* Update counts */
3203       if(matAij > 0)
3204         psdata->rows->plucount[rownr]--;
3205       else
3206         psdata->rows->negcount[rownr]--;
3207       if(my_sign(varLo) != my_sign(varHi))
3208         psdata->rows->pluneg[rownr]--;
3209 
3210       /* Add undo information for the deleted variable; note that we cannot link the
3211         deleted variable to the slack, since it may not be available during undo.
3212         We really should have a mini LP to compute this allocation ex-post. */
3213       if(RHS != 0)
3214         undoadded = addUndoPresolve(lp, TRUE, colnr, RHS/pivot, 0.0, 0);
3215     }
3216   }
3217 
3218   /* Update the OF constant */
3219   if(dual != 0) {
3220     presolve_adjustrhs(psdata, 0, dual * RHS, 0);
3221 /*    lp->orig_rhs[0] -= dual * RHS; */
3222     vecOF[colnr] = 0;
3223   }
3224 
3225   /* Do affine transformation with the constraint row */
3226   i = 0;
3227   for(ib = presolve_nextcol(psdata, rownr, &i); ib >= 0;
3228       ib = presolve_nextcol(psdata, rownr, &i)) {
3229 
3230     /* Get the constraint element */
3231     jx = ROW_MAT_COLNR(ib);
3232     if(jx == colnr)
3233       continue;
3234     matValue = ROW_MAT_VALUE(ib);
3235 
3236     /* Adjust OF for the variable to be deleted */
3237     if(dual != 0)
3238       vecOF[jx] -= dual * matValue;
3239 
3240     /* Add reconstruction/undo parameters for the deleted variable */
3241     if(!undoadded)
3242       undoadded = addUndoPresolve(lp, TRUE, colnr, 0.0, matValue/pivot, jx);
3243     else
3244       appendUndoPresolve(lp, TRUE, matValue/pivot, jx);
3245   }
3246 
3247   return( TRUE );
3248 }
3249 
3250 STATIC psrec *presolve_initpsrec(lprec *lp, int size)
3251 {
3252   psrec *ps = (psrec *) calloc(1, sizeof(*ps));
3253 
3254   createLink(size, &ps->varmap, NULL);
3255     fillLink(ps->varmap);
3256 
3257   size++;
3258 
3259   allocINT(lp, &ps->empty, size, FALSE);
3260   ps->empty[0] = 0;
3261 
3262   allocREAL(lp, &ps->pluupper,  size, FALSE);
3263   allocREAL(lp, &ps->negupper,  size, FALSE);
3264   allocREAL(lp, &ps->plulower,  size, FALSE);
3265   allocREAL(lp, &ps->neglower,  size, FALSE);
3266   allocINT(lp,  &ps->infcount,  size, FALSE);
3267 
3268   ps->next = (int **) calloc(size, sizeof(*(ps->next)));
3269 
3270   allocINT(lp,  &ps->plucount,  size, TRUE);
3271   allocINT(lp,  &ps->negcount,  size, TRUE);
3272   allocINT(lp,  &ps->pluneg,    size, TRUE);
3273 
3274   ps->allocsize = size;
3275 
3276   return( ps );
3277 }
3278 STATIC void presolve_freepsrec(psrec **ps)
3279 {
3280   FREE((*ps)->plucount);
3281   FREE((*ps)->negcount);
3282   FREE((*ps)->pluneg);
3283   FREE((*ps)->infcount);
3284 
3285   if((*ps)->next != NULL) {
3286     int i, n = (*ps)->allocsize;
3287     for(i = 0; i < n; i++)
3288       FREE((*ps)->next[i]);
3289     FREE((*ps)->next);
3290   }
3291 
3292   FREE((*ps)->plulower);
3293   FREE((*ps)->neglower);
3294   FREE((*ps)->pluupper);
3295   FREE((*ps)->negupper);
3296 
3297   FREE((*ps)->empty);
3298 
3299   freeLink(&(*ps)->varmap);
3300 
3301   FREE(*ps);
3302 }
3303 
3304 STATIC presolverec *presolve_init(lprec *lp)
3305 {
3306   int         k, i, ix, ixx, colnr,
3307               ncols = lp->columns,
3308               nrows = lp->rows;
3309   REAL        hold;
3310   MATrec      *mat = lp->matA;
3311   presolverec *psdata = NULL;
3312 
3313   /* Optimize memory usage if we have a very large model;
3314      this is to reduce the risk of out-of-memory situations. */
3315   ix  = get_nonzeros(lp);
3316   ixx = lp->matA->mat_alloc;
3317   if((ixx - ix > MAT_START_SIZE) && ((ixx - ix) * 20 > ixx))
3318     mat_memopt(lp->matA, nrows / 20, ncols / 20, ix / 20);
3319 
3320   psdata = (presolverec *) calloc(1, sizeof(*psdata));
3321 
3322   psdata->lp   = lp;
3323   psdata->rows = presolve_initpsrec(lp, nrows);
3324   psdata->cols = presolve_initpsrec(lp, ncols);
3325 
3326   psdata->epsvalue = PRESOLVE_EPSVALUE;
3327   psdata->epspivot = PRESOLVE_EPSPIVOT;
3328   psdata->forceupdate = TRUE;
3329 
3330   /* Save incoming primal bounds */
3331   k = lp->sum + 1;
3332   allocREAL(lp, &psdata->pv_lobo, k, FALSE);
3333   MEMCOPY(psdata->pv_lobo, lp->orig_lowbo, k);
3334   allocREAL(lp, &psdata->pv_upbo, k, FALSE);
3335   MEMCOPY(psdata->pv_upbo, lp->orig_upbo, k);
3336 
3337   /* Create and initialize dual value (Langrangean and slack) limits */
3338   allocREAL(lp, &psdata->dv_lobo, k, FALSE);
3339   allocREAL(lp, &psdata->dv_upbo, k, FALSE);
3340   for(i = 0; i <= nrows; i++) {
3341     psdata->dv_lobo[i] = (is_constr_type(lp, i, EQ) ? -lp->infinite : 0);
3342     psdata->dv_upbo[i] = lp->infinite;
3343   }
3344   k--;
3345   for(; i <= k; i++) {
3346     psdata->dv_lobo[i] = 0;
3347     psdata->dv_upbo[i] = lp->infinite;
3348   }
3349 
3350  /* Create NZ count and sign arrays, and do general initialization of row bounds */
3351   createLink(nrows, &psdata->EQmap, NULL);
3352   createLink(nrows, &psdata->LTmap, NULL);
3353   createLink(nrows, &psdata->INTmap, NULL);
3354   for(i = 1; i <= nrows; i++) {
3355     switch (get_constr_type(lp, i)) {
3356       case LE: appendLink(psdata->LTmap, i);
3357                 break;
3358       case EQ: appendLink(psdata->EQmap, i);
3359                 break;
3360     }
3361     k = mat_rowlength(mat, i);
3362     if((lp->int_vars > 0) && (k > 0))
3363       appendLink(psdata->INTmap, i);
3364   }
3365 
3366   /* Seek to reduce set of sum(INT*INT) rows (mainly for GCD coefficient reductions) */
3367   if(psdata->INTmap->count > 0)
3368   for(i = 1; i <= nrows; i++) {
3369     if(!isActiveLink(psdata->INTmap, i))
3370       continue;
3371     /* Disqualify if there is a non-int variable, otherwise find smallest absolute fractional row value */
3372     ix = mat->row_end[i - 1];
3373     ixx = mat->row_end[i];
3374     colnr = 0;
3375     for(; ix < ixx; ix++) {
3376       if(!is_int(lp, ROW_MAT_COLNR(ix))) {
3377         removeLink(psdata->INTmap, i);
3378         break;
3379       }
3380       hold = fabs(ROW_MAT_VALUE(ix));
3381       hold = fmod(hold, 1);
3382       /* Adjust colnr to be a decimal scalar */
3383       for(k = 0; (k <= MAX_FRACSCALE) && (hold+psdata->epsvalue < 1); k++)
3384         hold *= 10;
3385       if(k > MAX_FRACSCALE) {
3386         removeLink(psdata->INTmap, i);
3387         break;
3388       }
3389       SETMAX(colnr, k);
3390     }
3391     if(!isActiveLink(psdata->INTmap, i))
3392       continue;
3393     hold = pow(10.0, colnr);
3394     /* Also disqualify if the RHS is fractional after scaling */
3395     if(fabs(fmod(lp->orig_rhs[i] * hold, 1)) > psdata->epsvalue) {
3396       removeLink(psdata->INTmap, i);
3397       continue;
3398     }
3399     /* We have an all-int constraint, see if we should scale it up */
3400     if(k > 0) {
3401       ix = mat->row_end[i - 1];
3402       for(; ix < ixx; ix++) {
3403         ROW_MAT_VALUE(ix) *= hold;
3404       }
3405       lp->orig_rhs[i] *= hold;
3406       if(!my_infinite(lp, lp->orig_upbo[i]))
3407         lp->orig_upbo[i] *= hold; /* KE: Fix due to Andy Loto - 20070619 */
3408     }
3409   }
3410 
3411   /* Do the real tallying and ordering work */
3412   presolve_validate(psdata, TRUE);
3413 
3414   return( psdata );
3415 }
3416 
3417 STATIC void presolve_free(presolverec **psdata)
3418 {
3419   presolve_freepsrec(&(*psdata)->rows);
3420   presolve_freepsrec(&(*psdata)->cols);
3421   FREE((*psdata)->dv_lobo);
3422   FREE((*psdata)->dv_upbo);
3423   FREE((*psdata)->pv_lobo);
3424   FREE((*psdata)->pv_upbo);
3425   freeLink(&(*psdata)->EQmap);
3426   freeLink(&(*psdata)->LTmap);
3427   freeLink(&(*psdata)->INTmap);
3428   FREE(*psdata);
3429 }
3430 
3431 STATIC int presolve_makefree(presolverec *psdata)
3432 {
3433   lprec    *lp = psdata->lp;
3434   int      i, ix, j, nn = 0;
3435   REAL     Xlower, Xupper, losum, upsum, lorhs, uprhs, freeinf = lp->infinite / 10;
3436   MATrec   *mat = lp->matA;
3437   LLrec    *colLL = NULL;
3438 
3439   /* First see if we can relax ranged constraints */
3440   for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
3441     if(is_constr_type(lp, i, EQ))
3442       continue;
3443     presolve_range(lp, i, psdata->rows, &losum, &upsum);
3444     lorhs = get_rh_lower(lp, i);
3445     uprhs = get_rh_upper(lp, i);
3446 
3447     /* Look for opportunity to relax constraint bounds */
3448     if(presolve_rowlength(psdata, i) > 1) {
3449       if((is_constr_type(lp, i, GE) && (upsum <= uprhs)) ||
3450          (is_constr_type(lp, i, LE) && (losum >= lorhs)))
3451         set_rh_range(lp, i, lp->infinite);
3452     }
3453   }
3454 
3455   /* Collect columns available for bound relaxation (find implied free variables)
3456      (consider sorting the list in decending order of column lengths or do call to
3457       COLAMD to maximize impact) */
3458   createLink(lp->columns, &colLL, NULL);
3459   for(j = firstActiveLink(psdata->cols->varmap); j != 0; j = nextActiveLink(psdata->cols->varmap, j))
3460     if(presolve_impliedfree(lp, psdata, j))
3461       appendLink(colLL, j);
3462 
3463   /* Find what columns to relax (ideally one per row) */
3464   if(colLL->count > 0) {
3465     LLrec  *rowLL = NULL;
3466     MYBOOL canfree;
3467 
3468     /* Create row tracker */
3469     createLink(lp->rows, &rowLL, NULL);
3470     fillLink(rowLL);
3471 
3472     /* Loop over all column candidates */
3473     for(j = firstActiveLink(colLL); (j > 0) && (rowLL->count > 0); j = nextActiveLink(colLL, j)) {
3474 
3475       /* Verify that the variable is applicable */
3476       canfree = TRUE;
3477       for(ix = mat->col_end[j-1]; canfree && (ix < mat->col_end[j]); ix++)
3478         canfree = isActiveLink(rowLL, COL_MAT_ROWNR(ix));
3479 
3480       /* If so, then open the bounds and update the row availability mapper */
3481       if(canfree) {
3482         nn++;
3483         Xlower = get_lowbo(lp, j);
3484         Xupper = get_upbo(lp, j);
3485         if(Xlower >= 0)
3486           set_bounds(lp, j, 0, freeinf);
3487         else if(Xupper <= 0)
3488           set_bounds(lp, j, -freeinf, 0);
3489         else
3490 /*          set_bounds(lo, j, -freeinf, freeinf); */
3491           set_unbounded(lp, j);
3492         for(ix = mat->col_end[j-1]; ix < mat->col_end[j]; ix++)
3493           removeLink(rowLL, COL_MAT_ROWNR(ix));
3494       }
3495     }
3496     freeLink(&rowLL);
3497   }
3498 
3499   /* Free list and return */
3500   freeLink(&colLL);
3501   return( nn );
3502 }
3503 
3504 STATIC MYBOOL presolve_updatesums(presolverec *psdata)
3505 {
3506   lprec    *lp = psdata->lp;
3507   int      j;
3508 
3509   /* Initialize row accumulation arrays */
3510   MEMCLEAR(psdata->rows->pluupper, lp->rows + 1);
3511   MEMCLEAR(psdata->rows->negupper, lp->rows + 1);
3512   MEMCLEAR(psdata->rows->plulower, lp->rows + 1);
3513   MEMCLEAR(psdata->rows->neglower, lp->rows + 1);
3514   MEMCLEAR(psdata->rows->infcount, lp->rows + 1);
3515 
3516   /* Loop over active columns */
3517   for(j = firstActiveLink(psdata->cols->varmap); j != 0;
3518       j = nextActiveLink(psdata->cols->varmap, j)) {
3519     presolve_colfix(psdata, j, lp->infinite, FALSE, NULL);
3520   }
3521 
3522 #ifdef UseDualPresolve
3523   /* Initialize column accumulation arrays */
3524   MEMCLEAR(psdata->cols->pluupper, lp->columns + 1);
3525   MEMCLEAR(psdata->cols->negupper, lp->columns + 1);
3526   MEMCLEAR(psdata->cols->plulower, lp->columns + 1);
3527   MEMCLEAR(psdata->cols->neglower, lp->columns + 1);
3528   MEMCLEAR(psdata->cols->infcount, lp->columns + 1);
3529 
3530   /* Loop over active rows */
3531   for(j = firstActiveLink(psdata->rows->varmap); j != 0;
3532       j = nextActiveLink(psdata->rows->varmap, j)) {
3533     presolve_rowfix(psdata, j, lp->infinite, FALSE, NULL);
3534   }
3535 #endif
3536 
3537   return( TRUE );
3538 }
3539 
3540 STATIC MYBOOL presolve_finalize(presolverec *psdata)
3541 {
3542   lprec    *lp = psdata->lp;
3543   MYBOOL   compactvars = FALSE;
3544   int      ke, n;
3545 
3546   /* Save eliminated rows and columns for restoration purposes */
3547 #ifdef SavePresolveEliminated
3548   psdata->deletedA = mat_extractmat(lp->matA, rowmap, colmap, TRUE);
3549   if(!mat_validate(psdata->deletedA))
3550     report(lp, SEVERE, "presolve_finalize: Could not validate matrix with undo data\n");
3551 #endif
3552 
3553   /* Check if OF columns are to be deleted */
3554   lp->presolve_undo->OFcolsdeleted = FALSE;
3555   for(n = firstInactiveLink(psdata->cols->varmap); (n != 0) && !lp->presolve_undo->OFcolsdeleted;
3556       n = nextInactiveLink(psdata->cols->varmap, n))
3557     lp->presolve_undo->OFcolsdeleted = (MYBOOL) (lp->orig_obj[n] != 0);
3558 
3559   /* Delete eliminated columns */
3560   ke = lastInactiveLink(psdata->cols->varmap);
3561   n = countInactiveLink(psdata->cols->varmap);
3562   if((n > 0) && (ke > 0)) {
3563     del_columnex(lp, psdata->cols->varmap);
3564     mat_colcompact(lp->matA, lp->presolve_undo->orig_rows,
3565                              lp->presolve_undo->orig_columns);
3566     compactvars = TRUE;
3567   }
3568 
3569   /* Delete eliminated rows */
3570   ke = lastInactiveLink(psdata->rows->varmap);
3571   n = countInactiveLink(psdata->rows->varmap);
3572   if((n > 0) && (ke > 0)) {
3573     del_constraintex(lp, psdata->rows->varmap);
3574     mat_rowcompact(lp->matA, TRUE);
3575     compactvars = TRUE;
3576   }
3577   else if(psdata->nzdeleted > 0)
3578     mat_zerocompact(lp->matA);
3579 
3580   /* Do compacting and updating of variable maps */
3581   if(compactvars)
3582     varmap_compact(lp, lp->presolve_undo->orig_rows,
3583                        lp->presolve_undo->orig_columns);
3584 
3585   /* Reduce memory usage of postsolve matrices */
3586   if(lp->presolve_undo->primalundo != NULL)
3587     mat_memopt(lp->presolve_undo->primalundo->tracker, 0, 0, 0);
3588   if(lp->presolve_undo->dualundo != NULL)
3589     mat_memopt(lp->presolve_undo->dualundo->tracker, 0, 0, 0);
3590 
3591   /* Round near-zero objective function coefficients and RHS values */
3592   ke = lp->columns;
3593   for(n = 1; n <= ke; n++)
3594     my_roundzero(lp->orig_obj[n], lp->epsvalue);
3595   ke = lp->rows;
3596   for(n = 1; n <= ke; n++)
3597     my_roundzero(lp->orig_rhs[n], lp->epsvalue);
3598 
3599   /* Update the SOS sparse mapping */
3600   if(SOS_count(lp) > 0)
3601     SOS_member_updatemap(lp->SOS);
3602 
3603   /* Validate matrix and reconstruct row indexation */
3604   return(mat_validate(lp->matA));
3605 }
3606 
3607 STATIC MYBOOL presolve_debugdump(lprec *lp, presolverec *psdata, char *filename, MYBOOL doappend)
3608 {
3609   FILE   *output = stdout;
3610   int   size;
3611   MYBOOL ok;
3612 
3613   ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename, my_if(doappend, "a", "w"))) != NULL));
3614   if(!ok)
3615     return(ok);
3616   if((filename == NULL) && (lp->outstream != NULL))
3617     output = lp->outstream;
3618 
3619   fprintf(output, "\nPRESOLVE - Status at loop %d:%d:%d\n",
3620                   psdata->outerloops, psdata->middleloops, psdata->innerloops);
3621   fprintf(output, "Model size:     %d rows (%d equalities, %d less than), %d columns\n",
3622                   psdata->rows->varmap->count, psdata->EQmap->count, psdata->LTmap->count, psdata->cols->varmap->count);
3623 
3624   fprintf(output, "\nMAPPERS\n-------\n\n");
3625   size = 1;
3626   blockWriteINT(output,  "colmap", psdata->cols->varmap->map, 0, size*psdata->cols->varmap->size);
3627   blockWriteINT(output,  "rowmap", psdata->rows->varmap->map, 0, size*psdata->rows->varmap->size);
3628   blockWriteINT(output,  "EQmap",  psdata->EQmap->map,  0, size*psdata->EQmap->size);
3629   blockWriteINT(output,  "LTmap",  psdata->LTmap->map,  0, size*psdata->LTmap->size);
3630 
3631   fprintf(output, "\nCOUNTS\n------\n\n");
3632   blockWriteINT(output, "plucount",  psdata->rows->plucount,  0, lp->rows);
3633   blockWriteINT(output, "negcount",  psdata->rows->negcount,  0, lp->rows);
3634   blockWriteINT(output, "pluneg",    psdata->rows->pluneg,    0, lp->rows);
3635 
3636   fprintf(output, "\nSUMS\n----\n\n");
3637   blockWriteREAL(output, "pluupper", psdata->rows->pluupper, 0, lp->rows);
3638   blockWriteREAL(output, "negupper", psdata->rows->negupper, 0, lp->rows);
3639   blockWriteREAL(output, "plulower", psdata->rows->pluupper, 0, lp->rows);
3640   blockWriteREAL(output, "neglower", psdata->rows->negupper, 0, lp->rows);
3641 
3642   if(filename != NULL)
3643     fclose(output);
3644   return(ok);
3645 }
3646 
3647 int CMP_CALLMODEL compRedundant(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3648 {
3649   int start1 = (int) (current->int4.intpar1),
3650       start2 = (int) (candidate->int4.intpar1),
3651       result = CMP_COMPARE(start1, start2);
3652 
3653   if(result == 0) {
3654     start1 = (int) (current->int4.intpar2);
3655     start2 = (int) (candidate->int4.intpar2);
3656     result = -CMP_COMPARE(start1, start2);
3657   }
3658   return( result );
3659 }
3660 int CMP_CALLMODEL compSparsity(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3661 {
3662   int start1 = (int) (current->int4.intpar1),
3663       start2 = (int) (candidate->int4.intpar1),
3664       result = CMP_COMPARE(start1, start2);
3665 
3666   if(result == 0) {
3667     start1 = (int) (current->int4.intpar2);
3668     start2 = (int) (candidate->int4.intpar2);
3669     result = -CMP_COMPARE(start1, start2);
3670   }
3671 
3672   if(result == 0) {
3673     start1 = (int) (current->int4.intval);
3674     start2 = (int) (candidate->int4.intval);
3675     result = CMP_COMPARE(start1, start2);
3676   }
3677   return( result );
3678 }
3679 int CMP_CALLMODEL compAggregate(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3680 {
3681   int  index1 = (int) (current->pvoidint2.intval),
3682        index2 = (int) (candidate->pvoidint2.intval);
3683   lprec *lp   = (lprec *) current->pvoidint2.ptr;
3684   REAL value1 = lp->orig_obj[index1],
3685        value2 = lp->orig_obj[index2];
3686 
3687   /* Smallest objective coefficient (largest contribution to OF) */
3688   int  result = CMP_COMPARE(value1, value2);
3689 
3690   /* Smallest lower variable bound */
3691   if(result == 0) {
3692     index1 += lp->rows;
3693     index2 += lp->rows;
3694     value1 = lp->orig_lowbo[index1];
3695     value2 = lp->orig_lowbo[index2];
3696     result = CMP_COMPARE(value1, value2);
3697   }
3698 
3699   /* Largest upper variable bound */
3700   if(result == 0) {
3701     value1 = lp->orig_upbo[index1];
3702     value2 = lp->orig_upbo[index2];
3703     result = -CMP_COMPARE(value1, value2);
3704   }
3705   return( result );
3706 }
3707 
3708 STATIC int presolve_rowdominance(presolverec *psdata, int *nCoeffChanged, int *nRowsRemoved, int *nVarsFixed, int *nSum)
3709 {
3710   lprec    *lp = psdata->lp;
3711   MATrec   *mat = lp->matA;
3712   int      i, ii, ib, ie, n, jb, je, jx, *coldel = NULL, status = RUNNING, item,
3713            iCoeffChanged = 0, iRowRemoved = 0, iVarFixed = 0;
3714   REAL     ratio, *rowvalues = NULL;
3715   UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows+1, sizeof(*QS));
3716 
3717   /* Check if we were able to obtain working memory */
3718   if(QS == NULL)
3719     return( status);
3720 
3721   /* A dominating row of variables always satisfy the following criteria:
3722       1) The starting column position is never lower, but could be the same
3723       2) The non-zero row count is always lower */
3724   n = 0;
3725   for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) {
3726     /* Make sure we have no SOS or semi-continuous variables */
3727     jb = je = 0;
3728     if((SOS_count(lp) > 0) || (lp->sc_vars > 0)) {
3729       item = 0;
3730       for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3731           jb = presolve_nextcol(psdata, i, &item)) {
3732         jx = ROW_MAT_COLNR(jb);
3733         if(SOS_is_member(lp->SOS, 0, jx) || is_semicont(lp, jx))
3734           break;
3735       }
3736     }
3737 
3738     /* Add to list if we are Ok */
3739     if(jb < 0) {
3740       QS[n].int4.intval = i;
3741       item = 0;
3742       ii = presolve_nextcol(psdata, i, &item);
3743       QS[n].int4.intpar1 = ROW_MAT_COLNR(ii);
3744       QS[n].int4.intpar2 = presolve_rowlength(psdata, i);
3745       n++;
3746     }
3747   }
3748   if(n <= 1)
3749     goto Finish;
3750   QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
3751 
3752   /* Let us start from the top of the list, going forward and looking
3753     for the longest possible dominating row */
3754   if(!allocREAL(lp, &rowvalues, lp->columns + 1, TRUE) ||
3755      !allocINT(lp, &coldel, lp->columns + 1, FALSE))
3756     goto Finish;
3757 
3758   for(ib = 0; ib < n; ib++) {
3759 
3760     /* Get row and check if it was previously eliminated */
3761     i = QS[ib].int4.intval;
3762     if(i < 0)
3763       continue;
3764 
3765     /* Load the non-zero row values */
3766     item = 0;
3767     for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3768         jb = presolve_nextcol(psdata, i, &item)) {
3769       jx = ROW_MAT_COLNR(jb);
3770       rowvalues[jx] = ROW_MAT_VALUE(jb);
3771     }
3772 
3773     for(ie = ib+1; ie < n; ie++) {
3774 
3775       /* Get row and check if it was previously eliminated */
3776       ii = QS[ie].int4.intval;
3777       if(ii < 0)
3778         continue;
3779 
3780 #ifdef Paranoia
3781       if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
3782          ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
3783         report(lp, SEVERE, "presolve_rowdominance: Invalid sorted row order\n");
3784 #endif
3785 
3786       /* Loop over every row member to confirm that the candidate
3787         actually dominates in every position */
3788       if((lp->orig_rhs[i] == 0) && (lp->orig_rhs[ii] == 0))
3789         ratio = 0;
3790       else if((lp->orig_rhs[i] != 0) && (lp->orig_rhs[ii] != 0))
3791         ratio = lp->orig_rhs[i] / lp->orig_rhs[ii];
3792       else
3793         continue;
3794       item = 0;
3795       for(jb = presolve_nextcol(psdata, ii, &item); jb >= 0;
3796           jb = presolve_nextcol(psdata, ii, &item)) {
3797         jx = ROW_MAT_COLNR(jb);
3798         if(rowvalues[jx] == 0)
3799           break;
3800         if(ratio == 0)
3801           ratio = rowvalues[jx] / ROW_MAT_VALUE(jb);
3802         else if(fabs(rowvalues[jx] - ratio*ROW_MAT_VALUE(jb)) > psdata->epsvalue)
3803           break;
3804       }
3805 
3806       /* "We have contact" */
3807       if(jb < 0) {
3808         int sign_1 = 0, sign_j = 0;
3809 
3810         /* Need to fix any superset columns, but require that they have equal signs */
3811         coldel[0] = 0;
3812         item = 0;
3813         for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3814             jb = presolve_nextcol(psdata, i, &item)) {
3815           jx = ROW_MAT_COLNR(jb);
3816           if(mat_findelm(mat, ii, jx) <= 0) {
3817 
3818             /* Cancel if we detect a free or "quasi-free" variable */
3819             if((lp->orig_lowbo[lp->rows + jx] < 0) &&
3820                (lp->orig_upbo[lp->rows + jx] > 0)) {
3821               coldel[0] = -1;
3822               break;
3823             }
3824 
3825             /* Ensure that we are feasible */
3826             else if((lp->orig_lowbo[lp->rows + jx] > 0) ||
3827                (lp->orig_upbo[lp->rows + jx] < 0)) {
3828               report(lp, DETAILED, "presolve_rowdominate: Column %s is infeasible due to conflict in rows %s and %s\n",
3829                                     get_col_name(lp, jx), get_row_name(lp, i), get_row_name(lp, ii));
3830               coldel[0] = -1;
3831               break;
3832             }
3833 
3834             /* Check consistency / uniformity of signs */
3835             sign_j = my_sign(ROW_MAT_VALUE(jb));
3836             sign_j = my_chsign(is_negative(lp, jx), sign_j);
3837             if(coldel[0] == 0) {
3838               sign_1 = sign_j;
3839               coldel[++coldel[0]] = jx;
3840             }
3841             else if(sign_j == sign_1) {
3842               coldel[++coldel[0]] = jx;
3843             }
3844             else {
3845               coldel[0] = -1;
3846               break;
3847             }
3848           }
3849         }
3850 
3851         /* Force break / continuation if the superset columns were incompatible */
3852         if(coldel[0] < 0)
3853           continue;
3854 
3855         /* Do the column fixing and deletion (check for infeasibility in the process) */
3856         for(jb = 1; jb <= coldel[0]; jb++) {
3857           jx = coldel[jb];
3858           if(!presolve_colfix(psdata, jx, 0, TRUE, &iVarFixed)) {
3859              status = presolve_setstatus(psdata, INFEASIBLE);
3860              goto Finish;
3861           }
3862           presolve_colremove(psdata, jx, TRUE);
3863           rowvalues[jx] = 0;
3864         }
3865 
3866         /* Then delete the row */
3867         presolve_rowremove(psdata, ii, TRUE);
3868         iRowRemoved++;
3869         QS[ie].int4.intval = -ii;
3870       }
3871     }
3872 
3873     /* Clear the non-zero row values ahead of the next row candidate */
3874     ie = mat->row_end[i-1];
3875     ii = mat->row_end[i];
3876     for(; ie < ii; ie++)
3877       rowvalues[ROW_MAT_COLNR(ie)] = 0;
3878 
3879   }
3880 Finish:
3881   FREE(QS);
3882   FREE(rowvalues);
3883   FREE(coldel);
3884 
3885   (*nCoeffChanged) += iCoeffChanged;
3886   (*nRowsRemoved)  += iRowRemoved;
3887   (*nVarsFixed)    += iVarFixed;
3888   (*nSum)          += iCoeffChanged + iRowRemoved + iVarFixed;
3889 
3890   return( status );
3891 }
3892 
3893 #if 0
3894 STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum)
3895 /* The current version of this routine eliminates binary variables
3896    that are dominated via set coverage or unit knapsack constraints */
3897 {
3898   lprec    *lp = psdata->lp;
3899   MATrec   *mat = lp->matA;
3900   MYBOOL   first;
3901   int      i, ii, ib, ie, n, jb, je, jx, jj, item, item2,
3902            *coldel = NULL, status = RUNNING, iVarFixed = 0;
3903   REAL     scale, rhsval, *colvalues = NULL;
3904   UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QS));
3905 
3906   /* Check if we were able to obtain working memory */
3907   if(QS == NULL)
3908     return( status);
3909   if(lp->int_vars == 0)
3910     goto Finish;
3911 
3912   /* A column dominates another binary variable column with the following criteria:
3913       1) The relative matrix non-zero entries are identical
3914       2) The relative objective coefficient is worse than the other;
3915          if the OF coefficients are identical, we can delete an arbitrary variable */
3916   n = 0;
3917   for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
3918     if(is_binary(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
3919       /* Make sure we have an all-binary, unit-coefficient row */
3920       je = mat->col_end[i];
3921       item = 0;
3922       for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
3923           jb = presolve_nextrow(psdata, i, &item)) {
3924         jx = COL_MAT_ROWNR(jb);
3925         if(COL_MAT_VALUE(jb) != 1)
3926           break;
3927       }
3928 
3929       /* Add to list if we are Ok */
3930       if(jb < 0) {
3931         QS[n].int4.intval = i;
3932         item = 0;
3933         ii = presolve_nextrow(psdata, i, &item);
3934         QS[n].int4.intpar1 = COL_MAT_ROWNR(ii);
3935         ii = presolve_collength(psdata, i);
3936         QS[n].int4.intpar2 = ii;
3937         n++;
3938       }
3939     }
3940   if(n <= 1) {
3941     FREE(QS);
3942     return( status );
3943   }
3944   QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
3945 
3946   /* Let us start from the top of the list, going forward and looking
3947     for the longest possible dominated column */
3948   if(!allocREAL(lp, &colvalues, lp->rows + 1, TRUE) ||
3949      !allocINT(lp, &coldel, lp->columns + 1, FALSE))
3950     goto Finish;
3951 
3952   for(ib = 0; ib < n; ib++) {
3953 
3954     /* Get column and check if it was previously eliminated */
3955     i = QS[ib].int4.intval;
3956     if(i < 0)
3957       continue;
3958 
3959     /* Load the non-zero column values */
3960     item = 0;
3961     for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
3962         jb = presolve_nextrow(psdata, i, &item)) {
3963       jx = COL_MAT_ROWNR(jb);
3964       colvalues[jx] = COL_MAT_VALUE(jb);
3965     }
3966 
3967     coldel[0] = 0;
3968     for(ie = ib+1; ie < n; ie++) {
3969 
3970       /* Insist on identical column lengths (sort is decending in column lengths) */
3971       ii = QS[ib].int4.intpar2 - QS[ie].int4.intpar2;
3972       if(ii != 0)
3973         break;
3974 
3975       /* Also insist on identical starting positions */
3976       ii = QS[ib].int4.intpar1 - QS[ie].int4.intpar1;
3977       if(ii != 0)
3978         break;
3979 
3980       /* Get column and check if it was previously eliminated */
3981       ii = QS[ie].int4.intval;
3982       if(ii < 0)
3983         continue;
3984 
3985       /* Also make sure that the variables have "compatible" bounds */
3986 #if 1
3987       if((fabs(my_reldiff(lp->orig_lowbo[lp->rows + i], lp->orig_lowbo[lp->rows + ii])) > psdata->epsvalue) ||
3988          (fabs(my_reldiff(lp->orig_upbo[lp->rows + i],  lp->orig_upbo[lp->rows + ii] )) > psdata->epsvalue))
3989         continue;
3990 #endif
3991 
3992 #ifdef Paranoia
3993       if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
3994          ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
3995         report(lp, SEVERE, "presolve_coldominance01: Invalid sorted column order\n");
3996 #endif
3997 
3998       /* Loop over every column member to confirm that the candidate is
3999         relatively identical in every position */
4000       first = TRUE;
4001       item = 0;
4002       item2 = 0;
4003       scale = 1;
4004       for(jb = presolve_nextrow(psdata, ii, &item),
4005           jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4006           jb = presolve_nextrow(psdata, ii, &item),
4007           jj = presolve_nextrow(psdata, i, &item2)) {
4008         jx = COL_MAT_ROWNR(jb);
4009         if(jx != COL_MAT_ROWNR(jj))
4010           break;
4011         if(first) {
4012           first = !first;
4013           scale = colvalues[jx] / COL_MAT_VALUE(jb);
4014         }
4015         else {
4016           if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue)
4017             break;
4018         }
4019         /* Also make sure we have a compatible RHS (since this version of the
4020           dominance logic only applies to "sets") */
4021         rhsval = scale*lp->orig_rhs[jx] - 1.0;
4022         /* if((rhsval < 0) || (rhsval > 1 + psdata->epsvalue)) */
4023         if(fabs(rhsval) > psdata->epsvalue)
4024           break;
4025       }
4026 
4027       /* "We have contact" */
4028       if(jb < 0) {
4029         coldel[++coldel[0]] = ii;
4030         QS[ie].int4.intval = -ii;
4031       }
4032     }
4033 
4034     /* Find the dominant column and delete / fix the others;
4035        if there is a tie, simply delete the second candidate */
4036     ii = i;
4037     for(jb = 1; jb <= coldel[0]; jb++) {
4038       jx = coldel[jb];
4039       if(lp->orig_obj[jx] < lp->orig_obj[ii])
4040         swapINT(&ii, &coldel[jb]);
4041     }
4042     for(jb = 1; jb <= coldel[0]; jb++) {
4043       jx = coldel[jb];
4044       if(!presolve_colfix(psdata, jx, lp->orig_lowbo[lp->rows+jx], TRUE, &iVarFixed)) {
4045          status = presolve_setstatus(psdata, INFEASIBLE);
4046          goto Finish;
4047       }
4048       presolve_colremove(psdata, jx, TRUE);
4049     }
4050 
4051     /* Clear the non-zero row values ahead of the next row candidate */
4052     if(ib + 1 < n) {
4053       ie = mat->col_end[i-1];
4054       ii = mat->col_end[i];
4055       for(; ie < ii; ie++)
4056         colvalues[COL_MAT_ROWNR(ie)] = 0;
4057     }
4058   }
4059 Finish:
4060   FREE(QS);
4061   FREE(colvalues);
4062   FREE(coldel);
4063 
4064   (*nVarsFixed) += iVarFixed;
4065   (*nSum)       += iVarFixed;
4066 
4067   return( status );
4068 }
4069 #else
4070 
4071 /* DEVELOPMENT/TEST CODE FOR POSSIBLE REPLACEMENT OF SIMILAR FUNCTION IN lp_presolve.c */
4072 
4073 #define NATURAL int
4074 
4075 STATIC int presolve_coldominance01(presolverec *psdata, NATURAL *nConRemoved, NATURAL *nVarsFixed, NATURAL *nSum)
4076 /* The current version of this routine eliminates binary variables
4077    that are dominated via set coverage or unit knapsack constraints */
4078 {
4079   lprec    *lp = psdata->lp;
4080   MATrec   *mat = lp->matA;
4081   NATURAL  i, ib, ie, jx, item, item2,
4082            n = lp->int_vars, iVarFixed = 0, nrows = lp->rows,
4083            *coldel = NULL;
4084   int      jb, jj, ii,
4085            status = RUNNING;
4086   REAL     rhsval = 0.0,
4087            *colvalues = NULL, *colobj = NULL;
4088   LLrec    *sets = NULL;
4089   UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(n+1, sizeof(*QS));
4090 
4091   /* Check if we were able to obtain working memory */
4092   if(QS == NULL)
4093     return( status);
4094   if(n == 0)
4095     goto Finish;
4096 
4097   /* Create list of set coverage and knapsack constraints */
4098   createLink(nrows, &sets, NULL);
4099   for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
4100     if((lp->orig_rhs[i] < 0) || (psdata->rows->negcount[i] > 0))
4101       continue;
4102     item = 0;
4103     for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
4104         jb = presolve_nextcol(psdata, i, &item)) {
4105       jx = ROW_MAT_COLNR(jb);
4106       if(!is_binary(lp, jx))
4107         break;
4108       rhsval = ROW_MAT_VALUE(jb) - 1;
4109       if(fabs(rhsval) > lp->epsvalue)
4110         break;
4111     }
4112     if(jb < 0)
4113       setLink(sets, i);
4114   }
4115   if(countActiveLink(sets) == 0)
4116     goto Finish;
4117 
4118   /* A column dominates another binary variable column with the following criteria:
4119       1) The relative matrix non-zero entries are identical
4120       2) The relative objective coefficient is worse than the other;
4121          if the OF coefficients are identical, we can delete an arbitrary variable */
4122   n = 0;
4123   for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
4124     if(is_binary(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
4125       /* Make sure the column is member of at least one set */
4126       item = 0;
4127       for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4128           jb = presolve_nextrow(psdata, i, &item)) {
4129         jx = COL_MAT_ROWNR(jb);
4130         if(isActiveLink(sets, jx))
4131           break;
4132       }
4133 
4134       /* Add to list if set membership test is Ok */
4135       if(jb >= 0) {
4136         QS[n].int4.intval = i;
4137         item = 0;
4138         ii = presolve_nextrow(psdata, i, &item);
4139         QS[n].int4.intpar1 = COL_MAT_ROWNR(ii);
4140         ii = presolve_collength(psdata, i);
4141         QS[n].int4.intpar2 = ii;
4142         n++;
4143       }
4144     }
4145   if(n <= 1) {
4146     FREE(QS);
4147     return( status );
4148   }
4149   QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
4150 
4151   /* Let us start from the top of the list, going forward and looking
4152     for the longest possible dominated column */
4153   if(!allocREAL(lp, &colvalues, nrows + 1, TRUE) ||
4154      !allocREAL(lp, &colobj, n + 1, FALSE) ||
4155      !allocINT(lp, &coldel, n + 1, FALSE))
4156     goto Finish;
4157 
4158   for(ib = 0; ib < n; ib++) {
4159 
4160     /* Get column and check if it was previously eliminated */
4161     i = QS[ib].int4.intval;
4162     if(!isActiveLink(psdata->cols->varmap, i))
4163       continue;
4164 
4165     /* Load the non-zero column values */
4166     item = 0;
4167     for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4168         jb = presolve_nextrow(psdata, i, &item)) {
4169       jx = COL_MAT_ROWNR(jb);
4170       colvalues[jx] = COL_MAT_VALUE(jb);
4171     }
4172 
4173     /* Store data for current column */
4174     coldel[0] = 1;
4175     coldel[1] = i;
4176     colobj[1] = lp->orig_obj[i];
4177 
4178     /* Loop over all other columns to see if they have equal constraint coefficients */
4179     for(ie = ib+1; ie < n; ie++) {
4180 
4181       /* Check if this column was previously eliminated */
4182       ii = QS[ie].int4.intval;
4183       if(!isActiveLink(psdata->cols->varmap, ii))
4184         continue;
4185 
4186       /* Insist on identical column lengths (sort is decending in column lengths) */
4187       ii = QS[ib].int4.intpar2 - QS[ie].int4.intpar2;
4188       if(ii != 0)
4189         break;
4190 
4191       /* Also insist on identical starting positions */
4192       ii = QS[ib].int4.intpar1 - QS[ie].int4.intpar1;
4193       if(ii != 0)
4194         break;
4195 
4196       /* Get column and check if it was previously eliminated */
4197       ii = QS[ie].int4.intval;
4198 
4199 #ifdef Paranoia
4200       if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
4201          ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
4202         report(lp, SEVERE, "presolve_coldominance01: Invalid sorted column order\n");
4203 #endif
4204 
4205       /* Loop over every column member to confirm that the candidate is identical in every row;
4206          we also compute the minimal set order */
4207       rhsval = lp->infinite;
4208       item = 0;
4209       item2 = 0;
4210       for(jb = presolve_nextrow(psdata, ii, &item),
4211           jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4212           jb = presolve_nextrow(psdata, ii, &item),
4213           jj = presolve_nextrow(psdata, i, &item2)) {
4214         jx = COL_MAT_ROWNR(jb);
4215         if(jx != COL_MAT_ROWNR(jj))
4216           break;
4217         if(isActiveLink(sets, jx))
4218           SETMIN(rhsval, lp->orig_rhs[jx]);
4219       }
4220 
4221       /* "We have contact" */
4222       if(jb < 0) {
4223         coldel[++coldel[0]] = ii;
4224         colobj[coldel[0]] = lp->orig_obj[ii];
4225       }
4226     }
4227 
4228     /* Find the dominant columns, fix and delete the others */
4229     if(coldel[0] > 1) {
4230       qsortex(colobj+1, coldel[0], 0, sizeof(*colobj), FALSE, compareREAL, coldel+1, sizeof(*coldel));
4231       /* if(rhsval+lp->epsvalue < lp->infinite) { */
4232         jb = (NATURAL) (rhsval+lp->epsvalue);
4233         /* printf("%f / %d\n", rhsval, jb); */
4234         for(jb++; jb <= coldel[0]; jb++) {
4235           jx = coldel[jb];
4236           if(!presolve_colfix(psdata, jx, lp->orig_lowbo[nrows+jx], TRUE, &iVarFixed)) {
4237             status = presolve_setstatus(psdata, INFEASIBLE);
4238             goto Finish;
4239           }
4240           presolve_colremove(psdata, jx, TRUE);
4241         }
4242       /*} */
4243     }
4244 
4245     /* Clear the non-zero row values ahead of the next row candidate */
4246     if(ib + 1 < n) {
4247       ie = mat->col_end[i-1];
4248       ii = mat->col_end[i];
4249       for(; ie < ii; ie++)
4250         colvalues[COL_MAT_ROWNR(ie)] = 0;
4251     }
4252   }
4253 Finish:
4254   freeLink(&sets);
4255   FREE(QS);
4256   FREE(colvalues);
4257   FREE(coldel);
4258   FREE(colobj);
4259 
4260   (*nVarsFixed) += iVarFixed;
4261   (*nSum)       += iVarFixed;
4262 
4263   return( status );
4264 }
4265 
4266 #endif
4267 
4268 STATIC int presolve_aggregate(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum)
4269 /* This routine combines compatible or identical columns */
4270 {
4271   lprec    *lp = psdata->lp;
4272   MATrec   *mat = lp->matA;
4273   MYBOOL   first;
4274   int      i, ii, ib, ie, ix, n, jb, je, jx, jj, item, item2,
4275            *coldel = NULL, status = RUNNING, iVarFixed = 0;
4276   REAL     scale, *colvalues = NULL;
4277   UNIONTYPE QSORTrec *QScand = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QScand));
4278 
4279   /* Check if we were able to obtain working memory */
4280   if(QScand == NULL)
4281     return( status);
4282 
4283   /* Obtain the list of qualifying columns to be sorted */
4284   n = 0;
4285   for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
4286     if(!is_semicont(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
4287       QScand[n].int4.intval = i;
4288       item = 0;
4289       ii = presolve_nextrow(psdata, i, &item);
4290       QScand[n].int4.intpar1 = COL_MAT_ROWNR(ii);
4291       ii = presolve_collength(psdata, i);
4292       QScand[n].int4.intpar2 = ii;
4293       n++;
4294     }
4295   if(n <= 1) {
4296     FREE(QScand);
4297     return( status );
4298   }
4299   QS_execute(QScand, n, (findCompare_func *) compRedundant, NULL);
4300 
4301   /* Let us start from the top of the list, going forward and looking
4302     for the longest possible identical column */
4303   if(!allocREAL(lp, &colvalues, lp->rows + 1, TRUE) ||
4304      !allocINT(lp, &coldel, lp->columns + 1, FALSE))
4305     goto Finish;
4306 
4307   for(ib = 0; ib < n; ib++) {
4308 
4309     /* Get column and check if it was previously eliminated */
4310     i = QScand[ib].int4.intval;
4311     if(i < 0)
4312       continue;
4313 
4314     /* Load the non-zero column values of this active/reference column */
4315     item = 0;
4316     for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4317         jb = presolve_nextrow(psdata, i, &item)) {
4318       jx = COL_MAT_ROWNR(jb);
4319       colvalues[jx] = COL_MAT_VALUE(jb);
4320     }
4321 
4322     coldel[0] = 0;
4323     for(ie = ib+1; ie < n; ie++) {
4324 
4325       /* Insist on identical column lengths (sort is decending in column lengths) */
4326       ii = QScand[ib].int4.intpar2 - QScand[ie].int4.intpar2;
4327       if(ii != 0)
4328         break;
4329 
4330       /* Also insist on identical starting positions */
4331       ii = QScand[ib].int4.intpar1 - QScand[ie].int4.intpar1;
4332       if(ii != 0)
4333         break;
4334 
4335       /* Get column and check if it was previously eliminated */
4336       ii = QScand[ie].int4.intval;
4337       if(ii < 0)
4338         continue;
4339 
4340       /* Loop over every column member to confirm that the candidate is
4341         relatively identical in every position */
4342       first = TRUE;
4343       item = 0;
4344       item2 = 0;
4345       scale = 1;
4346       for(jb = presolve_nextrow(psdata, ii, &item),
4347           jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4348           jb = presolve_nextrow(psdata, ii, &item),
4349           jj = presolve_nextrow(psdata, i, &item2)) {
4350         jx = COL_MAT_ROWNR(jb);
4351         if(jx != COL_MAT_ROWNR(jj))
4352           break;
4353         if(first) {
4354           first = !first;
4355           scale = colvalues[jx] / COL_MAT_VALUE(jb);
4356         }
4357         else {
4358           if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue)
4359             break;
4360         }
4361       }
4362 
4363       /* "We have contact", store the column in the aggregation list */
4364       if(jb < 0) {
4365         coldel[++coldel[0]] = ii;
4366         QScand[ie].int4.intval = -ii;
4367       }
4368     }
4369 
4370     /* Sort the aggregation list if we have aggregation candidates */
4371     if(coldel[0] > 1) {
4372       REAL     of, ofelim, fixvalue;
4373       MYBOOL   isint;
4374       UNIONTYPE QSORTrec *QSagg = (UNIONTYPE QSORTrec *) calloc(coldel[0], sizeof(*QSagg));
4375 
4376       for(jb = 1; jb <= coldel[0]; jb++) {
4377         ii = jb - 1;
4378         QSagg[ii].pvoidint2.intval = coldel[jb];
4379         QSagg[ii].pvoidint2.ptr    = (void *) lp;
4380       }
4381       QS_execute(QSagg, coldel[0], (findCompare_func *) compAggregate, NULL);
4382 
4383       /* Process columns with identical OF coefficients */
4384       jb = 0;
4385       while((status == RUNNING) && (jb < coldel[0])) {
4386         ii = QSagg[jb].pvoidint2.intval;
4387         of = lp->orig_obj[ii];
4388         isint = is_int(lp, ii);
4389         je = jb + 1;
4390         while((status == RUNNING) && (je < coldel[0]) &&
4391               (fabs(lp->orig_obj[ix = QSagg[je].pvoidint2.intval] - of) < psdata->epsvalue)) {
4392            /* We now have two columns with equal OFs; the following cases are possible:
4393 
4394              1) The first column has Inf upper bound, which means that it can
4395                 "absorb" compatible columns, which are then fixed at the appropriate
4396                 bounds (or zero in case of free variables).
4397              2) The first column has a -Inf lower bound, and further columns are
4398                 Inf upper bounds, which means steps towards forming a free variable
4399                 can be made.
4400              3) The first column is a non-Inf upper bound, in which case the bounds
4401                 are summed into a helper variable and the variable simply deleted.
4402                 The deleted variables' value are allocated/distributed via a simple
4403                 linear programming routine at postsolve.
4404 
4405              In the current version of this code, we only handle case 1. */
4406           if(is_int(lp, ix) == isint) {
4407             ofelim = lp->orig_obj[ix];
4408             if(of == 0)
4409               scale = 1;
4410             else
4411               scale = ofelim / of;
4412 
4413             if(my_infinite(lp, lp->orig_upbo[lp->rows+ii])) { /* Case 1 (recipe.mps) */
4414               if(is_unbounded(lp, ix))
4415                 fixvalue = 0;
4416               else if(ofelim < 0)
4417                 fixvalue = lp->orig_upbo[lp->rows+ix];
4418               else
4419                 fixvalue = lp->orig_lowbo[lp->rows+ix];
4420               if(my_infinite(lp, fixvalue))
4421                 status = presolve_setstatus(psdata, UNBOUNDED);
4422               else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed))
4423                 status = presolve_setstatus(psdata, INFEASIBLE);
4424               else
4425                 presolve_colremove(psdata, ix, TRUE);
4426             }
4427 
4428             else if(my_infinite(lp, lp->orig_lowbo[lp->rows+ii])) { /* Case 2 */
4429               /* Do nothing */
4430             }
4431 
4432             else {                                            /* Case 3 */
4433 #if 0
4434               /* Do nothing */
4435 #else
4436               if(ofelim >= 0) {
4437                 fixvalue = lp->orig_lowbo[lp->rows+ix];
4438                 lp->orig_upbo[lp->rows+ii] += scale * (lp->orig_upbo[lp->rows+ix] - fixvalue);
4439               }
4440               else {
4441                 fixvalue = lp->orig_upbo[lp->rows+ix];
4442                 lp->orig_upbo[lp->rows+ii] -= scale * (fixvalue - lp->orig_lowbo[lp->rows+ix]);
4443               }
4444               if(my_infinite(lp, fixvalue))
4445                 status = presolve_setstatus(psdata, UNBOUNDED);
4446               else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed))
4447                 status = presolve_setstatus(psdata, INFEASIBLE);
4448               else
4449                 presolve_colremove(psdata, ix, TRUE);
4450 #ifdef xxParanoia
4451               if(presolve_rowlengthdebug(psdata) > 0)
4452                 report(lp, SEVERE, "presolve_aggregate: Invalid row count\n");
4453 #endif
4454               psdata->forceupdate = TRUE;
4455 #endif
4456             }
4457           }
4458           je++;
4459         }
4460         jb = je;
4461       }
4462       FREE(QSagg);
4463     }
4464 
4465     /* Clear the non-zero row values ahead of the next row candidate */
4466     if(ib + 1 < n) {
4467       ie = mat->col_end[i-1];
4468       ii = mat->col_end[i];
4469       for(; ie < ii; ie++)
4470         colvalues[COL_MAT_ROWNR(ie)] = 0;
4471     }
4472   }
4473 Finish:
4474   FREE(QScand);
4475   FREE(colvalues);
4476   FREE(coldel);
4477 
4478   (*nVarsFixed) += iVarFixed;
4479   (*nSum)       += iVarFixed;
4480 
4481   return( status );
4482 }
4483 
4484 STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum)
4485 {
4486   lprec    *lp = psdata->lp;
4487   MATrec   *mat = lp->matA;
4488   MYBOOL   chsign;
4489   int      i, ii, ib, ix, k, n, jb, je, jl, jjb, jje, jjl, jx, jjx, item, itemEQ,
4490            *nzidx = NULL, status = RUNNING, iObjChanged = 0, iCoeffChanged = 0, iConRemove = 0;
4491   REAL     test, ratio, value, valueEQ, *valptr;
4492   LLrec    *EQlist = NULL;
4493   UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows, sizeof(*QS));
4494 
4495   /* Check if we were able to obtain working memory */
4496   if((QS == NULL) || (psdata->rows->varmap->count == 0) || (psdata->EQmap->count == 0))
4497     return( status);
4498 
4499   /* Sort rows in 1) increasing order of start index, 2) decreasing length, and
4500      3) non-equalities (i.e. equalities last) */
4501   n = 0;
4502   for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
4503     k = presolve_rowlength(psdata, i);
4504     if(k >= 2) {
4505       item = 0;
4506       ii = presolve_nextcol(psdata, i, &item);
4507 #ifdef Paranoia
4508       if((ii < 0) || (item == 0)) {
4509         report(lp, SEVERE, "presolve_makesparser: Unexpected zero-length row %d\n", i);
4510         continue;
4511       }
4512 #endif
4513       QS[n].int4.intval  = my_chsign(is_constr_type(lp, i, EQ), i);
4514       QS[n].int4.intpar1 = ROW_MAT_COLNR(ii);
4515       QS[n].int4.intpar2 = k;
4516       n++;
4517     }
4518   }
4519   if(n <= 1) {
4520     FREE(QS);
4521     return( status );
4522   }
4523   QS_execute(QS, n, (findCompare_func *) compSparsity, NULL);
4524 
4525   /* Create associated sorted map of indeces to equality constraints;
4526      note that we need to have a unit offset for compatibility. */
4527   allocINT(lp, &nzidx, lp->columns + 1, FALSE);
4528   createLink(lp->rows, &EQlist, NULL);
4529   for(ib = 0; ib < n; ib++) {
4530     i = QS[ib].int4.intval;
4531     if(i < 0)
4532       appendLink(EQlist, ib + 1);
4533   }
4534 
4535   /* Loop over all equality masks */
4536   for(ix = firstActiveLink(EQlist); ix != 0; ) {
4537 
4538     /* Get row starting and ending positions of the mask */
4539     ii = abs(QS[ix-1].int4.intval);
4540     jjb = QS[ix-1].int4.intpar1;
4541     jje = presolve_lastcol(psdata, ii);
4542     jje = ROW_MAT_COLNR(jje);
4543     jjl = QS[ix-1].int4.intpar2;
4544 
4545     /* Scan the OF */
4546     i = 0;
4547     chsign = is_chsign(lp, i);
4548     test = ratio = 0.0;
4549     itemEQ = 0;
4550     nzidx[0] = 0;
4551     while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) && */
4552            (fabs(test-ratio) < psdata->epsvalue)) {
4553       valueEQ = ROW_MAT_VALUE(jjx);
4554       if(valueEQ == 0)
4555         continue;
4556       k = ROW_MAT_COLNR(jjx);
4557       value = lp->orig_obj[k];
4558       if(fabs(value) < psdata->epsvalue)
4559         break;
4560       if(ratio == 0.0) {
4561         test = ratio = value / valueEQ;
4562       }
4563       else
4564         test = value / valueEQ;
4565       /* Store nz index */
4566       nzidx[++nzidx[0]] = k;
4567     }
4568 
4569     /* We were successful if the equality was completely traversed; we will
4570       then zero-out the OF coefficients and update the constant term. */
4571     if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) {
4572       for(k = 1; k <= nzidx[0]; k++) {
4573         /* We should add recovery data for the zero'ed coefficient here */
4574         jx = nzidx[k];
4575         value = lp->orig_obj[jx];
4576         lp->orig_obj[jx] = 0.0;
4577         /* Update counts */
4578         value = my_chsign(chsign, value);
4579         if(value < 0) {
4580           psdata->rows->negcount[i]--;
4581           psdata->cols->negcount[jx]--;
4582         }
4583         else {
4584           psdata->rows->plucount[i]--;
4585           psdata->cols->plucount[jx]--;
4586         }
4587         iObjChanged++;
4588       }
4589       value = ratio * lp->orig_rhs[ii];
4590       presolve_adjustrhs(psdata, i, value, psdata->epsvalue);
4591     }
4592 
4593     /* Scan for compatible constraints that can be masked for sparsity elimination */
4594     for(ib = 1; ib < ix; ib++) {
4595 
4596       /* Get row starting and ending positions of the target constraint */
4597       i  = abs(QS[ib-1].int4.intval);
4598       jb = QS[ib-1].int4.intpar1;
4599       je = presolve_lastcol(psdata, i);
4600       je = ROW_MAT_COLNR(je);
4601       jl = QS[ib-1].int4.intpar2;
4602 
4603       /* Check if there is a window mismatch */
4604       if((jjb < jb) || (jje > je) || (jjl > jl))
4605         goto NextEQ;
4606 
4607       /* We have a window match; now check if there is a (scalar) member-by-member
4608         match as well.  We approach this in the following manner:
4609           1) Get first (or next) member of active equality
4610           2) Loop to matching member in the target constraint, but abandon if no match
4611           3) Set ratio if this is the first match, otherwise compare ratio and abandon
4612              on mismatch
4613           4) Go to 1) of there are more elements in the active equality
4614           5) Proceed to do sparsity elimination if we were successful. */
4615       chsign = is_chsign(lp, i);
4616       test = ratio = 0.0;
4617       itemEQ = 0;
4618       item = 0;
4619       nzidx[0] = 0;
4620       while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) &&*/
4621              (fabs(test-ratio) < psdata->epsvalue)) {
4622         valueEQ = ROW_MAT_VALUE(jjx);
4623         if(valueEQ == 0)
4624           continue;
4625         jx = 0;
4626         jjx = ROW_MAT_COLNR(jjx);
4627         for(k = presolve_nextcol(psdata, i, &item);
4628             (jx < jjx) && (item > 0);
4629             k = presolve_nextcol(psdata, i, &item)) {
4630           jx = ROW_MAT_COLNR(k);
4631           /* Do we have a column index match? */
4632           if(jx == jjx) {
4633             value = ROW_MAT_VALUE(k);
4634             /* Abandon if we have a zero value */
4635             if(value == 0)
4636               goto NextEQ;
4637             if(ratio == 0.0) {
4638               test = ratio = value / valueEQ;
4639             }
4640             else
4641               test = value / valueEQ;
4642            /* Store nz index */
4643             nzidx[++nzidx[0]] = k;
4644             break;
4645           }
4646           /* Give up matching if there is overshooting */
4647           else if(jx > jjx)
4648             goto NextEQ;
4649         }
4650       }
4651 
4652       /* We were successful if the equality was completely traversed */
4653       if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) {
4654 
4655         /* Check if we have found parametrically indentical constraints */
4656         if(presolve_rowlength(psdata, i) == presolve_rowlength(psdata,ii)) {
4657 
4658           value = lp->orig_rhs[i];
4659           valueEQ = lp->orig_rhs[ii];
4660 
4661           /* Are they both equalities? */
4662           if(is_constr_type(lp, i, EQ)) {
4663             /* Determine applicable ratio for the RHS */
4664             if(fabs(valueEQ) < psdata->epsvalue) {
4665               if(fabs(value) < psdata->epsvalue)
4666                 test = ratio;
4667               else
4668                 test = lp->infinite;
4669             }
4670             else
4671               test = value / valueEQ;
4672             /* Check for infeasibility */
4673             if(fabs(test-ratio) > psdata->epsvalue) {
4674               report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal constraints %d and %d\n",
4675                                  i, ii);
4676               status = presolve_setstatus(psdata, INFEASIBLE);
4677               goto Finish;
4678             }
4679             /* Otherwise we can delete a redundant constraint */
4680             else {
4681               removeLink(EQlist, i);
4682               presolve_rowremove(psdata, i, TRUE);
4683               MEMCOPY(&QS[ib-1], &QS[ib], n-ib);
4684               n--;
4685               iConRemove++;
4686             }
4687           }
4688           /* ... if not, then delete the inequality, since the equality dominates */
4689           else {
4690             /* First verify feasibility of the RHS */
4691             if((value+psdata->epsvalue < valueEQ) ||
4692                (value-get_rh_range(lp, i)-psdata->epsvalue > valueEQ)) {
4693               report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal RHS values for %d and %d\n",
4694                                  i, ii);
4695               status = presolve_setstatus(psdata, INFEASIBLE);
4696               goto Finish;
4697             }
4698             presolve_rowremove(psdata, i, TRUE);
4699             MEMCOPY(&QS[ib-1], &QS[ib], n-ib);
4700             n--;
4701             iConRemove++;
4702           }
4703         }
4704 
4705         /* Otherwise zero-out the target constraint coefficients and update the RHS */
4706         else {
4707           for(k = 1; k <= nzidx[0]; k++) {
4708             /* We should add recovery data for the zero'ed coefficient here */
4709             jjx = nzidx[k];
4710             jx = ROW_MAT_COLNR(jjx);
4711             valptr = &ROW_MAT_VALUE(jjx);
4712             value  = *valptr;
4713             *valptr = 0.0;
4714             /* Update counts */
4715             value = my_chsign(chsign, value);
4716             if(value < 0) {
4717               psdata->rows->negcount[i]--;
4718               psdata->cols->negcount[jx]--;
4719             }
4720             else {
4721               psdata->rows->plucount[i]--;
4722               psdata->cols->plucount[jx]--;
4723             }
4724             iCoeffChanged++;
4725           }
4726           value = ratio * lp->orig_rhs[ii];
4727           presolve_adjustrhs(psdata, i, value, psdata->epsvalue);
4728         }
4729       }
4730 
4731     }
4732     /* Get next equality index */
4733 NextEQ:
4734     ix = nextActiveLink(EQlist, ix);
4735   }
4736 
4737 Finish:
4738   FREE(QS);
4739   freeLink(&EQlist);
4740   FREE(nzidx);
4741 
4742   /* Let us condense the matrix if we modified the constraint matrix */
4743   if(iCoeffChanged > 0) {
4744     mat->row_end_valid = FALSE;
4745     mat_zerocompact(mat);
4746     presolve_validate(psdata, TRUE);
4747 #ifdef PresolveForceUpdateMax
4748     mat_computemax(mat /* , FALSE */);
4749 #endif
4750     psdata->forceupdate = TRUE;
4751   }
4752 
4753   (*nConRemove)    += iConRemove;
4754   (*nCoeffChanged) += iCoeffChanged + iObjChanged;
4755   (*nSum)          += iCoeffChanged + iObjChanged + iConRemove;
4756 
4757   return( status );
4758 }
4759 
4760 STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSOS, int *nSum)
4761 {
4762   lprec    *lp = psdata->lp;
4763   MYBOOL   candelete, SOS_GUBactive = FALSE;
4764   int      iCoeffChanged = 0, iConRemove = 0, iSOS = 0,
4765            i,ix,iix, j,jx,jjx, status = RUNNING;
4766   REAL     Value1;
4767   MATrec   *mat = lp->matA;
4768 
4769   for(i = lastActiveLink(psdata->rows->varmap); i > 0; ) {
4770     candelete = FALSE;
4771     Value1 = get_rh(lp, i);
4772     jx = get_constr_type(lp, i);
4773     if((Value1 == 1) && (presolve_rowlength(psdata, i) >= MIN_SOS1LENGTH) &&
4774        ((SOS_GUBactive && (jx != GE)) || (!SOS_GUBactive && (jx == LE)))) {
4775       jjx = mat->row_end[i-1];
4776       iix = mat->row_end[i];
4777       for(; jjx < iix; jjx++) {
4778         j = ROW_MAT_COLNR(jjx);
4779         if(!isActiveLink(psdata->cols->varmap, j))
4780           continue;
4781         if(!is_binary(lp, j) || (ROW_MAT_VALUE(jjx) != 1))
4782           break;
4783       }
4784       if(jjx >= iix) {
4785         char SOSname[16];
4786 
4787         /* Define a new SOS instance */
4788         ix = SOS_count(lp) + 1;
4789         sprintf(SOSname, "SOS_%d", ix);
4790         ix = add_SOS(lp, SOSname, 1, ix, 0, NULL, NULL);
4791         if(jx == EQ)
4792           SOS_set_GUB(lp->SOS, ix, TRUE);
4793         Value1 = 0;
4794         jjx = mat->row_end[i-1];
4795         for(; jjx < iix; jjx++) {
4796           j = ROW_MAT_COLNR(jjx);
4797           if(!isActiveLink(psdata->cols->varmap, j))
4798             continue;
4799           Value1 += 1;
4800           append_SOSrec(lp->SOS->sos_list[ix-1], 1, &j, &Value1);
4801         }
4802         candelete = TRUE;
4803         iSOS++;
4804       }
4805     }
4806 
4807     /* Get next row and do the deletion of the previous, if indicated */
4808     ix = i;
4809     i = prevActiveLink(psdata->rows->varmap, i);
4810     if(candelete) {
4811       presolve_rowremove(psdata, ix, TRUE);
4812       iConRemove++;
4813     }
4814   }
4815   if(iSOS)
4816     report(lp, DETAILED, "presolve_SOS1: Converted %5d constraints to SOS1.\n", iSOS);
4817   clean_SOSgroup(lp->SOS, (MYBOOL) (iSOS > 0));
4818 
4819   (*nCoeffChanged) += iCoeffChanged;
4820   (*nConRemove)    += iConRemove;
4821   (*nSOS)          += iSOS;
4822   (*nSum)          += iCoeffChanged+iConRemove+iSOS;
4823 
4824   return( status );
4825 }
4826 
4827 STATIC int presolve_boundconflict(presolverec *psdata, int baserowno, int colno)
4828 {
4829   REAL   Value1, Value2;
4830   lprec  *lp = psdata->lp;
4831   MATrec *mat = lp->matA;
4832   int    ix, item = 0,
4833          status = RUNNING;
4834 
4835   if(baserowno <= 0) do {
4836     ix = presolve_nextrow(psdata, colno, &item);
4837     if(ix < 0)
4838       return( status );
4839     baserowno = COL_MAT_ROWNR(ix);
4840   } while(presolve_rowlength(psdata, baserowno) != 1);
4841   Value1 = get_rh_upper(lp, baserowno),
4842   Value2 = get_rh_lower(lp, baserowno);
4843 
4844   if(presolve_singletonbounds(psdata, baserowno, colno, &Value2, &Value1, NULL)) {
4845     int iix;
4846     item = 0;
4847     for(ix = presolve_nextrow(psdata, colno, &item);
4848         ix >= 0; ix = presolve_nextrow(psdata, colno, &item)) {
4849       iix = COL_MAT_ROWNR(ix);
4850       if((iix != baserowno) &&
4851          (presolve_rowlength(psdata, iix) == 1) &&
4852          !presolve_altsingletonvalid(psdata, iix, colno, Value2, Value1)) {
4853         status = presolve_setstatus(psdata, INFEASIBLE);
4854         break;
4855       }
4856     }
4857   }
4858   else
4859     status = presolve_setstatus(psdata, INFEASIBLE);
4860   return( status );
4861 }
4862 
4863 STATIC int presolve_columns(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum)
4864 {
4865   lprec    *lp = psdata->lp;
4866   MYBOOL   candelete, isOFNZ, unbounded,
4867            probefix = is_presolve(lp, PRESOLVE_PROBEFIX),
4868 #if 0
4869            probereduce = is_presolve(lp, PRESOLVE_PROBEREDUCE),
4870 #endif
4871            colfixdual = is_presolve(lp, PRESOLVE_COLFIXDUAL);
4872   int      iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0,
4873            status = RUNNING, ix, j, countNZ, item;
4874   REAL     Value1;
4875 
4876   for(j = firstActiveLink(psdata->cols->varmap); (j != 0) && (status == RUNNING); ) {
4877 
4878     /* Don't presolve members SOS'es */
4879     if(SOS_is_member(lp->SOS, 0, j)) {
4880       j = nextActiveLink(psdata->cols->varmap, j);
4881       continue;
4882     }
4883 
4884     /* Initialize */
4885     countNZ = presolve_collength(psdata, j);
4886     isOFNZ  = isnz_origobj(lp, j);
4887     Value1  = get_lowbo(lp, j);
4888     unbounded = is_unbounded(lp, j);
4889 
4890     /* Clear unnecessary semicont-definitions */
4891     if((lp->sc_vars > 0) && (Value1 == 0) && is_semicont(lp, j))
4892       set_semicont(lp, j, FALSE);
4893 
4894     candelete = FALSE;
4895     item = 0;
4896     ix = lp->rows + j;
4897 
4898     /* Check if the variable is unused */
4899     if((countNZ == 0) && !isOFNZ) {
4900       if(Value1 != 0)
4901         report(lp, DETAILED, "presolve_columns: Eliminated unused variable %s\n",
4902                               get_col_name(lp,j));
4903       candelete = TRUE;
4904     }
4905 
4906     /* Check if the variable has a cost, but is not limited by constraints */
4907     else if((countNZ == 0) && isOFNZ) {
4908       if(lp->orig_obj[j] < 0)
4909         Value1 = get_upbo(lp, j);
4910       if(fabs(Value1) >= lp->infinite) {
4911         report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n",
4912                               get_col_name(lp,j));
4913         status = presolve_setstatus(psdata, UNBOUNDED);
4914       }
4915       else {
4916         /* Fix the value at its best bound */
4917         report(lp, DETAILED, "presolve_columns: Eliminated trivial variable %s fixed at %g\n",
4918                               get_col_name(lp,j), Value1);
4919         candelete = TRUE;
4920       }
4921     }
4922 
4923     /* Check if the variable can be eliminated because it is fixed */
4924     else if(isOrigFixed(lp, ix)) {
4925       if(countNZ > 0) {
4926         status = presolve_boundconflict(psdata, -1, j);
4927         if(status != RUNNING)
4928           break;
4929       }
4930       report(lp, DETAILED, "presolve_columns: Eliminated variable %s fixed at %g\n",
4931                             get_col_name(lp,j), Value1);
4932       candelete = TRUE;
4933     }
4934 
4935 #if 0
4936     /* Merge OF-constraint column doubleton in equality constraint (if it has
4937       not been captured by the singleton free variable rule above) */
4938     else if((countNZ == 1) && isOFNZ &&
4939              ((i = presolve_nextrow(psdata, j, &item)) >= 0) &&
4940              is_constr_type(lp, i = COL_MAT_ROWNR(i), EQ)) {
4941       MATrec *mat = lp->matA;
4942 
4943       /* Merge the constraint into the OF */
4944       Value1 = lp->orig_obj[j] / get_mat(lp, i, j);
4945       for(jx = mat->row_end[i-1]; jx < mat->row_end[i]; jx++) {
4946         jjx = ROW_MAT_COLNR(jx);
4947         lp->orig_obj[jjx] -= Value1 * ROW_MAT_VALUE(jx);
4948       }
4949       Value2 = lp->orig_rhs[i];
4950       presolve_adjustrhs(psdata, 0, Value1 * Value2, 0.0);
4951 
4952       /* Verify feasibility */
4953       Value2 /= get_mat(lp, i, j);
4954       if((Value2 < get_lowbo(lp, j)) || (Value2 > get_upbo(lp, j))) {
4955         status = presolve_setstatus(psdata, INFEASIBLE);
4956         break;
4957       }
4958 
4959       /* Do column (and flag row) deletion */
4960       presolve_rowremove(psdata, i, TRUE);
4961       psdata->forceupdate = TRUE;
4962       iConRemove++;
4963       candelete = TRUE;
4964     }
4965 #endif
4966     /* Look for opportunity to fix column based on the dual */
4967     else if(colfixdual && presolve_colfixdual(psdata, j, &Value1, &status)) {
4968       if(my_infinite(lp, Value1)) {
4969         report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n",
4970                               get_col_name(lp,j));
4971         status = presolve_setstatus(psdata, UNBOUNDED);
4972       }
4973       else {
4974         /* Fix the value at its best bound */
4975         report(lp, DETAILED, "presolve_columns: Eliminated dual-zero variable %s fixed at %g\n",
4976                               get_col_name(lp,j), Value1);
4977         candelete = TRUE;
4978       }
4979     }
4980 
4981     /* Do probing of binary variables to see if we can fix them */
4982     else if(probefix && is_binary(lp, j) &&
4983             presolve_probefix01(psdata, j, &Value1)) {
4984       report(lp, DETAILED, "presolve_columns: Fixed binary variable %s at %g\n",
4985                             get_col_name(lp,j), Value1);
4986       candelete = TRUE;
4987     }
4988 #if 0
4989     /* Do probing of binary variables to see if we can tighten their coefficients */
4990     else if(probereduce && is_binary(lp, j) &&
4991             (ix = presolve_probetighten01(psdata, j) > 0)) {
4992       report(lp, DETAILED, "presolve_columns: Tightened coefficients for binary variable %s in %d rows\n",
4993                             get_col_name(lp,j), ix);
4994       iCoeffChanged += ix;
4995       psdata->forceupdate = TRUE;
4996     }
4997 #endif
4998 
4999     /* Perform fixing and deletion, if indicated */
5000     if(candelete) {
5001 
5002       /* If we have a SOS1 member variable fixed at a non-zero value, then we
5003         must fix the other member variables at zero and delete the SOS(es) */
5004       if((Value1 != 0) && SOS_is_member(lp->SOS, 0, j)) {
5005         ix = iVarFixed;
5006         if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed))
5007           status = presolve_setstatus(psdata, INFEASIBLE);
5008         if(iVarFixed > ix)
5009           psdata->forceupdate = TRUE;
5010         break;
5011       }
5012       else {
5013         if(!presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) {
5014           status = presolve_setstatus(psdata, INFEASIBLE);
5015           break;
5016         }
5017         j = presolve_colremove(psdata, j, TRUE);
5018       }
5019     }
5020     else
5021       j = nextActiveLink(psdata->cols->varmap, j);
5022   }
5023 
5024   /* Remove any "hanging" empty row and columns */
5025   if(status == RUNNING)
5026     status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5027 
5028   (*nCoeffChanged) += iCoeffChanged;
5029   (*nConRemove)    += iConRemove;
5030   (*nVarFixed)     += iVarFixed;
5031   (*nBoundTighten) += iBoundTighten;
5032   (*nSum)          += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten;
5033 
5034   return( status );
5035 }
5036 
5037 STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum)
5038 {
5039   lprec    *lp = psdata->lp;
5040   MYBOOL   isOFNZ, unbounded,
5041            impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE),
5042            impliedslack = is_presolve(lp, PRESOLVE_IMPLIEDSLK);
5043   int      iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0,
5044            status = RUNNING, i, ix, j, countNZ;
5045   REAL     coeff_bl, coeff_bu;
5046   MATrec   *mat = lp->matA;
5047 
5048   if(impliedfree || impliedslack)
5049   for(j = firstActiveLink(psdata->cols->varmap); j != 0; ) {
5050 
5051     /* Check and initialize */
5052     if((presolve_collength(psdata, j) != 1) ||
5053        is_int(lp, j) || is_semicont(lp, j) ||
5054        !presolve_candeletevar(psdata, j)) {
5055       j = nextActiveLink(psdata->cols->varmap, j);
5056       continue;
5057     }
5058     ix = 0;
5059     i = COL_MAT_ROWNR(presolve_nextrow(psdata, j, &ix));
5060     isOFNZ  = isnz_origobj(lp, j);
5061     countNZ = presolve_rowlength(psdata, i);
5062     coeff_bu = get_upbo(lp, j);
5063     coeff_bl = get_lowbo(lp, j);
5064     unbounded = my_infinite(lp, coeff_bl) && my_infinite(lp, coeff_bu);
5065     ix = lp->rows + j;
5066 
5067     /* Eliminate singleton free variable and its associated constraint */
5068     if(impliedfree && unbounded &&
5069        presolve_impliedcolfix(psdata, i, j, TRUE)) {
5070       report(lp, DETAILED, "presolve_freeandslacks: Eliminated free variable %s and row %s\n",
5071                             get_col_name(lp, j), get_row_name(lp, i));
5072       presolve_rowremove(psdata, i, TRUE);
5073       iConRemove++;
5074       j = presolve_colremove(psdata, j, TRUE);
5075       iVarFixed++;
5076     }
5077 
5078     /* Check for implied slack variable in equality constraint */
5079     else if(impliedslack &&
5080              (countNZ > 1) &&
5081              is_constr_type(lp, i, EQ) &&
5082              presolve_impliedcolfix(psdata, i, j, FALSE)) {
5083       report(lp, DETAILED, "presolve_freeandslacks: Eliminated implied slack variable %s via row %s\n",
5084                             get_col_name(lp, j), get_row_name(lp, i));
5085       psdata->forceupdate = TRUE;
5086       j = presolve_colremove(psdata, j, TRUE);
5087       iVarFixed++;
5088     }
5089 
5090     /* Check for implied (generalized) slack variable in inequality constraint */
5091     else if(impliedslack && !isOFNZ &&
5092              my_infinite(lp, coeff_bu) &&                 /* Consider removing this test */
5093 #if 0 /* Force zero-bounded implicit slack  */
5094              (coeff_bl == 0)) &&
5095 #else
5096              !my_infinite(lp, coeff_bl) &&
5097 #endif
5098              (countNZ > 1) &&
5099              !is_constr_type(lp, i, EQ))  {
5100       REAL *target,
5101             ValueA   = COL_MAT_VALUE(presolve_lastrow(psdata, j));
5102 #if 0
5103       coeff_bu = get_rh_upper(lp, i);
5104       coeff_bl = get_rh_lower(lp, i);
5105       if(!presolve_singletonbounds(psdata, i, j, &coeff_bl, &coeff_bu, &ValueA)) {
5106         status = presolve_setstatus(psdata, INFEASIBLE);
5107         break;
5108       }
5109 #endif
5110       if((coeff_bl != 0) && !my_infinite(lp, coeff_bl) && !my_infinite(lp, coeff_bu))
5111         coeff_bu -= coeff_bl;
5112 
5113       /* If the coefficient is negative, reduce the lower bound / increase range */
5114       if(ValueA > 0) {
5115         target = &lp->orig_upbo[i];
5116         if(!my_infinite(lp, *target)) {
5117           if(my_infinite(lp, coeff_bu)) {
5118             *target = lp->infinite;
5119             psdata->forceupdate = TRUE;
5120           }
5121           else {
5122             *target += ValueA * coeff_bu;
5123             *target = presolve_roundrhs(lp, *target, FALSE);
5124           }
5125         }
5126       }
5127       /* Otherwise see if the upper bound should be changed */
5128       else {
5129         target = &lp->orig_rhs[i];
5130         if(my_infinite(lp, coeff_bu) || my_infinite(lp, *target)) {
5131           /* Do we suddenly find that the constraint becomes redundant? (e226.mps) */
5132           if(my_infinite(lp, lp->orig_upbo[i])) {
5133             presolve_rowremove(psdata, i, TRUE);
5134             iConRemove++;
5135           }
5136           /* Or does the upper bound of a ranged constraint become Inf? */
5137           else {
5138             *target -= lp->orig_upbo[i];
5139             *target = -(*target);
5140             mat_multrow(mat, i, -1);
5141             lp->orig_upbo[i] = lp->infinite;
5142             psdata->forceupdate = TRUE;
5143           }
5144         }
5145         else {
5146           *target -= ValueA * coeff_bu;
5147           *target = presolve_roundrhs(lp, *target, FALSE);
5148         }
5149       }
5150       presolve_colfix(psdata, j, coeff_bl, TRUE, &iVarFixed);
5151       report(lp, DETAILED, "presolve_freeandslacks: Eliminated duplicate slack variable %s via row %s\n",
5152                             get_col_name(lp, j), get_row_name(lp, i));
5153       j = presolve_colremove(psdata, j, TRUE);
5154     }
5155 
5156     /* Go to next column */
5157     else
5158       j = nextActiveLink(psdata->cols->varmap, j);
5159   }
5160 
5161   (*nCoeffChanged) += iCoeffChanged;
5162   (*nConRemove)    += iConRemove;
5163   (*nVarFixed)     += iVarFixed;
5164   (*nSum)          += iCoeffChanged+iConRemove+iVarFixed;
5165 
5166   return( status );
5167 }
5168 
5169 STATIC int presolve_preparerows(presolverec *psdata, int *nBoundTighten, int *nSum)
5170 {
5171   lprec    *lp = psdata->lp;
5172   MYBOOL   impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE),
5173            tightenbounds  = is_presolve(lp, PRESOLVE_BOUNDS);
5174   int      iRangeTighten = 0, iBoundTighten = 0, status = RUNNING, i, j;
5175   REAL     losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue;
5176   MATrec   *mat = lp->matA;
5177 
5178   for(i = lastActiveLink(psdata->rows->varmap); i > 0; i = prevActiveLink(psdata->rows->varmap, i)) {
5179 
5180    /* First identify any full row infeasibilities */
5181     j = presolve_rowlengthex(psdata, i);
5182 #ifdef Paranoia
5183     if(!presolve_testrow(psdata, nextActiveLink(psdata->rows->varmap, i))) {
5184 #else
5185     if((j > 1) && !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) {
5186 #endif
5187       status = presolve_setstatus(psdata, INFEASIBLE);
5188       break;
5189     }
5190 
5191     /* Do bound (LHS) or constraint range (RHS) tightening if we will later identify
5192       implied free variables (tends to produce degeneracy otherwise) */
5193     if(impliedfree && (j > 1) && mat_validate(mat)){
5194 
5195       /* Look for opportunity to tighten constraint bounds (and check for feasibility again) */
5196       presolve_range(lp, i, psdata->rows, &losum, &upsum);
5197       lorhs = get_rh_lower(lp, i);
5198       uprhs = get_rh_upper(lp, i);
5199       if((losum > MIN(upsum, uprhs)+epsvalue) ||
5200          (upsum < MAX(losum, lorhs)-epsvalue)) {
5201         report(lp, NORMAL, "presolve_preparerows: Variable bound / constraint value infeasibility in row %s.\n",
5202                            get_row_name(lp, i));
5203         status = presolve_setstatus(psdata, INFEASIBLE);
5204         break;
5205       }
5206 
5207       if(losum > lorhs+epsvalue) {
5208         set_rh_lower(lp, i, presolve_roundrhs(lp, losum, TRUE));
5209         iRangeTighten++;
5210       }
5211       if(upsum < uprhs-epsvalue) {
5212         set_rh_upper(lp, i, presolve_roundrhs(lp, upsum, FALSE));
5213         iRangeTighten++;
5214       }
5215     }
5216 
5217     /* Seek to tighten bounds on individual variables */
5218     if(tightenbounds && mat_validate(mat)) {
5219 #if 1
5220       if(j > 1)
5221         status = presolve_rowtighten(psdata, i, &iBoundTighten, FALSE);
5222 #else
5223       if((MIP_count(lp) > 0) && (j > 1))
5224         status = presolve_rowtighten(psdata, i, &iBoundTighten, TRUE);
5225 #endif
5226     }
5227 
5228     /* Look for opportunity to convert ranged constraint to equality-type */
5229     if(!is_constr_type(lp, i, EQ) && (get_rh_range(lp, i) < epsvalue)) {
5230       presolve_setEQ(psdata, i);
5231       iRangeTighten++;
5232     }
5233   }
5234 
5235   psdata->forceupdate |= (MYBOOL) (iBoundTighten > 0);
5236   (*nBoundTighten) += iBoundTighten+iRangeTighten;
5237   (*nSum)          += iBoundTighten+iRangeTighten;
5238 
5239   return( status );
5240 }
5241 
5242 STATIC int presolve_rows(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum)
5243 {
5244   lprec    *lp = psdata->lp;
5245   MYBOOL   candelete;
5246   int      iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0,
5247            status = RUNNING, i,ix, j,jx, item;
5248   REAL     Value1, Value2, losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue;
5249   MATrec   *mat = lp->matA;
5250 
5251   for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) {
5252 
5253     candelete = FALSE;
5254 
5255    /* First identify any full row infeasibilities
5256       Note: Handle singletons below to ensure that conflicting multiple singleton
5257             rows with this variable do not provoke notice of infeasibility */
5258     j = presolve_rowlengthex(psdata, i);
5259     if((j > 1) &&
5260        !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) {
5261       status = presolve_setstatus(psdata, INFEASIBLE);
5262       break;
5263     }
5264     presolve_range(lp, i, psdata->rows, &losum, &upsum);
5265     lorhs = get_rh_lower(lp, i);
5266     uprhs = get_rh_upper(lp, i);
5267 #ifdef Paranoia
5268     if((losum>uprhs+epsvalue) || (upsum<lorhs-epsvalue)) {
5269       status = presolve_setstatus(psdata, INFEASIBLE);
5270       break;
5271     }
5272 #endif
5273 
5274     /* Delete empty rows */
5275     if(j == 0)
5276       candelete = TRUE;
5277     else
5278 
5279     /* Convert non-fixed row singletons to bounds */
5280 #if 0  /* Version that deletes bound-fixed columns in presolve_columns() */
5281     if((j == 1) && (upsum-losum >= -epsvalue)) {
5282 #else  /* Version that deletes bound-fixed columns here */
5283     if((j == 1) && (uprhs-lorhs >= -epsvalue)) {
5284 #endif
5285       item = 0;
5286       jx = presolve_nextcol(psdata, i, &item);
5287       j = ROW_MAT_COLNR(jx);
5288 
5289       /* Make sure we don't have conflicting other singleton rows with this variable */
5290       Value1 = lp->infinite;
5291       Value2 = -Value1;
5292       if(presolve_collength(psdata, j) > 1)
5293         status = presolve_boundconflict(psdata, i, j);
5294       else if(is_constr_type(lp, i, EQ)) {
5295         Value2 = ROW_MAT_VALUE(jx);
5296         Value1 = lp->orig_rhs[i] / Value2;
5297         if(Value2 < 0)
5298           swapREAL(&losum, &upsum);
5299         if((Value1 < losum / my_if(my_infinite(lp, losum), my_sign(Value2), Value2) - epsvalue) ||
5300            (Value1 > upsum / my_if(my_infinite(lp, upsum), my_sign(Value2), Value2) + epsvalue))
5301           status = presolve_setstatus(psdata, INFEASIBLE);
5302         Value2 = Value1;
5303       }
5304 
5305       /* Proceed to fix and remove variable (if it is not a SOS member) */
5306       if(status == RUNNING) {
5307         if((fabs(Value2-Value1) < epsvalue) && (fabs(Value2) > epsvalue)) {
5308           MYBOOL isSOS     = (MYBOOL) (SOS_is_member(lp->SOS, 0, j) != FALSE),
5309                  deleteSOS = isSOS && presolve_candeletevar(psdata, j);
5310           if((Value1 != 0) && deleteSOS) {
5311             if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed))
5312               status = presolve_setstatus(psdata, INFEASIBLE);
5313               psdata->forceupdate = TRUE;
5314           }
5315           else {
5316             if(!presolve_colfix(psdata, j, Value1, (MYBOOL) !isSOS, NULL))
5317               status = presolve_setstatus(psdata, INFEASIBLE);
5318             else if(isSOS && !deleteSOS)
5319               iBoundTighten++;
5320             else {
5321               presolve_colremove(psdata, j, TRUE);
5322               iVarFixed++;
5323             }
5324           }
5325         }
5326         else
5327           status = presolve_colsingleton(psdata, i, j, &iBoundTighten);
5328       }
5329       if(status == INFEASIBLE) {
5330         break;
5331       }
5332       if(psdata->forceupdate != AUTOMATIC) {
5333         /* Store dual recovery information and code for deletion */
5334         presolve_storeDualUndo(psdata, i, j);
5335         candelete = TRUE;
5336       }
5337     }
5338 
5339     /* Delete non-empty rows and variables that are completely determined at zero */
5340     else if((j > 0)                            /* Only examine non-empty rows, */
5341        && (fabs(lp->orig_rhs[i]) < epsvalue)   /* .. and the current RHS is zero, */
5342        && ((psdata->rows->plucount[i] == 0) ||
5343            (psdata->rows->negcount[i] == 0))   /* .. and the parameter signs are all equal, */
5344        && (psdata->rows->pluneg[i] == 0)       /* .. and no (quasi) free variables, */
5345        && (is_constr_type(lp, i, EQ)
5346 #ifdef FindImpliedEqualities
5347            || (fabs(lorhs-upsum) < epsvalue)   /* Convert to equalities */
5348            || (fabs(uprhs-losum) < epsvalue)   /* Convert to equalities */
5349 #endif
5350           )
5351           ) {
5352 
5353       /* Delete the columns we can delete */
5354       status = presolve_rowfixzero(psdata, i, &iVarFixed);
5355 
5356       /* Then delete the row, which is redundant */
5357       if(status == RUNNING)
5358         candelete = TRUE;
5359     }
5360 
5361 
5362     /* Check if we have a constraint made redundant through bounds on individual
5363        variables; such constraints are often referred to as "forcing constraints" */
5364     else if((losum >= lorhs-epsvalue) &&
5365              (upsum <= uprhs+epsvalue)) {
5366 
5367       /* Check if we can also fix all the variables */
5368       if(fabs(losum-upsum) < epsvalue) {
5369         item = 0;
5370         jx = presolve_nextcol(psdata, i, &item);
5371         while((status == RUNNING) && (jx >= 0)) {
5372           j = ROW_MAT_COLNR(jx);
5373           Value1 = get_lowbo(lp, j);
5374           if(presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) {
5375             presolve_colremove(psdata, j, TRUE);
5376             iVarFixed++;
5377             jx = presolve_nextcol(psdata, i, &item);
5378           }
5379           else
5380             status = presolve_setstatus(psdata, INFEASIBLE);
5381         }
5382       }
5383       candelete = TRUE;
5384     }
5385 
5386     /* Get next row and do the deletion of the previous, if indicated */
5387     ix = i;
5388     i = prevActiveLink(psdata->rows->varmap, i);
5389     if(candelete) {
5390       presolve_rowremove(psdata, ix, TRUE);
5391       iConRemove++;
5392     }
5393   }
5394 
5395   /* Remove any "hanging" empty row and columns */
5396   if(status == RUNNING)
5397     status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5398 
5399   (*nCoeffChanged) += iCoeffChanged;
5400   (*nConRemove)    += iConRemove;
5401   (*nVarFixed)     += iVarFixed;
5402   (*nBoundTighten) += iBoundTighten;
5403   (*nSum)          += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten;
5404 
5405   return( status );
5406 }
5407 
5408 /* Top level presolve routine */
5409 STATIC int presolve(lprec *lp)
5410 {
5411   int    status = RUNNING,
5412          i, j = 0, jx = 0, jjx = 0, k, oSum,
5413          iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0, iSOS = 0, iSum = 0,
5414          nCoeffChanged = 0, nConRemove = 0, nVarFixed = 0, nBoundTighten = 0, nSOS = 0, nSum = 0;
5415   REAL   Value1, Value2, initrhs0 = lp->orig_rhs[0];
5416   presolverec *psdata = NULL;
5417   MATrec *mat = lp->matA;
5418 
5419 #if 0
5420   lp->do_presolve     = PRESOLVE_ROWS;
5421   report(lp, IMPORTANT, "presolve: Debug override of presolve setting to %d\n", lp->do_presolve);
5422 #endif
5423 
5424  /* Lock the variable mapping arrays and counts ahead of any row/column
5425     deletion or creation in the course of presolve, solvelp or postsolve */
5426   if(!lp->varmap_locked)
5427     varmap_lock(lp);
5428 
5429  /* Check if we have already done presolve */
5430   mat_validate(mat);
5431   if(lp->wasPresolved) {
5432     if(SOS_count(lp) > 0) {
5433       SOS_member_updatemap(lp->SOS);
5434       make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE));
5435     }
5436     if((lp->solvecount > 1) && (lp->bb_level < 1) &&
5437        ((lp->scalemode & SCALE_DYNUPDATE) != 0))
5438       auto_scale(lp);
5439     if(!lp->basis_valid) {
5440       crash_basis(lp);
5441       report(lp, DETAILED, "presolve: Had to repair broken basis.\n");
5442     }
5443     lp->timepresolved = timeNow();
5444     return(status);
5445   }
5446 
5447   /* Produce original model statistics (do hoops to produce correct stats if we have SOS'es) */
5448   i = SOS_count(lp);
5449   if(i > 0) {
5450     SOS_member_updatemap(lp->SOS);
5451     lp->sos_vars = SOS_memberships(lp->SOS, 0);
5452   }
5453   REPORT_modelinfo(lp, TRUE, "SUBMITTED");
5454   report(lp, NORMAL, " \n");
5455   if(i > 0)
5456     lp->sos_vars = 0;
5457 
5458   /* Finalize basis indicators; if no basis was created earlier via
5459      set_basis or crash_basis then simply set the default basis. */
5460   if(!lp->basis_valid)
5461     lp->var_basic[0] = AUTOMATIC; /* Flag that we are presolving */
5462 
5463 #if 0
5464 write_lp(lp, "test_in.lp");    /* Write to lp-formatted file for debugging */
5465 /*write_mps(lp, "test_in.mps");*/  /* Write to lp-formatted file for debugging */
5466 #endif
5467 
5468   /* Update inf norms and check for potential factorization trouble */
5469   mat_computemax(mat /*, FALSE */);
5470 #if 0
5471   Value1 = fabs(lp->negrange);
5472   if(is_obj_in_basis(lp) && (mat->dynrange < Value1) && vec_computeext(lp->orig_obj, 1, lp->columns, TRUE, &i, &j)) {
5473 
5474     /* Compute relative scale metric */
5475     Value2 = fabs(lp->orig_obj[j]/lp->orig_obj[i]) / mat->dynrange;
5476     if(Value2 < 1.0)
5477       Value2 = 1.0 / Value2;
5478 
5479     /* Determine if we should alert modeler and possibly move the OF out of the coefficient matrix */
5480     if((Value2 > Value1)           /* Case with extreme scale difference */
5481 #if 1
5482         || (mat->dynrange == 1.0)  /* Case where we have an all-unit coefficient matrix, possibly totally unimodular */
5483 #endif
5484       )
5485       if((lp->simplex_strategy & SIMPLEX_DYNAMIC) > 0) {
5486         clear_action(&lp->algopt, ALGOPT_OBJINBASIS);
5487         report(lp, NORMAL, "Moved objective function out of the basis matrix to enhance factorization accuracy.\n");
5488       }
5489       else if(mat->dynrange > 1.0)
5490         report(lp, IMPORTANT, "Warning: Objective/matrix coefficient magnitude differences will cause inaccuracy!\n");
5491   }
5492 #endif
5493 
5494   /* Do traditional simple presolve */
5495   yieldformessages(lp);
5496   if((lp->do_presolve & PRESOLVE_LASTMASKMODE) == PRESOLVE_NONE) {
5497     mat_checkcounts(mat, NULL, NULL, TRUE);
5498     i = 0;
5499   }
5500   else {
5501 
5502     if(lp->full_solution == NULL)
5503       allocREAL(lp, &lp->full_solution, lp->sum_alloc+1, TRUE);
5504 
5505     /* Identify infeasible SOS'es prior to any pruning */
5506     j = 0;
5507     for(i = 1; i <= SOS_count(lp); i++) {
5508       k = SOS_infeasible(lp->SOS, i);
5509       if(k > 0) {
5510         presolverec psdata;
5511 
5512         psdata.lp = lp;
5513         report(lp, NORMAL, "presolve: Found SOS %d (type %d) to be range-infeasible on variable %d\n",
5514                             i, SOS_get_type(lp->SOS, i), k);
5515         status = presolve_setstatus(&psdata, INFEASIBLE);
5516         j++;
5517       }
5518     }
5519     if(j > 0)
5520       goto Finish;
5521 
5522     /* Create and initialize the presolve data structures */
5523     psdata = presolve_init(lp);
5524 
5525     /* Reentry point for the outermost, computationally expensive presolve loop */
5526     psdata->outerloops = 0;
5527     do {
5528       psdata->outerloops++;
5529       iCoeffChanged = 0;
5530       iConRemove    = 0;
5531       iVarFixed     = 0;
5532       iBoundTighten = 0;
5533       iSOS          = 0;
5534       oSum          = nSum;
5535 
5536       /* Do the middle elimination loop */
5537       do {
5538         psdata->middleloops++;
5539         nSum += iSum;
5540         iSum = 0;
5541 
5542         /* Accumulate constraint bounds based on bounds on individual variables. */
5543         j = 0;
5544         while(presolve_statuscheck(psdata, &status) && psdata->forceupdate) {
5545           psdata->forceupdate = FALSE;
5546           /* Update sums, but limit iteration count to avoid possible
5547             "endless" loops with only marginal bound improvements */
5548           if(presolve_updatesums(psdata) && (j < MAX_PSBOUNDTIGHTENLOOPS)) {
5549             /* Do row preparation useful for subsequent column and row presolve operations */
5550             if((psdata->outerloops == 1) && (psdata->middleloops == 1))
5551               status = presolve_preparerows(psdata, &iBoundTighten, &iSum);
5552             nBoundTighten += iBoundTighten;
5553             iBoundTighten  = 0;
5554             nSum          += iSum;
5555             iSum           = 0;
5556             j++;
5557             if(status != RUNNING)
5558               report(lp, NORMAL, "presolve: Break after bound tightening iteration %d.\n", j);
5559           }
5560         }
5561         if(status != RUNNING)
5562           break;
5563 
5564         /* Do the relatively cheap innermost elimination loop */
5565         do {
5566 
5567           psdata->innerloops++;
5568           nSum += iSum;
5569           iSum = 0;
5570 
5571           /* Eliminate empty rows, convert row singletons to bounds,
5572             tighten bounds, and remove always satisfied rows */
5573           if(presolve_statuscheck(psdata, &status) &&
5574              is_presolve(lp, PRESOLVE_ROWS))
5575             status = presolve_rows(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum);
5576 
5577           /* Eliminate empty or fixed columns (including trivial OF column singletons) */
5578           if(presolve_statuscheck(psdata, &status) &&
5579              is_presolve(lp, PRESOLVE_COLS))
5580             status = presolve_columns(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum);
5581 
5582           /* Presolve SOS'es if possible (always do this) */
5583           if(presolve_statuscheck(psdata, &status))
5584             status = presolve_redundantSOS(psdata, &iBoundTighten, &iSum);
5585 
5586         } while((status == RUNNING) && (iSum > 0));
5587         if(status != RUNNING)
5588           break;
5589 
5590         /* Merge compatible similar rows; loop backwards over every row */
5591         if(presolve_statuscheck(psdata, &status) &&
5592            (psdata->outerloops == 1) && (psdata->middleloops <= MAX_PSMERGELOOPS) &&
5593            is_presolve(lp, PRESOLVE_MERGEROWS))
5594           status = presolve_mergerows(psdata, &iConRemove, &iSum);
5595 
5596         /* Eliminate dominated rows */
5597         if(presolve_statuscheck(psdata, &status) &&
5598            is_presolve(lp, PRESOLVE_ROWDOMINATE))
5599           presolve_rowdominance(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5600 
5601         /* See if we can convert some constraints to SOSes (only SOS1 handled) */
5602         if(presolve_statuscheck(psdata, &status) && (MIP_count(lp) > 0) &&
5603            is_presolve(lp, PRESOLVE_SOS))
5604           status = presolve_SOS1(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSOS, &iSum);
5605 
5606         /* Eliminate dominated columns in set coverage models */
5607         if(presolve_statuscheck(psdata, &status) && (lp->int_vars > 1) &&
5608            is_presolve(lp, PRESOLVE_COLDOMINATE))
5609           presolve_coldominance01(psdata, &iConRemove, &iVarFixed, &iSum);
5610 
5611         /* Aggregate compatible columns */
5612         if(presolve_statuscheck(psdata, &status) && /*TRUE ||*/
5613            is_presolve(lp, PRESOLVE_AGGREGATE))
5614           presolve_aggregate(psdata, &iConRemove, &iVarFixed, &iSum);
5615 
5616         /* Eliminate free variables and implied slacks */
5617         if(presolve_statuscheck(psdata, &status) &&
5618 /*           !is_presolve(lp, PRESOLVE_ELIMEQ2) && */
5619            is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE))
5620           status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5621 
5622       } while((status == RUNNING) && (iSum > 0));
5623       if(status != RUNNING)
5624         break;
5625 
5626       /* Check if we can do elimination of rank-deficient equality constraints */
5627       if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 1) &&
5628          is_presolve(lp, PRESOLVE_LINDEP)) {
5629 #if 0
5630         REPORT_mat_mmsave(lp, "A.mtx", NULL, FALSE, "Constraint matrix A");
5631 #endif
5632         presolve_singularities(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5633       }
5634 
5635       /* Eliminate variable and tighten bounds using 2-element EQs;
5636         note that this involves modifying the coefficients of A and
5637         can therefore be a slow operation. */
5638       if(presolve_statuscheck(psdata, &status) &&
5639          is_presolve(lp, PRESOLVE_ELIMEQ2)) {
5640         jjx = 0;
5641         do {
5642           jjx += iSum;
5643           status = presolve_elimeq2(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5644         } while((status == RUNNING) && (iSum > jjx));
5645         iSum = jjx;
5646 
5647 #if 0
5648         /* Eliminate free variables and implied slacks */
5649         if(presolve_statuscheck(psdata, &status) &&
5650            is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE))
5651           status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5652 #endif
5653       }
5654 
5655       /* Increase A matrix sparsity by discovering common subsets using EQs */
5656       if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 0) &&
5657          is_presolve(lp, PRESOLVE_SPARSER))
5658         status = presolve_makesparser(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5659 
5660       /* Do GCD-based coefficient reductions (also does row scaling,
5661         even if no rhs INT truncations are possible) */
5662       if(presolve_statuscheck(psdata, &status) && (psdata->INTmap->count > 0) &&
5663          is_presolve(lp, PRESOLVE_REDUCEGCD))
5664         if(!presolve_reduceGCD(psdata, &iCoeffChanged, &iBoundTighten, &iSum))
5665           status = presolve_setstatus(psdata, INFEASIBLE);
5666 
5667       /* Simplify knapsack or set coverage models where OF coefficients are
5668         duplicated in the constraints.  At the cost of adding helper columns, this
5669         increases sparsity and facilitates identification of lower and upper bounds. */
5670       if(presolve_statuscheck(psdata, &status) &&
5671           is_presolve(lp, PRESOLVE_KNAPSACK)) {
5672         i = iCoeffChanged;
5673         status = presolve_knapsack(psdata, &iCoeffChanged);
5674       }
5675 
5676       /* Remove any "hanging" empty row and columns */
5677       if(status == RUNNING)
5678         status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5679 
5680       nCoeffChanged += iCoeffChanged;
5681       nConRemove    += iConRemove;
5682       nVarFixed     += iVarFixed;
5683       nBoundTighten += iBoundTighten;
5684       nSOS          += iSOS;
5685       nSum          += iSum;
5686 
5687       iSum           = iConRemove + iVarFixed + iBoundTighten + iCoeffChanged;
5688       if(iSum > 0)
5689         report(lp, NORMAL, "Presolve O:%d -> Reduced rows:%5d, cols:%5d --- changed bnds:%5d, Ab:%5d.\n",
5690                            psdata->outerloops, iConRemove, iVarFixed, iBoundTighten, iCoeffChanged);
5691 
5692    /* Do the outermost loop again if we were successful in this presolve sequences */
5693     } while(presolve_statuscheck(psdata, &status) &&
5694            (psdata->forceupdate || (oSum < nSum)) &&
5695            (psdata->outerloops < get_presolveloops(lp)) &&
5696            (psdata->rows->varmap->count+psdata->cols->varmap->count > 0));
5697 
5698    /* Finalize presolve */
5699 #ifdef Paranoia
5700     i = presolve_debugcheck(lp, psdata->rows->varmap, psdata->cols->varmap);
5701     if(i > 0)
5702       report(lp, SEVERE, "presolve: %d internal consistency failure%s\n", i, my_plural_std(i));
5703     if((SOS_count(lp) > 0) && !presolve_SOScheck(psdata))
5704       report(lp, SEVERE, "presolve: SOS sparse member mapping problem - part 1\n");
5705 #endif
5706     /* Perform bound relaxation to reduce chance of degeneracy. */
5707     if((status == RUNNING) && !is_presolve(lp, PRESOLVE_IMPLIEDFREE))
5708       jjx = presolve_makefree(psdata);
5709     else
5710       jjx = 0;
5711 
5712 
5713     /* Finalize the presolve */
5714     if(!presolve_finalize(psdata))
5715       report(lp, SEVERE, "presolve: Unable to construct internal data representation\n");
5716 
5717    /* Report summary information */
5718     i = NORMAL;
5719     iVarFixed  = lp->presolve_undo->orig_columns - psdata->cols->varmap->count;
5720     iConRemove = lp->presolve_undo->orig_rows - psdata->rows->varmap->count;
5721     if(nSum > 0)
5722       report(lp, i, "PRESOLVE             Elimination loops performed.......... O%d:M%d:I%d\n",
5723                                   psdata->outerloops, psdata->middleloops, psdata->innerloops);
5724     if(nVarFixed)
5725       report(lp, i, "            %8d empty or fixed variables............. %s.\n", nVarFixed, "REMOVED");
5726     if(nConRemove)
5727       report(lp, i, "            %8d empty or redundant constraints....... %s.\n", nConRemove, "REMOVED");
5728     if(nBoundTighten)
5729       report(lp, i, "            %8d bounds............................... %s.\n", nBoundTighten, "TIGHTENED");
5730     if(nCoeffChanged)
5731       report(lp, i, "            %8d matrix coefficients.................. %s.\n", nCoeffChanged, "CHANGED");
5732     if(jjx > 0)
5733       report(lp, i, "            %8d variables' final bounds.............. %s.\n", jjx, "RELAXED");
5734     if(nSOS)
5735       report(lp, i, "            %8d constraints detected as SOS1......... %s.\n", nSOS, "CONVERTED");
5736 
5737     /* Report optimality or infeasibility */
5738     if(status == UNBOUNDED)
5739       report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "UNBOUNDED");
5740     else if(status == INFEASIBLE)
5741       report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "INFEASIBLE");
5742     else {
5743       if(psdata->cols->varmap->count == 0)
5744         Value1 = Value2 = lp->presolve_undo->fixed_rhs[0] -initrhs0;
5745       else
5746         presolve_rangeorig(lp, 0, psdata->rows, &Value1, &Value2, -initrhs0);
5747       if((fabs(Value1 - Value2) < psdata->epsvalue) || (fabs(my_reldiff(Value1, Value2)) < psdata->epsvalue)) {
5748         if((lp->rows == 0) && (lp->columns == 0)) {
5749           status = PRESOLVED;
5750           Value1 = my_chsign(is_maxim(lp), Value1);
5751           lp->solution[0] = Value1;
5752           lp->best_solution[0] = Value1;
5753           lp->full_solution[0] = Value1;
5754         }
5755         report(lp, NORMAL, "%20s OPTIMAL solution found............... %-g", "", Value1);
5756       }
5757       else if((status == RUNNING) && (i >= NORMAL)) {
5758         char lonum[20], upnum[20];
5759         if(my_infinite(lp, Value1))
5760           sprintf(lonum, "%13s", "-Inf");
5761         else
5762           sprintf(lonum, "%+12g", Value1);
5763         if(my_infinite(lp, Value2))
5764           sprintf(upnum, "%-13s", "Inf");
5765         else
5766           sprintf(upnum, "%+-12g", Value2);
5767         report(lp, i,    "%20s [ %s < Z < %s ]\n", "", lonum, upnum);
5768       }
5769 
5770       /* Update values for dual limit and best heuristic values */
5771       if((MIP_count(lp) > 0) || (get_Lrows(lp) > 0)) {
5772         if(is_maxim(lp)) {
5773           SETMAX(lp->bb_heuristicOF, Value1);
5774           SETMIN(lp->bb_limitOF, Value2);
5775         }
5776         else {
5777           SETMIN(lp->bb_heuristicOF, Value2);
5778           SETMAX(lp->bb_limitOF, Value1);
5779         }
5780       }
5781     }
5782     report(lp, NORMAL, " \n");
5783 
5784     /* Clean up (but save counts of constraint types for display later) */
5785     j = psdata->LTmap->count;
5786     jx = psdata->EQmap->count;
5787     jjx = lp->rows - j - jx;
5788     presolve_free(&psdata);
5789 
5790   }
5791 
5792   /* Signal that we are done presolving */
5793   if((lp->usermessage != NULL) &&
5794      ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != 0) && (lp->msgmask & MSG_PRESOLVE))
5795      lp->usermessage(lp, lp->msghandle, MSG_PRESOLVE);
5796 
5797   /* Create master SOS variable list */
5798   if(SOS_count(lp) > 0) {
5799     /*SOS_member_updatemap(lp->SOS); */
5800     make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE));
5801   }
5802 
5803   /* Finalize model not identified as infeasible or unbounded */
5804   if(status == RUNNING) {
5805 
5806     /* Resolve GUBs */
5807     if(is_bb_mode(lp, NODE_GUBMODE))
5808       identify_GUB(lp, TRUE);
5809 
5810 #if 0
5811     /* Mark rows containing hidden identity matrices so that supporting factorization
5812       engines can use this structural information to boost efficiency */
5813     if(is_algopt(lp, ALGOPT_COMPACTBPF))
5814       lp->bfpoptimize = (MYBOOL) (assist_factorization(lp, ROWTYPE_LOGICAL,
5815                                                        &lp->rowset1, &lp->rowno1) > 0);
5816 #endif
5817 
5818     /* Scale the model based on current settings */
5819     auto_scale(lp);
5820 
5821     /* Crash the basis, if specified */
5822     crash_basis(lp);
5823 
5824     /* Produce presolved model statistics */
5825     if(nConRemove+nVarFixed+nBoundTighten+nVarFixed+nCoeffChanged > 0) {
5826       REPORT_modelinfo(lp, FALSE, "REDUCED");
5827       if(nSum > 0) {
5828         report(lp, NORMAL, "Row-types:   %7d LE,          %7d GE,             %7d EQ.\n",
5829                            j, jjx, jx);
5830         report(lp, NORMAL, " \n");
5831       }
5832     }
5833   }
5834 
5835   /* Optionally produce data on constraint classes */
5836   if(lp->verbose > NORMAL) {
5837     report(lp, NORMAL, " \n");
5838     REPORT_constraintinfo(lp, "CONSTRAINT CLASSES");
5839     report(lp, NORMAL, " \n");
5840   }
5841 
5842 Finish:
5843   lp->wasPresolved  = TRUE;
5844   lp->timepresolved = timeNow();
5845 
5846 #if 0
5847 /*  write_mps(lp, "test_out.mps"); */ /* Must put here due to variable name mapping */
5848   write_lp(lp, "test_out.lp");   /* Must put here due to variable name mapping */
5849 #endif
5850 #if 0
5851   REPORT_debugdump(lp, "testint2.txt", FALSE);
5852 #endif
5853 
5854   return( status );
5855 
5856 }
5857 
5858 STATIC MYBOOL postsolve(lprec *lp, int status)
5859 {
5860   /* Verify solution */
5861   if(lp->lag_status != RUNNING) {
5862     int itemp;
5863 
5864     if(status == PRESOLVED)
5865       status = OPTIMAL;
5866 
5867     if((status == OPTIMAL) || (status == SUBOPTIMAL)) {
5868       itemp = check_solution(lp, lp->columns, lp->best_solution,
5869                                  lp->orig_upbo, lp->orig_lowbo, lp->epssolution);
5870       if((itemp != OPTIMAL) && (lp->spx_status == OPTIMAL))
5871         lp->spx_status = itemp;
5872       else if((itemp == OPTIMAL) && ((status == SUBOPTIMAL) || (lp->spx_status == PRESOLVED)))
5873         lp->spx_status = status;
5874     }
5875     else if(status != PRESOLVED) {
5876       report(lp, NORMAL, "lp_solve unsuccessful after %.0f iter and a last best value of %g\n",
5877              (double) get_total_iter(lp), lp->best_solution[0]);
5878       if(lp->bb_totalnodes > 0)
5879         report(lp, NORMAL, "lp_solve explored %.0f nodes before termination\n",
5880                (double) get_total_nodes(lp));
5881     }
5882     else
5883       lp->spx_status = OPTIMAL;
5884 
5885     /* Only rebuild primal solution here, since the dual is only computed on request */
5886     presolve_rebuildUndo(lp, TRUE);
5887   }
5888 
5889   /* Check if we can clear the variable map */
5890   if(varmap_canunlock(lp))
5891     lp->varmap_locked = FALSE;
5892 #if 0
5893   REPORT_mat_mmsave(lp, "basis.mtx", NULL, FALSE);  /* Write the current basis matrix (no OF) */
5894 #endif
5895 
5896   return( TRUE );
5897 }
5898