// Copyright (C) 2005, Pierre Bonami and others.  All Rights Reserved.
// Author:   Pierre Bonami
//           Tepper School of Business
//           Carnegie Mellon University, Pittsburgh, PA 15213
// Date:     07/21/05
//---------------------------------------------------------------------------
#ifndef CglLandP_H
#define CglLandP_H

//Several level of Debug
// 1 - A few simple sanity check
// 2 - do extra computations on cut quality
// 3 - Output cut in initial non-basic space (only if logLevel >= 3 )
// 4 - Double check (compute in two different ways) reduced cost and f+ f-
//#define LandP_DEBUG 1


#include "CglLandPValidator.hpp"
#include "CglCutGenerator.hpp"
#include "CglParam.hpp"

#ifdef DO_STAT
#include "CglLandPStats.hpp"
#endif
#include <iostream>
class CoinWarmStartBasis;
/** Performs one round of Lift & Project using CglLandPSimplex
    to build cuts
*/

namespace LAP
{
enum LapMessagesTypes {
    BEGIN_ROUND,
    END_ROUND,
    DURING_SEP,
    CUT_REJECTED,
    CUT_FAILED,
    LAP_CUT_FAILED_DO_MIG,
    LAP_MESSAGES_DUMMY_END
};
/** Output messages for Cgl */
class LapMessages : public CoinMessages
{
public:
    /** Constructor */
    LapMessages( );
    /** destructor.*/
    virtual ~LapMessages(){}
};
class CglLandPSimplex;
}

class CglLandP : public CglCutGenerator
{
    friend void CglLandPUnitTest(OsiSolverInterface *si, const std::string & mpsDir);

    friend class LAP::CglLandPSimplex;
    friend class CftCglp;

public:

    enum SelectionRules {
        mostNegativeRc /** select most negative reduced cost */,
        bestPivot /** select best possible pivot.*/,
        initialReducedCosts/** Select only those rows which had initialy a 0 reduced cost.*/
    };

    enum ExtraCutsMode {
        none/** Generate no extra cuts.*/,
        AtOptimalBasis /** Generate cuts from the optimal basis.*/,
        WhenEnteringBasis /** Generate cuts as soon as a structural enters the basis.*/,
        AllViolatedMigs/** Generate all violated Mixed integer Gomory cuts in the course of the optimization.*/
    };

    /** Space where cuts are optimized.*/
    enum SeparationSpaces {
        Fractional=0 /** True fractional space.*/,
        Fractional_rc/** Use fractional space only for computing reduced costs.*/,
        Full /** Work in full space.*/
    };

    /** Normalization */
    enum Normalization{
        Unweighted = 0,
        WeightRHS,
        WeightLHS,
        WeightBoth
    };

    enum LHSnorm {
       L1 = 0,
       L2,
       SupportSize,
       Infinity,
       Average,
       Uniform
    };
    /** RHS weight in normalization.*/
    enum RhsWeightType {
        Fixed = 0 /** 2*initial number of constraints. */,
        Dynamic /** 2 * current number of constraints. */
    };
    /** Class storing parameters.
        \remark I take all parameters from Ionut's code */
class Parameters : public CglParam
    {
    public:
        /** Default constructor (with default values)*/
        Parameters();
        /** Copy constructor */
        Parameters(const Parameters &other);
        /** Assignment opertator */
        Parameters & operator=(const Parameters &other);
        /// @name integer parameters
        ///@{

        /** Max number of pivots before we generate the cut
          \default 20 */
        int pivotLimit;
        /** Max number of pivots at regular nodes. Put a value if you want it lower than the global pivot limit.
         \default 100.*/
        int pivotLimitInTree;
        /** Maximum number of cuts generated at a given round*/
        int maxCutPerRound;
        /** Maximum number of failed pivots before aborting */
        int failedPivotLimit;
        /** maximum number of consecutive degenerate pivots
          \default 0 */
        int degeneratePivotLimit;
        /** Maximum number of extra rows to generate per round.*/
        int extraCutsLimit;
        ///@}
        /// @name double parameters
        ///@{
        /** Tolerance for small pivots values (should be the same as the solver */
        double pivotTol;
        /** A variable have to be at least away from integrity to be generated */
        double away;
        /** Total time limit for cut generation.*/
        mutable double timeLimit;
        /** Time limit for generating a single cut.*/
        double singleCutTimeLimit;
        /** Weight to put in RHS of normalization if static.*/
        double rhsWeight;
        ///@}

        /// @name Flags
        ///@{
        /** Do we use tableau row or the disjunction (I don't really get that there should be a way to always use the tableau)*/
        bool useTableauRow;
        /** Do we apply Egon Balas's Heuristic for modularized cuts */
        bool modularize;
        /** Do we strengthen the final cut (always do if modularize is 1) */
        bool strengthen;
        /** Wether to limit or not the number of mistaken RC (when perturbation is applied).*/
        bool countMistakenRc;
        /** Work in the reduced space (only non-structurals enter the basis) */
        SeparationSpaces sepSpace;
        /** Apply perturbation procedure. */
        bool perturb;
        /** How to weight normalization.*/
        Normalization normalization;
        /** How to weight RHS of normalization.*/
        RhsWeightType rhsWeightType;
        /** How to weight LHS of normalization.*/
        LHSnorm lhs_norm;
        /** Generate extra constraints from optimal lift-and-project basis.*/
        ExtraCutsMode generateExtraCuts;
        /** Which rule to apply for choosing entering and leaving variables.*/
        SelectionRules pivotSelection;
        ///@}
    };


    /** Constructor for the class*/
    CglLandP(const CglLandP::Parameters &params = CglLandP::Parameters(),
             const LAP::Validator &validator = LAP::Validator());
    /** Destructor */
    ~CglLandP();
    /** Copy constructor */
    CglLandP(const CglLandP &source);
    /** Assignment operator */
    CglLandP& operator=(const CglLandP &rhs);
    /** Clone function */
    CglCutGenerator * clone() const;

#ifdef DO_STAT
    void setIdString(const std::string &id) {
        roundsStats_.setIdString(id);
    }
#endif

    /**@name Generate Cuts */
    //@{

    virtual void generateCuts(const OsiSolverInterface & si, OsiCuts & cs,
                              const CglTreeInfo info = CglTreeInfo()) const;

    //@}

    virtual bool needsOptimalBasis() const {
        return true;
    }

    LAP::Validator & validator() {
        return validator_;
    }
    /** set level of log for cut generation procedure :
    <ol start=0 >
    	<li> for none </li>
    	<li> for log at begin and end of procedure + at some time interval </li>
    	<li> for log at every cut generated </li>
    	</ol>
    	*/
    void setLogLevel(int level) {
        handler_->setLogLevel(level);
    }

class NoBasisError : public CoinError
    {
    public:
        NoBasisError(): CoinError("No basis available","LandP","") {}
    };

class SimplexInterfaceError : public CoinError
    {
    public:
        SimplexInterfaceError(): CoinError("Invalid conversion to simplex interface", "CglLandP","CglLandP") {}
    };
    Parameters & parameter() {
        return params_;
    }
private:


    void scanExtraCuts(OsiCuts& cs, const double * colsol) const;

    Parameters params_;

    /** Some informations that will be changed by the pivots and that we want to keep*/
    struct CachedData {
        CachedData(int nBasics = 0 , int nNonBasics = 0);
        CachedData(const CachedData & source);

        CachedData& operator=(const CachedData &source);
        /** Get the data from a problem */
        void getData(const OsiSolverInterface &si);
        ~CachedData();
        /** Indices of basic variables in starting basis (ordered if variable basics_[i] s basic in row i)*/
        int * basics_;
        /** Indices of non-basic variables */
        int *nonBasics_;
        /** number of basics variables */
        int nBasics_;
        /** number of non-basics */
        int nNonBasics_;
        /** Optimal basis */
        CoinWarmStartBasis * basis_;
        /** Stores the value of the solution to cut */
        double * colsol_;
        /** Stores the values of the slacks */
        double * slacks_;
        /** Stores wheter slacks are integer constrained */
        bool * integers_;
    };
    /** Retrieve sorted integer variables which are fractional in the solution.
        Return the number of variables.*/
    int getSortedFractionals(CoinPackedVector &xFrac,
                             const CachedData & data,
                             const CglLandP::Parameters& params) const;
    /** Retrieve sorted integer variables which are fractional in the solution.
        Return the number of variables.*/
    void getSortedFractionalIndices(std::vector<int>& indices,
                                    const CachedData &data,
                                    const CglLandP::Parameters & params) const;
    /** Cached informations about problem.*/
    mutable CachedData cached_;
    /** message handler */
    CoinMessageHandler * handler_;
    /** messages */
    CoinMessages messages_;
    /** cut validator */
    LAP::Validator validator_;
    /** number of rows in the original problems. */
    mutable int numrows_;
    /** number of columns in the original problems. */
    mutable int numcols_;
    /** Original lower bounds for the problem (for lifting cuts).*/
    mutable double * originalColLower_;
    /** Original upper bounds for the problem (for lifting cuts).*/
    mutable double * originalColUpper_;
    /** Flag to say if cuts can be lifted.*/
    mutable bool canLift_;
    /** Store some extra cut which could be cheaply generated but do not cut current incumbent.*/
    mutable OsiCuts extraCuts_;
#ifdef DO_STAT
public:
    /** store statistics on separation */
    mutable roundsStatistics roundsStats_;
#endif
};
void CglLandPUnitTest(OsiSolverInterface *si, const std::string & mpsDir);

#endif

