// Copyright (C) 2000, International Business Machines
// Corporation and others.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

#ifndef OsiColCut_H
#define OsiColCut_H

#include <string>

#include "CoinPackedVector.hpp"

#include "OsiCollections.hpp"
#include "OsiCut.hpp"

/** Column Cut Class

Column Cut Class has:
  <ul>
  <li>a sparse vector of column lower bounds
  <li>a sparse vector of column upper bounds
  </ul>
*/
class OsiColCut : public OsiCut {
  friend void OsiColCutUnitTest(const OsiSolverInterface *baseSiP,
    const std::string &mpsDir);

public:
  //----------------------------------------------------------------

  /**@name Setting column bounds */
  //@{
  /// Set column lower bounds
  inline void setLbs(
    int nElements,
    const int *colIndices,
    const double *lbElements);

  /// Set column lower bounds from a packed vector
  inline void setLbs(const CoinPackedVector &lbs);

  /// Set column upper bounds
  inline void setUbs(
    int nElements,
    const int *colIndices,
    const double *ubElements);

  /// Set column upper bounds from a packed vector
  inline void setUbs(const CoinPackedVector &ubs);
  //@}

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

  /**@name Getting column bounds */
  //@{
  /// Get column lower bounds
  inline const CoinPackedVector &lbs() const;
  /// Get column upper bounds
  inline const CoinPackedVector &ubs() const;
  //@}

  /**@name Comparison operators  */
  //@{
#if __GNUC__ != 2
  using OsiCut::operator==;
#endif
  /** equal - true if lower bounds, upper bounds, 
  and OsiCut are equal.
  */
  inline virtual bool operator==(const OsiColCut &rhs) const;

#if __GNUC__ != 2
  using OsiCut::operator!=;
#endif
  /// not equal
  inline virtual bool operator!=(const OsiColCut &rhs) const;
  //@}

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

  /**@name Sanity checks on cut */
  //@{
  /** Returns true if the cut is consistent with respect to itself.
  This checks to ensure that:
  <ul>
  <li>The bound vectors do not have duplicate indices,
  <li>The bound vectors indices are >=0
  </ul>
  */
  inline virtual bool consistent() const;

  /** Returns true if cut is consistent with respect to the solver  
  interface's model. This checks to ensure that
  the lower & upperbound packed vectors:
  <ul>
  <li>do not have an index >= the number of column is the model.
  </ul>
  */
  inline virtual bool consistent(const OsiSolverInterface &im) const;

  /** Returns true if the cut is infeasible with respect to its bounds and the 
  column bounds in the solver interface's models.
  This checks whether:
  <ul>
  <li>the maximum of the new and existing lower bounds is strictly 
  greater than the minimum of the new and existing upper bounds.
</ul>
  */
  inline virtual bool infeasible(const OsiSolverInterface &im) const;
  /** Returns infeasibility of the cut with respect to solution 
      passed in i.e. is positive if cuts off that solution.  
      solution is getNumCols() long..
  */
  virtual double violated(const double *solution) const;
  //@}

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

  /**@name Constructors and destructors */
  //@{
  /// Assignment operator
  OsiColCut &operator=(const OsiColCut &rhs);

  /// Copy constructor
  OsiColCut(const OsiColCut &);

  /// Default Constructor
  OsiColCut();

  /// Clone
  virtual OsiColCut *clone() const;

  /// Destructor
  virtual ~OsiColCut();
  //@}

  /**@name Debug stuff */
  //@{
  /// Print cuts in collection
  virtual void print() const;
  //@}

private:
  /**@name Private member data */
  //@{
  /// Lower bounds
  CoinPackedVector lbs_;
  /// Upper bounds
  CoinPackedVector ubs_;
  //@}
};

//-------------------------------------------------------------------
// Set lower & upper bound vectors
//-------------------------------------------------------------------
void OsiColCut::setLbs(
  int size,
  const int *colIndices,
  const double *lbElements)
{
  lbs_.setVector(size, colIndices, lbElements);
}
//
void OsiColCut::setUbs(
  int size,
  const int *colIndices,
  const double *ubElements)
{
  ubs_.setVector(size, colIndices, ubElements);
}
//
void OsiColCut::setLbs(const CoinPackedVector &lbs)
{
  lbs_ = lbs;
}
//
void OsiColCut::setUbs(const CoinPackedVector &ubs)
{
  ubs_ = ubs;
}

//-------------------------------------------------------------------
// Get Column Lower Bounds and Column Upper Bounds
//-------------------------------------------------------------------
const CoinPackedVector &OsiColCut::lbs() const
{
  return lbs_;
}
//
const CoinPackedVector &OsiColCut::ubs() const
{
  return ubs_;
}

//----------------------------------------------------------------
// == operator
//-------------------------------------------------------------------
bool OsiColCut::operator==(
  const OsiColCut &rhs) const
{
  if (this->OsiCut::operator!=(rhs))
    return false;
  if (lbs() != rhs.lbs())
    return false;
  if (ubs() != rhs.ubs())
    return false;
  return true;
}
//
bool OsiColCut::operator!=(
  const OsiColCut &rhs) const
{
  return !((*this) == rhs);
}

//----------------------------------------------------------------
// consistent & infeasible
//-------------------------------------------------------------------
bool OsiColCut::consistent() const
{
  const CoinPackedVector &lb = lbs();
  const CoinPackedVector &ub = ubs();
  // Test for consistent cut.
  // Are packed vectors consistent?
  lb.duplicateIndex("consistent", "OsiColCut");
  ub.duplicateIndex("consistent", "OsiColCut");
  if (lb.getMinIndex() < 0)
    return false;
  if (ub.getMinIndex() < 0)
    return false;
  return true;
}
//
bool OsiColCut::consistent(const OsiSolverInterface &im) const
{
  const CoinPackedVector &lb = lbs();
  const CoinPackedVector &ub = ubs();

  // Test for consistent cut.
  if (lb.getMaxIndex() >= im.getNumCols())
    return false;
  if (ub.getMaxIndex() >= im.getNumCols())
    return false;

  return true;
}

#if 0
bool OsiColCut::feasible(const OsiSolverInterface &im) const
{
  const double * oldColLb = im.getColLower();
  const double * oldColUb = im.getColUpper();
  const CoinPackedVector & cutLbs = lbs();
  const CoinPackedVector & cutUbs = ubs();
  int i;
  
  for ( i=0; i<cutLbs.size(); i++ ) {
    int colIndx = cutLbs.indices()[i];
    double newLb;
    if ( cutLbs.elements()[i] > oldColLb[colIndx] )
      newLb = cutLbs.elements()[i];
    else
      newLb = oldColLb[colIndx];

    double newUb = oldColUb[colIndx];
    if ( cutUbs.indexExists(colIndx) )
      if ( cutUbs[colIndx] < newUb ) newUb = cutUbs[colIndx];
      if ( newLb > newUb ) 
        return false;
  }
  
  for ( i=0; i<cutUbs.size(); i++ ) {
    int colIndx = cutUbs.indices()[i];
    double newUb = cutUbs.elements()[i] < oldColUb[colIndx] ? cutUbs.elements()[i] : oldColUb[colIndx];
    double newLb = oldColLb[colIndx];
    if ( cutLbs.indexExists(colIndx) )
      if ( cutLbs[colIndx] > newLb ) newLb = cutLbs[colIndx];
      if ( newUb < newLb ) 
        return false;
  }
  
  return true;
}
#endif

bool OsiColCut::infeasible(const OsiSolverInterface &im) const
{
  const double *oldColLb = im.getColLower();
  const double *oldColUb = im.getColUpper();
  const CoinPackedVector &cutLbs = lbs();
  const CoinPackedVector &cutUbs = ubs();
  int i;

  for (i = 0; i < cutLbs.getNumElements(); i++) {
    int colIndx = cutLbs.getIndices()[i];
    double newLb = cutLbs.getElements()[i] > oldColLb[colIndx] ? cutLbs.getElements()[i] : oldColLb[colIndx];

    double newUb = oldColUb[colIndx];
    if (cutUbs.isExistingIndex(colIndx))
      if (cutUbs[colIndx] < newUb)
        newUb = cutUbs[colIndx];
    if (newLb > newUb)
      return true;
  }

  for (i = 0; i < cutUbs.getNumElements(); i++) {
    int colIndx = cutUbs.getIndices()[i];
    double newUb = cutUbs.getElements()[i] < oldColUb[colIndx] ? cutUbs.getElements()[i] : oldColUb[colIndx];
    double newLb = oldColLb[colIndx];
    if (cutLbs.isExistingIndex(colIndx))
      if (cutLbs[colIndx] > newLb)
        newLb = cutLbs[colIndx];
    if (newUb < newLb)
      return true;
  }

  return false;
}

#endif

/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/
