#ifndef OsiCuts_H
#define OsiCuts_H

#if defined(_MSC_VER)

#  pragma warning(disable:4786)
#endif

#include <cmath>
#include <cfloat>
#include "OsiCollections.hpp"
#include "OsiRowCut.hpp"
#include "OsiColCut.hpp"

class OsiCuts {
   friend void OsiCutsUnitTest();

public:

    class iterator {
      friend class OsiCuts;
    public:
      iterator(OsiCuts& cuts); 
      iterator(const iterator & src);
      iterator & operator=( const iterator& rhs);    
      ~iterator ();
      OsiCut* operator*() const { return cutP_; }    
      iterator operator++();

      iterator operator++(int)
      {
        iterator temp = *this;
        ++*this;
        return temp;
      }

      bool operator==(const iterator& it) const {
        return (colCutIndex_+rowCutIndex_)==(it.colCutIndex_+it.rowCutIndex_);
      }

      bool operator!=(const iterator& it) const {
        return !((*this)==it);
      }

      bool operator<(const iterator& it) const {
        return (colCutIndex_+rowCutIndex_)<(it.colCutIndex_+it.rowCutIndex_);
      }

    private: 
      iterator();

      iterator begin();
      iterator end();
      OsiCuts& cuts_;
      int rowCutIndex_;
      int colCutIndex_;
      OsiCut * cutP_;
    };

    class const_iterator {
      friend class OsiCuts;
    public:
      const_iterator(const OsiCuts& cuts);
      const_iterator(const const_iterator & src);
      const_iterator & operator=( const const_iterator& rhs);     
      ~const_iterator ();
      const OsiCut* operator*() const { return cutP_; } 

      const_iterator operator++();

      const_iterator operator++(int)
      {
        const_iterator temp = *this;
        ++*this;
        return temp;
      }

      bool operator==(const const_iterator& it) const {
        return (colCutIndex_+rowCutIndex_)==(it.colCutIndex_+it.rowCutIndex_);
      }

      bool operator!=(const const_iterator& it) const {
        return !((*this)==it);
      }

      bool operator<(const const_iterator& it) const {
        return (colCutIndex_+rowCutIndex_)<(it.colCutIndex_+it.rowCutIndex_);
      }
    private:  
      inline const_iterator();  

      const_iterator begin();
      const_iterator end();
      const OsiCuts * cutsPtr_;
      int rowCutIndex_;
      int colCutIndex_;
      const OsiCut * cutP_;
    };

    inline void insert( const OsiRowCut & rc );

    inline void insert( const OsiColCut & cc );

      inline void insert( OsiRowCut * & rcPtr );

      inline void insert( OsiColCut * & ccPtr );
#if 0
  inline void insert( OsiCut    * & cPtr  );
#endif

    inline int sizeRowCuts() const;

    inline int sizeColCuts() const;

    inline int sizeCuts() const;

    inline void printCuts() const;

    inline OsiRowCut * rowCutPtr(int i);

    inline const OsiRowCut * rowCutPtr(int i) const;

    inline OsiColCut * colCutPtr(int i);

    inline const OsiColCut * colCutPtr(int i) const;

    inline OsiRowCut & rowCut(int i);

    inline const OsiRowCut & rowCut(int i) const;

    inline OsiColCut & colCut(int i);

    inline const OsiColCut & colCut(int i) const;

    inline const OsiCut * mostEffectiveCutPtr() const;

    inline OsiCut * mostEffectiveCutPtr();

    inline void eraseRowCut(int i);

    inline void eraseColCut(int i); 

    inline void sort();

    inline iterator begin() { iterator it(*this); it.begin(); return it; }  

    inline const_iterator begin() const { const_iterator it(*this); it.begin(); return it; }  

    inline iterator end() { iterator it(*this); it.end(); return it; }

    inline const_iterator end() const { const_iterator it(*this); it.end(); return it; }  

    OsiCuts (); 

    OsiCuts ( const OsiCuts &);

    OsiCuts & operator=( const OsiCuts& rhs);

    virtual ~OsiCuts ();

private:
  /

    class OsiCutCompare
    { 
    public:

      inline bool operator()(const OsiCut * c1P,const OsiCut * c2P) 
      { return c1P->effectiveness() > c2P->effectiveness(); }
    };

    void gutsOfCopy( const OsiCuts & source );

    void gutsOfDestructor();

    OsiVectorRowCutPtr rowCutPtrs_;

    OsiVectorColCutPtr colCutPtrs_;

};

void OsiCuts::insert( const OsiRowCut & rc )
{
  OsiRowCut * newCutPtr = rc.clone();

  rowCutPtrs_.push_back(static_cast<OsiRowCut*>(newCutPtr));
}
void OsiCuts::insert( const OsiColCut & cc )
{
  OsiColCut * newCutPtr = cc.clone();

  colCutPtrs_.push_back(static_cast<OsiColCut*>(newCutPtr));
}

void OsiCuts::insert( OsiRowCut* & rcPtr )
{
  rowCutPtrs_.push_back(rcPtr);
  rcPtr = NULL;
}
void OsiCuts::insert( OsiColCut* &ccPtr )
{
  colCutPtrs_.push_back(ccPtr);
  ccPtr = NULL;
}
#if 0
void OsiCuts::insert( OsiCut* & cPtr )
{
  OsiRowCut * rcPtr = dynamic_cast<OsiRowCut*>(cPtr);
  if ( rcPtr != NULL ) {
    insert( rcPtr );
    cPtr = rcPtr;
  }
  else {
    OsiColCut * ccPtr = dynamic_cast<OsiColCut*>(cPtr);
    assert( ccPtr != NULL );
    insert( ccPtr );
    cPtr = ccPtr;
  }
}
#endif

void OsiCuts::sort() 
{
  std::sort(colCutPtrs_.begin(),colCutPtrs_.end(),OsiCutCompare()); 
  std::sort(rowCutPtrs_.begin(),rowCutPtrs_.end(),OsiCutCompare()); 
}

int OsiCuts::sizeRowCuts() const { return rowCutPtrs_.size(); }
int OsiCuts::sizeColCuts() const { return colCutPtrs_.size(); }
int OsiCuts::sizeCuts()    const { return sizeRowCuts()+sizeColCuts(); }

const OsiRowCut * OsiCuts::rowCutPtr(int i) const { return rowCutPtrs_[i]; }
const OsiColCut * OsiCuts::colCutPtr(int i) const { return colCutPtrs_[i]; }
OsiRowCut * OsiCuts::rowCutPtr(int i) { return rowCutPtrs_[i]; }
OsiColCut * OsiCuts::colCutPtr(int i) { return colCutPtrs_[i]; }

const OsiRowCut & OsiCuts::rowCut(int i) const { return *rowCutPtr(i); }
const OsiColCut & OsiCuts::colCut(int i) const { return *colCutPtr(i); }
OsiRowCut & OsiCuts::rowCut(int i) { return *rowCutPtr(i); }
OsiColCut & OsiCuts::colCut(int i) { return *colCutPtr(i); }

const OsiCut * OsiCuts::mostEffectiveCutPtr() const 
{ 
  const_iterator b=begin();
  const_iterator e=end();
  return *(std::min_element(b,e,OsiCutCompare()));
}
OsiCut * OsiCuts::mostEffectiveCutPtr()  
{ 
  iterator b=begin();
  iterator e=end();

  OsiCut * retVal = NULL;
  double maxEff = DBL_MIN;
  for ( OsiCuts::iterator it=b; it!=e; ++it ) {
    if (maxEff < (*it)->effectiveness() ) {
      maxEff = (*it)->effectiveness();
      retVal = *it;
    }
  }
  return retVal;
}

void
OsiCuts::printCuts() const  
{ 

  int i;
  int numberColCuts=sizeColCuts();
  for (i=0;i<numberColCuts;i++) {
    const OsiColCut * cut = colCutPtr(i);
    cut->print();
  }
  int numberRowCuts=sizeRowCuts();
  for (i=0;i<numberRowCuts;i++) {
    const OsiRowCut * cut = rowCutPtr(i);
    cut->print();
  }
}

void OsiCuts::eraseRowCut(int i) 
{
  delete rowCutPtrs_[i];
  rowCutPtrs_.erase( rowCutPtrs_.begin()+i ); 
}
void OsiCuts::eraseColCut(int i) 
{   
  delete colCutPtrs_[i];
  colCutPtrs_.erase( colCutPtrs_.begin()+i );
}

void
OsiCutsUnitTest();

#endif
// Copyright (C) 2000, International Business Machines
// Corporation and others.  All Rights Reserved.
#ifndef OsiCuts_H
#define OsiCuts_H

#if defined(_MSC_VER)
// Turn off compiler warning about long names
#  pragma warning(disable:4786)
#endif

#include <cmath>
#include <cfloat>
#include "OsiCollections.hpp"
#include "OsiRowCut.hpp"
#include "OsiColCut.hpp"

/** Collections of row cuts and column cuts
*/
class OsiCuts {
   friend void OsiCutsUnitTest();

public:
  /**@name Iterator classes
   */
  //@{
    /** Iterator

      This a class for interating over the collection of cuts.
    */
    class iterator {
      friend class OsiCuts;
    public:
      iterator(OsiCuts& cuts); 
      iterator(const iterator & src);
      iterator & operator=( const iterator& rhs);    
      ~iterator ();
      OsiCut* operator*() const { return cutP_; }    
      iterator operator++();

      iterator operator++(int)
      {
        iterator temp = *this;
        ++*this;
        return temp;
      }
    
      bool operator==(const iterator& it) const {
        return (colCutIndex_+rowCutIndex_)==(it.colCutIndex_+it.rowCutIndex_);
      }
    
      bool operator!=(const iterator& it) const {
        return !((*this)==it);
      }
    
      bool operator<(const iterator& it) const {
        return (colCutIndex_+rowCutIndex_)<(it.colCutIndex_+it.rowCutIndex_);
      }
    
    private: 
      iterator();
      // *THINK* : how to inline these without sticking the code here (ugly...)
      iterator begin();
      iterator end();
      OsiCuts& cuts_;
      int rowCutIndex_;
      int colCutIndex_;
      OsiCut * cutP_;
    };
   
    /** Const Iterator

      This a class for interating over the collection of cuts.
    */ 
    class const_iterator {
      friend class OsiCuts;
    public:
      const_iterator(const OsiCuts& cuts);
      const_iterator(const const_iterator & src);
      const_iterator & operator=( const const_iterator& rhs);     
      ~const_iterator ();
      const OsiCut* operator*() const { return cutP_; } 
       
      const_iterator operator++();
    
      const_iterator operator++(int)
      {
        const_iterator temp = *this;
        ++*this;
        return temp;
      }
    
      bool operator==(const const_iterator& it) const {
        return (colCutIndex_+rowCutIndex_)==(it.colCutIndex_+it.rowCutIndex_);
      }
    
      bool operator!=(const const_iterator& it) const {
        return !((*this)==it);
      }
    
      bool operator<(const const_iterator& it) const {
        return (colCutIndex_+rowCutIndex_)<(it.colCutIndex_+it.rowCutIndex_);
      }
    private:  
      inline const_iterator();  
      // *THINK* : how to inline these without sticking the code here (ugly...)
      const_iterator begin();
      const_iterator end();
      const OsiCuts * cutsPtr_;
      int rowCutIndex_;
      int colCutIndex_;
      const OsiCut * cutP_;
    };
  //@}

  //-------------------------------------------------------------------
  // 
  // Cuts class definition begins here:
  //
  //------------------------------------------------------------------- 
    
  /**@name Inserting a cut into collection */
  //@{
    /// Insert row cut
    inline void insert( const OsiRowCut & rc );
    /// Insert column cut
    inline void insert( const OsiColCut & cc );

    /**@name Inserting a cut into collection using a pointer reference

       The OsiCuts class takes control of and manages the storage for the
       cut including the deletion of the cut.
    */
    //@{ 
      /// Insert row cut. On return rcPtr is NULL.
      inline void insert( OsiRowCut * & rcPtr );
      /// Insert column cut. On return ccPtr is NULL.
      inline void insert( OsiColCut * & ccPtr );
#if 0
  inline void insert( OsiCut    * & cPtr  );
#endif
    //@}
  //@}

  /**@name Number of cuts in collection */
  //@{
    /// Number of row cuts in collection
    inline int sizeRowCuts() const;
    /// Number of column cuts in collection
    inline int sizeColCuts() const;
    /// Number of cuts in collection
    inline int sizeCuts() const;
  //@}
   
  /**@name Debug stuff */
  //@{
    /// Print cuts in collection
    inline void printCuts() const;
  //@}
   
  /**@name Get a cut from collection */
  //@{
    /// Get pointer to i'th row cut
    inline OsiRowCut * rowCutPtr(int i);
    /// Get const pointer to i'th row cut
    inline const OsiRowCut * rowCutPtr(int i) const;
    /// Get pointer to i'th column cut
    inline OsiColCut * colCutPtr(int i);
    /// Get const pointer to i'th column cut
    inline const OsiColCut * colCutPtr(int i) const;

    /// Get reference to i'th row cut
    inline OsiRowCut & rowCut(int i);
    /// Get const reference to i'th row cut
    inline const OsiRowCut & rowCut(int i) const;
    /// Get reference to i'th column cut
    inline OsiColCut & colCut(int i);
    /// Get const reference to i'th column cut
    inline const OsiColCut & colCut(int i) const;

    /// Get const pointer to the most effective cut
    inline const OsiCut * mostEffectiveCutPtr() const;
    /// Get pointer to the most effective cut
    inline OsiCut * mostEffectiveCutPtr();
  //@}

  /**@name Deleting cut from collection */
  //@{ 
    /// Remove i'th row cut from collection
    inline void eraseRowCut(int i);
    /// Remove i'th column cut from collection
    inline void eraseColCut(int i); 
  //@}
 
  /**@name Sorting collection */
  //@{ 
    /// Cuts with greatest effectiveness are first.
    inline void sort();
  //@}
  
   
  /**@name Iterators 
     Example of using an iterator to sum effectiveness
     of all cuts in the collection.
     <pre>
     double sumEff=0.0;
     for ( OsiCuts::iterator it=cuts.begin(); it!=cuts.end(); ++it )
           sumEff+= (*it)->effectiveness();
     </pre>
  */
  //@{ 
    /// Get iterator to beginning of collection
    inline iterator begin() { iterator it(*this); it.begin(); return it; }  
    /// Get const iterator to beginning of collection
    inline const_iterator begin() const { const_iterator it(*this); it.begin(); return it; }  
    /// Get iterator to end of collection 
    inline iterator end() { iterator it(*this); it.end(); return it; }
    /// Get const iterator to end of collection 
    inline const_iterator end() const { const_iterator it(*this); it.end(); return it; }  
  //@}
 
   
  /**@name Constructors and destructors */
  //@{
    /// Default constructor
    OsiCuts (); 

    /// Copy constructor 
    OsiCuts ( const OsiCuts &);
  
    /// Assignment operator 
    OsiCuts & operator=( const OsiCuts& rhs);
  
    /// Destructor 
    virtual ~OsiCuts ();
  //@}

private:
  //*@name Function operator for sorting cuts by efectiveness */
  //@{
    class OsiCutCompare
    { 
    public:
      /// Function for sorting cuts by effectiveness
      inline bool operator()(const OsiCut * c1P,const OsiCut * c2P) 
      { return c1P->effectiveness() > c2P->effectiveness(); }
    };
  //@}

  /**@name Private methods */
  //@{     
    /// Copy internal data
    void gutsOfCopy( const OsiCuts & source );
    /// Delete internal data
    void gutsOfDestructor();
  //@}
    
  /**@name Private member data */
  //@{   
    /// Vector of row cuts pointers
    OsiVectorRowCutPtr rowCutPtrs_;
    /// Vector of column cuts pointers
    OsiVectorColCutPtr colCutPtrs_;
  //@}

};


//-------------------------------------------------------------------
// insert cuts into collection
//-------------------------------------------------------------------
void OsiCuts::insert( const OsiRowCut & rc )
{
  OsiRowCut * newCutPtr = rc.clone();
  //assert(dynamic_cast<OsiRowCut*>(newCutPtr) != NULL );
  rowCutPtrs_.push_back(static_cast<OsiRowCut*>(newCutPtr));
}
void OsiCuts::insert( const OsiColCut & cc )
{
  OsiColCut * newCutPtr = cc.clone();
  //assert(dynamic_cast<OsiColCut*>(newCutPtr) != NULL );
  colCutPtrs_.push_back(static_cast<OsiColCut*>(newCutPtr));
}

void OsiCuts::insert( OsiRowCut* & rcPtr )
{
  rowCutPtrs_.push_back(rcPtr);
  rcPtr = NULL;
}
void OsiCuts::insert( OsiColCut* &ccPtr )
{
  colCutPtrs_.push_back(ccPtr);
  ccPtr = NULL;
}
#if 0
void OsiCuts::insert( OsiCut* & cPtr )
{
  OsiRowCut * rcPtr = dynamic_cast<OsiRowCut*>(cPtr);
  if ( rcPtr != NULL ) {
    insert( rcPtr );
    cPtr = rcPtr;
  }
  else {
    OsiColCut * ccPtr = dynamic_cast<OsiColCut*>(cPtr);
    assert( ccPtr != NULL );
    insert( ccPtr );
    cPtr = ccPtr;
  }
}
#endif

//-------------------------------------------------------------------
// sort
//------------------------------------------------------------------- 
void OsiCuts::sort() 
{
  std::sort(colCutPtrs_.begin(),colCutPtrs_.end(),OsiCutCompare()); 
  std::sort(rowCutPtrs_.begin(),rowCutPtrs_.end(),OsiCutCompare()); 
}


//-------------------------------------------------------------------
// Get number of in collections
//------------------------------------------------------------------- 
int OsiCuts::sizeRowCuts() const { return rowCutPtrs_.size(); }
int OsiCuts::sizeColCuts() const { return colCutPtrs_.size(); }
int OsiCuts::sizeCuts()    const { return sizeRowCuts()+sizeColCuts(); }

//----------------------------------------------------------------
// Get i'th cut from the collection
//----------------------------------------------------------------
const OsiRowCut * OsiCuts::rowCutPtr(int i) const { return rowCutPtrs_[i]; }
const OsiColCut * OsiCuts::colCutPtr(int i) const { return colCutPtrs_[i]; }
OsiRowCut * OsiCuts::rowCutPtr(int i) { return rowCutPtrs_[i]; }
OsiColCut * OsiCuts::colCutPtr(int i) { return colCutPtrs_[i]; }

const OsiRowCut & OsiCuts::rowCut(int i) const { return *rowCutPtr(i); }
const OsiColCut & OsiCuts::colCut(int i) const { return *colCutPtr(i); }
OsiRowCut & OsiCuts::rowCut(int i) { return *rowCutPtr(i); }
OsiColCut & OsiCuts::colCut(int i) { return *colCutPtr(i); }

//----------------------------------------------------------------
// Get most effective cut from collection
//----------------------------------------------------------------
const OsiCut * OsiCuts::mostEffectiveCutPtr() const 
{ 
  const_iterator b=begin();
  const_iterator e=end();
  return *(std::min_element(b,e,OsiCutCompare()));
}
OsiCut * OsiCuts::mostEffectiveCutPtr()  
{ 
  iterator b=begin();
  iterator e=end();
  //return *(std::min_element(b,e,OsiCutCompare()));
  OsiCut * retVal = NULL;
  double maxEff = DBL_MIN;
  for ( OsiCuts::iterator it=b; it!=e; ++it ) {
    if (maxEff < (*it)->effectiveness() ) {
      maxEff = (*it)->effectiveness();
      retVal = *it;
    }
  }
  return retVal;
}

//----------------------------------------------------------------
// Print all cuts
//----------------------------------------------------------------
void
OsiCuts::printCuts() const  
{ 
  // do all column cuts first
  int i;
  int numberColCuts=sizeColCuts();
  for (i=0;i<numberColCuts;i++) {
    const OsiColCut * cut = colCutPtr(i);
    cut->print();
  }
  int numberRowCuts=sizeRowCuts();
  for (i=0;i<numberRowCuts;i++) {
    const OsiRowCut * cut = rowCutPtr(i);
    cut->print();
  }
}

//----------------------------------------------------------------
// Erase i'th cut from the collection
//----------------------------------------------------------------
void OsiCuts::eraseRowCut(int i) 
{
  delete rowCutPtrs_[i];
  rowCutPtrs_.erase( rowCutPtrs_.begin()+i ); 
}
void OsiCuts::eraseColCut(int i) 
{   
  delete colCutPtrs_[i];
  colCutPtrs_.erase( colCutPtrs_.begin()+i );
}

//#############################################################################
/** A function that tests the methods in the OsiCuts class. The
    only reason for it not to be a member method is that this way it doesn't
    have to be compiled into the library. And that's a gain, because the
    library should be compiled with optimization on, but this method should be
    compiled with debugging. */
void
OsiCutsUnitTest();

#endif
