//===========================================================================//
// This file is part of the DIP Solver Framework.                            //
//                                                                           //
// DIP is distributed under the Eclipse Public License as part of the        //
// COIN-OR repository (http://www.coin-or.org).                              //
//                                                                           //
// Authors: Matthew Galati, SAS Institute Inc. (matthew.galati@sas.com)      //
//          Ted Ralphs, Lehigh University (ted@lehigh.edu)                   //
//          Jiadong Wang, Lehigh University (jiw408@lehigh.edu)              //
//                                                                           //
// Copyright (C) 2002-2015, Lehigh University, Matthew Galati, Ted Ralphs    //
// All Rights Reserved.                                                      //
//===========================================================================//

//this is a lambda_s variable
//assumption throughout is lambda is {0,1}

#ifndef DECOMP_VAR_INCLUDED
#define DECOMP_VAR_INCLUDED

#include "Decomp.h"
#include "UtilHash.h"
#include "UtilMacrosDecomp.h"

class DecompApp;
class DecompSubModel;

// --------------------------------------------------------------------- //
class DecompVar {
public:
   //THINK: or user overriden way to store it (like a tree)
   //and a function which says expandVarToCol - just as in cuts
   CoinPackedVector m_s;//this is the var in terms of x-space

private:
   //TODO: lb, ub, "type"?
   DecompVarType    m_varType;
   double           m_origCost;
   double           m_redCost; //(c - uA'')s - alpha
   int              m_effCnt;  //effectiveness counter
   std::string           m_strHash;
   int              m_blockId;
   int              m_colMasterIndex;
   double           m_norm;

public:
   inline DecompVarType getVarType()   const {
      return m_varType;
   }
   inline double getOriginalCost()   const {
      return m_origCost;
   }
   inline double getReducedCost()    const {
      return m_redCost;
   }
   inline int    getEffectiveness()  const {
      return m_effCnt;
   }
   inline double getLowerBound()     const {
      return 0.0;         //TODO
   }
   inline double getUpperBound()     const {
      return DecompInf;   //TODO
   }
   inline std::string getStrHash()        const {
      return m_strHash;
   }
   inline int    getBlockId()        const {
      return m_blockId;
   }
   inline int    getColMasterIndex() const {
      return m_colMasterIndex;
   }
   inline double getNorm()           const {
      return m_norm;
   }

   inline void   setVarType(const DecompVarType varType)  {
      m_varType = varType;
   }
   inline void   setColMasterIndex(const int colIndex)  {
      m_colMasterIndex = colIndex;
   }
   inline void   setBlockId (const int blockId)  {
      m_blockId = blockId;
   }
   inline void   setReducedCost (const double redCost)  {
      m_redCost = redCost;
   }
   inline void   setOriginalCost(const double origCost) {
      m_origCost = origCost;
   }

   inline void resetEffectiveness() {
      m_effCnt = 0;
   }

   /** Increase the effectiveness count by 1 (or to 1 if it was negative).
       Return the new effectiveness count. */
   inline void increaseEffCnt() {
      m_effCnt = m_effCnt <= 0 ? 1 : m_effCnt + 1;
   }

   /** Decrease the effectiveness count by 1 (or to -1 if it was positive).
       Return the new effectiveness count. */
   inline void decreaseEffCnt() {
      m_effCnt = m_effCnt >= 0 ? -1 : m_effCnt - 1;
   }

   inline double calcNorm() {
      return m_norm = m_s.twoNorm();
   }

   inline void sortVar() {
      m_s.sortIncrIndex();
   }

   bool   isEquivalent(const DecompVar& dvar) {
      return m_s.isEquivalent(dvar.m_s);
   }

   bool isDuplicate(const DecompVarList& vars) {
      DecompVarList::const_iterator vi;

      for (vi = vars.begin(); vi != vars.end(); vi++) {
         if ((*vi)->getStrHash() == this->getStrHash()) {
            return true;
         }
      }

      return false;
   }

   bool doesSatisfyBounds(int                     denseLen,
                          double*                 denseArr,
                          const DecompSubModel&   model,
                          const double*           lbs,
                          const double*           ubs);

   void fillDenseArr(int      len,
                     double* arr);

public:
   virtual void  print(std::ostream*    os  = &std::cout,
                       DecompApp* app = 0) const;
   virtual void  print(std::ostream*               os,
                       const std::vector<std::string>& colNames,
                       const double*          value    = NULL) const;

public:
   /** @name Copy Constructors */
   DecompVar(const DecompVar& source) :
      m_s       (source.m_s),
      m_varType (source.m_varType),
      m_origCost(source.m_origCost),
      m_effCnt  (source.m_effCnt),
      m_strHash (source.m_strHash),
      m_blockId (source.m_blockId),
      m_colMasterIndex (source.m_colMasterIndex),
      m_norm    (source.m_norm) {
   }

   DecompVar& operator=(const DecompVar& rhs) {
      if (this != &rhs) {
         m_s        = rhs.m_s;
         m_varType  = rhs.m_varType;
         m_origCost = rhs.m_origCost;
         m_redCost  = rhs.m_redCost;
         m_effCnt   = rhs.m_effCnt;
         m_strHash  = rhs.m_strHash;
         m_blockId  = rhs.m_blockId;
         m_colMasterIndex = rhs.m_colMasterIndex;
      }

      return *this;
   }

   DecompVar():
      m_s       (),
      m_varType (DecompVar_Point),
      m_origCost(0.0),
      m_redCost (0.0),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
      }

   DecompVar(const std::vector<int>&     ind,
             const double           els,
             const double           redCost,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 //m_varType = varType;
	 if (ind.size() > 0) {
	    m_s.setConstant(static_cast<int>(ind.size()),
			    &ind[0], els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(static_cast<int>(ind.size()),
					     &ind[0], els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const std::vector<int>&     ind,
             const std::vector<double>& els,
             const double           redCost,
             const double           origCost) :
      m_s       (),
      m_varType (DecompVar_Point),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 //m_varType = DecompVar_Point;
	 if (ind.size() > 0) {
	    m_s.setVector(static_cast<int>(ind.size()),
			  &ind[0], &els[0], DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(static_cast<int>(ind.size()),
					     &ind[0], &els[0]);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const std::vector<int>&     ind,
             const std::vector<double>& els,
             const double           redCost,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 //m_varType = varType;
	 if (ind.size() > 0) {
	    m_s.setVector(static_cast<int>(ind.size()),
			  &ind[0], &els[0], DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(static_cast<int>(ind.size()),
					     &ind[0], &els[0]);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double*          els,
             const double           origCost) :
      m_s       (),
      m_varType (DecompVar_Point),
      m_origCost(origCost),
      m_redCost (0.0),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setVector(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double*          els,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (0.0),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setVector(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double           els,
             const double           origCost) :
      m_s       (),
      m_varType (DecompVar_Point),
      m_origCost(origCost),
      m_redCost (0.0),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setConstant(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double           els,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (0.0),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setConstant(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double*          els,
             const double           redCost,
             const double           origCost) :
      m_s       (),
      m_varType (DecompVar_Point),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setVector(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              len,
             const int*             ind,
             const double*          els,
             const double           redCost,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 if (len > 0) {
	    m_s.setVector(len, ind, els, DECOMP_TEST_DUPINDEX);
	    m_strHash = UtilCreateStringHash(len, ind, els);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   DecompVar(const int              denseLen,
             const double*          denseArray,
             const double           redCost,
             const double           origCost,
             const DecompVarType    varType) :
      m_s       (DECOMP_TEST_DUPINDEX),
      m_varType (varType),
      m_origCost(origCost),
      m_redCost (redCost),
      m_effCnt  (0),
      m_strHash (),
      m_blockId (0),
      m_colMasterIndex(-1),
      m_norm    (0.0) {
	 UtilPackedVectorFromDense(denseLen, denseArray, DecompEpsilon, m_s);
	 
	 if (m_s.getNumElements() > 0) {
	    m_strHash = UtilCreateStringHash(denseLen, denseArray);
	    m_norm    = calcNorm();
	    sortVar();
	 }
      }

   virtual ~DecompVar() {};
};

#endif
