source: trunk/Clp/src/ClpSolve.cpp @ 1088

Last change on this file since 1088 was 1088, checked in by andreasw, 14 years ago

merging changes from Bug Squashing Party Aug 2007 to regular trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.8 KB
Line 
1// Copyright (C) 2003, International Business Machines
2// Corporation and others.  All Rights Reserved.
3
4// This file has higher level solve functions
5
6#include "ClpConfig.h"
7#include "CoinPragma.hpp"
8
9#include <math.h>
10
11#include "CoinHelperFunctions.hpp"
12#include "ClpHelperFunctions.hpp"
13#include "CoinSort.hpp"
14#include "ClpFactorization.hpp"
15#include "ClpSimplex.hpp"
16#include "ClpSimplexOther.hpp"
17#ifndef SLIM_CLP
18#include "ClpQuadraticObjective.hpp"
19#include "ClpInterior.hpp"
20#include "ClpCholeskyDense.hpp"
21#include "ClpCholeskyBase.hpp"
22#include "ClpPlusMinusOneMatrix.hpp"
23#include "ClpNetworkMatrix.hpp"
24#endif
25#include "ClpLinearObjective.hpp"
26#include "ClpSolve.hpp"
27#include "ClpPackedMatrix.hpp"
28#include "ClpMessage.hpp"
29#include "CoinTime.hpp"
30
31#include "ClpPresolve.hpp"
32#ifndef SLIM_CLP
33#include "Idiot.hpp"
34#ifdef WSSMP_BARRIER
35#include "ClpCholeskyWssmp.hpp"
36#include "ClpCholeskyWssmpKKT.hpp"
37#define FAST_BARRIER
38#endif
39#ifdef UFL_BARRIER
40#include "ClpCholeskyUfl.hpp"
41#define FAST_BARRIER
42#endif
43#ifdef TAUCS_BARRIER
44#include "ClpCholeskyTaucs.hpp"
45#define FAST_BARRIER
46#endif
47#ifdef COIN_DEVELOP
48#ifndef FAST_BARRIER
49static int numberBarrier=0;
50#endif
51#endif
52#ifdef COIN_HAS_VOL
53#include "VolVolume.hpp"
54#include "CoinHelperFunctions.hpp"
55#include "CoinPackedMatrix.hpp"
56#include "CoinMpsIO.hpp"
57
58//#############################################################################
59
60class lpHook : public VOL_user_hooks {
61private:
62   lpHook(const lpHook&);
63   lpHook& operator=(const lpHook&);
64private:
65   /// Pointer to dense vector of structural variable upper bounds
66   double  *colupper_;
67   /// Pointer to dense vector of structural variable lower bounds
68   double  *collower_;
69   /// Pointer to dense vector of objective coefficients
70   double  *objcoeffs_;
71   /// Pointer to dense vector of right hand sides
72   double  *rhs_;
73   /// Pointer to dense vector of senses
74   char    *sense_;
75
76   /// The problem matrix in a row ordered form
77   CoinPackedMatrix rowMatrix_;
78   /// The problem matrix in a column ordered form
79   CoinPackedMatrix colMatrix_;
80
81public:
82   lpHook(double* clb, double* cub, double* obj,
83          double* rhs, char* sense, const CoinPackedMatrix& mat);
84   virtual ~lpHook();
85   
86public:
87   // for all hooks: return value of -1 means that volume should quit
88   /** compute reduced costs   
89       @param u (IN) the dual variables
90       @param rc (OUT) the reduced cost with respect to the dual values
91   */
92   virtual int compute_rc(const VOL_dvector& u, VOL_dvector& rc);
93
94   /** Solve the subproblem for the subgradient step.
95       @param dual (IN) the dual variables
96       @param rc (IN) the reduced cost with respect to the dual values
97       @param lcost (OUT) the lagrangean cost with respect to the dual values
98       @param x (OUT) the primal result of solving the subproblem
99       @param v (OUT) b-Ax for the relaxed constraints
100       @param pcost (OUT) the primal objective value of <code>x</code>
101   */
102   virtual int solve_subproblem(const VOL_dvector& dual, const VOL_dvector& rc,
103                                double& lcost, VOL_dvector& x, VOL_dvector& v,
104                                double& pcost);
105   /** Starting from the primal vector x, run a heuristic to produce
106       an integer solution 
107       @param x (IN) the primal vector
108       @param heur_val (OUT) the value of the integer solution (return
109       <code>DBL_MAX</code> here if no feas sol was found
110   */
111   virtual int heuristics(const VOL_problem& p, 
112                          const VOL_dvector& x, double& heur_val) {
113      return 0;
114   }
115};
116 
117//#############################################################################
118
119lpHook::lpHook(double* clb, double* cub, double* obj,
120               double* rhs, char* sense,
121               const CoinPackedMatrix& mat)
122{
123   colupper_ = cub;
124   collower_ = clb;
125   objcoeffs_ = obj;
126   rhs_ = rhs;
127   sense_ = sense;
128   assert (mat.isColOrdered());
129   colMatrix_.copyOf(mat);
130   rowMatrix_.reverseOrderedCopyOf(mat);
131}
132
133//-----------------------------------------------------------------------------
134
135lpHook::~lpHook()
136{
137}
138
139//#############################################################################
140
141int
142lpHook::compute_rc(const VOL_dvector& u, VOL_dvector& rc)
143{
144   rowMatrix_.transposeTimes(u.v, rc.v);
145   const int psize = rowMatrix_.getNumCols();
146
147   for (int i = 0; i < psize; ++i)
148      rc[i] = objcoeffs_[i] - rc[i];
149   return 0;
150}
151
152//-----------------------------------------------------------------------------
153
154int
155lpHook::solve_subproblem(const VOL_dvector& dual, const VOL_dvector& rc,
156                         double& lcost, VOL_dvector& x, VOL_dvector& v,
157                         double& pcost)
158{
159   int i;
160   const int psize = x.size();
161   const int dsize = v.size();
162
163   // compute the lagrangean solution corresponding to the reduced costs
164   for (i = 0; i < psize; ++i) 
165      x[i] = (rc[i] >= 0.0) ? collower_[i] : colupper_[i];
166
167   // compute the lagrangean value (rhs*dual + primal*rc)
168   lcost = 0;
169   for (i = 0; i < dsize; ++i)
170      lcost += rhs_[i] * dual[i];
171   for (i = 0; i < psize; ++i)
172      lcost += x[i] * rc[i];
173
174   // compute the rhs - lhs
175   colMatrix_.times(x.v, v.v);
176   for (i = 0; i < dsize; ++i)
177      v[i] = rhs_[i] - v[i];
178
179   // compute the lagrangean primal objective
180   pcost = 0;
181   for (i = 0; i < psize; ++i)
182      pcost += x[i] * objcoeffs_[i];
183
184   return 0;
185}
186
187//#############################################################################
188/** A quick inlined function to convert from lb/ub style constraint
189    definition to sense/rhs/range style */
190inline void
191convertBoundToSense(const double lower, const double upper,
192                                        char& sense, double& right,
193                                        double& range) 
194{
195  range = 0.0;
196  if (lower > -1.0e20) {
197    if (upper < 1.0e20) {
198      right = upper;
199      if (upper==lower) {
200        sense = 'E';
201      } else {
202        sense = 'R';
203        range = upper - lower;
204      }
205    } else {
206      sense = 'G';
207      right = lower;
208    }
209  } else {
210    if (upper < 1.0e20) {
211      sense = 'L';
212      right = upper;
213    } else {
214      sense = 'N';
215      right = 0.0;
216    }
217  }
218}
219
220static int
221solveWithVolume(ClpSimplex * model, int numberPasses, int doIdiot)
222{
223   VOL_problem volprob;
224   volprob.parm.gap_rel_precision=0.00001;
225   volprob.parm.maxsgriters=3000;
226   if(numberPasses>3000) {
227     volprob.parm.maxsgriters=numberPasses;
228     volprob.parm.primal_abs_precision=0.0;
229     volprob.parm.minimum_rel_ascent=0.00001;
230   } else if (doIdiot>0) {
231     volprob.parm.maxsgriters=doIdiot;
232   }
233   if (model->logLevel()<2) 
234     volprob.parm.printflag=0;
235   else
236     volprob.parm.printflag=3;
237   const CoinPackedMatrix* mat = model->matrix();
238   int psize = model->numberColumns();
239   int dsize = model->numberRows();
240   char * sense = new char[dsize];
241   double * rhs = new double [dsize];
242
243   // Set the lb/ub on the duals
244   volprob.dsize = dsize;
245   volprob.psize = psize;
246   volprob.dual_lb.allocate(dsize);
247   volprob.dual_ub.allocate(dsize);
248   int i;
249   const double * rowLower = model->rowLower();
250   const double * rowUpper = model->rowUpper();
251   for (i = 0; i < dsize; ++i) {
252     double range;
253     convertBoundToSense(rowLower[i],rowUpper[i],
254                         sense[i],rhs[i],range);
255      switch (sense[i]) {
256       case 'E':
257         volprob.dual_lb[i] = -1.0e31;
258         volprob.dual_ub[i] = 1.0e31;
259         break;
260       case 'L':
261         volprob.dual_lb[i] = -1.0e31;
262         volprob.dual_ub[i] = 0.0;
263         break;
264       case 'G':
265         volprob.dual_lb[i] = 0.0;
266         volprob.dual_ub[i] = 1.0e31;
267         break;
268       default:
269         printf("Volume Algorithm can't work if there is a non ELG row\n");
270         return 1;
271      }
272   }
273   // Check bounds
274   double * saveLower = model->columnLower();
275   double * saveUpper = model->columnUpper();
276   bool good=true;
277   for (i=0;i<psize;i++) {
278     if (saveLower[i]<-1.0e20||saveUpper[i]>1.0e20) {
279       good=false;
280       break;
281     }
282   }
283   if (!good) {
284     saveLower = CoinCopyOfArray(model->columnLower(),psize);
285     saveUpper = CoinCopyOfArray(model->columnUpper(),psize);
286     for (i=0;i<psize;i++) {
287       if (saveLower[i]<-1.0e20)
288         saveLower[i]=-1.0e20;
289       if(saveUpper[i]>1.0e20) 
290         saveUpper[i]=1.0e20;
291     }
292   }
293   lpHook myHook(saveLower, saveUpper,
294                 model->objective(),
295                 rhs, sense, *mat);
296
297   volprob.solve(myHook, false /* no warmstart */);
298
299   if (saveLower!=model->columnLower()) {
300     delete [] saveLower;
301     delete [] saveUpper;
302   }
303   //------------- extract the solution ---------------------------
304
305   //printf("Best lagrangean value: %f\n", volprob.value);
306
307   double avg = 0;
308   for (i = 0; i < dsize; ++i) {
309      switch (sense[i]) {
310       case 'E':
311         avg += CoinAbs(volprob.viol[i]);
312         break;
313       case 'L':
314         if (volprob.viol[i] < 0)
315            avg +=  (-volprob.viol[i]);
316         break;
317       case 'G':
318         if (volprob.viol[i] > 0)
319            avg +=  volprob.viol[i];
320         break;
321      }
322   }
323     
324   //printf("Average primal constraint violation: %f\n", avg/dsize);
325
326   // volprob.dsol contains the dual solution (dual feasible)
327   // volprob.psol contains the primal solution
328   //              (NOT necessarily primal feasible)
329   CoinMemcpyN(volprob.dsol.v,dsize,model->dualRowSolution());
330   CoinMemcpyN(volprob.psol.v,psize,model->primalColumnSolution());
331   return 0;
332}
333#endif
334static ClpInterior * currentModel2 = NULL;
335#endif
336//#############################################################################
337// Allow for interrupts
338// But is this threadsafe ? (so switched off by option)
339
340#include "CoinSignal.hpp"
341static ClpSimplex * currentModel = NULL;
342
343extern "C" {
344   static void signal_handler(int whichSignal)
345   {
346      if (currentModel!=NULL) 
347         currentModel->setMaximumIterations(0); // stop at next iterations
348#ifndef SLIM_CLP
349      if (currentModel2!=NULL) 
350         currentModel2->setMaximumBarrierIterations(0); // stop at next iterations
351#endif
352      return;
353   }
354}
355
356/** General solve algorithm which can do presolve
357    special options (bits)
358    1 - do not perturb
359    2 - do not scale
360    4 - use crash (default allslack in dual, idiot in primal)
361    8 - all slack basis in primal
362    16 - switch off interrupt handling
363    32 - do not try and make plus minus one matrix
364    64 - do not use sprint even if problem looks good
365 */
366int 
367ClpSimplex::initialSolve(ClpSolve & options)
368{
369  ClpSolve::SolveType method=options.getSolveType();
370  //ClpSolve::SolveType originalMethod=method;
371  ClpSolve::PresolveType presolve = options.getPresolveType();
372  int saveMaxIterations = maximumIterations();
373  int finalStatus=-1;
374  int numberIterations=0;
375  double time1 = CoinCpuTime();
376  double timeX = time1;
377  double time2;
378  ClpMatrixBase * saveMatrix=NULL;
379  ClpObjective * savedObjective=NULL;
380  if (!objective_||!matrix_) {
381    // totally empty
382    handler_->message(CLP_EMPTY_PROBLEM,messages_)
383      <<0
384      <<0
385      <<0
386      <<CoinMessageEol;
387    return -1;
388  } else if (!numberRows_||!numberColumns_||!getNumElements()) {
389    presolve = ClpSolve::presolveOff;
390  }
391  if (objective_->type()>=2&&optimizationDirection_==0) {
392    // pretend linear
393    savedObjective=objective_;
394    // make up objective
395    double * obj = new double[numberColumns_];
396    for (int i=0;i<numberColumns_;i++) {
397      double l = fabs(columnLower_[i]);
398      double u = fabs(columnUpper_[i]);
399      obj[i]=0.0;
400      if (CoinMin(l,u)<1.0e20) {
401        if (l<u) 
402          obj[i]=1.0+CoinDrand48()*1.0e-2;
403        else
404          obj[i]=-1.0-CoinDrand48()*1.0e-2;
405      }
406    }
407    objective_= new ClpLinearObjective(obj,numberColumns_);
408    delete [] obj;
409  }
410  ClpSimplex * model2 = this;
411  bool interrupt = (options.getSpecialOption(2)==0);
412  CoinSighandler_t saveSignal=SIG_DFL;
413  if (interrupt) {
414    currentModel = model2;
415    // register signal handler
416    saveSignal = signal(SIGINT,signal_handler);
417  }
418  // If no status array - set up basis
419  if (!status_)
420    allSlackBasis();
421  ClpPresolve pinfo;
422  pinfo.setSubstitution(options.substitution());
423  int presolveOptions = options.presolveActions();
424  bool presolveToFile = (presolveOptions&0x40000000)!=0;
425  presolveOptions &= ~0x40000000;
426  if ((presolveOptions&0xffff)!=0)
427    pinfo.setPresolveActions(presolveOptions);
428  // switch off singletons to slacks
429  //pinfo.setDoSingletonColumn(false); // done by bits
430  int printOptions = options.getSpecialOption(5);
431  if ((printOptions&1)!=0)
432    pinfo.statistics();
433  double timePresolve=0.0;
434  double timeIdiot=0.0;
435  double timeCore=0.0;
436  int savePerturbation=perturbation_;
437  int saveScaling = scalingFlag_;
438#ifndef SLIM_CLP
439#ifndef NO_RTTI
440  if (dynamic_cast< ClpNetworkMatrix*>(matrix_)) {
441    // network - switch off stuff
442    presolve = ClpSolve::presolveOff;
443  }
444#else
445  if (matrix_->type()==11) {
446    // network - switch off stuff
447    presolve = ClpSolve::presolveOff;
448  }
449#endif
450#endif
451  // For below >0 overrides
452  // 0 means no, -1 means maybe
453  int doIdiot=0;
454  int doCrash=0;
455  int doSprint=0;
456  int doSlp=0;
457  int primalStartup=1;
458  if (method!=ClpSolve::useDual&&method!=ClpSolve::useBarrier
459      &&method!=ClpSolve::useBarrierNoCross) {
460    switch (options.getSpecialOption(1)) {
461    case 0:
462      doIdiot=-1;
463      doCrash=-1;
464      doSprint=-1;
465      break;
466    case 1:
467      doIdiot=0;
468      doCrash=1;
469      if (options.getExtraInfo(1)>0)
470        doCrash = options.getExtraInfo(1);
471      doSprint=0;
472      break;
473    case 2:
474      doIdiot=1;
475      if (options.getExtraInfo(1)>0)
476        doIdiot = options.getExtraInfo(1);
477      doCrash=0;
478      doSprint=0;
479      break;
480    case 3:
481      doIdiot=0;
482      doCrash=0;
483      doSprint=1;
484      break;
485    case 4:
486      doIdiot=0;
487      doCrash=0;
488      doSprint=0;
489      break;
490    case 5:
491      doIdiot=0;
492      doCrash=-1;
493      doSprint=-1;
494      break;
495    case 6:
496      doIdiot=-1;
497      doCrash=-1;
498      doSprint=0;
499      break;
500    case 7:
501      doIdiot=-1;
502      doCrash=0;
503      doSprint=-1;
504      break;
505    case 8:
506      doIdiot=-1;
507      doCrash=0;
508      doSprint=0;
509      break;
510    case 9:
511      doIdiot=0;
512      doCrash=0;
513      doSprint=-1;
514      break;
515    case 10:
516      doIdiot=0;
517      doCrash=0;
518      doSprint=0;
519      if (options.getExtraInfo(1)>0)
520        doSlp = options.getExtraInfo(1);
521      break;
522    case 11:
523      doIdiot=0;
524      doCrash=0;
525      doSprint=0;
526      primalStartup=0;
527      break;
528    default:
529      abort();
530    }
531  } else {
532    // Dual
533    switch (options.getSpecialOption(0)) {
534    case 0:
535      doIdiot=0;
536      doCrash=0;
537      doSprint=0;
538      break;
539    case 1:
540      doIdiot=0;
541      doCrash=1;
542      if (options.getExtraInfo(0)>0)
543        doCrash = options.getExtraInfo(0);
544      doSprint=0;
545      break;
546    case 2:
547      doIdiot=-1;
548      if (options.getExtraInfo(0)>0)
549        doIdiot = options.getExtraInfo(0);
550      doCrash=0;
551      doSprint=0;
552      break;
553    default:
554      abort();
555    }
556  }
557#ifndef NO_RTTI
558  ClpQuadraticObjective * quadraticObj = (dynamic_cast< ClpQuadraticObjective*>(objectiveAsObject()));
559#else
560  ClpQuadraticObjective * quadraticObj = NULL;
561  if (objective_->type()==2)
562    quadraticObj = (static_cast< ClpQuadraticObjective*>(objective_));
563#endif
564  // If quadratic then primal or barrier or slp
565  if (quadraticObj) {
566    doSprint=0;
567    doIdiot=0;
568    // off
569    if (method==ClpSolve::useBarrier)
570      method=ClpSolve::useBarrierNoCross;
571    else if (method!=ClpSolve::useBarrierNoCross)
572      method=ClpSolve::usePrimal;
573  }
574#ifdef COIN_HAS_VOL
575  // Save number of idiot
576  int saveDoIdiot=doIdiot;
577#endif
578  // Just do this number of passes in Sprint
579  int maxSprintPass=100;
580  bool costedSlacks=false;
581  if (presolve!=ClpSolve::presolveOff) {
582    int numberPasses=5;
583    if (presolve==ClpSolve::presolveNumber) {
584      numberPasses=options.getPresolvePasses();
585      presolve = ClpSolve::presolveOn;
586    } else if (presolve==ClpSolve::presolveNumberCost) {
587      numberPasses=options.getPresolvePasses();
588      presolve = ClpSolve::presolveOn;
589      costedSlacks=true;
590      // switch on singletons to slacks
591      pinfo.setDoSingletonColumn(true);
592    }
593#ifndef CLP_NO_STD
594    if (presolveToFile) {
595      // PreSolve to file - not fully tested
596      printf("Presolving to file - presolve.save\n");
597      pinfo.presolvedModelToFile(*this,"presolve.save",dblParam_[ClpPresolveTolerance],
598                           false,numberPasses);
599      model2=this;
600    } else {
601#endif
602      model2 = pinfo.presolvedModel(*this,dblParam_[ClpPresolveTolerance],
603                                    false,numberPasses,true,costedSlacks);
604#ifndef CLP_NO_STD
605    }
606#endif
607    time2 = CoinCpuTime();
608    timePresolve = time2-timeX;
609    handler_->message(CLP_INTERVAL_TIMING,messages_)
610      <<"Presolve"<<timePresolve<<time2-time1
611      <<CoinMessageEol;
612    timeX=time2;
613    if (!model2) {
614      handler_->message(CLP_INFEASIBLE,messages_)
615        <<CoinMessageEol;
616      model2 = this;
617      problemStatus_=1; // may be unbounded but who cares
618      if (options.infeasibleReturn()||(moreSpecialOptions_&1)!=0) {
619        return -1;
620      }
621      presolve=ClpSolve::presolveOff;
622    }
623    // We may be better off using original (but if dual leave because of bounds)
624    if (presolve!=ClpSolve::presolveOff&&
625        numberRows_<1.01*model2->numberRows_&&numberColumns_<1.01*model2->numberColumns_
626        &&model2!=this) {
627      if(method!=ClpSolve::useDual||
628         (numberRows_==model2->numberRows_&&numberColumns_==model2->numberColumns_)) {
629        delete model2;
630        model2 = this;
631        presolve=ClpSolve::presolveOff;
632      }
633    }
634  }
635  if (interrupt)
636    currentModel = model2;
637  // See if worth trying +- one matrix
638  bool plusMinus=false;
639  int numberElements=model2->getNumElements();
640#ifndef SLIM_CLP
641#ifndef NO_RTTI
642  if (dynamic_cast< ClpNetworkMatrix*>(matrix_)) {
643    // network - switch off stuff
644    doIdiot=0;
645    doSprint=0;
646  }
647#else
648  if (matrix_->type()==11) {
649    // network - switch off stuff
650    doIdiot=0;
651    doSprint=0;
652  }
653#endif
654#endif
655  int numberColumns = model2->numberColumns();
656  int numberRows = model2->numberRows();
657  // If not all slack basis - switch off all except sprint
658  int numberRowsBasic=0;
659  int iRow;
660  for (iRow=0;iRow<numberRows;iRow++)
661    if (model2->getRowStatus(iRow)==basic)
662      numberRowsBasic++;
663  if (numberRowsBasic<numberRows) {
664    doIdiot=0;
665    doCrash=0;
666    //doSprint=0;
667  }
668  if (options.getSpecialOption(3)==0) {
669    if(numberElements>100000)
670      plusMinus=true;
671    if(numberElements>10000&&(doIdiot||doSprint)) 
672      plusMinus=true;
673  }
674#ifndef SLIM_CLP
675  // Statistics (+1,-1, other) - used to decide on strategy if not +-1
676  CoinBigIndex statistics[3]={-1,0,0};
677  if (plusMinus) {
678    saveMatrix = model2->clpMatrix();
679#ifndef NO_RTTI
680    ClpPackedMatrix* clpMatrix =
681      dynamic_cast< ClpPackedMatrix*>(saveMatrix);
682#else
683    ClpPackedMatrix* clpMatrix = NULL;
684    if (saveMatrix->type()==1)
685      clpMatrix =
686        static_cast< ClpPackedMatrix*>(saveMatrix);
687#endif
688    if (clpMatrix) {
689      ClpPlusMinusOneMatrix * newMatrix = new ClpPlusMinusOneMatrix(*(clpMatrix->matrix()));
690      if (newMatrix->getIndices()) {
691        model2->replaceMatrix(newMatrix);
692      } else {
693        handler_->message(CLP_MATRIX_CHANGE,messages_)
694          <<"+- 1"
695          <<CoinMessageEol;
696        CoinMemcpyN(newMatrix->startPositive(),3,statistics);
697        saveMatrix=NULL;
698        plusMinus=false;
699        delete newMatrix;
700      }
701    } else {
702      saveMatrix=NULL;
703      plusMinus=false;
704    }
705  }
706#endif
707  if (this->factorizationFrequency()==200) {
708    // User did not touch preset
709    model2->defaultFactorizationFrequency();
710  } else if (model2!=this) {
711    // make sure model2 has correct value
712    model2->setFactorizationFrequency(this->factorizationFrequency());
713  }
714  if (method==ClpSolve::automatic) {
715    if (doSprint==0&&doIdiot==0) {
716      // off
717      method=ClpSolve::useDual;
718    } else {
719      // only do primal if sprint or idiot
720      if (doSprint>0) {
721        method=ClpSolve::usePrimalorSprint;
722      } else if (doIdiot>0) {
723        method=ClpSolve::usePrimal;
724      } else {
725        if (numberElements<500000) {
726          // Small problem
727          if(numberRows*10>numberColumns||numberColumns<6000
728             ||(numberRows*20>numberColumns&&!plusMinus))
729            doSprint=0; // switch off sprint
730        } else {
731          // larger problem
732          if(numberRows*8>numberColumns)
733            doSprint=0; // switch off sprint
734        }
735        // switch off sprint or idiot if any free variable
736        int iColumn;
737        double * columnLower = model2->columnLower();
738        double * columnUpper = model2->columnUpper();
739        for (iColumn=0;iColumn<numberColumns;iColumn++) {
740          if (columnLower[iColumn]<-1.0e10&&columnUpper[iColumn]>1.0e10) {
741            doSprint=0;
742            doIdiot=0;
743            break;
744          }
745        }
746        int nPasses=0;
747        // look at rhs
748        int iRow;
749        double largest=0.0;
750        double smallest = 1.0e30;
751        double largestGap=0.0;
752        int numberNotE=0;
753        bool notInteger=false;
754        for (iRow=0;iRow<numberRows;iRow++) {
755          double value1 = model2->rowLower_[iRow];
756          if (value1&&value1>-1.0e31) {
757            largest = CoinMax(largest,fabs(value1));
758            smallest=CoinMin(smallest,fabs(value1));
759            if (fabs(value1-floor(value1+0.5))>1.0e-8) {
760              notInteger=true;
761              break;
762            }
763          }
764          double value2 = model2->rowUpper_[iRow];
765          if (value2&&value2<1.0e31) {
766            largest = CoinMax(largest,fabs(value2));
767            smallest=CoinMin(smallest,fabs(value2));
768            if (fabs(value2-floor(value2+0.5))>1.0e-8) {
769              notInteger=true;
770              break;
771            }
772          }
773          if (value2>value1) {
774            numberNotE++;
775            if (value2>1.0e31||value1<-1.0e31)
776              largestGap = COIN_DBL_MAX;
777            else
778              largestGap = value2-value1;
779          }
780        }
781        bool tryIt= numberRows>200&&numberColumns>2000&&numberColumns>2*numberRows;
782        if (numberRows<1000&&numberColumns<3000)
783          tryIt=false;
784        if (notInteger)
785          tryIt=false;
786        if (largest/smallest>10||(largest/smallest>2.0&&largest>50))
787          tryIt=false;
788        if (tryIt) {
789          if (largest/smallest>2.0) {
790            nPasses = 10+numberColumns/100000;
791            nPasses = CoinMin(nPasses,50);
792            nPasses = CoinMax(nPasses,15);
793            if (numberRows>20000&&nPasses>5) {
794              // Might as well go for it
795              nPasses = CoinMax(nPasses,71);
796            } else if (numberRows>2000&&nPasses>5) {
797              nPasses = CoinMax(nPasses,50);
798            } else if (numberElements<3*numberColumns) {
799              nPasses=CoinMin(nPasses,10); // probably not worh it
800            }
801          } else if (largest/smallest>1.01||numberElements<=3*numberColumns) {
802            nPasses = 10+numberColumns/1000;
803            nPasses = CoinMin(nPasses,100);
804            nPasses = CoinMax(nPasses,30);
805            if (numberRows>25000) {
806              // Might as well go for it
807              nPasses = CoinMax(nPasses,71);
808            }
809            if (!largestGap)
810              nPasses *= 2;
811          } else {
812            nPasses = 10+numberColumns/1000;
813            nPasses = CoinMin(nPasses,200);
814            nPasses = CoinMax(nPasses,100);
815            if (!largestGap)
816              nPasses *= 2;
817          }
818        }
819        //printf("%d rows %d cols plus %c tryIt %c largest %g smallest %g largestGap %g npasses %d sprint %c\n",
820        //     numberRows,numberColumns,plusMinus ? 'Y' : 'N',
821        //     tryIt ? 'Y' :'N',largest,smallest,largestGap,nPasses,doSprint ? 'Y' :'N');
822        //exit(0);
823        if (!tryIt||nPasses<=5)
824          doIdiot=0;
825        if (doSprint) {
826          method = ClpSolve::usePrimalorSprint;
827        } else if (doIdiot) {
828          method = ClpSolve::usePrimal;
829        } else {
830          method = ClpSolve::useDual;
831        }
832      }
833    }
834  }
835  if (method==ClpSolve::usePrimalorSprint) {
836    if (doSprint<0) { 
837      if (numberElements<500000) {
838        // Small problem
839        if(numberRows*10>numberColumns||numberColumns<6000
840           ||(numberRows*20>numberColumns&&!plusMinus))
841          method=ClpSolve::usePrimal; // switch off sprint
842      } else {
843        // larger problem
844        if(numberRows*8>numberColumns)
845          method=ClpSolve::usePrimal; // switch off sprint
846        // but make lightweight
847        if(numberRows*10>numberColumns||numberColumns<6000
848           ||(numberRows*20>numberColumns&&!plusMinus))
849          maxSprintPass=5;
850      }
851    } else if (doSprint==0) {
852      method=ClpSolve::usePrimal; // switch off sprint
853    }
854  }
855  if (method==ClpSolve::useDual) {
856    double * saveLower=NULL;
857    double * saveUpper=NULL;
858    if (presolve==ClpSolve::presolveOn) {
859      int numberInfeasibilities = model2->tightenPrimalBounds(0.0,0);
860      if (numberInfeasibilities) {
861        handler_->message(CLP_INFEASIBLE,messages_)
862          <<CoinMessageEol;
863        model2 = this;
864        presolve=ClpSolve::presolveOff;
865      }
866    } else if (numberRows_+numberColumns_>5000) {
867      // do anyway
868      saveLower = new double[numberRows_+numberColumns_];
869      CoinMemcpyN(model2->columnLower(),numberColumns_,saveLower);
870      CoinMemcpyN(model2->rowLower(),numberRows_,saveLower+numberColumns_);
871      saveUpper = new double[numberRows_+numberColumns_];
872      CoinMemcpyN(model2->columnUpper(),numberColumns_,saveUpper);
873      CoinMemcpyN(model2->rowUpper(),numberRows_,saveUpper+numberColumns_);
874      int numberInfeasibilities = model2->tightenPrimalBounds();
875      if (numberInfeasibilities) {
876        handler_->message(CLP_INFEASIBLE,messages_)
877          <<CoinMessageEol;
878        CoinMemcpyN(saveLower,numberColumns_,model2->columnLower());
879        CoinMemcpyN(saveLower+numberColumns_,numberRows_,model2->rowLower());
880        delete [] saveLower;
881        saveLower=NULL;
882        CoinMemcpyN(saveUpper,numberColumns_,model2->columnUpper());
883        CoinMemcpyN(saveUpper+numberColumns_,numberRows_,model2->rowUpper());
884        delete [] saveUpper;
885        saveUpper=NULL;
886      }
887    }
888#ifndef COIN_HAS_VOL
889    // switch off idiot and volume for now
890    doIdiot=0; 
891#endif
892    // pick up number passes
893    int nPasses=0;
894    int numberNotE=0;
895#ifndef SLIM_CLP
896    if ((doIdiot<0&&plusMinus)||doIdiot>0) {
897      // See if candidate for idiot
898      nPasses=0;
899      Idiot info(*model2);
900      // Get average number of elements per column
901      double ratio  = ((double) numberElements/(double) numberColumns);
902      // look at rhs
903      int iRow;
904      double largest=0.0;
905      double smallest = 1.0e30;
906      double largestGap=0.0;
907      for (iRow=0;iRow<numberRows;iRow++) {
908        double value1 = model2->rowLower_[iRow];
909        if (value1&&value1>-1.0e31) {
910          largest = CoinMax(largest,fabs(value1));
911          smallest=CoinMin(smallest,fabs(value1));
912        }
913        double value2 = model2->rowUpper_[iRow];
914        if (value2&&value2<1.0e31) {
915          largest = CoinMax(largest,fabs(value2));
916          smallest=CoinMin(smallest,fabs(value2));
917        }
918        if (value2>value1) {
919          numberNotE++;
920          if (value2>1.0e31||value1<-1.0e31)
921            largestGap = COIN_DBL_MAX;
922          else
923            largestGap = value2-value1;
924        }
925      }
926      if (doIdiot<0) {
927        if (numberRows>200&&numberColumns>5000&&ratio>=3.0&&
928            largest/smallest<1.1&&!numberNotE) {
929          nPasses = 71;
930        }
931      } 
932      if (doIdiot>0) {
933        nPasses=CoinMax(nPasses,doIdiot);
934        if (nPasses>70) {
935          info.setStartingWeight(1.0e3);
936          info.setDropEnoughFeasibility(0.01);
937        }
938      }
939      if (nPasses>20) {
940#ifdef COIN_HAS_VOL
941        int returnCode = solveWithVolume(model2,nPasses,saveDoIdiot);
942        if (!returnCode) {
943          time2 = CoinCpuTime();
944          timeIdiot = time2-timeX;
945          handler_->message(CLP_INTERVAL_TIMING,messages_)
946            <<"Idiot Crash"<<timeIdiot<<time2-time1
947            <<CoinMessageEol;
948          timeX=time2;
949        } else {
950          nPasses=0;
951        }
952#else
953        nPasses=0;
954#endif
955      } else {
956        nPasses=0;
957      }
958    }
959#endif
960    if (doCrash) {
961      switch(doCrash) {
962        // standard
963      case 1:
964        model2->crash(1000,1);
965        break;
966        // As in paper by Solow and Halim (approx)
967      case 2:
968      case 3:
969        model2->crash(model2->dualBound(),0);
970        break;
971        // Just put free in basis
972      case 4:
973        model2->crash(0.0,3);
974        break;
975      }
976    }
977    if (!nPasses) {
978      int saveOptions = model2->specialOptions();
979      if (model2->numberRows()>100)
980        model2->setSpecialOptions(saveOptions|64); // go as far as possible
981      //int numberRows = model2->numberRows();
982      //int numberColumns = model2->numberColumns();
983      if (dynamic_cast< ClpPackedMatrix*>(matrix_)) {
984        // See if original wanted vector
985        ClpPackedMatrix * clpMatrixO = dynamic_cast< ClpPackedMatrix*>(matrix_);
986        ClpMatrixBase * matrix = model2->clpMatrix();
987        if (dynamic_cast< ClpPackedMatrix*>(matrix)&&clpMatrixO->wantsSpecialColumnCopy()) {
988          ClpPackedMatrix * clpMatrix = dynamic_cast< ClpPackedMatrix*>(matrix);
989          clpMatrix->makeSpecialColumnCopy();
990          //model2->setSpecialOptions(model2->specialOptions()|256); // to say no row copy for comparisons
991          model2->dual(0);
992          clpMatrix->releaseSpecialColumnCopy();
993        } else {
994          model2->dual(0);
995        }
996      } else {
997        model2->dual(0);
998      }
999    } else if (!numberNotE&&0) {
1000      // E so we can do in another way
1001      double * pi = model2->dualRowSolution();
1002      int i;
1003      int numberColumns = model2->numberColumns();
1004      int numberRows = model2->numberRows();
1005      double * saveObj = new double[numberColumns];
1006      CoinMemcpyN(model2->objective(),numberColumns,saveObj);
1007      CoinMemcpyN(model2->objective(),
1008             numberColumns,model2->dualColumnSolution());
1009      model2->clpMatrix()->transposeTimes(-1.0,pi,model2->dualColumnSolution());
1010      CoinMemcpyN(model2->dualColumnSolution(),
1011             numberColumns,model2->objective());
1012      const double * rowsol = model2->primalRowSolution();
1013      double offset=0.0;
1014      for (i=0;i<numberRows;i++) {
1015        offset += pi[i]*rowsol[i];
1016      }
1017      double value2;
1018      model2->getDblParam(ClpObjOffset,value2);
1019      //printf("Offset %g %g\n",offset,value2);
1020      model2->setDblParam(ClpObjOffset,value2-offset);
1021      model2->setPerturbation(51);
1022      //model2->setRowObjective(pi);
1023      // zero out pi
1024      //memset(pi,0,numberRows*sizeof(double));
1025      // Could put some in basis - only partially tested
1026      model2->allSlackBasis(); 
1027      //model2->factorization()->maximumPivots(200);
1028      //model2->setLogLevel(63);
1029      // solve
1030      model2->dual(0);
1031      model2->setDblParam(ClpObjOffset,value2);
1032      CoinMemcpyN(saveObj,numberColumns,model2->objective());
1033      // zero out pi
1034      //memset(pi,0,numberRows*sizeof(double));
1035      //model2->setRowObjective(pi);
1036      delete [] saveObj;
1037      //model2->dual(0);
1038      model2->setPerturbation(50);
1039      model2->primal();
1040    } else {
1041      // solve
1042      model2->setPerturbation(100);
1043      model2->dual(2);
1044      model2->setPerturbation(50);
1045      model2->dual(0);
1046    }
1047    if (saveLower) {
1048      CoinMemcpyN(saveLower,numberColumns_,model2->columnLower());
1049      CoinMemcpyN(saveLower+numberColumns_,numberRows_,model2->rowLower());
1050      delete [] saveLower;
1051      saveLower=NULL;
1052      CoinMemcpyN(saveUpper,numberColumns_,model2->columnUpper());
1053      CoinMemcpyN(saveUpper+numberColumns_,numberRows_,model2->rowUpper());
1054      delete [] saveUpper;
1055      saveUpper=NULL;
1056    }
1057    time2 = CoinCpuTime();
1058    timeCore = time2-timeX;
1059    handler_->message(CLP_INTERVAL_TIMING,messages_)
1060      <<"Dual"<<timeCore<<time2-time1
1061      <<CoinMessageEol;
1062    timeX=time2;
1063  } else if (method==ClpSolve::usePrimal) {
1064#ifndef SLIM_CLP
1065    if (doIdiot) {
1066      int nPasses=0;
1067      Idiot info(*model2);
1068      // Get average number of elements per column
1069      double ratio  = ((double) numberElements/(double) numberColumns);
1070      // look at rhs
1071      int iRow;
1072      double largest=0.0;
1073      double smallest = 1.0e30;
1074      double largestGap=0.0;
1075      int numberNotE=0;
1076      for (iRow=0;iRow<numberRows;iRow++) {
1077        double value1 = model2->rowLower_[iRow];
1078        if (value1&&value1>-1.0e31) {
1079          largest = CoinMax(largest,fabs(value1));
1080          smallest=CoinMin(smallest,fabs(value1));
1081        }
1082        double value2 = model2->rowUpper_[iRow];
1083        if (value2&&value2<1.0e31) {
1084          largest = CoinMax(largest,fabs(value2));
1085          smallest=CoinMin(smallest,fabs(value2));
1086        }
1087        if (value2>value1) {
1088          numberNotE++;
1089          if (value2>1.0e31||value1<-1.0e31)
1090            largestGap = COIN_DBL_MAX;
1091          else
1092            largestGap = value2-value1;
1093        }
1094      }
1095      bool increaseSprint=plusMinus;
1096      if (!plusMinus) {
1097        // If 90% +- 1 then go for sprint
1098        if (statistics[0]>=0&&10*statistics[2]<statistics[0]+statistics[1])
1099          increaseSprint=true;
1100      }
1101      bool tryIt= numberRows>200&&numberColumns>2000&&numberColumns>2*numberRows;
1102      if (numberRows<1000&&numberColumns<3000)
1103        tryIt=false;
1104      if (tryIt) {
1105        if (increaseSprint) {
1106          info.setStartingWeight(1.0e3);
1107          info.setReduceIterations(6);
1108          // also be more lenient on infeasibilities
1109          info.setDropEnoughFeasibility(0.5*info.getDropEnoughFeasibility());
1110          info.setDropEnoughWeighted(-2.0);
1111          if (largest/smallest>2.0) {
1112            nPasses = 10+numberColumns/100000;
1113            nPasses = CoinMin(nPasses,50);
1114            nPasses = CoinMax(nPasses,15);
1115            if (numberRows>20000&&nPasses>5) {
1116              // Might as well go for it
1117              nPasses = CoinMax(nPasses,71);
1118            } else if (numberRows>2000&&nPasses>5) {
1119              nPasses = CoinMax(nPasses,50);
1120            } else if (numberElements<3*numberColumns) {
1121              nPasses=CoinMin(nPasses,10); // probably not worh it
1122              if (doIdiot<0)
1123                info.setLightweight(1); // say lightweight idiot
1124            } else {
1125              if (doIdiot<0)
1126                info.setLightweight(1); // say lightweight idiot
1127            }
1128          } else if (largest/smallest>1.01||numberElements<=3*numberColumns) {
1129            nPasses = 10+numberColumns/1000;
1130            nPasses = CoinMin(nPasses,100);
1131            nPasses = CoinMax(nPasses,30);
1132            if (numberRows>25000) {
1133              // Might as well go for it
1134              nPasses = CoinMax(nPasses,71);
1135            }
1136            if (!largestGap)
1137              nPasses *= 2;
1138          } else {
1139            nPasses = 10+numberColumns/1000;
1140            nPasses = CoinMin(nPasses,200);
1141            nPasses = CoinMax(nPasses,100);
1142            info.setStartingWeight(1.0e-1);
1143            info.setReduceIterations(6);
1144            if (!largestGap)
1145              nPasses *= 2;
1146            //info.setFeasibilityTolerance(1.0e-7);
1147          }
1148          // If few passes - don't bother
1149          if (nPasses<=5)
1150            nPasses=0;
1151        } else {
1152          if (doIdiot<0)
1153            info.setLightweight(1); // say lightweight idiot
1154          if (largest/smallest>1.01||numberNotE||statistics[2]>statistics[0]+statistics[1]) {
1155            if (numberRows>25000||numberColumns>5*numberRows) {
1156              nPasses = 50;
1157            } else if (numberColumns>4*numberRows) {
1158              nPasses = 20;
1159            } else {
1160              nPasses=5;
1161            }
1162          } else {
1163            if (numberRows>25000||numberColumns>5*numberRows) {
1164              nPasses = 50;
1165              info.setLightweight(0); // say not lightweight idiot
1166            } else if (numberColumns>4*numberRows) {
1167              nPasses = 20;
1168            } else {
1169              nPasses=15;
1170            }
1171          }
1172          if (ratio<3.0) { 
1173            nPasses=(int) ((ratio*(double) nPasses)/4.0); // probably not worh it
1174          } else {
1175            nPasses = CoinMax(nPasses,5);
1176          }
1177          if (numberRows>25000&&nPasses>5) {
1178            // Might as well go for it
1179            nPasses = CoinMax(nPasses,71);
1180          } else if (increaseSprint) {
1181            nPasses *= 2;
1182            nPasses=CoinMin(nPasses,71);
1183          } else if (nPasses==5&&ratio>5.0) {
1184            nPasses = (int) (((double) nPasses)*(ratio/5.0)); // increase if lots of elements per column
1185          }
1186          if (nPasses<=5)
1187            nPasses=0;
1188          //info.setStartingWeight(1.0e-1);
1189        }
1190      }
1191      if (doIdiot>0) {
1192        // pick up number passes
1193        nPasses=options.getExtraInfo(1);
1194        if (nPasses>70) {
1195          info.setStartingWeight(1.0e3);
1196          info.setReduceIterations(6);
1197          if (nPasses>=5000) {
1198            int k= nPasses&100;
1199            nPasses /= 100;
1200            info.setReduceIterations(3);
1201            if (k)
1202              info.setStartingWeight(1.0e2);
1203          }
1204          // also be more lenient on infeasibilities
1205          info.setDropEnoughFeasibility(0.5*info.getDropEnoughFeasibility());
1206          info.setDropEnoughWeighted(-2.0);
1207        } else if (nPasses>=50) {
1208          info.setStartingWeight(1.0e3);
1209          //info.setReduceIterations(6);
1210        } 
1211        // For experimenting
1212        if (nPasses<70&&(nPasses%10)>0&&(nPasses%10)<4) {
1213          info.setStartingWeight(1.0e3);
1214          info.setLightweight(nPasses%10); // special testing
1215#ifdef COIN_DEVELOP
1216          printf("warning - odd lightweight %d\n",nPasses%10);
1217          //info.setReduceIterations(6);
1218#endif
1219        }
1220      }
1221      if (nPasses) {
1222        doCrash=0;
1223#if 0
1224        double * solution = model2->primalColumnSolution();
1225        int iColumn;
1226        double * saveLower = new double[numberColumns];
1227        CoinMemcpyN(model2->columnLower(),numberColumns,saveLower);
1228        double * saveUpper = new double[numberColumns];
1229        CoinMemcpyN(model2->columnUpper(),numberColumns,saveUpper);
1230        printf("doing tighten before idiot\n");
1231        model2->tightenPrimalBounds();
1232        // Move solution
1233        double * columnLower = model2->columnLower();
1234        double * columnUpper = model2->columnUpper();
1235        for (iColumn=0;iColumn<numberColumns;iColumn++) {
1236          if (columnLower[iColumn]>0.0)
1237            solution[iColumn]=columnLower[iColumn];
1238          else if (columnUpper[iColumn]<0.0)
1239            solution[iColumn]=columnUpper[iColumn];
1240          else
1241            solution[iColumn]=0.0;
1242        }
1243        CoinMemcpyN(saveLower,numberColumns,columnLower);
1244        CoinMemcpyN(saveUpper,numberColumns,columnUpper);
1245        delete [] saveLower;
1246        delete [] saveUpper;
1247#else
1248        // Allow for crossover
1249        //if (doIdiot>0)
1250          info.setStrategy(512|info.getStrategy());
1251        // Allow for scaling
1252        info.setStrategy(32|info.getStrategy());
1253        info.crash(nPasses,model2->messageHandler(),model2->messagesPointer());
1254#endif
1255        time2 = CoinCpuTime();
1256        timeIdiot = time2-timeX;
1257        handler_->message(CLP_INTERVAL_TIMING,messages_)
1258          <<"Idiot Crash"<<timeIdiot<<time2-time1
1259          <<CoinMessageEol;
1260        timeX=time2;
1261      }
1262    }
1263#endif
1264    // ?
1265    if (doCrash) {
1266      switch(doCrash) {
1267        // standard
1268      case 1:
1269        model2->crash(1000,1);
1270        break;
1271        // As in paper by Solow and Halim (approx)
1272      case 2:
1273        model2->crash(model2->dualBound(),0);
1274        break;
1275        // My take on it
1276      case 3:
1277        model2->crash(model2->dualBound(),-1);
1278        break;
1279        // Just put free in basis
1280      case 4:
1281        model2->crash(0.0,3);
1282        break;
1283      }
1284    }
1285#ifndef SLIM_CLP
1286    if (doSlp>0&&objective_->type()==2) {
1287      model2->nonlinearSLP(doSlp,1.0e-5);
1288    }
1289#endif
1290    if (dynamic_cast< ClpPackedMatrix*>(matrix_)) {
1291      // See if original wanted vector
1292      ClpPackedMatrix * clpMatrixO = dynamic_cast< ClpPackedMatrix*>(matrix_);
1293      ClpMatrixBase * matrix = model2->clpMatrix();
1294      if (dynamic_cast< ClpPackedMatrix*>(matrix)&&clpMatrixO->wantsSpecialColumnCopy()) {
1295        ClpPackedMatrix * clpMatrix = dynamic_cast< ClpPackedMatrix*>(matrix);
1296        clpMatrix->makeSpecialColumnCopy();
1297        //model2->setSpecialOptions(model2->specialOptions()|256); // to say no row copy for comparisons
1298        model2->primal(primalStartup);
1299        clpMatrix->releaseSpecialColumnCopy();
1300      } else {
1301        model2->primal(primalStartup);
1302      }
1303    } else {
1304      model2->primal(primalStartup);
1305    }
1306    time2 = CoinCpuTime();
1307    timeCore = time2-timeX;
1308    handler_->message(CLP_INTERVAL_TIMING,messages_)
1309      <<"Primal"<<timeCore<<time2-time1
1310      <<CoinMessageEol;
1311    timeX=time2;
1312  } else if (method==ClpSolve::usePrimalorSprint) {
1313    // Sprint
1314    /*
1315      This driver implements what I called Sprint when I introduced the idea
1316      many years ago.  Cplex calls it "sifting" which I think is just as silly.
1317      When I thought of this trivial idea
1318      it reminded me of an LP code of the 60's called sprint which after
1319      every factorization took a subset of the matrix into memory (all
1320      64K words!) and then iterated very fast on that subset.  On the
1321      problems of those days it did not work very well, but it worked very
1322      well on aircrew scheduling problems where there were very large numbers
1323      of columns all with the same flavor.
1324    */
1325   
1326    /* The idea works best if you can get feasible easily.  To make it
1327       more general we can add in costed slacks */
1328   
1329    int originalNumberColumns = model2->numberColumns();
1330    int numberRows = model2->numberRows();
1331   
1332    // We will need arrays to choose variables.  These are too big but ..
1333    double * weight = new double [numberRows+originalNumberColumns];
1334    int * sort = new int [numberRows+originalNumberColumns];
1335    int numberSort=0;
1336    // We are going to add slacks to get feasible.
1337    // initial list will just be artificials
1338    int iColumn;
1339    const double * columnLower = model2->columnLower();
1340    const double * columnUpper = model2->columnUpper();
1341    double * columnSolution = model2->primalColumnSolution();
1342
1343    // See if we have costed slacks
1344    int * negSlack = new int[numberRows];
1345    int * posSlack = new int[numberRows];
1346    int iRow;
1347    for (iRow=0;iRow<numberRows;iRow++) {
1348      negSlack[iRow]=-1;
1349      posSlack[iRow]=-1;
1350    }
1351    const double * element = model2->matrix()->getElements();
1352    const int * row = model2->matrix()->getIndices();
1353    const CoinBigIndex * columnStart = model2->matrix()->getVectorStarts();
1354    const int * columnLength = model2->matrix()->getVectorLengths();
1355    //bool allSlack = (numberRowsBasic==numberRows);
1356    for (iColumn=0;iColumn<originalNumberColumns;iColumn++) {
1357      if (!columnSolution[iColumn]||fabs(columnSolution[iColumn])>1.0e20) {
1358        double value =0.0;
1359        if (columnLower[iColumn]>0.0)
1360          value = columnLower[iColumn];
1361        else if (columnUpper[iColumn]<0.0)
1362          value = columnUpper[iColumn];
1363        columnSolution[iColumn]=value;
1364      }
1365      if (columnLength[iColumn]==1) {
1366        int jRow=row[columnStart[iColumn]];
1367        if (!columnLower[iColumn]) {
1368          if (element[columnStart[iColumn]]>0.0&&posSlack[jRow]<0)
1369            posSlack[jRow]=iColumn;
1370          else if (element[columnStart[iColumn]]<0.0&&negSlack[jRow]<0)
1371            negSlack[jRow]=iColumn;
1372        } else if (!columnUpper[iColumn]) {
1373          if (element[columnStart[iColumn]]<0.0&&posSlack[jRow]<0)
1374            posSlack[jRow]=iColumn;
1375          else if (element[columnStart[iColumn]]>0.0&&negSlack[jRow]<0)
1376            negSlack[jRow]=iColumn;
1377        }
1378      }
1379    }
1380    // now see what that does to row solution
1381    double * rowSolution = model2->primalRowSolution();
1382    CoinZeroN (rowSolution,numberRows);
1383    model2->clpMatrix()->times(1.0,columnSolution,rowSolution);
1384    // See if we can adjust using costed slacks
1385    double penalty=CoinMin(infeasibilityCost_,1.0e10)*optimizationDirection_;
1386    const double * lower = model2->rowLower();
1387    const double * upper = model2->rowUpper();
1388    for (iRow=0;iRow<numberRows;iRow++) {
1389      if (lower[iRow]>rowSolution[iRow]+1.0e-8) {
1390        int jColumn = posSlack[iRow];
1391        if (jColumn>=0) {
1392          if (columnSolution[jColumn])
1393            continue;
1394          double difference = lower[iRow]-rowSolution[iRow];
1395          double elementValue = element[columnStart[jColumn]];
1396          if (elementValue>0.0) {
1397            double movement = CoinMin(difference/elementValue,columnUpper[jColumn]);
1398            columnSolution[jColumn] = movement;
1399            rowSolution[iRow] += movement*elementValue;
1400          } else {
1401            double movement = CoinMax(difference/elementValue,columnLower[jColumn]);
1402            columnSolution[jColumn] = movement;
1403            rowSolution[iRow] += movement*elementValue;
1404          }
1405        }
1406      } else if (upper[iRow]<rowSolution[iRow]-1.0e-8) {
1407        int jColumn = negSlack[iRow];
1408        if (jColumn>=0) {
1409          if (columnSolution[jColumn])
1410            continue;
1411          double difference = upper[iRow]-rowSolution[iRow];
1412          double elementValue = element[columnStart[jColumn]];
1413          if (elementValue<0.0) {
1414            double movement = CoinMin(difference/elementValue,columnUpper[jColumn]);
1415            columnSolution[jColumn] = movement;
1416            rowSolution[iRow] += movement*elementValue;
1417          } else {
1418            double movement = CoinMax(difference/elementValue,columnLower[jColumn]);
1419            columnSolution[jColumn] = movement;
1420            rowSolution[iRow] += movement*elementValue;
1421          }
1422        }
1423      }
1424    }
1425    delete [] negSlack;
1426    delete [] posSlack;
1427    int * addStarts = new int [numberRows+1];
1428    int * addRow = new int[numberRows];
1429    double * addElement = new double[numberRows];
1430    addStarts[0]=0;
1431    int numberArtificials=0;
1432    double * addCost = new double [numberRows];
1433    for (iRow=0;iRow<numberRows;iRow++) {
1434      if (lower[iRow]>rowSolution[iRow]+1.0e-8) {
1435        addRow[numberArtificials]=iRow;
1436        addElement[numberArtificials]=1.0;
1437        addCost[numberArtificials]=penalty;
1438        numberArtificials++;
1439        addStarts[numberArtificials]=numberArtificials;
1440      } else if (upper[iRow]<rowSolution[iRow]-1.0e-8) {
1441        addRow[numberArtificials]=iRow;
1442        addElement[numberArtificials]=-1.0;
1443        addCost[numberArtificials]=penalty;
1444        numberArtificials++;
1445        addStarts[numberArtificials]=numberArtificials;
1446      }
1447    }
1448    if (numberArtificials) {
1449      // need copy so as not to disturb original
1450      model2 = new ClpSimplex(*model2);
1451    }
1452    model2->addColumns(numberArtificials,NULL,NULL,addCost,
1453                       addStarts,addRow,addElement);
1454    delete [] addStarts;
1455    delete [] addRow;
1456    delete [] addElement;
1457    delete [] addCost;
1458    // look at rhs to see if to perturb
1459    double largest=0.0;
1460    double smallest = 1.0e30;
1461    for (iRow=0;iRow<numberRows;iRow++) {
1462      double value;
1463      value = fabs(model2->rowLower_[iRow]);
1464      if (value&&value<1.0e30) {
1465        largest = CoinMax(largest,value);
1466        smallest=CoinMin(smallest,value);
1467      }
1468      value = fabs(model2->rowUpper_[iRow]);
1469      if (value&&value<1.0e30) {
1470        largest = CoinMax(largest,value);
1471        smallest=CoinMin(smallest,value);
1472      }
1473    }
1474    double * saveLower = NULL;
1475    double * saveUpper = NULL;
1476    if (largest<2.01*smallest) {
1477      // perturb - so switch off standard
1478      model2->setPerturbation(100);
1479      saveLower = new double[numberRows];
1480      CoinMemcpyN(model2->rowLower_,numberRows,saveLower);
1481      saveUpper = new double[numberRows];
1482      CoinMemcpyN(model2->rowUpper_,numberRows,saveUpper);
1483      double * lower = model2->rowLower();
1484      double * upper = model2->rowUpper();
1485      for (iRow=0;iRow<numberRows;iRow++) {
1486        double lowerValue=lower[iRow], upperValue=upper[iRow];
1487        double value = CoinDrand48();
1488        if (upperValue>lowerValue+primalTolerance_) {
1489          if (lowerValue>-1.0e20&&lowerValue)
1490            lowerValue -= value * 1.0e-4*fabs(lowerValue); 
1491          if (upperValue<1.0e20&&upperValue)
1492            upperValue += value * 1.0e-4*fabs(upperValue); 
1493        } else if (upperValue>0.0) {
1494          upperValue -= value * 1.0e-4*fabs(lowerValue); 
1495          lowerValue -= value * 1.0e-4*fabs(lowerValue); 
1496        } else if (upperValue<0.0) {
1497          upperValue += value * 1.0e-4*fabs(lowerValue); 
1498          lowerValue += value * 1.0e-4*fabs(lowerValue); 
1499        } else {
1500        }
1501        lower[iRow]=lowerValue;
1502        upper[iRow]=upperValue;
1503      }
1504    }
1505    int i;
1506    // Just do this number of passes in Sprint
1507    if (doSprint>0)
1508      maxSprintPass=options.getExtraInfo(1);
1509    // but if big use to get ratio
1510    double ratio=3;
1511    if (maxSprintPass>1000) {
1512      ratio = ((double) maxSprintPass)*0.0001;
1513      ratio = CoinMax(ratio,1.1);
1514      maxSprintPass= maxSprintPass %1000;
1515#ifdef COIN_DEVELOP
1516      printf("%d passes wanted with ratio of %g\n",maxSprintPass,ratio);
1517#endif
1518    }
1519    // Just take this number of columns in small problem
1520    int smallNumberColumns = (int) CoinMin(ratio*numberRows,(double) numberColumns);
1521    smallNumberColumns = CoinMax(smallNumberColumns,3000);
1522    smallNumberColumns = CoinMin(smallNumberColumns,numberColumns);
1523    //int smallNumberColumns = CoinMin(12*numberRows/10,numberColumns);
1524    //smallNumberColumns = CoinMax(smallNumberColumns,3000);
1525    //smallNumberColumns = CoinMax(smallNumberColumns,numberRows+1000);
1526    // redo as may have changed
1527    columnLower = model2->columnLower();
1528    columnUpper = model2->columnUpper();
1529    columnSolution = model2->primalColumnSolution();
1530    // Set up initial list
1531    numberSort=0;
1532    if (numberArtificials) {
1533      numberSort=numberArtificials;
1534      for (i=0;i<numberSort;i++)
1535        sort[i] = i+originalNumberColumns;
1536    } 
1537    // maybe a solution there already
1538    for (iColumn=0;iColumn<originalNumberColumns;iColumn++) {
1539      if (model2->getColumnStatus(iColumn)==basic)
1540        sort[numberSort++]=iColumn;
1541    }
1542    for (iColumn=0;iColumn<originalNumberColumns;iColumn++) {
1543      if (model2->getColumnStatus(iColumn)!=basic) {
1544        if (columnSolution[iColumn]>columnLower[iColumn]&&
1545            columnSolution[iColumn]<columnUpper[iColumn]&&
1546            columnSolution[iColumn])
1547          sort[numberSort++]=iColumn;
1548      }
1549    }
1550    numberSort = CoinMin(numberSort,smallNumberColumns);
1551   
1552    int numberColumns = model2->numberColumns();
1553    double * fullSolution = model2->primalColumnSolution();
1554   
1555   
1556    int iPass;
1557    double lastObjective=1.0e31;
1558    // It will be safe to allow dense
1559    model2->setInitialDenseFactorization(true);
1560   
1561    // We will be using all rows
1562    int * whichRows = new int [numberRows];
1563    for (iRow=0;iRow<numberRows;iRow++)
1564      whichRows[iRow]=iRow;
1565    double originalOffset;
1566    model2->getDblParam(ClpObjOffset,originalOffset);
1567    int totalIterations=0;
1568    for (iPass=0;iPass<maxSprintPass;iPass++) {
1569      //printf("Bug until submodel new version\n");
1570      //CoinSort_2(sort,sort+numberSort,weight);
1571      // Create small problem
1572      ClpSimplex small(model2,numberRows,whichRows,numberSort,sort);
1573      small.setPerturbation(model2->perturbation());
1574      small.setInfeasibilityCost(model2->infeasibilityCost());
1575      if (model2->factorizationFrequency()==200) {
1576        // User did not touch preset
1577        small.defaultFactorizationFrequency();
1578      }
1579      // now see what variables left out do to row solution
1580      double * rowSolution = model2->primalRowSolution();
1581      double * sumFixed = new double[numberRows];
1582      CoinZeroN (sumFixed,numberRows);
1583      int iRow,iColumn;
1584      // zero out ones in small problem
1585      for (iColumn=0;iColumn<numberSort;iColumn++) {
1586        int kColumn = sort[iColumn];
1587        fullSolution[kColumn]=0.0;
1588      }
1589      // Get objective offset
1590      double offset=0.0;
1591      const double * objective = model2->objective();
1592      for (iColumn=0;iColumn<numberColumns;iColumn++) 
1593        offset += fullSolution[iColumn]*objective[iColumn];
1594      small.setDblParam(ClpObjOffset,originalOffset-offset);
1595      model2->clpMatrix()->times(1.0,fullSolution,sumFixed);
1596     
1597      double * lower = small.rowLower();
1598      double * upper = small.rowUpper();
1599      for (iRow=0;iRow<numberRows;iRow++) {
1600        if (lower[iRow]>-1.0e50) 
1601          lower[iRow] -= sumFixed[iRow];
1602        if (upper[iRow]<1.0e50)
1603          upper[iRow] -= sumFixed[iRow];
1604        rowSolution[iRow] -= sumFixed[iRow];
1605      }
1606      delete [] sumFixed;
1607      // Solve
1608      if (interrupt)
1609        currentModel = &small;
1610      small.defaultFactorizationFrequency();
1611      if (dynamic_cast< ClpPackedMatrix*>(matrix_)) {
1612        // See if original wanted vector
1613        ClpPackedMatrix * clpMatrixO = dynamic_cast< ClpPackedMatrix*>(matrix_);
1614        ClpMatrixBase * matrix = small.clpMatrix();
1615        if (dynamic_cast< ClpPackedMatrix*>(matrix)&&clpMatrixO->wantsSpecialColumnCopy()) {
1616          ClpPackedMatrix * clpMatrix = dynamic_cast< ClpPackedMatrix*>(matrix);
1617          clpMatrix->makeSpecialColumnCopy();
1618          small.primal(1);
1619          clpMatrix->releaseSpecialColumnCopy();
1620        } else {
1621#if 1
1622          small.primal(1);
1623#else
1624          int numberColumns = small.numberColumns();
1625          int numberRows = small.numberRows();
1626          // Use dual region
1627          double * rhs = small.dualRowSolution();
1628          int * whichRow = new int[3*numberRows];
1629          int * whichColumn = new int[2*numberColumns];
1630          int nBound;
1631          ClpSimplex * small2 = ((ClpSimplexOther *) (&small))->crunch(rhs,whichRow,whichColumn,
1632                                                                        nBound,false,false);
1633          if (small2) {
1634            small2->primal(1);
1635            if (small2->problemStatus()==0) {
1636              small.setProblemStatus(0);
1637              ((ClpSimplexOther *) (&small))->afterCrunch(*small2,whichRow,whichColumn,nBound);
1638            } else {
1639              small2->primal(1);
1640              if (small2->problemStatus())
1641                small.primal(1);
1642            }
1643            delete small2;
1644          } else {
1645            small.primal(1);
1646          }
1647          delete [] whichRow;
1648          delete [] whichColumn;
1649#endif
1650        }
1651      } else {
1652        small.primal(1);
1653      }
1654      totalIterations += small.numberIterations();
1655      // move solution back
1656      const double * solution = small.primalColumnSolution();
1657      for (iColumn=0;iColumn<numberSort;iColumn++) {
1658        int kColumn = sort[iColumn];
1659        model2->setColumnStatus(kColumn,small.getColumnStatus(iColumn));
1660        fullSolution[kColumn]=solution[iColumn];
1661      }
1662      for (iRow=0;iRow<numberRows;iRow++) 
1663        model2->setRowStatus(iRow,small.getRowStatus(iRow));
1664      CoinMemcpyN(small.primalRowSolution(),
1665             numberRows,model2->primalRowSolution());
1666      // get reduced cost for large problem
1667      double * djs = model2->dualColumnSolution();
1668      CoinMemcpyN(model2->objective(),numberColumns,djs);
1669      model2->clpMatrix()->transposeTimes(-1.0,small.dualRowSolution(),djs);
1670      int numberNegative=0;
1671      double sumNegative = 0.0;
1672      // now massage weight so all basic in plus good djs
1673      // first count and do basic
1674      numberSort=0;
1675      for (iColumn=0;iColumn<numberColumns;iColumn++) {
1676        double dj = djs[iColumn]*optimizationDirection_;
1677        double value = fullSolution[iColumn];
1678        if (model2->getColumnStatus(iColumn)==ClpSimplex::basic) {
1679          sort[numberSort++] = iColumn;
1680        } else if (dj<-dualTolerance_&&value<columnUpper[iColumn]) {
1681          numberNegative++;
1682          sumNegative -= dj;
1683        } else if (dj>dualTolerance_&&value>columnLower[iColumn]) {
1684          numberNegative++;
1685          sumNegative += dj;
1686        }
1687      }
1688      handler_->message(CLP_SPRINT,messages_)
1689        <<iPass+1<<small.numberIterations()<<small.objectiveValue()<<sumNegative
1690        <<numberNegative
1691        <<CoinMessageEol;
1692      if ((small.objectiveValue()*optimizationDirection_>lastObjective-1.0e-7&&iPass>5)||
1693          (!small.numberIterations()&&iPass)||
1694          iPass==maxSprintPass-1||small.status()==3) {
1695       
1696        break; // finished
1697      } else {
1698        lastObjective = small.objectiveValue()*optimizationDirection_;
1699        double tolerance;
1700        double averageNegDj = sumNegative/((double) (numberNegative+1));
1701        if (numberNegative+numberSort>smallNumberColumns)
1702          tolerance = -dualTolerance_;
1703        else 
1704          tolerance = 10.0*averageNegDj;
1705        int saveN = numberSort;
1706        for (iColumn=0;iColumn<numberColumns;iColumn++) {
1707          double dj = djs[iColumn]*optimizationDirection_;
1708          double value = fullSolution[iColumn];
1709          if (model2->getColumnStatus(iColumn)!=ClpSimplex::basic) {
1710            if (dj<-dualTolerance_&&value<columnUpper[iColumn])
1711              dj = dj;
1712            else if (dj>dualTolerance_&&value>columnLower[iColumn])
1713              dj = -dj;
1714            else if (columnUpper[iColumn]>columnLower[iColumn])
1715              dj = fabs(dj);
1716            else
1717              dj = 1.0e50;
1718            if (dj<tolerance) {
1719              weight[numberSort] = dj;
1720              sort[numberSort++] = iColumn;
1721            }
1722          }
1723        }
1724        // sort
1725        CoinSort_2(weight+saveN,weight+numberSort,sort+saveN);
1726        numberSort = CoinMin(smallNumberColumns,numberSort);
1727      }
1728    }
1729    if (interrupt) 
1730      currentModel = model2;
1731    for (i=0;i<numberArtificials;i++)
1732      sort[i] = i + originalNumberColumns;
1733    model2->deleteColumns(numberArtificials,sort);
1734    delete [] weight;
1735    delete [] sort;
1736    delete [] whichRows;
1737    if (saveLower) {
1738      // unperturb and clean
1739      for (iRow=0;iRow<numberRows;iRow++) {
1740        double diffLower = saveLower[iRow]-model2->rowLower_[iRow];
1741        double diffUpper = saveUpper[iRow]-model2->rowUpper_[iRow];
1742        model2->rowLower_[iRow]=saveLower[iRow];
1743        model2->rowUpper_[iRow]=saveUpper[iRow];
1744        if (diffLower) 
1745          assert (!diffUpper||fabs(diffLower-diffUpper)<1.0e-5);
1746        else
1747          diffLower = diffUpper;
1748        model2->rowActivity_[iRow] += diffLower;
1749      }
1750      delete [] saveLower;
1751      delete [] saveUpper;
1752    }
1753    model2->primal(1);
1754    model2->setPerturbation(savePerturbation);
1755    time2 = CoinCpuTime();
1756    timeCore = time2-timeX;
1757    handler_->message(CLP_INTERVAL_TIMING,messages_)
1758      <<"Sprint"<<timeCore<<time2-time1
1759      <<CoinMessageEol;
1760    timeX=time2;
1761    model2->setNumberIterations(model2->numberIterations()+totalIterations);
1762  } else if (method==ClpSolve::useBarrier||method==ClpSolve::useBarrierNoCross) {
1763#ifndef SLIM_CLP
1764    //printf("***** experimental pretty crude barrier\n");
1765    //#define SAVEIT 2
1766#ifndef SAVEIT
1767#define BORROW
1768#endif
1769#ifdef BORROW
1770    ClpInterior barrier;
1771    barrier.borrowModel(*model2);
1772#else
1773    ClpInterior barrier(*model2);
1774#endif
1775    if (interrupt) 
1776      currentModel2 = &barrier;
1777    int barrierOptions = options.getSpecialOption(4);
1778    int aggressiveGamma=0;
1779    bool presolveInCrossover=false;
1780    bool scale=false;
1781    bool doKKT=false;
1782    if (barrierOptions&16) {
1783      barrierOptions &= ~16;
1784      doKKT=true;
1785    }
1786    if (barrierOptions&(32+64+128)) {
1787      aggressiveGamma=(barrierOptions&(32+64+128))>>5;
1788      barrierOptions &= ~(32+64+128);
1789    }
1790    if (barrierOptions&256) {
1791      barrierOptions &= ~256;
1792      presolveInCrossover=true;
1793    }
1794    if (barrierOptions&8) {
1795      barrierOptions &= ~8;
1796      scale=true;
1797    }
1798#ifdef COIN_DEVELOP
1799#ifndef FAST_BARRIER
1800    if (!numberBarrier)
1801      std::cout<<"Warning - the default ordering is just on row counts! "
1802               <<"The factorization is being improved"<<std::endl;
1803    numberBarrier++;
1804#endif
1805#endif
1806    // If quadratic force KKT
1807    if (quadraticObj) {
1808      doKKT=true;
1809    }
1810    switch (barrierOptions) {
1811    case 0:
1812    default:
1813      if (!doKKT) {
1814        ClpCholeskyBase * cholesky = new ClpCholeskyBase();
1815        barrier.setCholesky(cholesky);
1816      } else {
1817        ClpCholeskyBase * cholesky = new ClpCholeskyBase();
1818        cholesky->setKKT(true);
1819        barrier.setCholesky(cholesky);
1820      }
1821      break;
1822    case 1:
1823      if (!doKKT) {
1824        ClpCholeskyDense * cholesky = new ClpCholeskyDense();
1825        barrier.setCholesky(cholesky);
1826      } else {
1827        ClpCholeskyDense * cholesky = new ClpCholeskyDense();
1828        cholesky->setKKT(true);
1829        barrier.setCholesky(cholesky);
1830      }
1831      break;
1832#ifdef WSSMP_BARRIER
1833    case 2:
1834      {
1835        ClpCholeskyWssmp * cholesky = new ClpCholeskyWssmp(CoinMax(100,model2->numberRows()/10));
1836        barrier.setCholesky(cholesky);
1837        assert (!doKKT);
1838      }
1839      break;
1840    case 3:
1841      if (!doKKT) {
1842        ClpCholeskyWssmp * cholesky = new ClpCholeskyWssmp();
1843        barrier.setCholesky(cholesky);
1844      } else {
1845        ClpCholeskyWssmpKKT * cholesky = new ClpCholeskyWssmpKKT(CoinMax(100,model2->numberRows()/10));
1846        barrier.setCholesky(cholesky);
1847      }
1848      break;
1849#endif
1850#ifdef UFL_BARRIER
1851    case 4:
1852      if (!doKKT) {
1853        ClpCholeskyUfl * cholesky = new ClpCholeskyUfl();
1854        barrier.setCholesky(cholesky);
1855      } else {
1856        ClpCholeskyUfl * cholesky = new ClpCholeskyUfl();
1857        cholesky->setKKT(true);
1858        barrier.setCholesky(cholesky);
1859      }
1860      break;
1861#endif
1862#ifdef TAUCS_BARRIER
1863    case 5:
1864      {
1865        ClpCholeskyTaucs * cholesky = new ClpCholeskyTaucs();
1866        barrier.setCholesky(cholesky);
1867        assert (!doKKT);
1868      }
1869      break;
1870#endif
1871    }
1872    int numberRows = model2->numberRows();
1873    int numberColumns = model2->numberColumns();
1874    int saveMaxIts = model2->maximumIterations();
1875    if (saveMaxIts<1000) {
1876      barrier.setMaximumBarrierIterations(saveMaxIts);
1877      model2->setMaximumIterations(1000000);
1878    }
1879#ifndef SAVEIT
1880    //barrier.setDiagonalPerturbation(1.0e-25);
1881    if (aggressiveGamma) {
1882      switch (aggressiveGamma) {
1883      case 1:
1884        barrier.setGamma(1.0e-5);
1885        barrier.setDelta(1.0e-5);
1886        break;
1887      case 2:
1888        barrier.setGamma(1.0e-5);
1889        break;
1890      case 3:
1891        barrier.setDelta(1.0e-5);
1892        break;
1893      case 4:
1894        barrier.setGamma(1.0e-3);
1895        barrier.setDelta(1.0e-3);
1896        break;
1897      case 5:
1898        barrier.setGamma(1.0e-3);
1899        break;
1900      case 6:
1901        barrier.setDelta(1.0e-3);
1902        break;
1903      }
1904    }
1905    if (scale)
1906      barrier.scaling(1);
1907    else
1908      barrier.scaling(0);
1909    barrier.primalDual();
1910#elif SAVEIT==1
1911    barrier.primalDual();
1912#else
1913    model2->restoreModel("xx.save");
1914    // move solutions
1915    CoinMemcpyN(model2->primalRowSolution(),
1916                numberRows,barrier.primalRowSolution());
1917    CoinMemcpyN(model2->dualRowSolution(),
1918                numberRows,barrier.dualRowSolution());
1919    CoinMemcpyN(model2->primalColumnSolution(),
1920                numberColumns,barrier.primalColumnSolution());
1921    CoinMemcpyN(model2->dualColumnSolution(),
1922                numberColumns,barrier.dualColumnSolution());
1923#endif
1924    time2 = CoinCpuTime();
1925    timeCore = time2-timeX;
1926    handler_->message(CLP_INTERVAL_TIMING,messages_)
1927      <<"Barrier"<<timeCore<<time2-time1
1928      <<CoinMessageEol;
1929    timeX=time2;
1930    int maxIts = barrier.maximumBarrierIterations();
1931    int barrierStatus=barrier.status();
1932    double gap = barrier.complementarityGap();
1933    // get which variables are fixed
1934    double * saveLower=NULL;
1935    double * saveUpper=NULL;
1936    ClpPresolve pinfo2;
1937    ClpSimplex * saveModel2=NULL;
1938    bool extraPresolve=false;
1939    int numberFixed = barrier.numberFixed();
1940    if (numberFixed) {
1941      int numberRows = barrier.numberRows();
1942      int numberColumns = barrier.numberColumns();
1943      int numberTotal = numberRows+numberColumns;
1944      saveLower = new double [numberTotal];
1945      saveUpper = new double [numberTotal];
1946      CoinMemcpyN(barrier.columnLower(),numberColumns,saveLower);
1947      CoinMemcpyN(barrier.rowLower(),numberRows,saveLower+numberColumns);
1948      CoinMemcpyN(barrier.columnUpper(),numberColumns,saveUpper);
1949      CoinMemcpyN(barrier.rowUpper(),numberRows,saveUpper+numberColumns);
1950    }
1951    if (numberFixed*20>barrier.numberRows()&&numberFixed>5000&&
1952        presolveInCrossover) {
1953      // may as well do presolve
1954      barrier.fixFixed();
1955      saveModel2=model2;
1956      extraPresolve=true;
1957    } else if (numberFixed) {
1958      // Set fixed to bounds (may have restored earlier solution)
1959      barrier.fixFixed(false);
1960    }
1961#ifdef BORROW   
1962    barrier.returnModel(*model2);
1963    double * rowPrimal = new double [numberRows];
1964    double * columnPrimal = new double [numberColumns];
1965    double * rowDual = new double [numberRows];
1966    double * columnDual = new double [numberColumns];
1967    // move solutions other way
1968    CoinMemcpyN(model2->primalRowSolution(),
1969                numberRows,rowPrimal);
1970    CoinMemcpyN(model2->dualRowSolution(),
1971                numberRows,rowDual);
1972    CoinMemcpyN(model2->primalColumnSolution(),
1973                numberColumns,columnPrimal);
1974    CoinMemcpyN(model2->dualColumnSolution(),
1975                  numberColumns,columnDual);
1976#else
1977    double * rowPrimal = barrier.primalRowSolution();
1978    double * columnPrimal = barrier.primalColumnSolution();
1979    double * rowDual = barrier.dualRowSolution();
1980    double * columnDual = barrier.dualColumnSolution();
1981    // move solutions
1982    CoinMemcpyN(rowPrimal,
1983                numberRows,model2->primalRowSolution());
1984    CoinMemcpyN(rowDual,
1985                numberRows,model2->dualRowSolution());
1986    CoinMemcpyN(columnPrimal,
1987                numberColumns,model2->primalColumnSolution());
1988    CoinMemcpyN(columnDual,
1989                  numberColumns,model2->dualColumnSolution());
1990#endif
1991    if (saveModel2) {
1992      // do presolve
1993      model2 = pinfo2.presolvedModel(*model2,dblParam_[ClpPresolveTolerance],
1994                                    false,5,true);
1995      if (!model2) {
1996        model2=saveModel2;
1997        saveModel2=NULL;
1998        int numberRows = model2->numberRows();
1999        int numberColumns = model2->numberColumns();
2000        CoinMemcpyN(saveLower,numberColumns,model2->columnLower());
2001        CoinMemcpyN(saveLower+numberColumns,numberRows,model2->rowLower());
2002        delete [] saveLower;
2003        CoinMemcpyN(saveUpper,numberColumns,model2->columnUpper());
2004        CoinMemcpyN(saveUpper+numberColumns,numberRows,model2->rowUpper());
2005        delete [] saveUpper;
2006        saveLower=NULL;
2007        saveUpper=NULL;
2008      }
2009    }
2010    if (method==ClpSolve::useBarrier) {
2011      if (maxIts&&barrierStatus<4&&!quadraticObj) {
2012        //printf("***** crossover - needs more thought on difficult models\n");
2013#if SAVEIT==1
2014        model2->ClpSimplex::saveModel("xx.save");
2015#endif
2016        // make sure no status left
2017        model2->createStatus();
2018        // solve
2019        model2->setPerturbation(100);
2020        if (model2->factorizationFrequency()==200) {
2021          // User did not touch preset
2022          model2->defaultFactorizationFrequency();
2023        }
2024#if 1
2025        // throw some into basis
2026        {
2027          int numberRows = model2->numberRows();
2028          int numberColumns = model2->numberColumns();
2029          double * dsort = new double[numberColumns];
2030          int * sort = new int[numberColumns];
2031          int n=0;
2032          const double * columnLower = model2->columnLower();
2033          const double * columnUpper = model2->columnUpper();
2034          double * primalSolution = model2->primalColumnSolution();
2035          const double * dualSolution = model2->dualColumnSolution();
2036          double tolerance = 10.0*primalTolerance_;
2037          int i;
2038          for ( i=0;i<numberRows;i++) 
2039            model2->setRowStatus(i,superBasic);
2040          for ( i=0;i<numberColumns;i++) {
2041            double distance = CoinMin(columnUpper[i]-primalSolution[i],
2042                                      primalSolution[i]-columnLower[i]);
2043            if (distance>tolerance) {
2044              if (fabs(dualSolution[i])<1.0e-5)
2045                distance *= 100.0;
2046              dsort[n]=-distance;
2047              sort[n++]=i;
2048              model2->setStatus(i,superBasic);
2049            } else if (distance>primalTolerance_) {
2050              model2->setStatus(i,superBasic);
2051            } else if (primalSolution[i]<=columnLower[i]+primalTolerance_) {
2052              model2->setStatus(i,atLowerBound);
2053              primalSolution[i]=columnLower[i];
2054            } else {
2055              model2->setStatus(i,atUpperBound);
2056              primalSolution[i]=columnUpper[i];
2057            }
2058          }
2059          CoinSort_2(dsort,dsort+n,sort);
2060          n = CoinMin(numberRows,n);
2061          for ( i=0;i<n;i++) {
2062            int iColumn = sort[i];
2063            model2->setStatus(iColumn,basic);
2064          }
2065          delete [] sort;
2066          delete [] dsort;
2067        }
2068        // model2->allSlackBasis();
2069        if (gap<1.0e-3*((double) (numberRows+numberColumns))) {
2070          if (saveUpper) {
2071            int numberRows = model2->numberRows();
2072            int numberColumns = model2->numberColumns();
2073            CoinMemcpyN(saveLower,numberColumns,model2->columnLower());
2074            CoinMemcpyN(saveLower+numberColumns,numberRows,model2->rowLower());
2075            delete [] saveLower;
2076            CoinMemcpyN(saveUpper,numberColumns,model2->columnUpper());
2077            CoinMemcpyN(saveUpper+numberColumns,numberRows,model2->rowUpper());
2078            delete [] saveUpper;
2079            saveLower=NULL;
2080            saveUpper=NULL;
2081          }
2082          int numberRows = model2->numberRows();
2083          int numberColumns = model2->numberColumns();
2084          // just primal values pass
2085          double saveScale = model2->objectiveScale();
2086          model2->setObjectiveScale(1.0e-3);
2087          model2->primal(2);
2088          model2->setObjectiveScale(saveScale);
2089          // save primal solution and copy back dual
2090          CoinMemcpyN(model2->primalRowSolution(),
2091                      numberRows,rowPrimal);
2092          CoinMemcpyN(rowDual,
2093                      numberRows,model2->dualRowSolution());
2094          CoinMemcpyN(model2->primalColumnSolution(),
2095                      numberColumns,columnPrimal);
2096          CoinMemcpyN(columnDual,
2097                      numberColumns,model2->dualColumnSolution());
2098          //model2->primal(1);
2099          // clean up reduced costs and flag variables
2100          {
2101            double * dj = model2->dualColumnSolution();
2102            double * cost = model2->objective();
2103            double * saveCost = new double[numberColumns];
2104            CoinMemcpyN(cost,numberColumns,saveCost);
2105            double * saveLower = new double[numberColumns];
2106            double * lower = model2->columnLower();
2107            CoinMemcpyN(lower,numberColumns,saveLower);
2108            double * saveUpper = new double[numberColumns];
2109            double * upper = model2->columnUpper();
2110            CoinMemcpyN(upper,numberColumns,saveUpper);
2111            int i;
2112            double tolerance = 10.0*dualTolerance_;
2113            for ( i=0;i<numberColumns;i++) {
2114              if (model2->getStatus(i)==basic) {
2115                dj[i]=0.0;
2116              } else if (model2->getStatus(i)==atLowerBound) {
2117                if (optimizationDirection_*dj[i]<tolerance) {
2118                  if (optimizationDirection_*dj[i]<0.0) {
2119                    //if (dj[i]<-1.0e-3)
2120                    //printf("bad dj at lb %d %g\n",i,dj[i]);
2121                    cost[i] -= dj[i];
2122                    dj[i]=0.0;
2123                  }
2124                } else {
2125                  upper[i]=lower[i];
2126                }
2127              } else if (model2->getStatus(i)==atUpperBound) {
2128                if (optimizationDirection_*dj[i]>tolerance) {
2129                  if (optimizationDirection_*dj[i]>0.0) {
2130                    //if (dj[i]>1.0e-3)
2131                    //printf("bad dj at ub %d %g\n",i,dj[i]);
2132                    cost[i] -= dj[i];
2133                    dj[i]=0.0;
2134                  }
2135                } else {
2136                  lower[i]=upper[i];
2137                }
2138              }
2139            }
2140            // just dual values pass
2141            //model2->setLogLevel(63);
2142            //model2->setFactorizationFrequency(1);
2143            model2->dual(2);
2144            CoinMemcpyN(saveCost,numberColumns,cost);
2145            delete [] saveCost;
2146            CoinMemcpyN(saveLower,numberColumns,lower);
2147            delete [] saveLower;
2148            CoinMemcpyN(saveUpper,numberColumns,upper);
2149            delete [] saveUpper;
2150          }
2151          // and finish
2152          // move solutions
2153          CoinMemcpyN(rowPrimal,
2154                      numberRows,model2->primalRowSolution());
2155          CoinMemcpyN(columnPrimal,
2156                      numberColumns,model2->primalColumnSolution());
2157        }
2158        double saveScale = model2->objectiveScale();
2159        model2->setObjectiveScale(1.0e-3);
2160        model2->primal(2);
2161        model2->setObjectiveScale(saveScale);
2162        model2->primal(1);
2163#else
2164        // just primal
2165        model2->primal(1);
2166#endif
2167      } else if (barrierStatus==4) {
2168        // memory problems
2169        model2->setPerturbation(savePerturbation);
2170        model2->createStatus();
2171        model2->dual();
2172      } else if (maxIts&&quadraticObj) {
2173        // make sure no status left
2174        model2->createStatus();
2175        // solve
2176        model2->setPerturbation(100);
2177        model2->reducedGradient(1);
2178      }
2179    }
2180    model2->setMaximumIterations(saveMaxIts);
2181#ifdef BORROW
2182    delete [] rowPrimal;
2183    delete [] columnPrimal;
2184    delete [] rowDual;
2185    delete [] columnDual;
2186#endif
2187    if (extraPresolve) {
2188      pinfo2.postsolve(true);
2189      delete model2;
2190      model2=saveModel2;
2191    }
2192    if (saveUpper) {
2193      int numberRows = model2->numberRows();
2194      int numberColumns = model2->numberColumns();
2195      CoinMemcpyN(saveLower,numberColumns,model2->columnLower());
2196      CoinMemcpyN(saveLower+numberColumns,numberRows,model2->rowLower());
2197      delete [] saveLower;
2198      CoinMemcpyN(saveUpper,numberColumns,model2->columnUpper());
2199      CoinMemcpyN(saveUpper+numberColumns,numberRows,model2->rowUpper());
2200      delete [] saveUpper;
2201      saveLower=NULL;
2202      saveUpper=NULL;
2203      if (method!=ClpSolve::useBarrierNoCross) 
2204        model2->primal(1);
2205    }
2206    model2->setPerturbation(savePerturbation);
2207    time2 = CoinCpuTime();
2208    timeCore = time2-timeX;
2209    handler_->message(CLP_INTERVAL_TIMING,messages_)
2210      <<"Crossover"<<timeCore<<time2-time1
2211      <<CoinMessageEol;
2212    timeX=time2;
2213#else
2214    abort();
2215#endif
2216  } else {
2217    assert (method!=ClpSolve::automatic); // later
2218    time2=0.0;
2219  }
2220  if (saveMatrix&&model2==this) {
2221    // delete and replace
2222    delete model2->clpMatrix();
2223    model2->replaceMatrix(saveMatrix);
2224  }
2225  numberIterations = model2->numberIterations();
2226  finalStatus=model2->status();
2227  int finalSecondaryStatus = model2->secondaryStatus();
2228  if (presolve==ClpSolve::presolveOn) {
2229    int saveLevel = logLevel();
2230    if ((specialOptions_&1024)==0)
2231      setLogLevel(CoinMin(1,saveLevel));
2232    else
2233      setLogLevel(CoinMin(0,saveLevel));
2234    pinfo.postsolve(true);
2235    factorization_->areaFactor(model2->factorization()->adjustedAreaFactor());
2236    time2 = CoinCpuTime();
2237    timePresolve += time2-timeX;
2238    handler_->message(CLP_INTERVAL_TIMING,messages_)
2239      <<"Postsolve"<<time2-timeX<<time2-time1
2240      <<CoinMessageEol;
2241    timeX=time2;
2242    if (!presolveToFile)
2243      delete model2;
2244    if (interrupt)
2245      currentModel = this;
2246    // checkSolution(); already done by postSolve
2247    setLogLevel(saveLevel);
2248    if (finalStatus!=3&&(finalStatus||status()==-1)) {
2249      int savePerturbation = perturbation();
2250      if (!finalStatus||(moreSpecialOptions_&2)==0) {
2251        if (finalStatus==2) {
2252          // unbounded - get feasible first
2253          double save = optimizationDirection_;
2254          optimizationDirection_=0.0;
2255          primal(1);
2256          optimizationDirection_=save;
2257          primal(1);
2258        } else if (finalStatus==1) {
2259          dual();
2260        } else {
2261          setPerturbation(100);
2262          primal(1);
2263        }
2264      } else {
2265        // just set status
2266        problemStatus_=finalStatus;
2267      }
2268      setPerturbation(savePerturbation);
2269      numberIterations += numberIterations_;
2270      numberIterations_ = numberIterations;
2271      finalStatus=status();
2272      time2 = CoinCpuTime();
2273      handler_->message(CLP_INTERVAL_TIMING,messages_)
2274      <<"Cleanup"<<time2-timeX<<time2-time1
2275      <<CoinMessageEol;
2276      timeX=time2;
2277    } else {
2278      secondaryStatus_=finalSecondaryStatus;
2279    }
2280  } else if (model2!=this) {
2281    // not presolved - but different model used (sprint probably)
2282    CoinMemcpyN(model2->primalRowSolution(),
2283                numberRows_,this->primalRowSolution());
2284    CoinMemcpyN(model2->dualRowSolution(),
2285                numberRows_,this->dualRowSolution());
2286    CoinMemcpyN(model2->primalColumnSolution(),
2287                numberColumns_,this->primalColumnSolution());
2288    CoinMemcpyN(model2->dualColumnSolution(),
2289                numberColumns_,this->dualColumnSolution());
2290    CoinMemcpyN(model2->statusArray(),
2291                numberColumns_+numberRows_,this->statusArray());
2292    objectiveValue_=model2->objectiveValue_;
2293    numberIterations_ =model2->numberIterations_;
2294    problemStatus_ =model2->problemStatus_;
2295    secondaryStatus_ =model2->secondaryStatus_;
2296    delete model2;
2297  }
2298  setMaximumIterations(saveMaxIterations);
2299  std::string statusMessage[]={"Unknown","Optimal","PrimalInfeasible","DualInfeasible","Stopped",
2300                               "Errors","User stopped"};
2301  assert (finalStatus>=-1&&finalStatus<=5);
2302  handler_->message(CLP_TIMING,messages_)
2303    <<statusMessage[finalStatus+1]<<objectiveValue()<<numberIterations<<time2-time1;
2304  handler_->printing(presolve==ClpSolve::presolveOn)
2305    <<timePresolve;
2306  handler_->printing(timeIdiot!=0.0)
2307    <<timeIdiot;
2308  handler_->message()<<CoinMessageEol;
2309  if (interrupt) 
2310    signal(SIGINT,saveSignal);
2311  perturbation_=savePerturbation;
2312  scalingFlag_=saveScaling;
2313  // If faking objective - put back correct one
2314  if (savedObjective) {
2315    delete objective_;
2316    objective_=savedObjective;
2317  }
2318  return finalStatus;
2319}
2320// General solve
2321int 
2322ClpSimplex::initialSolve()
2323{
2324  // Default so use dual
2325  ClpSolve options;
2326  return initialSolve(options);
2327}
2328// General dual solve
2329int 
2330ClpSimplex::initialDualSolve()
2331{
2332  ClpSolve options;
2333  // Use dual
2334  options.setSolveType(ClpSolve::useDual);
2335  return initialSolve(options);
2336}
2337// General dual solve
2338int 
2339ClpSimplex::initialPrimalSolve()
2340{
2341  ClpSolve options;
2342  // Use primal
2343  options.setSolveType(ClpSolve::usePrimal);
2344  return initialSolve(options);
2345}
2346// barrier solve, not to be followed by crossover
2347int 
2348ClpSimplex::initialBarrierNoCrossSolve()
2349{
2350  ClpSolve options;
2351  // Use primal
2352  options.setSolveType(ClpSolve::useBarrierNoCross);
2353  return initialSolve(options);
2354}
2355
2356// General barrier solve
2357int 
2358ClpSimplex::initialBarrierSolve()
2359{
2360  ClpSolve options;
2361  // Use primal
2362  options.setSolveType(ClpSolve::useBarrier);
2363  return initialSolve(options);
2364}
2365
2366// Default constructor
2367ClpSolve::ClpSolve (  )
2368{
2369  method_ = automatic;
2370  presolveType_=presolveOn;
2371  numberPasses_=5;
2372  int i;
2373  for (i=0;i<7;i++)
2374    options_[i]=0;
2375  // say no +-1 matrix
2376  options_[3]=1;
2377  for (i=0;i<7;i++)
2378    extraInfo_[i]=-1;
2379  independentOptions_[0]=0;
2380  // But switch off slacks
2381  independentOptions_[1]=512;
2382  // Substitute up to 3
2383  independentOptions_[2]=3;
2384 
2385}
2386// Constructor when you really know what you are doing
2387ClpSolve::ClpSolve ( SolveType method, PresolveType presolveType,
2388             int numberPasses, int options[6],
2389             int extraInfo[6], int independentOptions[3])
2390{
2391  method_ = method;
2392  presolveType_=presolveType;
2393  numberPasses_=numberPasses;
2394  int i;
2395  for (i=0;i<6;i++)
2396    options_[i]=options[i];
2397  options_[6]=0;
2398  for (i=0;i<6;i++)
2399    extraInfo_[i]=extraInfo[i];
2400  extraInfo_[6]=0;
2401  for (i=0;i<3;i++)
2402    independentOptions_[i]=independentOptions[i];
2403}
2404
2405// Copy constructor.
2406ClpSolve::ClpSolve(const ClpSolve & rhs)
2407{
2408  method_ = rhs.method_;
2409  presolveType_=rhs.presolveType_;
2410  numberPasses_=rhs.numberPasses_;
2411  int i;
2412  for ( i=0;i<7;i++)
2413    options_[i]=rhs.options_[i];
2414  for ( i=0;i<7;i++)
2415    extraInfo_[i]=rhs.extraInfo_[i];
2416  for (i=0;i<3;i++)
2417    independentOptions_[i]=rhs.independentOptions_[i];
2418}
2419// Assignment operator. This copies the data
2420ClpSolve & 
2421ClpSolve::operator=(const ClpSolve & rhs)
2422{
2423  if (this != &rhs) {
2424    method_ = rhs.method_;
2425    presolveType_=rhs.presolveType_;
2426    numberPasses_=rhs.numberPasses_;
2427    int i;
2428    for (i=0;i<7;i++)
2429      options_[i]=rhs.options_[i];
2430    for (i=0;i<7;i++)
2431      extraInfo_[i]=rhs.extraInfo_[i];
2432    for (i=0;i<3;i++)
2433      independentOptions_[i]=rhs.independentOptions_[i];
2434  }
2435  return *this;
2436
2437}
2438// Destructor
2439ClpSolve::~ClpSolve (  )
2440{
2441}
2442// See header file for deatils
2443void 
2444ClpSolve::setSpecialOption(int which,int value,int extraInfo)
2445{
2446  options_[which]=value;
2447  extraInfo_[which]=extraInfo;
2448}
2449int 
2450ClpSolve::getSpecialOption(int which) const
2451{
2452  return options_[which];
2453}
2454
2455// Solve types
2456void 
2457ClpSolve::setSolveType(SolveType method, int extraInfo)
2458{
2459  method_=method;
2460}
2461
2462ClpSolve::SolveType
2463ClpSolve::getSolveType()
2464{
2465  return method_;
2466}
2467
2468// Presolve types
2469void 
2470ClpSolve::setPresolveType(PresolveType amount, int extraInfo)
2471{
2472  presolveType_=amount;
2473  numberPasses_=extraInfo;
2474}
2475ClpSolve::PresolveType
2476ClpSolve::getPresolveType()
2477{
2478  return presolveType_;
2479}
2480// Extra info for idiot (or sprint)
2481int 
2482ClpSolve::getExtraInfo(int which) const
2483{
2484  return extraInfo_[which];
2485}
2486int 
2487ClpSolve::getPresolvePasses() const
2488{
2489  return numberPasses_;
2490}
2491/* Say to return at once if infeasible,
2492   default is to solve */
2493void 
2494ClpSolve::setInfeasibleReturn(bool trueFalse)
2495{
2496  independentOptions_[0]= trueFalse ? 1 : 0;
2497}
2498#include <string>
2499// Generates code for above constructor
2500void 
2501ClpSolve::generateCpp(FILE * fp)
2502{
2503  std::string solveType[] = {
2504    "ClpSolve::useDual",
2505    "ClpSolve::usePrimal",
2506    "ClpSolve::usePrimalorSprint",
2507    "ClpSolve::useBarrier",
2508    "ClpSolve::useBarrierNoCross",
2509    "ClpSolve::automatic",
2510    "ClpSolve::notImplemented"
2511  };
2512  std::string presolveType[] =  {
2513    "ClpSolve::presolveOn",
2514    "ClpSolve::presolveOff",
2515    "ClpSolve::presolveNumber",
2516    "ClpSolve::presolveNumberCost"
2517  };
2518  fprintf(fp,"3  ClpSolve::SolveType method = %s;\n",solveType[method_].c_str());
2519  fprintf(fp,"3  ClpSolve::PresolveType presolveType = %s;\n",
2520    presolveType[presolveType_].c_str());
2521  fprintf(fp,"3  int numberPasses = %d;\n",numberPasses_);
2522  fprintf(fp,"3  int options[] = {%d,%d,%d,%d,%d,%d};\n",
2523    options_[0],options_[1],options_[2],
2524    options_[3],options_[4],options_[5]);
2525  fprintf(fp,"3  int extraInfo[] = {%d,%d,%d,%d,%d,%d};\n",
2526    extraInfo_[0],extraInfo_[1],extraInfo_[2],
2527    extraInfo_[3],extraInfo_[4],extraInfo_[5]);
2528  fprintf(fp,"3  int independentOptions[] = {%d,%d,%d};\n",
2529    independentOptions_[0],independentOptions_[1],independentOptions_[2]);
2530  fprintf(fp,"3  ClpSolve clpSolve(method,presolveType,numberPasses,\n");
2531  fprintf(fp,"3                    options,extraInfo,independentOptions);\n");
2532}
Note: See TracBrowser for help on using the repository browser.