// copyright (C) 2000, International Business Machines
// Corporation and others.  All Rights Reserved.


/* OPEN: have a look at the OPEN tags */
/* OPEN: read/set more controls ... */

#include <cassert>
#include <numeric>
#include <sstream>

#include "CoinPragma.hpp"

#define __ANSIC_
#include <xprs.h>
#undef  __ANSIC_

#include "OsiXprSolverInterface.hpp"

#include "CoinHelperFunctions.hpp"
#include "OsiCuts.hpp"
#include "OsiColCut.hpp"
#include "CoinPackedMatrix.hpp"
#include "OsiRowCut.hpp"
#include "CoinWarmStartBasis.hpp"
#include "CoinMessage.hpp"

//#define DEBUG

static
void XPRS_CC OsiXprMessageCallback(XPRSprob prob, void *vUserDat, const char *msg, int msgLen, int msgType)
{ 
	if (vUserDat) {
		if (msgType < 0 ) {
			((CoinMessageHandler*)vUserDat)->finish();
		} else {
			/* CoinMessageHandler does not recognize that a "\0" string means that a newline should be printed.
			 * So we let it print a space (followed by a newline).
			 */
			if (((CoinMessageHandler*)vUserDat)->logLevel() > 0)
			  ((CoinMessageHandler*)vUserDat)->message(0, "XPRS", *msg ? msg : " ", ' ') << CoinMessageEol;
		}
	} else {
		if (msgType < 0) {
			fflush(stdout); 
		} else {
			printf("%.*s\n", msgLen, msg); 
			fflush(stdout); 
		}
	}
}

void reporterror(const char *fname, int iline, int ierr)
{
  fprintf( stdout, "ERROR: %s in line %d error %d occured\n",
	   fname, iline, ierr );
}

#define XPRS_CHECKED(function, args) do { 		\
      int _nReturn;					\
      if( (_nReturn = function args ) !=0 ) {		\
	 reporterror (#function,__LINE__,_nReturn);	\
      }							\
   } while (0)




//#############################################################################
// Solve methods
//#############################################################################

void
OsiXprSolverInterface::initialSolve() {

  freeSolution();

#if XPVERSION <= 20
   if ( objsense_ == 1.0 ) {
       XPRS_CHECKED( XPRSminim, (prob_,"l") );
   }
   else if ( objsense_ == -1.0 ) {
     XPRS_CHECKED( XPRSmaxim, (prob_,"l"));
   }
#else

   XPRS_CHECKED( XPRSlpoptimize, (prob_, "l") );
#endif

   lastsolvewasmip = false;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::resolve() {

   freeSolution();

#if XPVERSION <= 20
   if ( objsense_ == 1.0 ) {
       XPRS_CHECKED( XPRSminim, (prob_,"dl") );
   }
   else if ( objsense_ == -1.0 ) {
       XPRS_CHECKED( XPRSmaxim, (prob_,"dl") ) ;
   }
#else

   XPRS_CHECKED( XPRSlpoptimize, (prob_, "dl") );
#endif

   lastsolvewasmip = false;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::branchAndBound(){

  freeSolution();

#if XPVERSION <= 20
  int status;
  /* XPRSglobal cannot be called if there is no LP relaxation available yet
   * -> solve LP relaxation first, if no LP solution available */
  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
  if (status != XPRS_LP_OPTIMAL)
  	initialSolve();

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
  if (status != XPRS_LP_OPTIMAL) {
  	messageHandler()->message(0, "XPRS", "XPRESS failed to solve LP relaxation; cannot call XPRSglobal", ' ') << CoinMessageEol;
  	return;
  }
#else

  XPRS_CHECKED( XPRSmipoptimize, (prob_, "") );
#endif

  lastsolvewasmip = true;
}

//#############################################################################
// Parameter related methods
//#############################################################################
bool
OsiXprSolverInterface::setIntParam(OsiIntParam key, int value)
{
  bool retval = false;
  
  switch (key) {
      case OsiMaxNumIteration:
	  retval = XPRSsetintcontrol(prob_,XPRS_LPITERLIMIT, value) == 0;
	  break;
      case OsiMaxNumIterationHotStart:
	  retval = false;
	  break;
      case OsiLastIntParam:
	 retval = false;
	 break;
      case OsiNameDiscipline:
         retval = false;
	 break;
  }
  return retval;
}

//-----------------------------------------------------------------------------

/* OPEN: more dbl parameters ... */
bool
OsiXprSolverInterface::setDblParam(OsiDblParam key, double value)
{
  bool retval = false;
  switch (key) {
  case OsiDualObjectiveLimit:
    retval = XPRSsetdblcontrol(prob_,XPRS_MIPABSCUTOFF, value) == 0;
    break;
  case OsiPrimalObjectiveLimit:
    retval = false;	
    break;
  case OsiDualTolerance:
    retval = XPRSsetdblcontrol(prob_, XPRS_FEASTOL, value) == 0;	
    break;
  case OsiPrimalTolerance:
    retval = XPRSsetdblcontrol(prob_, XPRS_FEASTOL, value) == 0;	
    break;
  case OsiObjOffset: 
    return OsiSolverInterface::setDblParam(key, value);
  case OsiLastDblParam:
    retval = false;
    break;
  }
  return retval;
}
//-----------------------------------------------------------------------------

bool
OsiXprSolverInterface::setStrParam(OsiStrParam key, const std::string & value)
{
    bool retval=false;
    switch (key) {
	
	case OsiProbName:
/* OPEN: what does this mean */
	    OsiSolverInterface::setStrParam(key,value);
	    return retval = true;
	    
	case OsiLastStrParam:
	    return false;
	case OsiSolverName:
	    return false;
    }
    return false;
}
//-----------------------------------------------------------------------------

bool
OsiXprSolverInterface::getIntParam(OsiIntParam key, int& value) const
{
    bool retval = false;
    
    switch (key) {
	case OsiMaxNumIteration:
	    /* OPEN: the return value was logically wrong */
	    retval = XPRSgetintcontrol(prob_,XPRS_LPITERLIMIT, &value) == 0;
	    break;
	case OsiMaxNumIterationHotStart:
	    retval = false;
	    break;
	case OsiLastIntParam:
	    retval = false;
	    break;
        case OsiNameDiscipline:
	    retval = false;
	    break;
    }
    return retval;
}

//-----------------------------------------------------------------------------

bool
OsiXprSolverInterface::getDblParam(OsiDblParam key, double& value) const
{
  bool retval = false;

  switch (key) {
      case OsiDualObjectiveLimit:
	  retval = XPRSgetdblcontrol(prob_,XPRS_MIPABSCUTOFF, &value) == 0;
	  break;
      case OsiPrimalObjectiveLimit:
	  retval = false;
	  break;
      case OsiDualTolerance:
	  retval = XPRSgetdblcontrol(prob_, XPRS_FEASTOL, &value) == 0;
	  break;
      case OsiPrimalTolerance:
	  retval = XPRSgetdblcontrol(prob_, XPRS_FEASTOL, &value) == 0;
	  break;
      case OsiObjOffset:
	  retval = OsiSolverInterface::getDblParam(key, value);
	  break;
      case OsiLastDblParam:
	  retval = false;
	  break;
  }
  return retval;
}

//-----------------------------------------------------------------------------

bool
OsiXprSolverInterface::getStrParam(OsiStrParam key, std::string & value) const
{
  bool retval = false;
  switch (key) {
  case OsiProbName:
    OsiSolverInterface::getStrParam(key, value);
    retval = true;
    break;
  case OsiSolverName:
    value = "xpress";
    retval = true;
    break;
  case OsiLastStrParam:
    retval = false;
  }
  return retval;
}

//#############################################################################
// Methods returning info on how the solution process terminated
//#############################################################################

bool OsiXprSolverInterface::isAbandoned() const
{
  int  status, glstat;

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_MIPSTATUS, &glstat) );

  return status == XPRS_LP_UNFINISHED	  // LP unfinished
      || glstat == XPRS_MIP_NO_SOL_FOUND  // global search incomplete -- no int sol
      || glstat == XPRS_MIP_SOLUTION;	  // global search incomplete -- int sol found
}

bool OsiXprSolverInterface::isProvenOptimal() const
{
  int  status, glstat;

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_MIPSTATUS, &glstat) );

  return status == XPRS_LP_OPTIMAL   // LP optimal
      || status == XPRS_LP_CUTOFF    // LP obj worse than cutoff
      || glstat == XPRS_MIP_OPTIMAL; // global search complete -- int found
}

bool OsiXprSolverInterface::isProvenPrimalInfeasible() const
{
  int status;

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );

  return status == XPRS_LP_INFEAS; // LP infeasible
}

bool OsiXprSolverInterface::isProvenDualInfeasible() const
{
  int status;

   XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );

  return status == XPRS_LP_UNBOUNDED; // LP Unbounded
}

bool OsiXprSolverInterface::isPrimalObjectiveLimitReached() const
{
  /* OPEN: what does that mean	*/
  return false;			// N/A in XOSL
}

bool OsiXprSolverInterface::isDualObjectiveLimitReached() const
{
  int status;

   XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );

  return status == XPRS_LP_CUTOFF_IN_DUAL; // LP cut off in dual
}

bool OsiXprSolverInterface::isIterationLimitReached() const
{
    int itrlim, itcnt;
    
    XPRS_CHECKED( XPRSgetintcontrol, (prob_,XPRS_LPITERLIMIT, &itrlim) );
    XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_SIMPLEXITER, &itcnt) );
    
    return itcnt >= itrlim;
}

//#############################################################################
// WarmStart related methods
//#############################################################################

CoinWarmStart* OsiXprSolverInterface::getEmptyWarmStart () const
{
	return (dynamic_cast<CoinWarmStart *>(new CoinWarmStartBasis()));
}

CoinWarmStart* OsiXprSolverInterface::getWarmStart() const
{
  int pstat, retstat;

  
  /* OPEN: what does this mean */
  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &pstat) );
  if ( (pstat & 128) == 0 ) return NULL;

  CoinWarmStartBasis *ws = NULL;
  int numcols = getNumCols();
  int numrows = getNumRows();
  const double *lb = getColLower();
  double infty = getInfinity();
  int *rstatus = new int[numrows];
  int *cstatus = new int[numcols];

  retstat = XPRSgetbasis(prob_,rstatus, cstatus);

  if ( retstat == 0 ) {
    int i;

    ws = new CoinWarmStartBasis;
    ws->setSize( numcols, numrows );
      
    for( i = 0;	 i < numrows;  i++ ) {
      switch( rstatus[i] ) {
      case 0:
	ws->setArtifStatus(i, CoinWarmStartBasis::atLowerBound);
	break;
      case 1:
	ws->setArtifStatus(i, CoinWarmStartBasis::basic);
	break;
      case 2:
	ws->setArtifStatus(i, CoinWarmStartBasis::atUpperBound);
	break;
      case 3:
 	ws->setArtifStatus(i, CoinWarmStartBasis::isFree);
 	break;
      default:	// unknown row status
	delete ws;
	ws = NULL;
	goto TERMINATE;
      }
    }

    for( i = 0;	 i < numcols;  i++ ) {
      switch( cstatus[i] ) {
      case 0:
	if ( lb[i] <= -infty )
	  ws->setStructStatus(i, CoinWarmStartBasis::isFree);
	else
	  ws->setStructStatus(i, CoinWarmStartBasis::atLowerBound);
	break;
      case 1:
	ws->setStructStatus( i, CoinWarmStartBasis::basic );
	break;
      case 2:
	ws->setStructStatus( i, CoinWarmStartBasis::atUpperBound );
	break;
      case 3:
  ws->setStructStatus(i, CoinWarmStartBasis::isFree);
  break;
      default:	// unknown column status
	delete ws;
	ws = NULL;
	goto TERMINATE;
      }
    }
  }

 TERMINATE:
  delete[] cstatus;
  delete[] rstatus;

  return ws;
}

//-----------------------------------------------------------------------------

bool OsiXprSolverInterface::setWarmStart(const CoinWarmStart* warmstart)
{
  const CoinWarmStartBasis* ws = dynamic_cast<const CoinWarmStartBasis*>(warmstart);

  if ( !ws ) return false;

  int numcols = ws->getNumStructural();
  int numrows = ws->getNumArtificial();

  if ( numcols != getNumCols() || numrows != getNumRows() )
    return false;

  bool retval;
  int retstat;
  int *cstatus = new int[numcols];
  int *rstatus = new int[numrows];
  int i;

  for ( i = 0;	i < numrows;  i++ ) {
    switch( ws->getArtifStatus(i) ) {
    case CoinWarmStartBasis::atLowerBound:
      rstatus[i] = 0;
      break;
    case CoinWarmStartBasis::basic:
      rstatus[i] = 1;
      break;
    case CoinWarmStartBasis::atUpperBound:
      rstatus[i] = 2;
      break;
    default:  // unknown row status
      retval = false;
      goto TERMINATE;
    }
  }

  for( i = 0;  i < numcols;  i++ ) {
    switch( ws->getStructStatus(i) ) {
    case CoinWarmStartBasis::atLowerBound: 
    case CoinWarmStartBasis::isFree:
      cstatus[i] = 0;
      break;
    case CoinWarmStartBasis::basic:
      cstatus[i] = 1;
      break;
    case CoinWarmStartBasis::atUpperBound:
      cstatus[i] = 2;
      break;
    default:  // unknown row status
      retval = false;
      goto TERMINATE;
    }
  }

  retstat = XPRSloadbasis(prob_,rstatus, cstatus);
  retval = (retstat == 0);

 TERMINATE:
  delete[] cstatus;
  delete[] rstatus;
  return retval;
  
}

//#############################################################################
// Hotstart related methods (primarily used in strong branching)
//#############################################################################

void OsiXprSolverInterface::markHotStart()
{
  // *FIXME* : do better... -LL
  OsiSolverInterface::markHotStart();
}

void OsiXprSolverInterface::solveFromHotStart()
{
  // *FIXME* : do better... -LL
  OsiSolverInterface::solveFromHotStart();
}

void OsiXprSolverInterface::unmarkHotStart()
{
  // *FIXME* : do better... -LL
  OsiSolverInterface::unmarkHotStart();
}

//#############################################################################
// Problem information methods (original data)
//#############################################################################

//------------------------------------------------------------------
// Get number of rows and columns
//------------------------------------------------------------------
int
OsiXprSolverInterface::getNumCols() const
{
  if ( !isDataLoaded() ) return 0;

  int	  ncols;
  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_ORIGINALCOLS, &ncols) );

  return ncols;
}
//-----------------------------------------------------------------------------
int
OsiXprSolverInterface::getNumRows() const
{
   if ( !isDataLoaded() ) return 0;

   int	   nrows;

   XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_ORIGINALROWS, &nrows) );

   return nrows;
}
//-----------------------------------------------------------------------------
int
OsiXprSolverInterface::getNumElements() const
{
   if ( !isDataLoaded() ) return 0;

   int	   retVal;

   XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_ELEMS, &retVal) );

   return retVal;
}

//------------------------------------------------------------------
// Get pointer to rim vectors
//------------------------------------------------------------------  
const double *
OsiXprSolverInterface::getColLower() const
{
    if ( collower_ == NULL ) {
	if ( isDataLoaded() ) {
	    int	    ncols = getNumCols();
	    
	    if ( ncols > 0 ) {
	       collower_ = new double[ncols];
	       XPRS_CHECKED( XPRSgetlb, (prob_,collower_, 0, ncols - 1) );
	    }
	}
    }
    
    return collower_;
}

//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getColUpper() const
{
   if ( colupper_ == NULL ) {
      if ( isDataLoaded() ) {
	 int	 ncols = getNumCols();	 
	 
	 if ( ncols > 0 ) {
	    colupper_ = new double[ncols];
	    XPRS_CHECKED( XPRSgetub, (prob_,colupper_, 0, ncols - 1) );
	 }
      }
   }

   return colupper_;
}
//-----------------------------------------------------------------------------
const char *
OsiXprSolverInterface::getRowSense() const
{
    if ( rowsense_ == NULL ) {
	if ( isDataLoaded() ) {
	    int nrows = getNumRows();

	    if ( nrows > 0 ) {
	       rowsense_ = new char[nrows];
	       XPRS_CHECKED( XPRSgetrowtype, (prob_,rowsense_, 0, nrows - 1) );
	    }
	}
    }

   return rowsense_;
}
//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getRightHandSide() const
{
   if ( rhs_ == NULL ) {
      if ( isDataLoaded() ) {
	 int	 nrows = getNumRows();	

	 if ( nrows > 0 ) {
	    rhs_ = new double[nrows];
	    XPRS_CHECKED( XPRSgetrhs, (prob_,rhs_, 0, nrows - 1) );

	    // Make sure free rows have rhs of zero
	    const char * rs = getRowSense();
	    int nr = getNumRows();
	    int i;
	    for ( i = 0;  i < nr;	i++ ) {
	       if ( rs[i] == 'N' ) rhs_[i]=0.0;
	    }
	 }
      }
   }

   return rhs_;
}
//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getRowRange() const
{
   if ( rowrange_ == NULL ) {
      if ( isDataLoaded() ) {
	 int	 nrows = getNumRows();

	 if ( nrows > 0 ) {
	    rowrange_ = new double[nrows];
	    XPRS_CHECKED( XPRSgetrhsrange, (prob_,rowrange_, 0, nrows - 1) );

	    // Make sure non-R rows have range of 0.0
	    // XPRESS seems to set N and L rows to a range of Infinity
	    const char * rs = getRowSense();
	    int nr = getNumRows();
	    int i;
	    for ( i = 0;  i < nr;	i++ ) {
	       if ( rs[i] != 'R' ) rowrange_[i] = 0.0;
	    }
	 }
      }
   }

   return rowrange_;
}
//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getRowLower() const
{
   if ( rowlower_ == NULL ) {
      int     nrows = getNumRows();
      const   char    *rowsense = getRowSense();
      const   double  *rhs	= getRightHandSide();
      const   double  *rowrange = getRowRange();

      if ( nrows > 0 ) {
	 rowlower_ = new double[nrows];

	 double dum1;
	 for ( int i = 0;  i < nrows;  i++ ) {
	   convertSenseToBound(rowsense[i], rhs[i], rowrange[i],
			       rowlower_[i], dum1);
	 }
      }
   }

   return rowlower_;
}
//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getRowUpper() const
{
   if ( rowupper_ == NULL ) {
      int     nrows = getNumRows();
      const   char    *rowsense = getRowSense();
      const   double  *rhs	= getRightHandSide();
      const   double  *rowrange = getRowRange();

      if ( nrows > 0 ) {
	 rowupper_ = new double[nrows];

	 double dum1;
	 for ( int i = 0;  i < nrows;  i++ ) {
	   convertSenseToBound(rowsense[i], rhs[i], rowrange[i],
			       dum1, rowupper_[i]);
	 }
      }
   }

   return rowupper_;
}
//-----------------------------------------------------------------------------
const double *
OsiXprSolverInterface::getObjCoefficients() const
{
   if ( objcoeffs_ == NULL ) {
      if ( isDataLoaded() ) {
	 int	 ncols = getNumCols();

	 if ( ncols > 0 ) {
	    objcoeffs_ = new double[ncols];
	    XPRS_CHECKED( XPRSgetobj, (prob_,objcoeffs_, 0, ncols - 1) );
	 }
      }
   }

   return objcoeffs_;
}
//-----------------------------------------------------------------------------
double
OsiXprSolverInterface::getObjSense() const
{
   return objsense_;
}

//-----------------------------------------------------------------------------
// Return information on integrality
//-----------------------------------------------------------------------------

bool
OsiXprSolverInterface::isContinuous(int colNumber) const
{
   getVarTypes();

   //std::cerr <<"OsiXprSolverInterface::isContinuous " <<vartype_[colNumber] <<std::endl;
   if ( vartype_ == NULL ) return true;
   if ( vartype_[colNumber] == 'C' ) return true;
   return false;
}
//-----------------------------------------------------------------------------
#if 0
bool
OsiXprSolverInterface::isInteger( int colNumber ) const
{
   return !(isContinuous(colNumber));
}
//-----------------------------------------------------------------------------
bool
OsiXprSolverInterface::isBinary( int colNumber ) const
{
   const double *cu = colupper();
   const double *cl = collower();
  
   getVarTypes();

   if ( vartype_ == NULL ) return false;
   return (vartype_[colNumber] == 'I' || vartype_[colNumber] == 'B') && 
      (cu[colNumber] == 0.0 || cu[colNumber] == 1.0) && 
      (cl[colNumber] == 0.0 || cl[colNumber] == 1.0);
}
//-----------------------------------------------------------------------------
bool
OsiXprSolverInterface::isIntegerNonBinary( int colNumber ) const
{
   getVarTypes();

   if ( vartype_ == NULL ) return false;
   return (vartype_[colNumber] == 'I' || vartype_[colNumber] == 'B') &&
      !isBinary(colNumber);  
}
//-----------------------------------------------------------------------------
bool
OsiXprSolverInterface::isFreeBinary( int colNumber ) const
{
   const   double  *colupper = this->colupper();
   const   double  *collower = this->collower();

   getVarTypes();

   return isBinary(colNumber) && colupper[colNumber] != collower[colNumber];
}
#endif

//------------------------------------------------------------------
// Row and column copies of the matrix ...
//------------------------------------------------------------------

const CoinPackedMatrix *
OsiXprSolverInterface::getMatrixByRow() const
{
  if ( matrixByRow_ == NULL ) {
    if ( isDataLoaded() ) {

      int     nrows = getNumRows();
      int     ncols = getNumCols();
      int     nelems;

      XPRS_CHECKED( XPRSgetrows, (prob_,NULL, NULL, NULL, 0, &nelems, 0, nrows - 1) );
	 
      int     *start   = new int   [nrows + 1];
      int     *length  = new int   [nrows];
      int     *index   = new int   [nelems];
      double  *element = new double[nelems];

      XPRS_CHECKED( XPRSgetrows, (prob_,start, index, element, nelems, &nelems, 0, nrows - 1) );

      std::adjacent_difference(start + 1, start + (nrows+1), length);
      
      matrixByRow_ = new CoinPackedMatrix(true,0,0);	// (mjs) No gaps!
							// Gaps =><= presolve.
      matrixByRow_->assignMatrix(false /* not column ordered */,
				 ncols, nrows, nelems,
				 element, index, start, length);
    } else {
       matrixByRow_ = new CoinPackedMatrix(true,0,0);	// (mjs) No gaps!
      matrixByRow_->reverseOrdering();
    }
  }

  return matrixByRow_;
} 

//-----------------------------------------------------------------------------
const CoinPackedMatrix *
OsiXprSolverInterface::getMatrixByCol() const
{
   if ( matrixByCol_ == NULL ) {
      matrixByCol_ = new CoinPackedMatrix(*getMatrixByRow());
      matrixByCol_->reverseOrdering();
   }

   return matrixByCol_;
}

//------------------------------------------------------------------
// Get solver's value for infinity
//------------------------------------------------------------------
double
OsiXprSolverInterface::getInfinity() const
{
   return XPRS_PLUSINFINITY;
}

//#############################################################################
// Problem information methods (results)
//#############################################################################

const double *
OsiXprSolverInterface::getColSolution() const
{
	if ( colsol_ == NULL ) {
		if ( isDataLoaded() ) {
			int status;
			int nc = getNumCols();

			if ( nc > 0 ) {
				colsol_ = new double[nc];
				if( lastsolvewasmip ) {

				  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_MIPSTATUS, &status) );
				  if( status == XPRS_MIP_SOLUTION || status == XPRS_MIP_OPTIMAL )
				  	XPRS_CHECKED( XPRSgetmipsol, (prob_,colsol_, NULL) );
				  else
				  	XPRS_CHECKED( XPRSgetlb, (prob_,colsol_, 0, nc-1) );

				} else {

				  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
				  if( status == XPRS_LP_OPTIMAL )
				  	XPRS_CHECKED( XPRSgetlpsol, (prob_,colsol_, NULL, NULL, NULL) );
				  else
				  	XPRS_CHECKED( XPRSgetlb, (prob_,colsol_, 0, nc-1) );
				}
			}
		}
	}

   return colsol_;
}

//-----------------------------------------------------------------------------

const double *
OsiXprSolverInterface::getRowPrice() const
{
	if ( rowprice_ == NULL ) {
		if ( isDataLoaded() ) {
			int nr = getNumRows();

			if ( nr > 0 ) {
	    	int status;

				rowprice_ = new double[nr];

			  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
			  if( status == XPRS_LP_OPTIMAL )
			  	XPRS_CHECKED( XPRSgetlpsol, (prob_,NULL, NULL, rowprice_, NULL) );
			  else
			  	memset(rowprice_, 0, nr * sizeof(double));
			}
		}
	}

	return rowprice_;
}

//-----------------------------------------------------------------------------

const double * OsiXprSolverInterface::getReducedCost() const
{
  if ( colprice_ == NULL ) {
    if ( isDataLoaded() ) {
    	int status;
      int nc = getNumCols();

      colprice_ = new double[nc];

		  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
		  if( status == XPRS_LP_OPTIMAL )
		  	XPRS_CHECKED( XPRSgetlpsol, (prob_,NULL, NULL,NULL, colprice_) );
		  else
		  	memset(colprice_, 0, nc * sizeof(double));
    }
  }
  return colprice_;
}

//-----------------------------------------------------------------------------

const double * OsiXprSolverInterface::getRowActivity() const
{
	if( rowact_ == NULL ) {
		if ( isDataLoaded() ) {
			int nrows = getNumRows();
			if( nrows > 0 ) {
				int status;
				int i;
				const double* rhs = getRightHandSide();

				rowact_ = new double[nrows];

				if( lastsolvewasmip ) {

					XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_MIPSTATUS, &status) );
					if( status == XPRS_MIP_SOLUTION || status == XPRS_MIP_OPTIMAL ) {
						XPRS_CHECKED( XPRSgetmipsol, (prob_,NULL,rowact_) );
						for ( i = 0;	i < nrows;  i++ )
							rowact_[i] = rhs[i] - rowact_[i];
					} else
						memset(rowact_, 0, nrows * sizeof(double));

				} else {

					XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
					if( status == XPRS_LP_OPTIMAL ) {
						XPRS_CHECKED( XPRSgetlpsol, (prob_,NULL, rowact_, NULL, NULL) );
						for ( i = 0;	i < nrows;  i++ )
							rowact_[i] = rhs[i] - rowact_[i];
					} else
						memset(rowact_, 0, nrows * sizeof(double));
				}

#if 0
				/* OPEN: why do we need the presolve state here ??? */
				XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &status) );

				if ( status == 7 ) {
					int i;

					XPRS_CHECKED( XPRSgetsol, (prob_,NULL, rowact_, NULL, NULL) );

					for ( i = 0;	i < nrows;  i++ )
						rowact_[i] = rhs[i] - rowact_[i];
				} else {
					CoinFillN(rowact_, nrows, 0.0);
				}
#endif
			}
		}
	}

	return rowact_;
}

//-----------------------------------------------------------------------------

double
OsiXprSolverInterface::getObjValue() const
{
	double  objvalue = 0;
	double  objconstant = 0;

	if ( isDataLoaded() ) {
		int status;

		if( lastsolvewasmip ) {

			XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_MIPSTATUS, &status) );
			if( status == XPRS_MIP_SOLUTION || status == XPRS_MIP_OPTIMAL )
				XPRS_CHECKED( XPRSgetdblattrib, (prob_, XPRS_MIPOBJVAL, &objvalue) );
			else
				objvalue = CoinPackedVector(getNumCols(), getObjCoefficients()).dotProduct(getColSolution());

		} else {

			XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_LPSTATUS, &status) );
			if( status == XPRS_LP_OPTIMAL )
				XPRS_CHECKED( XPRSgetdblattrib, (prob_, XPRS_LPOBJVAL, &objvalue) );
			else
				objvalue = CoinPackedVector(getNumCols(), getObjCoefficients()).dotProduct(getColSolution());

		}

		OsiSolverInterface::getDblParam(OsiObjOffset, objconstant);
		// Constant offset is not saved with the xpress representation,
		// but it has to be returned from here anyway.
	}

	return objvalue - objconstant;
}

//-----------------------------------------------------------------------------

int OsiXprSolverInterface::getIterationCount() const
{
  int itcnt;

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_SIMPLEXITER, &itcnt) );

  return itcnt;
}

//-----------------------------------------------------------------------------

std::vector<double*> OsiXprSolverInterface::getDualRays(int maxNumRays,
							bool fullRay) const
{
  // *FIXME* : must write the method -LL
  throw CoinError("method is not yet written", "getDualRays",
		 "OsiXprSolverInterface");
  return std::vector<double*>();
}

//-----------------------------------------------------------------------------

std::vector<double*> OsiXprSolverInterface::getPrimalRays(int maxNumRays) const
{
#if 0
  // *FIXME* : Still need to expand column into full ncols-length vector

  const int nrows = getNumRows();
  int nrspar;

  XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_SPAREROWS, &nrspar) );
  int junb;
  int retcode;

  retcode = XPRSgetunbvec(prob_,&junb);

  if ( retcode != 0 ) 
      return std::vector<double *>(0, (double *) NULL);;
  
  double *ray = new double[nrows];
  

  if ( junb < nrows ) {		// it's a slack
    int i;

    for ( i = 0;  i < nrows;  i++ ) ray[i] = 0.0; 
    ray[junb] = 1.0; 

    XPRS_CHECKED( XPRSftran, (prob_,ray) );
  } else if ( junb >= nrows + nrspar && 
	      junb < nrows + nrspar + getNumCols() ){			
				// it's a structural variable
    int *mstart = new int[nrows];
    int *mrowind = new int[nrows];
    double *dmatval = new double[nrows];
    int nelt;
    int jcol = junb - nrows - nrspar;

    XPRS_CHECKED( XPRSgetcols, (prob_,mstart, mrowind, dmatval, nrows, &nelt, 
				jcol, jcol) ); 
    /* Unpack into the zeroed array y */ 
    int i, ielt;

    for ( i = 0;  i < nrows;  i++ ) ray[i] = 0.0; 
    for ( ielt = 0;  ielt < nelt;  ielt++ ) 
      ray[mrowind[ielt]] = dmatval[ielt]; 

    XPRS_CHECKED( XPRSftran, (prob_,ray) );

    delete [] mstart;
    delete [] mrowind;
    delete [] dmatval;
  } else {			// it's an error
    retcode = 1;
  }

  if ( retcode == 0 ) return std::vector<double *>(1, ray);
  else {
    delete ray;
    return std::vector<double *>(0, (double *) NULL); 
  }
#endif

  // *FIXME* : must write the method -LL
  throw CoinError("method is not yet written", "getPrimalRays",
		 "OsiXprSolverInterface");
  return std::vector<double*>();
}

//-----------------------------------------------------------------------------

#if 0
OsiVectorInt
OsiXprSolverInterface::getFractionalIndices(const double etol) const
{
   OsiVectorInt retVal;
   int	   numInts = numintvars();
   const   double  *sol = colsol();

   getVarTypes();
  
   OsiRelFltEq eq(etol);

   for ( int i = 0;  i < numInts;  i++ ) {
      double colSolElem = sol[ivarind_[i]];
      double distanceFromInteger = colSolElem - floor(colSolElem + 0.5);

      if ( !eq( distanceFromInteger, 0.0 ) )
	 retVal.push_back(ivarind_[i]);
   }

   return retVal;
}
#endif

//#############################################################################
// Problem modifying methods (rim vectors)
//#############################################################################

void
OsiXprSolverInterface::setObjCoeff( int elementIndex, double elementValue )
{
   if ( isDataLoaded() ) {
       XPRS_CHECKED( XPRSchgobj, (prob_,1, &elementIndex, &elementValue) );
      freeCachedResults();
   }
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setColLower( int elementIndex, double elementValue )
{
   if ( isDataLoaded() ) {
      char boundType = 'L';

      getVarTypes();
      
      if ( vartype_ &&
	   vartype_[elementIndex] == 'B' && 
	   (elementValue != 0.0 && elementValue != 1.0) ) {
	char elementType = 'I';
	
	XPRS_CHECKED( XPRSchgcoltype, (prob_,1, &elementIndex, &elementType) );
      }
      XPRS_CHECKED( XPRSchgbounds, (prob_,1, &elementIndex, &boundType, &elementValue) );

      freeCachedResults();
      //    delete [] collower_;
      //    collower_ = NULL;
   }
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setColUpper( int elementIndex, double elementValue )
{
   if ( isDataLoaded() ) {
      char boundType = 'U';

      getVarTypes();	  
      
      XPRS_CHECKED( XPRSchgbounds, (prob_,1, &elementIndex, &boundType, &elementValue) );
      if ( vartype_ &&
	   vartype_[elementIndex] == 'B' && 
	   (elementValue != 0.0 && elementValue != 1.0) ) {
	 char elementType = 'I';  
	 
	 XPRS_CHECKED( XPRSchgcoltype, (prob_,1, &elementIndex, &elementType) );
      }
      freeCachedResults();
      //    delete [] colupper_;
      //    colupper_ = NULL;
   } 
}

//-----------------------------------------------------------------------------

void OsiXprSolverInterface::setColBounds(const int elementIndex, double lower, double upper )
{
   if ( isDataLoaded() ) {
     char qbtype[2] = { 'L', 'U' };
     int mindex[2];
     double bnd[2];

     getVarTypes();
     mindex[0] = elementIndex;
     mindex[1] = elementIndex;
     bnd[0] = lower;
     bnd[1] = upper;

     XPRS_CHECKED( XPRSchgbounds, (prob_,2, mindex, qbtype, bnd) );
       
     if ( vartype_ && 
	  vartype_[mindex[0]] == 'B' && 
	  !((lower == 0.0 && upper == 0.0) ||
	    (lower == 1.0 && upper == 1.0) ||
	    (lower == 0.0 && upper == 1.0)) ) {
       char elementType = 'I';	
	 
       XPRS_CHECKED( XPRSchgcoltype, (prob_,1, &mindex[0], &elementType) );
     }
     freeCachedResults();
     //	   delete [] colupper_;
     //	   colupper_ = NULL;
   }
}

//-----------------------------------------------------------------------------

void OsiXprSolverInterface::setColSetBounds(const int* indexFirst,
					    const int* indexLast,
					    const double* boundList)
{
  OsiSolverInterface::setColSetBounds(indexFirst, indexLast, boundList);
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowLower( int elementIndex, double elementValue )
{
  double rhs   = getRightHandSide()[elementIndex];
  double range = getRowRange()[elementIndex];
  char	 sense = getRowSense()[elementIndex];
  double lower = 0, upper = 0;

  convertSenseToBound(sense, rhs, range, lower, upper);
  if( lower != elementValue ) {
    convertBoundToSense(elementValue, upper, sense, rhs, range);
    setRowType(elementIndex, sense, rhs, range);
    // freeCachedResults(); --- invoked in setRowType()
  }
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowUpper( int elementIndex, double elementValue )
{
  double rhs   = getRightHandSide()[elementIndex];
  double range = getRowRange()[elementIndex];
  char	 sense = getRowSense()[elementIndex];
  double lower = 0, upper = 0;

  convertSenseToBound( sense, rhs, range, lower, upper );
  if( upper != elementValue ) {
    convertBoundToSense(lower, elementValue, sense, rhs, range);
    setRowType(elementIndex, sense, rhs, range);
    // freeCachedResults(); --- invoked in setRowType()
  }
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowBounds( int elementIndex, double lower, double upper )
{
  double rhs, range;
  char sense;
  
  convertBoundToSense( lower, upper, sense, rhs, range );
  setRowType( elementIndex, sense, rhs, range );
  // freeCachedRowRim(); --- invoked in setRowType()
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowType(int index, char sense, double rightHandSide,
				  double range)
{
  if ( isDataLoaded() ) {
    int mindex[1] = {index};
    char qrtype[1] = {sense}; 
    double rhs[1] = {rightHandSide};
    double rng[1] = {range};

    XPRS_CHECKED( XPRSchgrowtype, (prob_,1, mindex, qrtype) );
    XPRS_CHECKED( XPRSchgrhs, (prob_,1, mindex, rhs) );
  	// range is properly defined only for range-type rows, so we call XPRSchgrhsrange only for ranged rows
    if (sense == 'R')
    	XPRS_CHECKED( XPRSchgrhsrange, (prob_,1, mindex, rng) );

    freeCachedResults();
  }
}

//-----------------------------------------------------------------------------

void OsiXprSolverInterface::setRowSetBounds(const int* indexFirst,
					    const int* indexLast,
					    const double* boundList)
{
  OsiSolverInterface::setRowSetBounds(indexFirst, indexLast, boundList);
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowSetTypes(const int* indexFirst,
				      const int* indexLast,
				      const char* senseList,
				      const double* rhsList,
				      const double* rangeList)
{
  OsiSolverInterface::setRowSetTypes(indexFirst, indexLast, senseList, rhsList, rangeList);
}

//#############################################################################
void 
OsiXprSolverInterface::setContinuous(int index) 
{
  if ( isDataLoaded() ) {
    int pstat;

    XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &pstat) );

    if ( (pstat & 6) == 0 ) {		// not presolved
      char qctype = 'C';

      XPRS_CHECKED( XPRSchgcoltype, (prob_,1, &index, &qctype) );
      freeCachedResults();
    }
  }
}

void 
OsiXprSolverInterface::setInteger(int index) 
{
  if ( isDataLoaded() ) {
    int pstat;

    XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &pstat) );

    if ( (pstat & 6) == 0 ) {		// not presolved
      char qctype;

      if ( getColLower()[index] == 0.0 && 
	   getColUpper()[index] == 1.0 ) 
	qctype = 'B';
      else
	qctype = 'I';
      XPRS_CHECKED( XPRSchgcoltype, (prob_,1, &index, &qctype) );
      freeCachedResults();
    }
  }
}

void 
OsiXprSolverInterface::setContinuous(const int* indices, int len) 
{
  if ( isDataLoaded() ) {
    int pstat;

    XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &pstat) );

    if ( (pstat & 6) == 0 ) {		// not presolved
      char *qctype = new char[len];

      CoinFillN(qctype, len, 'C');
      
      XPRS_CHECKED( XPRSchgcoltype, (prob_,len, const_cast<int *>(indices), qctype) );
      freeCachedResults();
      delete[] qctype;
    }
  }
}

void 
OsiXprSolverInterface::setInteger(const int* indices, int len) 
{
  if ( isDataLoaded() ) {
    int pstat;

    XPRS_CHECKED( XPRSgetintattrib, (prob_,XPRS_PRESOLVESTATE, &pstat) );

    if ( (pstat & 6) == 0 ) {		// not presolved
      char *qctype = new char[len];
      const double* clb = getColLower();
      const double* cub = getColUpper();

      for ( int i = 0;	i < len;  i++ ) {
	if ( clb[indices[i]] == 0.0 && cub[indices[i]] == 1.0 )
	  qctype[i] = 'B';
	else 
	  qctype[i] = 'I';
      }

      XPRS_CHECKED( XPRSchgcoltype, (prob_,len, const_cast<int *>(indices), qctype) );
      freeCachedResults();
      delete[] qctype;
    }
  }
}

//#############################################################################

void
OsiXprSolverInterface::setObjSense(double s) 
{
   assert(s == 1.0 || s == -1.0);

   objsense_ = s;

   XPRS_CHECKED( XPRSchgobjsense, (prob_, (int)s) );
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setColSolution(const double *colsol)
{
   freeSolution();

   colsol_ = new double[getNumCols()];

   for ( int i = 0;  i < getNumCols();	i++ )
      colsol_[i] = colsol[i];
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::setRowPrice(const double *rowprice)
{
   freeSolution();

   rowprice_ = new double[getNumRows()];

   for ( int i = 0;  i < getNumRows();	i++ )
      rowprice_[i] = rowprice[i];
}

//#############################################################################
// Problem modifying methods (matrix)
//#############################################################################

void 
OsiXprSolverInterface::addCol(const CoinPackedVectorBase& vec,
			      const double collb, const double colub,	
			      const double obj)
{
  if ( isDataLoaded() ) {
    freeCachedResults();

    int mstart = 0;

    XPRS_CHECKED( XPRSaddcols, (prob_,1, vec.getNumElements(), const_cast<double*>(&obj),
	    &mstart,
	    const_cast<int*>(vec.getIndices()),
	    const_cast<double*>(vec.getElements()),
	    const_cast<double*>(&collb),
	    const_cast<double*>(&colub)) );
  }
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::addCols(const int numcols,
			       const CoinPackedVectorBase * const * cols,
			       const double* collb, const double* colub,   
			       const double* obj)
{
  // freeCachedResults();

  for( int i = 0;  i < numcols;	 i++ )
      addCol( *(cols[i]), collb[i], colub[i], obj[i] );
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::deleteCols(const int num, const int *columnIndices)
{
  freeCachedResults();
  XPRS_CHECKED( XPRSdelcols, (prob_,num, const_cast<int *>(columnIndices)) );
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::addRow(const CoinPackedVectorBase& vec,
			      const double rowlb, const double rowub)
{
  // freeCachedResults(); -- will be invoked

  char sense;
  double rhs, range;

  convertBoundToSense(rowlb, rowub, sense, rhs, range);
  addRow(vec, sense, rhs, range);
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::addRow(const CoinPackedVectorBase& vec,
			      const char rowsen, const double rowrhs,	
			      const double rowrng)
{
  freeCachedResults();

   int mstart[2] = {0, vec.getNumElements()};

  XPRS_CHECKED( XPRSaddrows, (prob_,1, vec.getNumElements(), 
	  const_cast<char *>(&rowsen), const_cast<double *>(&rowrhs), 
	  const_cast<double *>(&rowrng), mstart,
	  const_cast<int *>(vec.getIndices()),
	  const_cast<double *>(vec.getElements())) );
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::addRows(const int numrows,
			       const CoinPackedVectorBase * const * rows,
			       const double* rowlb, const double* rowub)
{
  // *FIXME* : must write the method -LL
  throw CoinError("method is not yet written", "addRows",
		 "OsiXprSolverInterface");
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::addRows(const int numrows,
			       const CoinPackedVectorBase * const * rows,
			       const char* rowsen, const double* rowrhs,   
			       const double* rowrng)
{
  // *FIXME* : must write the method -LL
  throw CoinError("method is not yet written", "addRows",
		 "OsiXprSolverInterface");
}
//-----------------------------------------------------------------------------
void 
OsiXprSolverInterface::deleteRows(const int num, const int * rowIndices)
{
  freeCachedResults();

  XPRS_CHECKED( XPRSdelrows, (prob_,num, const_cast<int *>(rowIndices)) );
}

//#############################################################################
// Methods to input a problem
//#############################################################################

void
OsiXprSolverInterface::loadProblem(const CoinPackedMatrix& matrix,
				   const double* collb, const double* colub,   
				   const double* obj,
				   const double* rowlb, const double* rowub)
{
  const double inf = getInfinity();
  
  char	 * rowSense = new char	[matrix.getNumRows()];
  double * rowRhs   = new double[matrix.getNumRows()];
  double * rowRange = new double[matrix.getNumRows()];
  
  int i;
  for ( i = matrix.getNumRows() - 1; i >= 0; --i) {
    
    double rlb;
    if ( rowlb!=NULL )
      rlb = rowlb[i];
    else
      rlb = -inf;
    
     double rub;
     if ( rowub!=NULL )
       rub = rowub[i];
     else
       rub = inf;
     
     convertBoundToSense(rlb,rub,rowSense[i],rowRhs[i],rowRange[i]);
#if 0
     if ( rlb==rub ) {
       rowSense[i]='E';
       rowRhs[i]  =rlb;
       rowRange[i]=0.0;
       continue;
     }
     if ( rlb<=-inf && rub>=inf ) {
       rowSense[i]='N';
       rowRhs[i]  =inf;
       rowRange[i]=0.0;
       continue;
     }
     if ( rlb<=-inf && !(rub>=inf) ) {
       rowSense[i]='L';
       rowRhs[i]  =rub;
       rowRange[i]=0.0;
       continue;
     }
     if ( !(rlb<=-inf) && rub>=inf ) {
       rowSense[i]='G';
       rowRhs[i]  =rlb;
       rowRange[i]=0.0;
       continue;
     }
     if ( !(rlb<=-inf) && !(rub>=inf) ) {
       rowSense[i]='R';
       rowRhs[i]  =rub;
       rowRange[i]=rub-rlb;
       continue;
     }
#endif
   }
  
   loadProblem(matrix, collb, colub, obj, rowSense, rowRhs, rowRange ); 
   
   delete [] rowSense;
   delete [] rowRhs;
   delete [] rowRange;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::assignProblem(CoinPackedMatrix*& matrix,
				     double*& collb, double*& colub,
				     double*& obj,
				     double*& rowlb, double*& rowub)
{
   loadProblem(*matrix, collb, colub, obj, rowlb, rowub);
   delete matrix;   matrix = 0;
   delete[] collb;  collb = 0;
   delete[] colub;  colub = 0;
   delete[] obj;    obj = 0;
   delete[] rowlb;  rowlb = 0;
   delete[] rowub;  rowub = 0;
}

//-----------------------------------------------------------------------------

// #define OSIXPR_ADD_OBJ_ROW

void
OsiXprSolverInterface::loadProblem(const CoinPackedMatrix& matrix,
				   const double* collb, const double* colub,
				   const double* obj,
				   const char* rowsen, const double* rowrhs,   
				   const double* rowrng)
{
  freeCachedResults();
  int i;

  char*   rsen;
  double* rrhs;
 
  // Set column values to defaults if NULL pointer passed
  int nc=matrix.getNumCols();
  int nr=matrix.getNumRows();
  double * clb;	 
  double * cub;
  double * ob;
  if ( collb!=NULL ) {
    clb=const_cast<double*>(collb);
  }
  else {
    clb = new double[nc];
    for( i=0; i<nc; i++ ) clb[i]=0.0;
  }
  if ( colub!=NULL ) 
    cub=const_cast<double*>(colub);
  else {
    cub = new double[nc];
    for( i=0; i<nc; i++ ) cub[i]=XPRS_PLUSINFINITY;
  }
  if ( obj!=NULL ) 
    ob=const_cast<double*>(obj);
  else {
    ob = new double[nc];
    for( i=0; i<nc; i++ ) ob[i]=0.0;
  }
  if ( rowsen != NULL )
  	rsen = const_cast<char*>(rowsen);
  else {
  	rsen = new char[nr];
  	for( i = 0; i < nr; ++i ) rsen[i] = 'G';
  }
  if ( rowrhs != NULL )
  	rrhs = const_cast<double*>(rowrhs);
  else {
  	rrhs = new double[nr];
  	for( i = 0; i < nr; ++i ) rrhs[i] = 0.0;
  }

  bool freeMatrixRequired = false;
  CoinPackedMatrix * m = NULL;
  if ( !matrix.isColOrdered() ) {
     m = new CoinPackedMatrix(true,0,0);	// (mjs) No gaps!
    m->reverseOrderedCopyOf(matrix);
    freeMatrixRequired = true;
  } else {
    m = const_cast<CoinPackedMatrix *>(&matrix);
  }
  
  // Generate a problem name
  char probName[256];
  sprintf(probName, "Prob%i", osiSerial_);
  
  nc = m->getNumCols();
  nr = m->getNumRows();
  
  if ( getLogFilePtr()!=NULL ) {   
    fprintf(getLogFilePtr(),"{\n"); 

    fprintf(getLogFilePtr(),"  char rowsen[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowsen[%d]='%c';\n",i,rsen[i]);

    fprintf(getLogFilePtr(),"  double rowrhs[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowrhs[%d]=%f;\n",i,rrhs[i]);
    
    fprintf(getLogFilePtr(),"  double rowrng[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowrng[%d]=%f;\n",i,rowrng ? rowrng[i] : 0.0);

    fprintf(getLogFilePtr(),"  double ob[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  ob[%d]=%f;\n",i,ob[i]);

    fprintf(getLogFilePtr(),"  double clb[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  clb[%d]=%f;\n",i,clb[i]);

    fprintf(getLogFilePtr(),"  double cub[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  cub[%d]=%f;\n",i,cub[i]);

    fprintf(getLogFilePtr(),"  int vectorStarts[%d];\n",nc+1);
    for ( i=0; i<=nc; i++ )
      fprintf(getLogFilePtr(),"  vectorStarts[%d]=%d;\n",i,m->getVectorStarts()[i]);

    fprintf(getLogFilePtr(),"  int vectorLengths[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  vectorLengths[%d]=%d;\n",i,m->getVectorLengths()[i]);
    
    fprintf(getLogFilePtr(),"  int indices[%d];\n",m->getVectorStarts()[nc]);
    for ( i=0; i<m->getVectorStarts()[nc]; i++ )
      fprintf(getLogFilePtr(),"  indices[%d]=%d;\n",i,m->getIndices()[i]);

    fprintf(getLogFilePtr(),"  double elements[%d];\n",m->getVectorStarts()[nc]);
    for ( i=0; i<m->getVectorStarts()[nc]; i++ )
      fprintf(getLogFilePtr(),"  elements[%d]=%f;\n",i,m->getElements()[i]);

    fprintf(getLogFilePtr(),"}\n");
  }
  int iret = XPRSloadlp( prob_,
			 probName,
			 nc,
			 nr,
			 const_cast<char*>(rsen),
			 const_cast<double*>(rrhs),
			 const_cast<double*>(rowrng),
			 ob,
			 const_cast<int*>(m->getVectorStarts()),
			 const_cast<int*>(m->getVectorLengths()),
			 const_cast<int*>(m->getIndices()),
			 const_cast<double*>(m->getElements()),
			 clb,
			 cub );
  setStrParam(OsiProbName,probName);
  
  if ( iret != 0 )
      XPRSgetintattrib(prob_,XPRS_ERRORCODE, &iret);
  assert( iret == 0 );
  
   
  char pname[256];      // Problem names can be 200 chars in XPRESS 12
  XPRS_CHECKED( XPRSgetprobname,(prob_,pname) );
  xprProbname_ = pname;
  
  if ( collb==NULL ) delete[] clb;
  if ( colub==NULL ) delete[] cub;
  if ( obj  ==NULL ) delete[] ob;
  if ( rowsen==NULL ) delete[] rsen;
  if ( rowrhs==NULL ) delete[] rrhs;
  
  if (freeMatrixRequired) {
    delete m;
  }
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::assignProblem(CoinPackedMatrix*& matrix,
				     double*& collb, double*& colub,
				     double*& obj,
				     char*& rowsen, double*& rowrhs,
				     double*& rowrng)
{
   loadProblem(*matrix, collb, colub, obj, rowsen, rowrhs, rowrng);
   delete matrix;   matrix = 0;
   delete[] collb;  collb = 0;
   delete[] colub;  colub = 0;
   delete[] obj;    obj = 0;
   delete[] rowsen; rowsen = 0;
   delete[] rowrhs; rowrhs = 0;
   delete[] rowrng; rowrng = 0;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::loadProblem(const int numcols, const int numrows,
				   const int* start, const int* index,
				   const double* value,
				   const double* collb, const double* colub,   
				   const double* obj,
				   const double* rowlb, const double* rowub )
{
  const double inf = getInfinity();
  
  char   * rowSense = new char  [numrows];
  double * rowRhs   = new double[numrows];
  double * rowRange = new double[numrows];
  
  for ( int i = numrows - 1; i >= 0; --i ) {
    const double lower = rowlb ? rowlb[i] : -inf;
    const double upper = rowub ? rowub[i] : inf;
    convertBoundToSense( lower, upper, rowSense[i], rowRhs[i], rowRange[i] );
  }

  loadProblem(numcols, numrows, start, index, value, collb, colub, obj,
	      rowSense, rowRhs, rowRange);
  delete [] rowSense;
  delete [] rowRhs;
  delete [] rowRange;

}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::loadProblem(const int numcols, const int numrows,
				   const int* start, const int* index,
				   const double* value,
				   const double* collb, const double* colub,   
				   const double* obj,
				   const char* rowsen, const double* rowrhs,
				   const double* rowrng )
{
  freeCachedResults();
  int i;
 
  // Set column values to defaults if NULL pointer passed
  int nc = numcols;
  int nr = numrows;
  int * len = new int[nc+1];
  double * clb;  
  double * cub;
  double * ob;
  char*   rsen;
  double* rrhs;

  std::adjacent_difference(start, start + (nc+1), len);
  
  if ( collb!=NULL ) {
    clb=const_cast<double*>(collb);
  }
  else {
    clb = new double[nc];
    for( i=0; i<nc; i++ ) clb[i]=0.0;
  }
  if ( colub!=NULL ) 
    cub=const_cast<double*>(colub);
  else {
    cub = new double[nc];
    for( i=0; i<nc; i++ ) cub[i]=XPRS_PLUSINFINITY;
  }
  if ( obj!=NULL ) 
    ob=const_cast<double*>(obj);
  else {
    ob = new double[nc];
    for( i=0; i<nc; i++ ) ob[i]=0.0;
  }
  if ( rowsen != NULL )
  	rsen = const_cast<char*>(rowsen);
  else {
  	rsen = new char[nr];
  	for( i = 0; i < nr; ++i ) rsen[i] = 'G';
  }
  if ( rowrhs != NULL )
  	rrhs = const_cast<double*>(rowrhs);
  else {
  	rrhs = new double[nr];
  	for( i = 0; i < nr; ++i ) rrhs[i] = 0.0;
  }

  // Generate a problem name
  char probName[256];
  sprintf(probName, "Prob%i", osiSerial_);

  if ( getLogFilePtr()!=NULL ) {   
    fprintf(getLogFilePtr(),"{\n"); 

    fprintf(getLogFilePtr(),"  char rowsen[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowsen[%d]='%c';\n",i,rsen[i]);

    fprintf(getLogFilePtr(),"  double rowrhs[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowrhs[%d]=%f;\n",i,rrhs[i]);
    
    fprintf(getLogFilePtr(),"  double rowrng[%d];\n",nr);
    for ( i=0; i<nr; i++ )
      fprintf(getLogFilePtr(),"  rowrng[%d]=%f;\n",i,rowrng ? rowrng[i] : 0.0);

    fprintf(getLogFilePtr(),"  double ob[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  ob[%d]=%f;\n",i,ob[i]);

    fprintf(getLogFilePtr(),"  double clb[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  clb[%d]=%f;\n",i,clb[i]);

    fprintf(getLogFilePtr(),"  double cub[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  cub[%d]=%f;\n",i,cub[i]);

    fprintf(getLogFilePtr(),"  int vectorStarts[%d];\n",nc+1);
    for ( i=0; i<=nc; i++ )
      fprintf(getLogFilePtr(),"  vectorStarts[%d]=%d;\n",i,start[i]);

    fprintf(getLogFilePtr(),"  int vectorLengths[%d];\n",nc);
    for ( i=0; i<nc; i++ )
      fprintf(getLogFilePtr(),"  vectorLengths[%d]=%d;\n",i,len[i+1]);
    
    fprintf(getLogFilePtr(),"  int indices[%d];\n",start[nc]);
    for ( i=0; i<start[nc]; i++ )
      fprintf(getLogFilePtr(),"  indices[%d]=%d;\n",i,index[i]);

    fprintf(getLogFilePtr(),"  double elements[%d];\n",start[nc]);
    for ( i=0; i<start[nc]; i++ )
      fprintf(getLogFilePtr(),"  elements[%d]=%f;\n",i,value[i]);

    fprintf(getLogFilePtr(),"}\n");
  }
  int iret = XPRSloadlp(prob_,probName,
			nc,
			nr,
			const_cast<char*>(rsen),
			const_cast<double*>(rrhs),
			const_cast<double*>(rowrng),
			ob,
			const_cast<int*>(start),
			const_cast<int*>(len+1),
			const_cast<int*>(index),
			const_cast<double*>(value),
			clb,
			cub );
  setStrParam(OsiProbName,probName);
  
  if ( iret != 0 )
    XPRSgetintattrib(prob_,XPRS_ERRORCODE, &iret);
  assert( iret == 0 );
  
  char pname[256];      // Problem names can be 200 chars in XPRESS 12
  XPRS_CHECKED( XPRSgetprobname, (prob_,pname) );
  xprProbname_ = pname;
  
  if ( collb == NULL ) delete[] clb;
  if ( colub == NULL ) delete[] cub;
  if ( obj   == NULL ) delete[] ob;
  if ( rowsen== NULL ) delete[] rsen;
  if ( rowrhs== NULL ) delete[] rrhs;
  delete[] len;
}



//-----------------------------------------------------------------------------
// Read mps files
//-----------------------------------------------------------------------------

int
OsiXprSolverInterface::readMps(const char *filename, const char *extension) 
{
#if 0
  if (getLogFilePtr()!=NULL) {
    fprintf(getLogFilePtr(),"{\n");
    fprintf(getLogFilePtr(),"  XPRSreadprob(prob_,\"%s\");\n",filename);
    fprintf(getLogFilePtr(),"  int namlen;\n");
    fprintf(getLogFilePtr(),"  XPRSgetintcontrol(prob_,XPRS_NAMELENGTH,&namlen);\n");
    fprintf(getLogFilePtr(),"  namlen *= 8;\n");
  }
 
  // Read Mps file.
  // XPRESS generates its own file extensions, so we ignore any supplied.
  int iret = XPRSreadprob(prob_,filename);
  if ( iret != 0 )  
    XPRSgetintattrib(prob_,XPRS_ERRORCODE, &iret);
  assert( iret == 0 );

  // Get length of Mps names
  int namlen;
  XPRSgetintattrib(prob_,XPRS_NAMELENGTH, &namlen);
  namlen *= 8;

  if (getLogFilePtr()!=NULL) {
    fprintf(getLogFilePtr(),"  char objRowName[%d],rowName[%d];\n",namlen,namlen);
    fprintf(getLogFilePtr(),"  XPRSgetstrcontrol(prob_,XPRS_MPSOBJNAME, objRowName);\n");
    fprintf(getLogFilePtr(),"  int nr;\n");
    fprintf(getLogFilePtr(),"  XPRSsetintcontrol(prob_,XPRS_ROWS, &nr);");
  }

  // Allocate space to hold row names.
  char * objRowName = new char[namlen+1];
  char * rowName    = new char[namlen+1];

  // Get name of objective row.
  // If "" returned, then first N row is objective row
  XPRSgetstrcontrol(prob_,XPRS_MPSOBJNAME,objRowName);

  // Get number of rows
  int nr;
  XPRSgetintattrib(prob_,XPRS_ROWS, &nr);
    
  if (getLogFilePtr()!=NULL) {
    fprintf(getLogFilePtr(),"  char rs[%d];\n",nr);
    fprintf(getLogFilePtr(),"  XPRSgetrowtype(prob_,rs, 0, %d);\n",nr-1);
  }

  // Get row sense.
  char * rs = new char[nr];
  XPRSgetrowtype(prob_,rs, 0, nr - 1);

  // Loop once for each row looking for objective row
  int i;
  for ( i=0; i<nr; i++ ) {
    
    // Objective row must be an N row
    if ( rs[i]=='N' ) {      
      
      if (getLogFilePtr()!=NULL) {
        fprintf(getLogFilePtr(),"  XPRSgetnames(prob_,1,rowName,%d,%d);\n",i,i);
      }
      
      // Get name of this row
      XPRSgetnames(prob_,1,rowName,i,i);
      
      // Is this the objective row?
      if( strcmp(rowName,objRowName)==0 ||
        strcmp("",objRowName) == 0 ) {
        
        if (getLogFilePtr()!=NULL) {
          fprintf(getLogFilePtr(),"  int rowToDelete[1];\n");
          fprintf(getLogFilePtr(),"  rowToDelete[0]=%d;\n",i);
          fprintf(getLogFilePtr(),"  XPRSdelrows(prob_,1,rowToDelete);\n");
        }
        
        // found objective row. now delete it
        int rowToDelete[1];
        rowToDelete[0] = i;
        XPRSdelrows(prob_,1,rowToDelete);
        break;
      }
    }
  }

  delete [] rs;
  delete [] rowName;
  delete [] objRowName;
  
  if (getLogFilePtr()!=NULL) {
    fprintf(getLogFilePtr(),"  char pname[256];\n");
    fprintf(getLogFilePtr(),"  XPRSgetprobname(prob_,pname);\n");
    fprintf(getLogFilePtr(),"}\n");
  }
  
  char pname[256];      // Problem names can be 200 chars in XPRESS 12
  XPRSgetprobname(prob_,pname);
  xprProbname_ = pname;
  return 0;
#else
  int retVal = OsiSolverInterface::readMps(filename,extension);
  getStrParam(OsiProbName,xprProbname_);
  return retVal;
#endif
}


//-----------------------------------------------------------------------------
// Write mps files
//-----------------------------------------------------------------------------
void OsiXprSolverInterface::writeMps(const char *filename,
				     const char *extension,
				     double objSense) const
{
	// Note: XPRESS insists on ignoring the extension and
  // adding ".mat" instead.  Forewarned is forearmed.  NOTE: that does not happen for me (Xpress 21).
  // Note: Passing an empty filename produces a file named after
  // the problem.

	assert(filename != NULL);
	assert(extension != NULL);

	char* fullname = new char[strlen(filename) + strlen(extension) + 2];
	sprintf(fullname, "%s.%s", filename, extension);

  XPRS_CHECKED( XPRSwriteprob, (prob_,fullname, "") );

  delete[] fullname;
}

//-----------------------------------------------------------------------------
// Message Handling
//-----------------------------------------------------------------------------
void OsiXprSolverInterface::passInMessageHandler(CoinMessageHandler * handler)
{
	OsiSolverInterface::passInMessageHandler(handler);
	
  XPRS_CHECKED( XPRSsetcbmessage, (prob_, OsiXprMessageCallback, messageHandler()) );
}


//#############################################################################
// Static data and methods
//#############################################################################

void
OsiXprSolverInterface::incrementInstanceCounter()
{
#ifdef DEBUG
    fprintf( stdout, "incrementInstanceCounter:: numInstances %d\n", 
	     numInstances_);
#endif
    if ( numInstances_ == 0 ) {          

       if( XPRSinit(NULL) != 0 ) {
         throw CoinError("failed to init XPRESS, maybe no license", "incrementInstanceCounter", "OsiXprSolverInterface");
       }
    }

    numInstances_++;
    osiSerial_++;
    
    //  cout << "Instances = " << numInstances_ << "; Serial = " << osiSerial_ << endl;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::decrementInstanceCounter()
{
#ifdef DEBUG
    fprintf( stdout, "decrementInstanceCounter:: numInstances %d\n", 
	     numInstances_);
#endif
  assert( numInstances_ != 0 );

  numInstances_--;
 
  //  cout << "Instances = " << numInstances_ << endl;
  
  if ( numInstances_ == 0 ) {
      XPRSfree();
  }
}

//-----------------------------------------------------------------------------

unsigned int
OsiXprSolverInterface::getNumInstances()
{
#ifdef DEBUG
    fprintf( stdout, "getNumInstances:: numInstances %d\n", 
	     numInstances_);
#endif
   return numInstances_;
}

//-----------------------------------------------------------------------------

// Return XPRESS-MP Version number
int OsiXprSolverInterface::version()
{
/* OPEN: handle version properly !!! */

/*
    int retVal;
    XPRSprob p;
    incrementInstanceCounter();
    XPRS_CHECKED( XPRScreateprob, ( &p ) );
    XPRS_CHECKED( XPRSgetintcontrol, ( p, XPRS_VERSION, &retVal ) );
    XPRS_CHECKED( XPRSdestroyprob, ( p ) );
    decrementInstanceCounter();
*/

    return XPVERSION;

}

//-----------------------------------------------------------------------------

FILE * OsiXprSolverInterface::getLogFilePtr()
{
  if (logFilePtr_!=NULL){fclose(logFilePtr_);logFilePtr_=NULL;}
  if (logFileName_!=NULL) logFilePtr_ = fopen(logFileName_,"a+");
  return logFilePtr_;
}

//-----------------------------------------------------------------------------

void OsiXprSolverInterface::setLogFileName( const char * filename )
{
  logFileName_ = filename;
}

//-----------------------------------------------------------------------------

int OsiXprSolverInterface::iXprCallCount_ = 1;

unsigned int OsiXprSolverInterface::numInstances_ = 0;

unsigned int OsiXprSolverInterface::osiSerial_ = 0;  

FILE * OsiXprSolverInterface::logFilePtr_ = NULL;  

const char * OsiXprSolverInterface::logFileName_ = NULL;

//#############################################################################
// Constructors, destructors clone and assignment
//#############################################################################

//-------------------------------------------------------------------
// Default Constructor 
//-------------------------------------------------------------------
OsiXprSolverInterface::OsiXprSolverInterface (int newrows, int newnz) :
OsiSolverInterface(),
prob_(NULL),
matrixByRow_(NULL),
matrixByCol_(NULL),
colupper_(NULL),
collower_(NULL),
rowupper_(NULL),
rowlower_(NULL),
rowsense_(NULL),
rhs_(NULL),
rowrange_(NULL),
objcoeffs_(NULL),
objsense_(1),
colsol_(NULL),
rowsol_(NULL),
rowact_(NULL),
rowprice_(NULL),
colprice_(NULL),
ivarind_(NULL),
ivartype_(NULL),
vartype_(NULL)
{
    incrementInstanceCounter();
    
    xprProbname_ = "";

    gutsOfConstructor();
  
  // newrows and newnz specify room to leave in the solver's matrix
  // structure for cuts.  Note that this is a *global* parameter.
  // The value in effect when the problem is loaded pertains.
  
  if ( newrows > 0 && newnz > 0 ) {         
      XPRS_CHECKED( XPRSsetintcontrol,(prob_,XPRS_EXTRAROWS, newrows) );
      XPRS_CHECKED( XPRSsetintcontrol, (prob_,XPRS_EXTRAELEMS, newnz) );
  }
}

//----------------------------------------------------------------
// Clone
//----------------------------------------------------------------
OsiSolverInterface *
OsiXprSolverInterface::clone(bool copyData) const
{  
   return (new OsiXprSolverInterface(*this));
}

//-------------------------------------------------------------------
// Copy constructor 
//-------------------------------------------------------------------
OsiXprSolverInterface::
OsiXprSolverInterface (const OsiXprSolverInterface & source) :
    OsiSolverInterface(source),
    prob_(NULL),
   matrixByRow_(NULL),
   matrixByCol_(NULL),
   colupper_(NULL),
   collower_(NULL),
   rowupper_(NULL),
   rowlower_(NULL),
   rowsense_(NULL),
   rhs_(NULL),
   rowrange_(NULL),
   objcoeffs_(NULL),
   objsense_(source.objsense_),
   colsol_(NULL),
   rowsol_(NULL),
   rowact_(NULL),
   rowprice_(NULL),
   colprice_(NULL),
   ivarind_(NULL),
   ivartype_(NULL),
   vartype_(NULL)
{
    incrementInstanceCounter();
    xprProbname_ = "";
    gutsOfConstructor();
    
    gutsOfCopy(source);
    
    // Other values remain NULL until requested
}

//-------------------------------------------------------------------
// Destructor 
//-------------------------------------------------------------------
OsiXprSolverInterface::~OsiXprSolverInterface ()
{
   gutsOfDestructor();
   XPRSdestroyprob(prob_);
   decrementInstanceCounter();
}

//-------------------------------------------------------------------
// Assignment operator 
//-------------------------------------------------------------------
OsiXprSolverInterface &
OsiXprSolverInterface::operator=(const OsiXprSolverInterface& rhs)
{
   if ( this != &rhs ) {    
      OsiSolverInterface::operator=(rhs);
      osiSerial_++;       // even though numInstances_ doesn't change
      gutsOfDestructor();
      gutsOfCopy(rhs);
   }
   return *this;
}

//#############################################################################
// Applying cuts
//#############################################################################

void
OsiXprSolverInterface::applyColCut( const OsiColCut & cc )
{
   const double  *collower = getColLower();
   const double  *colupper = getColUpper();
   const CoinPackedVector & lbs = cc.lbs();
   const CoinPackedVector & ubs = cc.ubs();

   int     *index = new int   [lbs.getNumElements() + ubs.getNumElements()];
   char    *btype = new char  [lbs.getNumElements() + ubs.getNumElements()];
   double  *value = new double[lbs.getNumElements() + ubs.getNumElements()];

   int     i, nbds;

   for ( i = nbds = 0;  i < lbs.getNumElements();  i++, nbds++ ) {
      index[nbds] = lbs.getIndices()[i];
      btype[nbds] = 'L';
      value[nbds] = (collower[index[nbds]] > lbs.getElements()[i]) ?
	 collower[index[nbds]] : lbs.getElements()[i];
   }

   for ( i = 0;  i < ubs.getNumElements();  i++, nbds++ ) {
      index[nbds] = ubs.getIndices()[i];
      btype[nbds] = 'U';
      value[nbds] = (colupper[index[nbds]] < ubs.getElements()[i]) ?
	 colupper[index[nbds]] : ubs.getElements()[i];
   }
   
    if (getLogFilePtr()!=NULL) {
      fprintf(getLogFilePtr(),"{\n");
      fprintf(getLogFilePtr(),"   int index[%d];\n",nbds);
      fprintf(getLogFilePtr(),"   char btype[%d];\n",nbds);
      fprintf(getLogFilePtr(),"   double value[%d];\n",nbds);
      for ( i=0; i<nbds; i++ ) {
        fprintf(getLogFilePtr(),"   index[%d]=%d;\n",i,index[i]);
        fprintf(getLogFilePtr(),"   btype[%d]='%c';\n",i,btype[i]);
        fprintf(getLogFilePtr(),"   value[%d]=%f;\n",i,value[i]);
      }
      fprintf(getLogFilePtr(),"}\n");
    }

    XPRS_CHECKED( XPRSchgbounds, (prob_,nbds, index, btype, value) );

   delete [] index;
   delete [] btype;
   delete [] value;

   freeCachedResults();
   //  delete [] colupper_;      colupper_ = NULL;
   //  delete [] collower_;      collower_ = NULL;
}

//-----------------------------------------------------------------------------

void
OsiXprSolverInterface::applyRowCut( const OsiRowCut & rowCut )
{
   const   CoinPackedVector & row=rowCut.row();
   int     start[2] = {0, row.getNumElements()};
   char    sense = rowCut.sense();
   double  rhs   = rowCut.rhs();

   double  r = rowCut.range();

   if (getLogFilePtr()!=NULL) {
       int i;
       fprintf(getLogFilePtr(),"{\n");
       fprintf(getLogFilePtr(),"  char sense = '%c';\n",sense);
       fprintf(getLogFilePtr(),"  double rhs = '%f';\n",rhs);
       fprintf(getLogFilePtr(),"  double r = '%f';\n",r);
       fprintf(getLogFilePtr(),"  int start[2] = {0,%d};\n",start[1]);
       fprintf(getLogFilePtr(),"  int indices[%d];\n",row.getNumElements());
       fprintf(getLogFilePtr(),"  double elements[%d];\n",row.getNumElements());
       for ( i=0; i<row.getNumElements(); i++ ) {
	   fprintf(getLogFilePtr(),"  indices[%d]=%d;\n",i,row.getIndices()[i]);
	   fprintf(getLogFilePtr(),"  elements[%d]=%f;\n",i,row.getElements()[i]);
       }
       fprintf(getLogFilePtr(),"\n");
   }

   // In XPRESS XPRSaddrows(prob_) prototype, indices and elements should be const, but
   // they're not. 
   int rc;

   rc = XPRSaddrows(prob_,1, row.getNumElements(), &sense, &rhs, &r,
			start, const_cast<int *>(row.getIndices()),
			const_cast<double *>(row.getElements())); 
   assert( rc == 0 );
   
   freeCachedResults();
}

//#############################################################################
// Private methods
//#############################################################################

void
OsiXprSolverInterface::gutsOfCopy( const OsiXprSolverInterface & source )
{
   if ( source.xprProbname_ != "" ) {    // source has data
     std::ostringstream pname;
     pname << xprProbname_ << "#" << osiSerial_ <<'\0';
     xprProbname_ = pname.str();
     //     sprintf(xprProbname_, "%s#%d", source.xprProbname_, osiSerial_);
     
     //     std::cout << "Problem " << xprProbname_ << " copied to matrix ";
     

     XPRS_CHECKED( XPRScopyprob, ( prob_, source.prob_, xprProbname_.c_str() ) );
     XPRS_CHECKED( XPRScopycontrols, ( prob_, source.prob_ ) );
     /* if the callbacks are used, a XPRScopycallbacks needs to be added */
   }
}


//-------------------------------------------------------------------
void
OsiXprSolverInterface::gutsOfConstructor()
{
    
    XPRS_CHECKED( XPRScreateprob, (&prob_) );  

    XPRS_CHECKED( XPRSsetcbmessage, (prob_, OsiXprMessageCallback, messageHandler()) );

    /* always create an empty problem to initialize all data structures
     * since the user had no chance to pass in his own message handler, we turn off the output for the following operation
     */

    int outputlog;
    XPRS_CHECKED( XPRSgetintcontrol, (prob_, XPRS_OUTPUTLOG, &outputlog) );
    XPRS_CHECKED( XPRSsetintcontrol, (prob_, XPRS_OUTPUTLOG, 0) );
    
    int istart = 0;
    XPRS_CHECKED( XPRSloadlp, ( prob_, "empty", 0, 0, NULL, NULL, NULL, NULL,
				&istart,
				NULL, NULL, NULL, NULL, NULL) );
    XPRS_CHECKED( XPRSchgobjsense, (prob_, (int)objsense_) );

    XPRS_CHECKED( XPRSsetintcontrol, (prob_, XPRS_OUTPUTLOG, outputlog) );

    //OPEN: only switched off for debugging
    //XPRS_CHECKED( XPRSsetlogfile, (prob_,"xpress.log") );
    // **FIXME** (mjs) Integer problems require solution file or callback to save 
    // optimal solution.  The quick fix is to leave the default solutionfile setting,
    // but implementing a callback would be better.
    //    XPRS_CHECKED( XPRSsetintcontrol, (prob_, XPRS_SOLUTIONFILE,0) );
    
    //    XPRS_CHECKED( XPRSsetintcontrol, ( prob_, XPRS_PRESOLVE, 0) );

    lastsolvewasmip = false;

}
//-------------------------------------------------------------------
void
OsiXprSolverInterface::gutsOfDestructor()
{
   freeCachedResults();

   assert(matrixByRow_ == NULL);
   assert(matrixByCol_ == NULL);
   assert(colupper_    == NULL);
   assert(collower_    == NULL);
   assert(rowupper_    == NULL);
   assert(rowlower_    == NULL);
                    
   assert(rowsense_    == NULL);
   assert(rhs_         == NULL);
   assert(rowrange_    == NULL);
                    
   assert(objcoeffs_   == NULL);
                    
   assert(colsol_      == NULL);
   assert(rowsol_      == NULL);
   assert(rowprice_    == NULL);
   assert(colprice_    == NULL);
   assert(rowact_      == NULL);
   assert(vartype_     == NULL);

}

//-------------------------------------------------------------------

void
OsiXprSolverInterface::freeSolution()
{
   delete [] colsol_;	    colsol_      = NULL;
   delete [] rowsol_;       rowsol_      = NULL;
   delete [] rowact_;       rowact_      = NULL;
   delete [] rowprice_;	    rowprice_    = NULL;
   delete [] colprice_;	    colprice_    = NULL;
}

//-------------------------------------------------------------------

void
OsiXprSolverInterface::freeCachedResults()
{
   delete matrixByRow_;     matrixByRow_ = NULL;
   delete matrixByCol_;     matrixByCol_ = NULL;
   delete [] colupper_;     colupper_    = NULL;
   delete [] collower_;	    collower_    = NULL;
   delete [] rowupper_;	    rowupper_    = NULL;
   delete [] rowlower_;	    rowlower_    = NULL;

   delete [] rowsense_;	    rowsense_    = NULL;
   delete [] rhs_;	    rhs_         = NULL;
   delete [] rowrange_;	    rowrange_    = NULL;

   delete [] objcoeffs_;    objcoeffs_   = NULL;

   freeSolution();

   delete [] ivarind_;      ivarind_     = NULL;
   delete [] ivartype_;     ivartype_    = NULL;
   delete [] vartype_;      vartype_     = NULL;
}

//-------------------------------------------------------------------
// Set up lists of integer variables
//-------------------------------------------------------------------
int
OsiXprSolverInterface::getNumIntVars() const
{
  int     nintvars = 0, nsets = 0;
  
  if ( isDataLoaded() ) {  
    
      XPRS_CHECKED( XPRSgetglobal, (prob_,&nintvars, &nsets, 
      NULL, NULL, NULL, NULL, NULL, NULL, NULL) );
  }
  
  return nintvars;
}

void
OsiXprSolverInterface::getVarTypes() const
{
   int     nintvars = getNumIntVars();
   int     nsets;
   int     ncols = getNumCols();

   if ( vartype_ == NULL && nintvars > 0 ) {
   
    if (getLogFilePtr()!=NULL) {
      fprintf(getLogFilePtr(),"{\n");
      fprintf(getLogFilePtr(),"   int nintvars = %d;\n",nintvars);
      fprintf(getLogFilePtr(),"   int nsets;\n");
      fprintf(getLogFilePtr(),"   char ivartype[%d];\n",nintvars);
      fprintf(getLogFilePtr(),"   char ivarind[%d];\n",nintvars);
      fprintf(getLogFilePtr(),"}\n");
    }

      ivartype_ = new char[nintvars];
      ivarind_  = new int[nintvars];

      XPRS_CHECKED( XPRSgetglobal, (prob_,&nintvars, &nsets, 
		ivartype_, ivarind_, NULL, NULL, NULL, NULL, NULL) );
      // Currently, only binary and integer vars are supported.

      vartype_  = new char[ncols];

      int     i, j;
      for ( i = j = 0;  j < ncols;  j++ ) {
	 if ( i < nintvars && j == ivarind_[i] ) {
	    vartype_[j] = ivartype_[i];
	    i++;
	 } else 
	    vartype_[j] = 'C';
      }
   }
}


bool
OsiXprSolverInterface::isDataLoaded() const
{
  int istatus;

  XPRS_CHECKED( XPRSgetintattrib, ( prob_, XPRS_MIPSTATUS, &istatus ) );
  istatus &= XPRS_MIP_NOT_LOADED;

  return istatus == 0;

}

//#############################################################################
