source: branches/devel/Clp/src/ClpNonLinearCost.cpp @ 1018

Last change on this file since 1018 was 1018, checked in by forrest, 12 years ago

fix tolerance problem

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.1 KB
Line 
1// Copyright (C) 2002, International Business Machines
2// Corporation and others.  All Rights Reserved.
3
4#include "CoinPragma.hpp"
5#include <iostream>
6#include <cassert>
7
8#include "CoinIndexedVector.hpp"
9
10#include "ClpSimplex.hpp"
11#include "CoinHelperFunctions.hpp"
12#include "ClpNonLinearCost.hpp"
13//#############################################################################
14// Constructors / Destructor / Assignment
15//#############################################################################
16
17//-------------------------------------------------------------------
18// Default Constructor
19//-------------------------------------------------------------------
20ClpNonLinearCost::ClpNonLinearCost () :
21  changeCost_(0.0),
22  feasibleCost_(0.0),
23  infeasibilityWeight_(-1.0),
24  largestInfeasibility_(0.0),
25  sumInfeasibilities_(0.0),
26  averageTheta_(0.0),
27  numberRows_(0),
28  numberColumns_(0),
29  start_(NULL),
30  whichRange_(NULL),
31  offset_(NULL),
32  lower_(NULL),
33  cost_(NULL),
34  model_(NULL),
35  infeasible_(NULL),
36  numberInfeasibilities_(-1),
37  status_(NULL),
38  bound_(NULL),
39  cost2_(NULL),
40  method_(1),
41  convex_(true),
42  bothWays_(false)
43{
44
45}
46//#define VALIDATE
47#ifdef VALIDATE
48static double * saveLowerV=NULL;
49static double * saveUpperV=NULL;
50#ifdef NDEBUG
51Validate sgould not be set if no debug
52#endif
53#endif
54/* Constructor from simplex.
55   This will just set up wasteful arrays for linear, but
56   later may do dual analysis and even finding duplicate columns
57*/
58ClpNonLinearCost::ClpNonLinearCost ( ClpSimplex * model,int method)
59{
60  method=2;
61  model_ = model;
62  numberRows_ = model_->numberRows();
63  //if (numberRows_==402) {
64  //model_->setLogLevel(63);
65  //model_->setMaximumIterations(30000);
66  //}
67  numberColumns_ = model_->numberColumns();
68  // If gub then we need this extra
69  int numberExtra = model_->numberExtraRows();
70  if (numberExtra)
71    method=1;
72  int numberTotal1 = numberRows_+numberColumns_;
73  int numberTotal = numberTotal1+numberExtra;
74  convex_ = true;
75  bothWays_ = false;
76  method_=method;
77  numberInfeasibilities_=0;
78  changeCost_=0.0;
79  feasibleCost_=0.0;
80  infeasibilityWeight_ = -1.0;
81  double infeasibilityCost = model_->infeasibilityCost();
82  sumInfeasibilities_=0.0;
83  averageTheta_=0.0;
84  largestInfeasibility_=0.0;
85  // All arrays NULL to start
86  status_ = NULL;
87  bound_ = NULL;
88  cost2_ = NULL;
89  start_ = NULL;
90  whichRange_ = NULL;
91  offset_ = NULL;
92  lower_ = NULL;
93  cost_= NULL;
94  infeasible_=NULL;
95
96  int iSequence;
97  double * upper = model_->upperRegion();
98  double * lower = model_->lowerRegion();
99  double * cost = model_->costRegion();
100
101  // See how we are storing things
102  bool always4 = (model_->clpMatrix()->
103                  generalExpanded(model_,10,iSequence)!=0);
104  if (always4)
105    method_=1;
106  if (CLP_METHOD1) {
107    start_ = new int [numberTotal+1];
108    whichRange_ = new int [numberTotal];
109    offset_ = new int [numberTotal];
110    memset(offset_,0,numberTotal*sizeof(int));
111   
112   
113    // First see how much space we need
114    int put=0;
115   
116    // For quadratic we need -inf,0,0,+inf
117    for (iSequence=0;iSequence<numberTotal1;iSequence++) {
118      if (!always4) {
119        if (lower[iSequence]>-COIN_DBL_MAX)
120          put++;
121        if (upper[iSequence]<COIN_DBL_MAX)
122          put++;
123        put += 2;
124      } else {
125        put += 4;
126      }
127    }
128   
129    // and for extra
130    put += 4*numberExtra;
131#ifndef NDEBUG
132    int kPut=put;
133#endif
134    lower_ = new double [put];
135    cost_ = new double [put];
136    infeasible_ = new unsigned int[(put+31)>>5];
137    memset(infeasible_,0,((put+31)>>5)*sizeof(unsigned int));
138   
139    put=0;
140   
141    start_[0]=0;
142   
143    for (iSequence=0;iSequence<numberTotal1;iSequence++) {
144      if (!always4) {
145        if (lower[iSequence]>-COIN_DBL_MAX) {
146          lower_[put] = -COIN_DBL_MAX;
147          setInfeasible(put,true);
148          cost_[put++] = cost[iSequence]-infeasibilityCost;
149        }
150        whichRange_[iSequence]=put;
151        lower_[put] = lower[iSequence];
152        cost_[put++] = cost[iSequence];
153        lower_[put] = upper[iSequence];
154        cost_[put++] = cost[iSequence]+infeasibilityCost;
155        if (upper[iSequence]<COIN_DBL_MAX) {
156          lower_[put] = COIN_DBL_MAX;
157          setInfeasible(put-1,true);
158          cost_[put++] = 1.0e50;
159        }
160      } else {
161        lower_[put] = -COIN_DBL_MAX;
162        setInfeasible(put,true);
163        cost_[put++] = cost[iSequence]-infeasibilityCost;
164        whichRange_[iSequence]=put;
165        lower_[put] = lower[iSequence];
166        cost_[put++] = cost[iSequence];
167        lower_[put] = upper[iSequence];
168        cost_[put++] = cost[iSequence]+infeasibilityCost;
169        lower_[put] = COIN_DBL_MAX;
170        setInfeasible(put-1,true);
171        cost_[put++] = 1.0e50;
172      }
173      start_[iSequence+1]=put;
174    }
175    for (;iSequence<numberTotal;iSequence++) {
176     
177      lower_[put] = -COIN_DBL_MAX;
178      setInfeasible(put,true);
179      put++;
180      whichRange_[iSequence]=put;
181      lower_[put] = 0.0;
182      cost_[put++] = 0.0;
183      lower_[put] = 0.0;
184      cost_[put++] = 0.0;
185      lower_[put] = COIN_DBL_MAX;
186      setInfeasible(put-1,true);
187      cost_[put++] = 1.0e50;
188      start_[iSequence+1]=put;
189    }
190    assert (put<=kPut);
191  }
192#ifdef FAST_CLPNON
193  // See how we are storing things
194  CoinAssert (model_->clpMatrix()->
195                  generalExpanded(model_,10,iSequence)==0);
196#endif
197  if (CLP_METHOD2) {
198    assert (!numberExtra);
199    bound_ = new double[numberTotal];
200    cost2_ = new double[numberTotal];
201    status_ = new unsigned char[numberTotal];
202#ifdef VALIDATE
203    delete [] saveLowerV;
204    saveLowerV = CoinCopyOfArray(model_->lowerRegion(),numberTotal);
205    delete [] saveUpperV;
206    saveUpperV = CoinCopyOfArray(model_->upperRegion(),numberTotal);
207#endif
208    for (iSequence=0;iSequence<numberTotal;iSequence++) {
209      bound_[iSequence]=0.0;
210      cost2_[iSequence]=cost[iSequence];
211      setInitialStatus(status_[iSequence]);
212    }
213  }
214}
215// Refreshes costs always makes row costs zero
216void 
217ClpNonLinearCost::refreshCosts(const double * columnCosts)
218{
219  double * cost = model_->costRegion();
220  // zero row costs
221  memset(cost+numberColumns_,0,numberRows_*sizeof(double));
222  // copy column costs
223  memcpy(cost,columnCosts,numberColumns_*sizeof(double));
224  if ((method_&1)!=0) {
225    for (int iSequence=0;iSequence<numberRows_+numberColumns_;iSequence++) {
226      int start = start_[iSequence];
227      int end = start_[iSequence+1]-1;
228      double thisFeasibleCost=cost[iSequence];
229      if (infeasible(start)) {
230        cost_[start] = thisFeasibleCost-infeasibilityWeight_;
231        cost_[start+1] = thisFeasibleCost;
232      } else {
233        cost_[start] = thisFeasibleCost;
234      }
235      if (infeasible(end-1)) {
236        cost_[end-1] = thisFeasibleCost+infeasibilityWeight_;
237      }
238    }
239  }
240  if (CLP_METHOD2) {
241    for (int iSequence=0;iSequence<numberRows_+numberColumns_;iSequence++) {
242      cost2_[iSequence]=cost[iSequence];
243    }
244  }
245}
246ClpNonLinearCost::ClpNonLinearCost(ClpSimplex * model,const int * starts,
247                   const double * lowerNon, const double * costNon)
248{
249#ifndef FAST_CLPNON
250  // what about scaling? - only try without it initially
251  assert(!model->scalingFlag());
252  model_ = model;
253  numberRows_ = model_->numberRows();
254  numberColumns_ = model_->numberColumns();
255  int numberTotal = numberRows_+numberColumns_;
256  convex_ = true;
257  bothWays_ = true;
258  start_ = new int [numberTotal+1];
259  whichRange_ = new int [numberTotal];
260  offset_ = new int [numberTotal];
261  memset(offset_,0,numberTotal*sizeof(int));
262 
263  double whichWay = model_->optimizationDirection();
264  printf("Direction %g\n",whichWay);
265
266  numberInfeasibilities_=0;
267  changeCost_=0.0;
268  feasibleCost_=0.0;
269  double infeasibilityCost = model_->infeasibilityCost();
270  infeasibilityWeight_ = infeasibilityCost;;
271  largestInfeasibility_=0.0;
272  sumInfeasibilities_=0.0;
273
274  int iSequence;
275  assert (!model_->rowObjective());
276  double * cost = model_->objective();
277
278  // First see how much space we need
279  // Also set up feasible bounds
280  int put=starts[numberColumns_];
281
282  double * columnUpper = model_->columnUpper();
283  double * columnLower = model_->columnLower();
284  for (iSequence=0;iSequence<numberColumns_;iSequence++) {
285    if (columnLower[iSequence]>-1.0e20)
286      put++;
287    if (columnUpper[iSequence]<1.0e20)
288      put++;
289  }
290
291  double * rowUpper = model_->rowUpper();
292  double * rowLower = model_->rowLower();
293  for (iSequence=0;iSequence<numberRows_;iSequence++) {
294    if (rowLower[iSequence]>-1.0e20)
295      put++;
296    if (rowUpper[iSequence]<1.0e20)
297      put++;
298    put +=2;
299  }
300  lower_ = new double [put];
301  cost_ = new double [put];
302  infeasible_ = new unsigned int[(put+31)>>5];
303  memset(infeasible_,0,((put+31)>>5)*sizeof(unsigned int));
304
305  // now fill in
306  put=0;
307
308  start_[0]=0;
309  for (iSequence=0;iSequence<numberTotal;iSequence++) {
310    lower_[put] = -COIN_DBL_MAX;
311    whichRange_[iSequence]=put+1;
312    double thisCost;
313    double lowerValue;
314    double upperValue;
315    if (iSequence>=numberColumns_) {
316      // rows
317      lowerValue = rowLower[iSequence-numberColumns_];
318      upperValue = rowUpper[iSequence-numberColumns_];
319      if (lowerValue>-1.0e30) {
320        setInfeasible(put,true);
321        cost_[put++] = -infeasibilityCost;
322        lower_[put] = lowerValue;
323      }
324      cost_[put++] = 0.0;
325      thisCost = 0.0;
326    } else {
327      // columns - move costs and see if convex
328      lowerValue = columnLower[iSequence];
329      upperValue = columnUpper[iSequence];
330      if (lowerValue>-1.0e30) {
331        setInfeasible(put,true);
332        cost_[put++] = whichWay*cost[iSequence]-infeasibilityCost;
333        lower_[put] = lowerValue;
334      }
335      int iIndex = starts[iSequence];
336      int end = starts[iSequence+1];
337      assert (fabs(columnLower[iSequence]-lowerNon[iIndex])<1.0e-8);
338      thisCost = -COIN_DBL_MAX;
339      for (;iIndex<end;iIndex++) {
340        if (lowerNon[iIndex]<columnUpper[iSequence]-1.0e-8) {
341          lower_[put] = lowerNon[iIndex];
342          cost_[put++] = whichWay*costNon[iIndex];
343          // check convexity
344          if (whichWay*costNon[iIndex]<thisCost-1.0e-12)
345            convex_ = false;
346          thisCost = whichWay*costNon[iIndex];
347        } else {
348          break;
349        }
350      }
351    }
352    lower_[put] = upperValue;
353    setInfeasible(put,true);
354    cost_[put++] = thisCost+infeasibilityCost;
355    if (upperValue<1.0e20) {
356      lower_[put] = COIN_DBL_MAX;
357      cost_[put++] = 1.0e50;
358    }
359    int iFirst = start_[iSequence];
360    if (lower_[iFirst] != -COIN_DBL_MAX) {
361      setInfeasible(iFirst,true);
362      whichRange_[iSequence]=iFirst+1;
363    } else {
364      whichRange_[iSequence]=iFirst;
365    }
366    start_[iSequence+1]=put;
367  }
368  // can't handle non-convex at present
369  assert(convex_);
370  status_ = NULL;
371  bound_ = NULL;
372  cost2_ = NULL;
373  method_ = 1;
374#else
375  printf("recompile ClpNonLinearCost without FAST_CLPNON\n");
376  abort();
377#endif
378}
379//-------------------------------------------------------------------
380// Copy constructor
381//-------------------------------------------------------------------
382ClpNonLinearCost::ClpNonLinearCost (const ClpNonLinearCost & rhs) :
383  changeCost_(0.0),
384  feasibleCost_(0.0),
385  infeasibilityWeight_(-1.0),
386  largestInfeasibility_(0.0),
387  sumInfeasibilities_(0.0),
388  averageTheta_(0.0),
389  numberRows_(rhs.numberRows_),
390  numberColumns_(rhs.numberColumns_),
391  start_(NULL),
392  whichRange_(NULL),
393  offset_(NULL),
394  lower_(NULL),
395  cost_(NULL),
396  model_(NULL),
397  infeasible_(NULL),
398  numberInfeasibilities_(-1),
399  status_(NULL),
400  bound_(NULL),
401  cost2_(NULL),
402  method_(rhs.method_),
403  convex_(true),
404  bothWays_(rhs.bothWays_)
405{ 
406  if (numberRows_) {
407    int numberTotal = numberRows_+numberColumns_;
408    model_ = rhs.model_;
409    numberInfeasibilities_=rhs.numberInfeasibilities_;
410    changeCost_ = rhs.changeCost_;
411    feasibleCost_ = rhs.feasibleCost_;
412    infeasibilityWeight_ = rhs.infeasibilityWeight_;
413    largestInfeasibility_ = rhs.largestInfeasibility_;
414    sumInfeasibilities_ = rhs.sumInfeasibilities_;
415    averageTheta_ = rhs.averageTheta_;
416    convex_ = rhs.convex_;
417    if (CLP_METHOD1) {
418      start_ = new int [numberTotal+1];
419      memcpy(start_,rhs.start_,(numberTotal+1)*sizeof(int));
420      whichRange_ = new int [numberTotal];
421      memcpy(whichRange_,rhs.whichRange_,numberTotal*sizeof(int));
422      offset_ = new int [numberTotal];
423      memcpy(offset_,rhs.offset_,numberTotal*sizeof(int));
424      int numberEntries = start_[numberTotal];
425      lower_ = new double [numberEntries];
426      memcpy(lower_,rhs.lower_,numberEntries*sizeof(double));
427      cost_ = new double [numberEntries];
428      memcpy(cost_,rhs.cost_,numberEntries*sizeof(double));
429      infeasible_ = new unsigned int[(numberEntries+31)>>5];
430      memcpy(infeasible_,rhs.infeasible_,
431             ((numberEntries+31)>>5)*sizeof(unsigned int));
432    }
433    if (CLP_METHOD2) {
434      bound_ = CoinCopyOfArray(rhs.bound_,numberTotal);
435      cost2_ = CoinCopyOfArray(rhs.cost2_,numberTotal);
436      status_ = CoinCopyOfArray(rhs.status_,numberTotal);
437    }
438  }
439}
440
441//-------------------------------------------------------------------
442// Destructor
443//-------------------------------------------------------------------
444ClpNonLinearCost::~ClpNonLinearCost ()
445{
446  delete [] start_;
447  delete [] whichRange_;
448  delete [] offset_;
449  delete [] lower_;
450  delete [] cost_;
451  delete [] infeasible_;
452  delete [] status_;
453  delete [] bound_;
454  delete [] cost2_;
455}
456
457//----------------------------------------------------------------
458// Assignment operator
459//-------------------------------------------------------------------
460ClpNonLinearCost &
461ClpNonLinearCost::operator=(const ClpNonLinearCost& rhs)
462{
463  if (this != &rhs) {
464    numberRows_ = rhs.numberRows_;
465    numberColumns_ = rhs.numberColumns_;
466    delete [] start_;
467    delete [] whichRange_;
468    delete [] offset_;
469    delete [] lower_;
470    delete [] cost_;
471    delete [] infeasible_;
472    delete [] status_;
473    delete [] bound_;
474    delete [] cost2_;
475    start_ = NULL;
476    whichRange_ = NULL;
477    lower_ = NULL;
478    cost_= NULL;
479    infeasible_=NULL;
480    status_ = NULL;
481    bound_ = NULL;
482    cost2_ = NULL;
483    method_=rhs.method_;
484    if (numberRows_) {
485      int numberTotal = numberRows_+numberColumns_;
486      if (CLP_METHOD1) {
487        start_ = new int [numberTotal+1];
488        memcpy(start_,rhs.start_,(numberTotal+1)*sizeof(int));
489        whichRange_ = new int [numberTotal];
490        memcpy(whichRange_,rhs.whichRange_,numberTotal*sizeof(int));
491        offset_ = new int [numberTotal];
492        memcpy(offset_,rhs.offset_,numberTotal*sizeof(int));
493        int numberEntries = start_[numberTotal];
494        lower_ = new double [numberEntries];
495        memcpy(lower_,rhs.lower_,numberEntries*sizeof(double));
496        cost_ = new double [numberEntries];
497        memcpy(cost_,rhs.cost_,numberEntries*sizeof(double));
498        infeasible_ = new unsigned int[(numberEntries+31)>>5];
499        memcpy(infeasible_,rhs.infeasible_,
500               ((numberEntries+31)>>5)*sizeof(unsigned int));
501      }
502      if (CLP_METHOD2) {
503        bound_ = CoinCopyOfArray(rhs.bound_,numberTotal);
504        cost2_ = CoinCopyOfArray(rhs.cost2_,numberTotal);
505        status_ = CoinCopyOfArray(rhs.status_,numberTotal);
506      }
507    }     
508    model_ = rhs.model_;
509    numberInfeasibilities_=rhs.numberInfeasibilities_;
510    changeCost_ = rhs.changeCost_;
511    feasibleCost_ = rhs.feasibleCost_;
512    infeasibilityWeight_ = rhs.infeasibilityWeight_;
513    largestInfeasibility_ = rhs.largestInfeasibility_;
514    sumInfeasibilities_ = rhs.sumInfeasibilities_;
515    averageTheta_ = rhs.averageTheta_;
516    convex_ = rhs.convex_;
517    bothWays_ = rhs.bothWays_;
518  }
519  return *this;
520}
521// Changes infeasible costs and computes number and cost of infeas
522// We will need to re-think objective offsets later
523// We will also need a 2 bit per variable array for some
524// purpose which will come to me later
525void 
526ClpNonLinearCost::checkInfeasibilities(double oldTolerance)
527{
528  numberInfeasibilities_=0;
529  double infeasibilityCost = model_->infeasibilityCost();
530  changeCost_=0.0;
531  largestInfeasibility_ = 0.0;
532  sumInfeasibilities_ = 0.0;
533  double primalTolerance = model_->currentPrimalTolerance();
534  int iSequence;
535  double * solution = model_->solutionRegion();
536  double * upper = model_->upperRegion();
537  double * lower = model_->lowerRegion();
538  double * cost = model_->costRegion();
539  bool toNearest = oldTolerance<=0.0;
540  feasibleCost_=0.0;
541  //bool checkCosts = (infeasibilityWeight_ != infeasibilityCost);
542  infeasibilityWeight_ = infeasibilityCost;
543  int numberTotal = numberColumns_+numberRows_;
544  //#define NONLIN_DEBUG
545#ifdef NONLIN_DEBUG
546  double * saveSolution=NULL;
547  double * saveLower=NULL;
548  double * saveUpper=NULL;
549  unsigned char * saveStatus=NULL;
550  if (method_==3) {
551    // Save solution as we will be checking
552    saveSolution = CoinCopyOfArray(solution,numberTotal);
553    saveLower = CoinCopyOfArray(lower,numberTotal);
554    saveUpper = CoinCopyOfArray(upper,numberTotal);
555    saveStatus = CoinCopyOfArray(model_->statusArray(),numberTotal);
556  }
557#else
558  assert (method_!=3);
559#endif
560  if (CLP_METHOD1) {
561    // nonbasic should be at a valid bound
562    for (iSequence=0;iSequence<numberTotal;iSequence++) {
563      double lowerValue;
564      double upperValue;
565      double value=solution[iSequence];
566      int iRange;
567      // get correct place
568      int start = start_[iSequence];
569      int end = start_[iSequence+1]-1;
570      // correct costs for this infeasibility weight
571      // If free then true cost will be first
572      double thisFeasibleCost=cost_[start];
573      if (infeasible(start)) {
574        thisFeasibleCost = cost_[start+1];
575        cost_[start] = thisFeasibleCost-infeasibilityCost;
576      }
577      if (infeasible(end-1)) {
578        thisFeasibleCost = cost_[end-2];
579        cost_[end-1] = thisFeasibleCost+infeasibilityCost;
580      }
581      for (iRange=start; iRange<end;iRange++) {
582        if (value<lower_[iRange+1]+primalTolerance) {
583          // put in better range if infeasible
584          if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
585            iRange++;
586          whichRange_[iSequence]=iRange;
587          break;
588        }
589      }
590      assert(iRange<end);
591      lowerValue = lower_[iRange];
592      upperValue = lower_[iRange+1];
593      ClpSimplex::Status status = model_->getStatus(iSequence);
594      if (upperValue==lowerValue && status!=ClpSimplex::isFixed) {
595        if (status != ClpSimplex::basic) {
596          model_->setStatus(iSequence,ClpSimplex::isFixed);
597          status = ClpSimplex::isFixed;
598        }
599      }
600      switch(status) {
601       
602      case ClpSimplex::basic:
603      case ClpSimplex::superBasic:
604        // iRange is in correct place
605        // slot in here
606        if (infeasible(iRange)) {
607          if (lower_[iRange]<-1.0e50) {
608            //cost_[iRange] = cost_[iRange+1]-infeasibilityCost;
609            // possibly below
610            lowerValue = lower_[iRange+1];
611            if (value-lowerValue<-primalTolerance) {
612              value = lowerValue-value;
613#ifndef NDEBUG
614              if(value>1.0e15)
615                printf("nonlincostb %d %g %g %g\n",
616                       iSequence,lowerValue,solution[iSequence],lower_[iRange+2]);
617#endif
618              sumInfeasibilities_ += value;
619              largestInfeasibility_ = CoinMax(largestInfeasibility_,value);
620              changeCost_ -= lowerValue*
621                (cost_[iRange]-cost[iSequence]);
622              numberInfeasibilities_++;
623            }
624          } else {
625            //cost_[iRange] = cost_[iRange-1]+infeasibilityCost;
626            // possibly above
627            upperValue = lower_[iRange];
628            if (value-upperValue>primalTolerance) {
629              value = value-upperValue;
630#ifndef NDEBUG
631              if(value>1.0e15)
632                printf("nonlincostu %d %g %g %g\n",
633                       iSequence,lower_[iRange-1],solution[iSequence],upperValue);
634#endif
635              sumInfeasibilities_ += value;
636              largestInfeasibility_ = CoinMax(largestInfeasibility_,value);
637              changeCost_ -= upperValue*
638                (cost_[iRange]-cost[iSequence]);
639              numberInfeasibilities_++;
640            }
641          }
642        }
643        //lower[iSequence] = lower_[iRange];
644        //upper[iSequence] = lower_[iRange+1];
645        //cost[iSequence] = cost_[iRange];
646        break;
647      case ClpSimplex::isFree:
648        //if (toNearest)
649        //solution[iSequence] = 0.0;
650        break;
651      case ClpSimplex::atUpperBound:
652        if (!toNearest) {
653          // With increasing tolerances - we may be at wrong place
654          if (fabs(value-upperValue)>oldTolerance*1.0001) {
655            if (fabs(value-lowerValue)<=oldTolerance*1.0001) {
656              if  (fabs(value-lowerValue)>primalTolerance)
657                solution[iSequence]=lowerValue;
658              model_->setStatus(iSequence,ClpSimplex::atLowerBound);
659            } else {
660              model_->setStatus(iSequence,ClpSimplex::superBasic);
661            }
662          } else if  (fabs(value-upperValue)>primalTolerance) {
663            solution[iSequence]=upperValue;
664          }
665        } else {
666          // Set to nearest and make at upper bound
667          int kRange;
668          iRange=-1;
669          double nearest = COIN_DBL_MAX;
670          for (kRange=start; kRange<end;kRange++) {
671            if (fabs(lower_[kRange]-value)<nearest) {
672              nearest = fabs(lower_[kRange]-value);
673              iRange=kRange;
674            }
675          }
676          assert (iRange>=0);
677          iRange--;
678          whichRange_[iSequence]=iRange;
679          solution[iSequence]=lower_[iRange+1];
680        }
681        break;
682      case ClpSimplex::atLowerBound:
683        if (!toNearest) {
684          // With increasing tolerances - we may be at wrong place
685          // below stops compiler error with gcc 3.2!!!
686          if (iSequence==-119)
687            printf("ZZ %g %g %g %g\n",lowerValue,value,upperValue,oldTolerance);
688          if (fabs(value-lowerValue)>oldTolerance*1.0001) {
689            if (fabs(value-upperValue)<=oldTolerance*1.0001) {
690              if  (fabs(value-upperValue)>primalTolerance)
691                solution[iSequence]=upperValue;
692              model_->setStatus(iSequence,ClpSimplex::atUpperBound);
693            } else {
694              model_->setStatus(iSequence,ClpSimplex::superBasic);
695            }
696          } else if  (fabs(value-lowerValue)>primalTolerance) {
697            solution[iSequence]=lowerValue;
698          }
699        } else {
700          // Set to nearest and make at lower bound
701          int kRange;
702          iRange=-1;
703          double nearest = COIN_DBL_MAX;
704          for (kRange=start; kRange<end;kRange++) {
705            if (fabs(lower_[kRange]-value)<nearest) {
706              nearest = fabs(lower_[kRange]-value);
707              iRange=kRange;
708            }
709          }
710          assert (iRange>=0);
711          whichRange_[iSequence]=iRange;
712          solution[iSequence]=lower_[iRange];
713        }
714        break;
715      case ClpSimplex::isFixed:
716        if (toNearest) {
717          // Set to true fixed
718          for (iRange=start; iRange<end;iRange++) {
719            if (lower_[iRange]==lower_[iRange+1])
720              break;
721          }
722          if (iRange==end) {
723            // Odd - but make sensible
724            // Set to nearest and make at lower bound
725            int kRange;
726            iRange=-1;
727            double nearest = COIN_DBL_MAX;
728            for (kRange=start; kRange<end;kRange++) {
729              if (fabs(lower_[kRange]-value)<nearest) {
730                nearest = fabs(lower_[kRange]-value);
731                iRange=kRange;
732              }
733            }
734            assert (iRange>=0);
735            whichRange_[iSequence]=iRange;
736            if (lower_[iRange]!=lower_[iRange+1])
737              model_->setStatus(iSequence,ClpSimplex::atLowerBound);
738            else
739              model_->setStatus(iSequence,ClpSimplex::atUpperBound);
740          }
741          solution[iSequence]=lower_[iRange];
742        }
743        break;
744      }
745      lower[iSequence] = lower_[iRange];
746      upper[iSequence] = lower_[iRange+1];
747      cost[iSequence] = cost_[iRange];
748      feasibleCost_ += thisFeasibleCost*solution[iSequence];
749      //assert (iRange==whichRange_[iSequence]);
750    }
751  }
752#ifdef NONLIN_DEBUG
753  double saveCost=feasibleCost_;
754  if (method_==3) {
755    feasibleCost_=0.0;
756    // Put back solution as we will be checking
757    unsigned char * statusA = model_->statusArray();
758    for (iSequence=0;iSequence<numberTotal;iSequence++) {
759      double value = solution[iSequence];
760      solution[iSequence]=saveSolution[iSequence];
761      saveSolution[iSequence]=value;
762      value = lower[iSequence];
763      lower[iSequence]=saveLower[iSequence];
764      saveLower[iSequence]=value;
765      value = upper[iSequence];
766      upper[iSequence]=saveUpper[iSequence];
767      saveUpper[iSequence]=value;
768      unsigned char value2 = statusA[iSequence];
769      statusA[iSequence]=saveStatus[iSequence];
770      saveStatus[iSequence]=value2;
771    }
772  }
773#endif
774  if (CLP_METHOD2) {
775    // nonbasic should be at a valid bound
776    for (iSequence=0;iSequence<numberTotal;iSequence++) {
777      double value=solution[iSequence];
778      int iStatus = status_[iSequence];
779      assert (currentStatus(iStatus)==CLP_SAME);
780      double lowerValue=lower[iSequence];
781      double upperValue=upper[iSequence];
782      double costValue = cost2_[iSequence];
783      double trueCost = costValue;
784      int iWhere = originalStatus(iStatus);
785      if (iWhere==CLP_BELOW_LOWER) {
786        lowerValue=upperValue;
787        upperValue=bound_[iSequence];
788        costValue -= infeasibilityCost;
789      } else if (iWhere==CLP_ABOVE_UPPER) {
790        upperValue=lowerValue;
791        lowerValue=bound_[iSequence];
792        costValue += infeasibilityCost;
793      }
794      // get correct place
795      int newWhere=CLP_FEASIBLE;
796      ClpSimplex::Status status = model_->getStatus(iSequence);
797      if (upperValue==lowerValue && status!=ClpSimplex::isFixed) {
798        if (status != ClpSimplex::basic) {
799          model_->setStatus(iSequence,ClpSimplex::isFixed);
800          status = ClpSimplex::isFixed;
801        }
802      }
803      switch(status) {
804       
805      case ClpSimplex::basic:
806      case ClpSimplex::superBasic:
807        if (value-upperValue<=primalTolerance) {
808          if (value-lowerValue>=-primalTolerance) {
809            // feasible
810            //newWhere=CLP_FEASIBLE;
811          } else {
812            // below
813            newWhere=CLP_BELOW_LOWER;
814            double infeasibility = lowerValue-value;
815            sumInfeasibilities_ += infeasibility;
816            largestInfeasibility_ = CoinMax(largestInfeasibility_,infeasibility);
817            costValue = trueCost - infeasibilityCost;
818            changeCost_ -= lowerValue*(costValue-cost[iSequence]);
819            numberInfeasibilities_++;
820          }
821        } else {
822          // above
823          newWhere = CLP_ABOVE_UPPER;
824          double infeasibility = value-upperValue;
825          sumInfeasibilities_ += infeasibility;
826          largestInfeasibility_ = CoinMax(largestInfeasibility_,infeasibility);
827          costValue = trueCost + infeasibilityCost;
828          changeCost_ -= upperValue*(costValue-cost[iSequence]);
829          numberInfeasibilities_++;
830        }
831        break;
832      case ClpSimplex::isFree:
833        break;
834      case ClpSimplex::atUpperBound:
835        if (!toNearest) {
836          // With increasing tolerances - we may be at wrong place
837          if (fabs(value-upperValue)>oldTolerance*1.0001) {
838            if (fabs(value-lowerValue)<=oldTolerance*1.0001) {
839              if  (fabs(value-lowerValue)>primalTolerance) {
840                solution[iSequence]=lowerValue;
841                value = lowerValue;
842              }
843              model_->setStatus(iSequence,ClpSimplex::atLowerBound);
844            } else {
845              if (value<upperValue) {
846                if (value>lowerValue) {
847                  model_->setStatus(iSequence,ClpSimplex::superBasic);
848                } else {
849                  // set to lower bound as infeasible
850                  solution[iSequence]=lowerValue;
851                  value = lowerValue;
852                  model_->setStatus(iSequence,ClpSimplex::atLowerBound);
853                }
854              } else {
855                // set to upper bound as infeasible
856                solution[iSequence]=upperValue;
857                value = upperValue;
858              }
859            }
860          } else if  (fabs(value-upperValue)>primalTolerance) {
861            solution[iSequence]=upperValue;
862            value = upperValue;
863          }
864        } else {
865          // Set to nearest and make at bound
866          if (fabs(value-lowerValue)<fabs(value-upperValue)) {
867            solution[iSequence]=lowerValue;
868            value = lowerValue;
869            model_->setStatus(iSequence,ClpSimplex::atLowerBound);
870          } else {
871            solution[iSequence]=upperValue;
872            value = upperValue;
873          }
874        }
875        break;
876      case ClpSimplex::atLowerBound:
877        if (!toNearest) {
878          // With increasing tolerances - we may be at wrong place
879          if (fabs(value-lowerValue)>oldTolerance*1.0001) {
880            if (fabs(value-upperValue)<=oldTolerance*1.0001) {
881              if  (fabs(value-upperValue)>primalTolerance) {
882                solution[iSequence]=upperValue;
883                value = upperValue;
884              }
885              model_->setStatus(iSequence,ClpSimplex::atUpperBound);
886            } else {
887              if (value<upperValue) {
888                if (value>lowerValue) {
889                  model_->setStatus(iSequence,ClpSimplex::superBasic);
890                } else {
891                  // set to lower bound as infeasible
892                  solution[iSequence]=lowerValue;
893                  value = lowerValue;
894                }
895              } else {
896                // set to upper bound as infeasible
897                solution[iSequence]=upperValue;
898                value = upperValue;
899                model_->setStatus(iSequence,ClpSimplex::atUpperBound);
900              }
901            }
902          } else if  (fabs(value-lowerValue)>primalTolerance) {
903            solution[iSequence]=lowerValue;
904            value = lowerValue;
905          }
906        } else {
907          // Set to nearest and make at bound
908          if (fabs(value-lowerValue)<fabs(value-upperValue)) {
909            solution[iSequence]=lowerValue;
910            value = lowerValue;
911          } else {
912            solution[iSequence]=upperValue;
913            value = upperValue;
914            model_->setStatus(iSequence,ClpSimplex::atUpperBound);
915          }
916        }
917        break;
918      case ClpSimplex::isFixed:
919        solution[iSequence]=lowerValue;
920        value = lowerValue;
921        break;
922      }
923#ifdef NONLIN_DEBUG
924      double lo=saveLower[iSequence];
925      double up=saveUpper[iSequence];
926      double cc=cost[iSequence];
927      unsigned char ss=saveStatus[iSequence];
928      unsigned char snow=model_->statusArray()[iSequence];
929#endif
930      if (iWhere!=newWhere) {
931        setOriginalStatus(status_[iSequence],newWhere);
932        if (newWhere==CLP_BELOW_LOWER) {
933          bound_[iSequence]=upperValue;
934          upperValue=lowerValue;
935          lowerValue=-COIN_DBL_MAX;
936          costValue = trueCost - infeasibilityCost;
937        } else if (newWhere==CLP_ABOVE_UPPER) {
938          bound_[iSequence]=lowerValue;
939          lowerValue=upperValue;
940          upperValue=COIN_DBL_MAX;
941          costValue = trueCost + infeasibilityCost;
942        } else {
943          costValue = trueCost;
944        }
945        lower[iSequence] = lowerValue;
946        upper[iSequence] = upperValue;
947      }
948      // always do as other things may change
949      cost[iSequence] = costValue;
950#ifdef NONLIN_DEBUG
951      if (method_==3) {
952        assert (ss==snow);
953        assert (cc==cost[iSequence]);
954        assert (lo==lower[iSequence]);
955        assert (up==upper[iSequence]);
956        assert (value==saveSolution[iSequence]);
957      }
958#endif
959      feasibleCost_ += trueCost*value;
960    }
961  }
962#ifdef NONLIN_DEBUG
963  if (method_==3)
964    assert (fabs(saveCost-feasibleCost_)<0.001*(1.0+CoinMax(fabs(saveCost),fabs(feasibleCost_))));
965  delete [] saveSolution;
966  delete [] saveLower;
967  delete [] saveUpper;
968  delete [] saveStatus;
969#endif
970  //feasibleCost_ /= (model_->rhsScale()*model_->objScale());
971}
972// Puts feasible bounds into lower and upper
973void 
974ClpNonLinearCost::feasibleBounds()
975{
976  if (CLP_METHOD2) {
977    int iSequence;
978    double * upper = model_->upperRegion();
979    double * lower = model_->lowerRegion();
980    double * cost = model_->costRegion();
981    int numberTotal = numberColumns_+numberRows_;
982    for (iSequence=0;iSequence<numberTotal;iSequence++) {
983      int iStatus = status_[iSequence];
984      assert (currentStatus(iStatus)==CLP_SAME);
985      double lowerValue=lower[iSequence];
986      double upperValue=upper[iSequence];
987      double costValue = cost2_[iSequence];
988      int iWhere = originalStatus(iStatus);
989      if (iWhere==CLP_BELOW_LOWER) {
990        lowerValue=upperValue;
991        upperValue=bound_[iSequence];
992      } else if (iWhere==CLP_ABOVE_UPPER) {
993        upperValue=lowerValue;
994        lowerValue=bound_[iSequence];
995      }
996      setOriginalStatus(status_[iSequence],CLP_FEASIBLE);
997      lower[iSequence] = lowerValue;
998      upper[iSequence] = upperValue;
999      cost[iSequence] = costValue;
1000    }
1001  }
1002}
1003/* Goes through one bound for each variable.
1004   If array[i]*multiplier>0 goes down, otherwise up.
1005   The indices are row indices and need converting to sequences
1006*/
1007void 
1008ClpNonLinearCost::goThru(int numberInArray, double multiplier,
1009              const int * index, const double * array,
1010                         double * rhs)
1011{
1012  assert (model_!=NULL);
1013  abort();
1014  const int * pivotVariable = model_->pivotVariable();
1015  if (CLP_METHOD1) {
1016    for (int i=0;i<numberInArray;i++) {
1017      int iRow = index[i];
1018      int iSequence = pivotVariable[iRow];
1019      double alpha = multiplier*array[iRow];
1020      // get where in bound sequence
1021      int iRange = whichRange_[iSequence];
1022      iRange += offset_[iSequence]; //add temporary bias
1023      double value = model_->solution(iSequence);
1024      if (alpha>0.0) {
1025        // down one
1026        iRange--;
1027        assert(iRange>=start_[iSequence]);
1028        rhs[iRow] = value - lower_[iRange];
1029      } else {
1030        // up one
1031        iRange++;
1032        assert(iRange<start_[iSequence+1]-1);
1033        rhs[iRow] = lower_[iRange+1] - value;
1034      }
1035      offset_[iSequence] = iRange - whichRange_[iSequence];
1036    }
1037  }
1038#ifdef NONLIN_DEBUG
1039  double * saveRhs = NULL;
1040  if (method_==3) {
1041    int numberRows = model_->numberRows();
1042    saveRhs = CoinCopyOfArray(rhs,numberRows);
1043  }
1044#endif
1045  if (CLP_METHOD2) {
1046    const double * solution = model_->solutionRegion();
1047    const double * upper = model_->upperRegion();
1048    const double * lower = model_->lowerRegion();
1049    for (int i=0;i<numberInArray;i++) {
1050      int iRow = index[i];
1051      int iSequence = pivotVariable[iRow];
1052      double alpha = multiplier*array[iRow];
1053      double value = solution[iSequence];
1054      int iStatus = status_[iSequence];
1055      int iWhere = currentStatus(iStatus);
1056      if (iWhere==CLP_SAME)
1057        iWhere = originalStatus(iStatus);
1058      if (iWhere==CLP_FEASIBLE) {
1059        if (alpha>0.0) {
1060          // going below
1061          iWhere=CLP_BELOW_LOWER;
1062          rhs[iRow] = value - lower[iSequence];
1063        } else {
1064          // going above
1065          iWhere=CLP_ABOVE_UPPER;
1066          rhs[iRow] = upper[iSequence] - value;
1067        }
1068      } else if(iWhere==CLP_BELOW_LOWER) {
1069        assert (alpha<0);
1070        // going feasible
1071        iWhere=CLP_FEASIBLE;
1072        rhs[iRow] = upper[iSequence] - value;
1073      } else {
1074        assert (iWhere==CLP_ABOVE_UPPER);
1075        // going feasible
1076        iWhere=CLP_FEASIBLE;
1077        rhs[iRow] = value - lower[iSequence];
1078      }
1079#ifdef NONLIN_DEBUG
1080      if (method_==3)
1081        assert (rhs[iRow]==saveRhs[iRow]);
1082#endif
1083      setCurrentStatus(status_[iSequence],iWhere);
1084    }
1085  }
1086#ifdef NONLIN_DEBUG
1087  delete [] saveRhs;
1088#endif
1089}
1090/* Takes off last iteration (i.e. offsets closer to 0)
1091 */
1092void 
1093ClpNonLinearCost::goBack(int numberInArray, const int * index, 
1094              double * rhs)
1095{
1096  assert (model_!=NULL);
1097  abort();
1098  const int * pivotVariable = model_->pivotVariable();
1099  if (CLP_METHOD1) {
1100    for (int i=0;i<numberInArray;i++) {
1101      int iRow = index[i];
1102      int iSequence = pivotVariable[iRow];
1103      // get where in bound sequence
1104      int iRange = whichRange_[iSequence];
1105      // get closer to original
1106      if (offset_[iSequence]>0) {
1107        offset_[iSequence]--;
1108        assert (offset_[iSequence]>=0);
1109        iRange += offset_[iSequence]; //add temporary bias
1110        double value = model_->solution(iSequence);
1111        // up one
1112        assert(iRange<start_[iSequence+1]-1);
1113        rhs[iRow] = lower_[iRange+1] - value; // was earlier lower_[iRange]
1114      } else {
1115        offset_[iSequence]++;
1116        assert (offset_[iSequence]<=0);
1117        iRange += offset_[iSequence]; //add temporary bias
1118        double value = model_->solution(iSequence);
1119        // down one
1120        assert(iRange>=start_[iSequence]);
1121        rhs[iRow] = value - lower_[iRange]; // was earlier lower_[iRange+1]
1122      }
1123    }
1124  }
1125#ifdef NONLIN_DEBUG
1126  double * saveRhs = NULL;
1127  if (method_==3) {
1128    int numberRows = model_->numberRows();
1129    saveRhs = CoinCopyOfArray(rhs,numberRows);
1130  }
1131#endif
1132  if (CLP_METHOD2) {
1133    const double * solution = model_->solutionRegion();
1134    const double * upper = model_->upperRegion();
1135    const double * lower = model_->lowerRegion();
1136    for (int i=0;i<numberInArray;i++) {
1137      int iRow = index[i];
1138      int iSequence = pivotVariable[iRow];
1139      double value = solution[iSequence];
1140      int iStatus = status_[iSequence];
1141      int iWhere = currentStatus(iStatus);
1142      int original = originalStatus(iStatus);
1143      assert (iWhere!=CLP_SAME);
1144      if (iWhere==CLP_FEASIBLE) {
1145        if (original==CLP_BELOW_LOWER) {
1146          // going below
1147          iWhere=CLP_BELOW_LOWER;
1148          rhs[iRow] = lower[iSequence] - value;
1149        } else {
1150          // going above
1151          iWhere=CLP_ABOVE_UPPER;
1152          rhs[iRow] = value - upper[iSequence];
1153        }
1154      } else if(iWhere==CLP_BELOW_LOWER) {
1155        // going feasible
1156        iWhere=CLP_FEASIBLE;
1157        rhs[iRow] = value - upper[iSequence];
1158      } else {
1159        // going feasible
1160        iWhere=CLP_FEASIBLE;
1161        rhs[iRow] = lower[iSequence] - value;
1162      }
1163#ifdef NONLIN_DEBUG
1164      if (method_==3)
1165        assert (rhs[iRow]==saveRhs[iRow]);
1166#endif
1167      setCurrentStatus(status_[iSequence],iWhere);
1168    }
1169  }
1170#ifdef NONLIN_DEBUG
1171  delete [] saveRhs;
1172#endif
1173}
1174void 
1175ClpNonLinearCost::goBackAll(const CoinIndexedVector * update)
1176{
1177  assert (model_!=NULL);
1178  const int * pivotVariable = model_->pivotVariable();
1179  int number = update->getNumElements();
1180  const int * index = update->getIndices();
1181  if (CLP_METHOD1) {
1182    for (int i=0;i<number;i++) {
1183      int iRow = index[i];
1184      int iSequence = pivotVariable[iRow];
1185      offset_[iSequence]=0;
1186    }
1187#ifdef CLP_DEBUG
1188    for (i=0;i<numberRows_+numberColumns_;i++) 
1189      assert(!offset_[i]);
1190#endif
1191  }
1192  if (CLP_METHOD2) {
1193    for (int i=0;i<number;i++) {
1194      int iRow = index[i];
1195      int iSequence = pivotVariable[iRow];
1196      setSameStatus(status_[iSequence]);
1197    }
1198  }
1199}
1200void 
1201ClpNonLinearCost::checkInfeasibilities(int numberInArray, const int * index)
1202{
1203  assert (model_!=NULL);
1204  double primalTolerance = model_->currentPrimalTolerance();
1205  const int * pivotVariable = model_->pivotVariable();
1206  if (CLP_METHOD1) {
1207    for (int i=0;i<numberInArray;i++) {
1208      int iRow = index[i];
1209      int iSequence = pivotVariable[iRow];
1210      // get where in bound sequence
1211      int iRange;
1212      int currentRange = whichRange_[iSequence];
1213      double value = model_->solution(iSequence);
1214      int start = start_[iSequence];
1215      int end = start_[iSequence+1]-1;
1216      for (iRange=start; iRange<end;iRange++) {
1217        if (value<lower_[iRange+1]+primalTolerance) {
1218          // put in better range
1219          if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
1220            iRange++;
1221          break;
1222        }
1223      }
1224      assert(iRange<end);
1225      assert(model_->getStatus(iSequence)==ClpSimplex::basic);
1226      double & lower = model_->lowerAddress(iSequence);
1227      double & upper = model_->upperAddress(iSequence);
1228      double & cost = model_->costAddress(iSequence);
1229      whichRange_[iSequence]=iRange;
1230      if (iRange!=currentRange) {
1231        if (infeasible(iRange))
1232          numberInfeasibilities_++;
1233        if (infeasible(currentRange))
1234          numberInfeasibilities_--;
1235      }
1236      lower = lower_[iRange];
1237      upper = lower_[iRange+1];
1238      cost = cost_[iRange];
1239    }
1240  }
1241  if (CLP_METHOD2) {
1242    double * solution = model_->solutionRegion();
1243    double * upper = model_->upperRegion();
1244    double * lower = model_->lowerRegion();
1245    double * cost = model_->costRegion();
1246    for (int i=0;i<numberInArray;i++) {
1247      int iRow = index[i];
1248      int iSequence = pivotVariable[iRow];
1249      double value=solution[iSequence];
1250      int iStatus = status_[iSequence];
1251      assert (currentStatus(iStatus)==CLP_SAME);
1252      double lowerValue=lower[iSequence];
1253      double upperValue=upper[iSequence];
1254      double costValue = cost2_[iSequence];
1255      int iWhere = originalStatus(iStatus);
1256      if (iWhere==CLP_BELOW_LOWER) {
1257        lowerValue=upperValue;
1258        upperValue=bound_[iSequence];
1259        numberInfeasibilities_--;
1260      } else if (iWhere==CLP_ABOVE_UPPER) {
1261        upperValue=lowerValue;
1262        lowerValue=bound_[iSequence];
1263        numberInfeasibilities_--;
1264      }
1265      // get correct place
1266      int newWhere=CLP_FEASIBLE;
1267      if (value-upperValue<=primalTolerance) {
1268        if (value-lowerValue>=-primalTolerance) {
1269          // feasible
1270          //newWhere=CLP_FEASIBLE;
1271        } else {
1272          // below
1273          newWhere=CLP_BELOW_LOWER;
1274          costValue -= infeasibilityWeight_;
1275          numberInfeasibilities_++;
1276        }
1277      } else {
1278        // above
1279        newWhere = CLP_ABOVE_UPPER;
1280        costValue += infeasibilityWeight_;
1281        numberInfeasibilities_++;
1282      }
1283      if (iWhere!=newWhere) {
1284        setOriginalStatus(status_[iSequence],newWhere);
1285        if (newWhere==CLP_BELOW_LOWER) {
1286          bound_[iSequence]=upperValue;
1287          upperValue=lowerValue;
1288          lowerValue=-COIN_DBL_MAX;
1289        } else if (newWhere==CLP_ABOVE_UPPER) {
1290          bound_[iSequence]=lowerValue;
1291          lowerValue=upperValue;
1292          upperValue=COIN_DBL_MAX;
1293        }
1294        lower[iSequence] = lowerValue;
1295        upper[iSequence] = upperValue;
1296        cost[iSequence] = costValue;
1297      }
1298    }
1299  }
1300}
1301/* Puts back correct infeasible costs for each variable
1302   The input indices are row indices and need converting to sequences
1303   for costs.
1304   On input array is empty (but indices exist).  On exit just
1305   changed costs will be stored as normal CoinIndexedVector
1306*/
1307void 
1308ClpNonLinearCost::checkChanged(int numberInArray, CoinIndexedVector * update)
1309{
1310  assert (model_!=NULL);
1311  double primalTolerance = model_->currentPrimalTolerance();
1312  const int * pivotVariable = model_->pivotVariable();
1313  int number=0;
1314  int * index = update->getIndices();
1315  double * work = update->denseVector();
1316  if (CLP_METHOD1) {
1317    for (int i=0;i<numberInArray;i++) {
1318      int iRow = index[i];
1319      int iSequence = pivotVariable[iRow];
1320      // get where in bound sequence
1321      int iRange;
1322      double value = model_->solution(iSequence);
1323      int start = start_[iSequence];
1324      int end = start_[iSequence+1]-1;
1325      for (iRange=start; iRange<end;iRange++) {
1326        if (value<lower_[iRange+1]+primalTolerance) {
1327          // put in better range
1328          if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
1329            iRange++;
1330          break;
1331        }
1332      }
1333      assert(iRange<end);
1334      assert(model_->getStatus(iSequence)==ClpSimplex::basic);
1335      int jRange = whichRange_[iSequence];
1336      if (iRange!=jRange) {
1337        // changed
1338        work[iRow] = cost_[jRange]-cost_[iRange];
1339        index[number++]=iRow;
1340        double & lower = model_->lowerAddress(iSequence);
1341        double & upper = model_->upperAddress(iSequence);
1342        double & cost = model_->costAddress(iSequence);
1343        whichRange_[iSequence]=iRange;
1344        if (infeasible(iRange))
1345          numberInfeasibilities_++;
1346        if (infeasible(jRange))
1347          numberInfeasibilities_--;
1348        lower = lower_[iRange];
1349        upper = lower_[iRange+1];
1350        cost = cost_[iRange];
1351      }
1352    }
1353  }
1354  if (CLP_METHOD2) {
1355    double * solution = model_->solutionRegion();
1356    double * upper = model_->upperRegion();
1357    double * lower = model_->lowerRegion();
1358    double * cost = model_->costRegion();
1359    for (int i=0;i<numberInArray;i++) {
1360      int iRow = index[i];
1361      int iSequence = pivotVariable[iRow];
1362      double value=solution[iSequence];
1363      int iStatus = status_[iSequence];
1364      assert (currentStatus(iStatus)==CLP_SAME);
1365      double lowerValue=lower[iSequence];
1366      double upperValue=upper[iSequence];
1367      double costValue = cost2_[iSequence];
1368      int iWhere = originalStatus(iStatus);
1369      if (iWhere==CLP_BELOW_LOWER) {
1370        lowerValue=upperValue;
1371        upperValue=bound_[iSequence];
1372        numberInfeasibilities_--;
1373      } else if (iWhere==CLP_ABOVE_UPPER) {
1374        upperValue=lowerValue;
1375        lowerValue=bound_[iSequence];
1376        numberInfeasibilities_--;
1377      }
1378      // get correct place
1379      int newWhere=CLP_FEASIBLE;
1380      if (value-upperValue<=primalTolerance) {
1381        if (value-lowerValue>=-primalTolerance) {
1382          // feasible
1383          //newWhere=CLP_FEASIBLE;
1384        } else {
1385          // below
1386          newWhere=CLP_BELOW_LOWER;
1387          costValue -= infeasibilityWeight_;
1388          numberInfeasibilities_++;
1389        }
1390      } else {
1391        // above
1392        newWhere = CLP_ABOVE_UPPER;
1393        costValue += infeasibilityWeight_;
1394        numberInfeasibilities_++;
1395      }
1396      if (iWhere!=newWhere) {
1397        work[iRow] = cost[iSequence]-costValue;
1398        index[number++]=iRow;
1399        setOriginalStatus(status_[iSequence],newWhere);
1400        if (newWhere==CLP_BELOW_LOWER) {
1401          bound_[iSequence]=upperValue;
1402          upperValue=lowerValue;
1403          lowerValue=-COIN_DBL_MAX;
1404        } else if (newWhere==CLP_ABOVE_UPPER) {
1405          bound_[iSequence]=lowerValue;
1406          lowerValue=upperValue;
1407          upperValue=COIN_DBL_MAX;
1408        }
1409        lower[iSequence] = lowerValue;
1410        upper[iSequence] = upperValue;
1411        cost[iSequence] = costValue;
1412      }
1413    }
1414  }
1415  update->setNumElements(number);
1416}
1417/* Sets bounds and cost for one variable - returns change in cost*/
1418double 
1419ClpNonLinearCost::setOne(int iSequence, double value)
1420{
1421  assert (model_!=NULL);
1422  double primalTolerance = model_->currentPrimalTolerance();
1423  // difference in cost
1424  double difference=0.0;
1425  if (CLP_METHOD1) {
1426    // get where in bound sequence
1427    int iRange;
1428    int currentRange = whichRange_[iSequence];
1429    int start = start_[iSequence];
1430    int end = start_[iSequence+1]-1;
1431    if (!bothWays_) {
1432      // If fixed try and get feasible
1433      if (lower_[start+1]==lower_[start+2]&&fabs(value-lower_[start+1])<1.001*primalTolerance) {
1434        iRange =start+1;
1435      } else {
1436        for (iRange=start; iRange<end;iRange++) {
1437          if (value<=lower_[iRange+1]+primalTolerance) {
1438            // put in better range
1439            if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
1440              iRange++;
1441            break;
1442          }
1443        }
1444      }
1445    } else {
1446      // leave in current if possible
1447      iRange = whichRange_[iSequence];
1448      if (value<lower_[iRange]-primalTolerance||value>lower_[iRange+1]+primalTolerance) {
1449        for (iRange=start; iRange<end;iRange++) {
1450          if (value<lower_[iRange+1]+primalTolerance) {
1451            // put in better range
1452            if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
1453              iRange++;
1454            break;
1455          }
1456        }
1457      }
1458    }
1459    assert(iRange<end);
1460    whichRange_[iSequence]=iRange;
1461    if (iRange!=currentRange) {
1462      if (infeasible(iRange))
1463        numberInfeasibilities_++;
1464      if (infeasible(currentRange))
1465        numberInfeasibilities_--;
1466    }
1467    double & lower = model_->lowerAddress(iSequence);
1468    double & upper = model_->upperAddress(iSequence);
1469    double & cost = model_->costAddress(iSequence);
1470    lower = lower_[iRange];
1471    upper = lower_[iRange+1];
1472    ClpSimplex::Status status = model_->getStatus(iSequence);
1473    if (upper==lower) {
1474      if (status != ClpSimplex::basic) {
1475        model_->setStatus(iSequence,ClpSimplex::isFixed);
1476        status = ClpSimplex::basic; // so will skip
1477      }
1478    }
1479    switch(status) {
1480     
1481    case ClpSimplex::basic:
1482    case ClpSimplex::superBasic:
1483    case ClpSimplex::isFree:
1484      break;
1485    case ClpSimplex::atUpperBound:
1486    case ClpSimplex::atLowerBound:
1487    case ClpSimplex::isFixed:
1488      // set correctly
1489      if (fabs(value-lower)<=primalTolerance*1.001){
1490        model_->setStatus(iSequence,ClpSimplex::atLowerBound);
1491      } else if (fabs(value-upper)<=primalTolerance*1.001){
1492        model_->setStatus(iSequence,ClpSimplex::atUpperBound);
1493      } else {
1494        // set superBasic
1495        model_->setStatus(iSequence,ClpSimplex::superBasic);
1496      }
1497      break;
1498    }
1499    difference = cost-cost_[iRange]; 
1500    cost = cost_[iRange];
1501  }
1502  if (CLP_METHOD2) {
1503    double * upper = model_->upperRegion();
1504    double * lower = model_->lowerRegion();
1505    double * cost = model_->costRegion();
1506    int iStatus = status_[iSequence];
1507    assert (currentStatus(iStatus)==CLP_SAME);
1508    double lowerValue=lower[iSequence];
1509    double upperValue=upper[iSequence];
1510    double costValue = cost2_[iSequence];
1511    int iWhere = originalStatus(iStatus);
1512    if (iWhere==CLP_BELOW_LOWER) {
1513      lowerValue=upperValue;
1514      upperValue=bound_[iSequence];
1515      numberInfeasibilities_--;
1516    } else if (iWhere==CLP_ABOVE_UPPER) {
1517      upperValue=lowerValue;
1518      lowerValue=bound_[iSequence];
1519      numberInfeasibilities_--;
1520    }
1521    // get correct place
1522    int newWhere=CLP_FEASIBLE;
1523    if (value-upperValue<=primalTolerance) {
1524      if (value-lowerValue>=-primalTolerance) {
1525        // feasible
1526        //newWhere=CLP_FEASIBLE;
1527      } else {
1528        // below
1529        newWhere=CLP_BELOW_LOWER;
1530        costValue -= infeasibilityWeight_;
1531        numberInfeasibilities_++;
1532      }
1533    } else {
1534      // above
1535      newWhere = CLP_ABOVE_UPPER;
1536      costValue += infeasibilityWeight_;
1537      numberInfeasibilities_++;
1538    }
1539    if (iWhere!=newWhere) {
1540      difference = cost[iSequence]-costValue; 
1541      setOriginalStatus(status_[iSequence],newWhere);
1542      if (newWhere==CLP_BELOW_LOWER) {
1543        bound_[iSequence]=upperValue;
1544        upperValue=lowerValue;
1545        lowerValue=-COIN_DBL_MAX;
1546      } else if (newWhere==CLP_ABOVE_UPPER) {
1547        bound_[iSequence]=lowerValue;
1548        lowerValue=upperValue;
1549        upperValue=COIN_DBL_MAX;
1550      }
1551      lower[iSequence] = lowerValue;
1552      upper[iSequence] = upperValue;
1553      cost[iSequence] = costValue;
1554    }
1555    ClpSimplex::Status status = model_->getStatus(iSequence);
1556    if (upperValue==lowerValue) {
1557      if (status != ClpSimplex::basic) {
1558        model_->setStatus(iSequence,ClpSimplex::isFixed);
1559        status = ClpSimplex::basic; // so will skip
1560      }
1561    }
1562    switch(status) {
1563     
1564    case ClpSimplex::basic:
1565    case ClpSimplex::superBasic:
1566    case ClpSimplex::isFree:
1567      break;
1568    case ClpSimplex::atUpperBound:
1569    case ClpSimplex::atLowerBound:
1570    case ClpSimplex::isFixed:
1571      // set correctly
1572      if (fabs(value-lowerValue)<=primalTolerance*1.001){
1573        model_->setStatus(iSequence,ClpSimplex::atLowerBound);
1574      } else if (fabs(value-upperValue)<=primalTolerance*1.001){
1575        model_->setStatus(iSequence,ClpSimplex::atUpperBound);
1576      } else {
1577        // set superBasic
1578        model_->setStatus(iSequence,ClpSimplex::superBasic);
1579      }
1580      break;
1581    }
1582  }
1583  changeCost_ += value*difference;
1584  return difference;
1585}
1586/* Sets bounds and infeasible cost and true cost for one variable
1587   This is for gub and column generation etc */
1588void 
1589ClpNonLinearCost::setOne(int sequence, double solutionValue, double lowerValue, double upperValue,
1590                         double costValue)
1591{
1592  if (CLP_METHOD1) {
1593    int iRange=-1;
1594    int start = start_[sequence];
1595    double infeasibilityCost = model_->infeasibilityCost();
1596    cost_[start] = costValue-infeasibilityCost;
1597    lower_[start+1]=lowerValue;
1598    cost_[start+1] = costValue;
1599    lower_[start+2]=upperValue;
1600    cost_[start+2] = costValue+infeasibilityCost;
1601    double primalTolerance = model_->currentPrimalTolerance();
1602    if (solutionValue-lowerValue>=-primalTolerance) {
1603      if (solutionValue-upperValue<=primalTolerance) {
1604        iRange=start+1;
1605      } else {
1606        iRange=start+2;
1607      }
1608    } else {
1609      iRange = start;
1610    }
1611    model_->costRegion()[sequence]=cost_[iRange];
1612    whichRange_[sequence]=iRange;
1613  }
1614  if (CLP_METHOD2) {
1615    abort(); // may never work
1616  }
1617 
1618}
1619/* Sets bounds and cost for outgoing variable
1620   may change value
1621   Returns direction */
1622int 
1623ClpNonLinearCost::setOneOutgoing(int iSequence, double & value)
1624{
1625  assert (model_!=NULL);
1626  double primalTolerance = model_->currentPrimalTolerance();
1627  // difference in cost
1628  double difference=0.0;
1629  int direction=0;
1630  if (CLP_METHOD1) {
1631    // get where in bound sequence
1632    int iRange;
1633    int currentRange = whichRange_[iSequence];
1634    int start = start_[iSequence];
1635    int end = start_[iSequence+1]-1;
1636    // Set perceived direction out
1637    if (value<=lower_[currentRange]+1.001*primalTolerance) {
1638      direction=1;
1639    } else if (value>=lower_[currentRange+1]-1.001*primalTolerance) {
1640      direction=-1;
1641    } else {
1642      // odd
1643      direction=0;
1644    }
1645    // If fixed try and get feasible
1646    if (lower_[start+1]==lower_[start+2]&&fabs(value-lower_[start+1])<1.001*primalTolerance) {
1647      iRange =start+1;
1648    } else {
1649      // See if exact
1650      for (iRange=start; iRange<end;iRange++) {
1651        if (value==lower_[iRange+1]) {
1652          // put in better range
1653          if (infeasible(iRange)&&iRange==start) 
1654            iRange++;
1655          break;
1656        }
1657      }
1658      if (iRange==end) {
1659        // not exact
1660        for (iRange=start; iRange<end;iRange++) {
1661          if (value<=lower_[iRange+1]+primalTolerance) {
1662            // put in better range
1663            if (value>=lower_[iRange+1]-primalTolerance&&infeasible(iRange)&&iRange==start) 
1664              iRange++;
1665            break;
1666          }
1667        }
1668      }
1669    }
1670    assert(iRange<end);
1671    whichRange_[iSequence]=iRange;
1672    if (iRange!=currentRange) {
1673      if (infeasible(iRange))
1674        numberInfeasibilities_++;
1675      if (infeasible(currentRange))
1676        numberInfeasibilities_--;
1677    }
1678    double & lower = model_->lowerAddress(iSequence);
1679    double & upper = model_->upperAddress(iSequence);
1680    double & cost = model_->costAddress(iSequence);
1681    lower = lower_[iRange];
1682    upper = lower_[iRange+1];
1683    if (upper==lower) {
1684      value=upper;
1685    } else {
1686      // set correctly
1687      if (fabs(value-lower)<=primalTolerance*1.001){
1688        value = CoinMin(value,lower+primalTolerance);
1689      } else if (fabs(value-upper)<=primalTolerance*1.001){
1690        value = CoinMax(value,upper-primalTolerance);
1691      } else {
1692        //printf("*** variable wandered off bound %g %g %g!\n",
1693        //     lower,value,upper);
1694        if (value-lower<=upper-value) 
1695          value = lower+primalTolerance;
1696        else 
1697          value = upper-primalTolerance;
1698      }
1699    }
1700    difference = cost-cost_[iRange]; 
1701    cost = cost_[iRange];
1702  }
1703  if (CLP_METHOD2) {
1704    double * upper = model_->upperRegion();
1705    double * lower = model_->lowerRegion();
1706    double * cost = model_->costRegion();
1707    int iStatus = status_[iSequence];
1708    assert (currentStatus(iStatus)==CLP_SAME);
1709    double lowerValue=lower[iSequence];
1710    double upperValue=upper[iSequence];
1711    double costValue = cost2_[iSequence];
1712    // Set perceived direction out
1713    if (value<=lowerValue+1.001*primalTolerance) {
1714      direction=1;
1715    } else if (value>=upperValue-1.001*primalTolerance) {
1716      direction=-1;
1717    } else {
1718      // odd
1719      direction=0;
1720    }
1721    int iWhere = originalStatus(iStatus);
1722    if (iWhere==CLP_BELOW_LOWER) {
1723      lowerValue=upperValue;
1724      upperValue=bound_[iSequence];
1725      numberInfeasibilities_--;
1726    } else if (iWhere==CLP_ABOVE_UPPER) {
1727      upperValue=lowerValue;
1728      lowerValue=bound_[iSequence];
1729      numberInfeasibilities_--;
1730    }
1731    // get correct place
1732    // If fixed give benefit of doubt
1733    if (lowerValue==upperValue)
1734      value=lowerValue;
1735    int newWhere=CLP_FEASIBLE;
1736    if (value-upperValue<=primalTolerance) {
1737      if (value-lowerValue>=-primalTolerance) {
1738        // feasible
1739        //newWhere=CLP_FEASIBLE;
1740      } else {
1741        // below
1742        newWhere=CLP_BELOW_LOWER;
1743        costValue -= infeasibilityWeight_;
1744        numberInfeasibilities_++;
1745      }
1746    } else {
1747      // above
1748      newWhere = CLP_ABOVE_UPPER;
1749      costValue += infeasibilityWeight_;
1750      numberInfeasibilities_++;
1751    }
1752    if (iWhere!=newWhere) {
1753      difference = cost[iSequence]-costValue; 
1754      setOriginalStatus(status_[iSequence],newWhere);
1755      if (newWhere==CLP_BELOW_LOWER) {
1756        bound_[iSequence]=upperValue;
1757        upper[iSequence]=lowerValue;
1758        lower[iSequence]=-COIN_DBL_MAX;
1759      } else if (newWhere==CLP_ABOVE_UPPER) {
1760        bound_[iSequence]=lowerValue;
1761        lower[iSequence]=upperValue;
1762        upper[iSequence]=COIN_DBL_MAX;
1763      } else {
1764        lower[iSequence] = lowerValue;
1765        upper[iSequence] = upperValue;
1766      }
1767      cost[iSequence] = costValue;
1768    }
1769    // set correctly
1770    if (fabs(value-lowerValue)<=primalTolerance*1.001){
1771      value = CoinMin(value,lowerValue+primalTolerance);
1772    } else if (fabs(value-upperValue)<=primalTolerance*1.001){
1773      value = CoinMax(value,upperValue-primalTolerance);
1774    } else {
1775      //printf("*** variable wandered off bound %g %g %g!\n",
1776      //     lowerValue,value,upperValue);
1777      if (value-lowerValue<=upperValue-value) 
1778        value = lowerValue+primalTolerance;
1779      else 
1780        value = upperValue-primalTolerance;
1781    }
1782  }
1783  changeCost_ += value*difference;
1784  return direction;
1785}
1786// Returns nearest bound
1787double 
1788ClpNonLinearCost::nearest(int iSequence, double solutionValue)
1789{
1790  assert (model_!=NULL);
1791  double nearest=0.0;
1792  if (CLP_METHOD1) {
1793    // get where in bound sequence
1794    int iRange;
1795    int start = start_[iSequence];
1796    int end = start_[iSequence+1];
1797    int jRange=-1;
1798    nearest=COIN_DBL_MAX;
1799    for (iRange=start; iRange<end;iRange++) {
1800      if (fabs(solutionValue-lower_[iRange])<nearest) {
1801        jRange=iRange;
1802        nearest=fabs(solutionValue-lower_[iRange]);
1803      }
1804    }
1805    assert(jRange<end);
1806    nearest= lower_[jRange];
1807  }
1808  if (CLP_METHOD2) {
1809    const double * upper = model_->upperRegion();
1810    const double * lower = model_->lowerRegion();
1811    double lowerValue=lower[iSequence];
1812    double upperValue=upper[iSequence];
1813    int iWhere = originalStatus(status_[iSequence]);
1814    if (iWhere==CLP_BELOW_LOWER) {
1815      lowerValue=upperValue;
1816      upperValue=bound_[iSequence];
1817    } else if (iWhere==CLP_ABOVE_UPPER) {
1818      upperValue=lowerValue;
1819      lowerValue=bound_[iSequence];
1820    }
1821    if (fabs(solutionValue-lowerValue)<fabs(solutionValue-upperValue))
1822      nearest = lowerValue;
1823    else
1824      nearest = upperValue;
1825  }
1826  return nearest;
1827}
1828/// Feasible cost with offset and direction (i.e. for reporting)
1829double 
1830ClpNonLinearCost::feasibleReportCost() const
1831{ 
1832  double value;
1833  model_->getDblParam(ClpObjOffset,value);
1834  return (feasibleCost_+model_->objectiveAsObject()->nonlinearOffset())*model_->optimizationDirection()/
1835    (model_->objectiveScale()*model_->rhsScale())-value;
1836}
1837// Get rid of real costs (just for moment)
1838void 
1839ClpNonLinearCost::zapCosts()
1840{
1841  int iSequence;
1842  double infeasibilityCost = model_->infeasibilityCost();
1843  // zero out all costs
1844  int numberTotal = numberColumns_+numberRows_;
1845  if (CLP_METHOD1) {
1846    int n = start_[numberTotal];
1847    memset(cost_,0,n*sizeof(double));
1848    for (iSequence=0;iSequence<numberTotal;iSequence++) {
1849      int start = start_[iSequence];
1850      int end = start_[iSequence+1]-1;
1851      // correct costs for this infeasibility weight
1852      if (infeasible(start)) {
1853        cost_[start] = -infeasibilityCost;
1854      }
1855      if (infeasible(end-1)) {
1856        cost_[end-1] = infeasibilityCost;
1857      }
1858    }
1859  }
1860  if (CLP_METHOD2) {
1861  }
1862}
1863#ifdef VALIDATE
1864// For debug
1865void 
1866ClpNonLinearCost::validate()
1867{
1868  double primalTolerance = model_->currentPrimalTolerance();
1869  int iSequence;
1870  const double * solution = model_->solutionRegion();
1871  const double * upper = model_->upperRegion();
1872  const double * lower = model_->lowerRegion();
1873  const double * cost = model_->costRegion();
1874  double infeasibilityCost = model_->infeasibilityCost();
1875  int numberTotal = numberRows_+numberColumns_;
1876  int numberInfeasibilities=0;
1877  double sumInfeasibilities = 0.0;
1878 
1879  for (iSequence=0;iSequence<numberTotal;iSequence++) {
1880    double value=solution[iSequence];
1881    int iStatus = status_[iSequence];
1882    assert (currentStatus(iStatus)==CLP_SAME);
1883    double lowerValue=lower[iSequence];
1884    double upperValue=upper[iSequence];
1885    double costValue = cost2_[iSequence];
1886    int iWhere = originalStatus(iStatus);
1887    if (iWhere==CLP_BELOW_LOWER) {
1888      lowerValue=upperValue;
1889      upperValue=bound_[iSequence];
1890      costValue -= infeasibilityCost;
1891      assert (value<=lowerValue-primalTolerance);
1892      numberInfeasibilities++;
1893      sumInfeasibilities += lowerValue-value-primalTolerance;
1894      assert (model_->getStatus(iSequence)==ClpSimplex::basic);
1895    } else if (iWhere==CLP_ABOVE_UPPER) {
1896      upperValue=lowerValue;
1897      lowerValue=bound_[iSequence];
1898      costValue += infeasibilityCost;
1899      assert (value>=upperValue+primalTolerance);
1900      numberInfeasibilities++;
1901      sumInfeasibilities += value -upperValue-primalTolerance;
1902      assert (model_->getStatus(iSequence)==ClpSimplex::basic);
1903    } else {
1904      assert (value>=lowerValue-primalTolerance&&value<=upperValue+primalTolerance);
1905    }
1906    assert (lowerValue==saveLowerV[iSequence]);
1907    assert (upperValue==saveUpperV[iSequence]);
1908    assert (costValue==cost[iSequence]);
1909  }
1910  if (numberInfeasibilities)
1911    printf("JJ %d infeasibilities summing to %g\n",
1912           numberInfeasibilities,sumInfeasibilities);
1913}
1914#endif
Note: See TracBrowser for help on using the repository browser.