/* BEGIN LICENSE BLOCK
 * Version: CMPL 1.1
 *
 * The contents of this file are subject to the Cisco-style Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file except
 * in compliance with the License.  You may obtain a copy of the License
 * at www.eclipseclp.org/license.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License. 
 * 
 * The Original Code is  The ECLiPSe Constraint Logic Programming System. 
 * The Initial Developer of the Original Code is  Cisco Systems, Inc. 
 * Portions created by the Initial Developer are
 * Copyright (C) 1995-2012 Cisco Systems, Inc.  All Rights Reserved.
 * 
 * Contributor(s): Joachim Schimpf, Kish Shen and Andrew Eremin, IC-Parc
 * 
 * END LICENSE BLOCK */

/*
 * ECLiPSe/CPLEX interface (for inclusion in eplex.c)
 */

/* Hack needed for Cplex 12.3: to select the correct definitions, pretend to be the MSC compiler. */
#if defined(_WIN32) && !defined(_MSC_VER) /* Note that _WIN32 is also defined for _WIN_64 */
#define _MSC_VER
#include "cplex.h"		/* CPLEX declarations */
#undef _MSC_VER
#else
#include "cplex.h"		/* CPLEX declarations */
#endif

/* The values CPX_VERSION_VERSION and CPX_VERSION_RELEASE were introduced in CPLEX 12.3.
 * Kish 2016-03-18: added support for CPX_VERSION_MODIFICATION, need for CPLEX 12.6.2
 * For earlier versions, we compute them from the 4-digit CPX_VERSION.
 */
#ifndef CPX_VERSION_VERSION
#define CPX_VERSION_VERSION (CPX_VERSION/100)
#endif
#ifndef CPX_VERSION_RELEASE
#define CPX_VERSION_RELEASE ((CPX_VERSION/10)%10)
#endif
#ifndef CPX_VERSION_MODIFICATION
#define CPX_VERSION_MODIFICATION 0
#endif

#if (CPLEX == 0)
#undef CPLEX
#define CPLEX CPX_VERSION_VERSION
#elif (CPLEX != CPX_VERSION_VERSION)
Version mismatch!
#endif

#ifndef CPLEXMINOR
#define CPLEXMINOR CPX_VERSION_RELEASE
#elif (CPLEXMINOR != CPX_VERSION_RELEASE)
Version mismatch!
#endif

#ifndef CPLEXMINORMINOR
#define CPLEXMINORMINOR CPX_VERSION_MODIFICATION
#elif (CPLEXMINORMINOR != VERSION_MODIFICATION)
Version mismatch!
#endif
 
#ifndef CPXPUBLIC
#define CPXPUBLIC
#endif

typedef int param_id_t;
typedef char sostype_t;
typedef int direction_t;       /* branching direction indicator type */

#include "eplex.h"		/* needs declarations above! */


#define SOLVER_SHORT_NAME CPX
#define SOLVER_ATOMIC_NAME "cplex"
#define SOLVER_VERSION_INT	(10*CPLEX+CPLEXMINOR)
#define SOLVER_HAS_STR_PARAMS
#define STRBUFFERSIZE  CPX_STR_PARAM_MAX  /* size of string parameter buffer size */
#define SOLVER_HAS_FIXED_PROBE

#define SOLVER_SENSE_LE	'L'
#define SOLVER_SENSE_GE	'G'
#define SOLVER_SENSE_EQ	'E'

#define SOLVER_SOS_TYPE1	'1'
#define SOLVER_SOS_TYPE2	'2'

#define SOLVER_HAS_LOADORDER
#define SOLVER_DIR_DOWN	    CPX_BRANCH_DOWN
#define SOLVER_DIR_UP       CPX_BRANCH_UP
#define SOLVER_DIR_DEFAULT  CPX_BRANCH_GLOBAL

# define CPX_COL_AT_LOWER   CPX_AT_LOWER
# define CPX_COL_AT_UPPER   CPX_AT_UPPER
# define CPX_COL_BASIC      CPX_BASIC
# define CPX_COL_FREE_SUPER CPX_FREE_SUPER
/* next two not tested */
# define CPX_COL_NONBASIC_ZERO_BOUNDED	CPX_COL_AT_LOWER
# define CPX_COL_NONBASIC_ZERO_UNBOUNDED CPX_COL_FREE_SUPER

#if CPLEX >= 9
#define HAS_CONCURRENT
#endif

#if CPLEX >= 12
#define HAS_INDICATOR_CONSTRAINTS
#endif

#if CPLEX >= 20
#undef SUPPORT_IIS
/* TODO: CPXrefineconflict/CPXgetconflict are deprecated,
 * port to CPXrefineconflictext/CPXgetconflictext
 */
#define CPXERR_NO_INT_SOLN CPXERR_NO_SOLN 

#elif CPLEX >= 10
#define SUPPORT_IIS

/* CPLEX 10+ has more generic error for no solution state and the more general conflict set 
   rather than IIS (which is for LP only) for infeasible analyses
*/
# define CPXERR_NO_INT_SOLN CPXERR_NO_SOLN 

# define HAS_GENERAL_CONFLICT_REFINER

# define Find_Conflict(Res, L, NRows, NCols)  Res = CPXrefineconflict(cpx_env, L, &(NRows), &(NCols))

# define Get_Conflict(L, Status, RowIdxs, RowStat, NRows_p, ColIdxs, ColStat, NCols_p) \
	CPXgetconflict(cpx_env, L, &(Status), RowIdxs, RowStat, NRows_p, ColIdxs, ColStat, NCols_p)

#else
#define SUPPORT_IIS

/* mapping the calls to find a conflict set to the older and less general routines (LP only) to get 
   the IIS 
*/
# define Find_Conflict(Res, L, NRows, NCols)    Res = CPXfindiis(cpx_env, L, &(NRows), &(NCols))

# define Get_Conflict(L, Status, RowIdxs, RowStat, NRows_p, ColIdxs, ColStat, NCols_p) \
	CPXgetiis(cpx_env, L, &(Status), RowIdxs, RowStat, NRows_p, ColIdxs, ColStat, NCols_p)

# define CPX_CONFLICT_LB           CPXIIS_AT_LOWER
# define CPX_CONFLICT_UB           CPXIIS_AT_UPPER
# define CPX_STAT_CONFLICT_MINIMAL CPXIIS_COMPLETE

#endif 

#if CPLEX >= 7
# define CPX_HAS_LPOPT
#endif

#if CPLEX < 9
/* CPLEX 8 and older has no valid bestobjval for an optimal MIP solution! 
   cutoff
*/
# define NO_MIPBESTBOUND_IF_OPTIMAL
#endif

#if (CPLEX >= 8)

/* uniform treatment of solution status (independent of primal/dual) */
# define UNIFORM_SOL_STAT       
# define HAS_SIFT
# define CPX_HAS_DEFAULTALG /* has way of specifying `default' method without
			       naming method, for all LP/MIP/QP */
# define HAS_MIQP

# define SetCPXAlg(cpx_env, method)  { \
    CPXsetintparam(cpx_env, CPX_PARAM_LPMETHOD, method);\
    CPXsetintparam(cpx_env, CPX_PARAM_STARTALG, method);\
    if (method != CPX_ALG_SIFTING) \
        CPXsetintparam(cpx_env, CPX_PARAM_QPMETHOD, method);\
    else \
        CPXsetintparam(cpx_env, CPX_PARAM_QPMETHOD, CPX_ALG_AUTOMATIC);\
}

# define SetSiftAlg(cpx_env, meth) {  \
    switch (meth)  \
    { \
    case METHOD_AUTO: \
	CPXsetintparam(cpx_env, CPX_PARAM_SIFTALG, CPX_ALG_AUTOMATIC); \
	break; \
    case METHOD_PRIMAL: \
	CPXsetintparam(cpx_env, CPX_PARAM_SIFTALG, CPX_ALG_PRIMAL); \
	break; \
    case METHOD_DUAL: \
	CPXsetintparam(cpx_env, CPX_PARAM_SIFTALG, CPX_ALG_DUAL); \
	break; \
    case METHOD_BAR: \
	CPXsetintparam(cpx_env, CPX_PARAM_SIFTALG, CPX_ALG_BARRIER); \
	break; \
    case METHOD_NET: \
	CPXsetintparam(cpx_env, CPX_PARAM_SIFTALG, CPX_ALG_NET); \
	break; \
    default: \
	/* use error stream as warning stream unavailable */ \
	Fprintf(Current_Error, "Eplex warning: aux. method chosen for" \
		" sifting is unavailable. Using default instead\n"); \
	ec_flush(Current_Error); \
        meth = METHOD_DEFAULT; \
    case METHOD_DEFAULT: \
	break; \
    } \
}

#else /* CPLX < 8 */

/* pre-CPLEX 8 names */
# define CPXPROB_MILP			  CPXPROB_MIP
# define CPXPROB_FIXEDMILP		  CPXPROB_FIXED

# define CPX_STAT_OPTIMAL                 CPX_OPTIMAL
# define CPX_STAT_INFEASIBLE              CPX_INFEASIBLE
# define CPX_STAT_OPTIMAL_INFEAS          CPX_OPTIMAL_INFEAS
# define CPX_STAT_UNBOUNDED               CPX_UNBOUNDED
# define CPX_STAT_INForUNBD               CPX_INForUNBD
# define CPX_STAT_OPTIMAL_FACE_UNBOUNDED  CPX_OPTIMAL_FACE_UNBOUNDED

# define CPXqpopt(A,B) CPXbaropt(A,B) /* no CPXqpopt() */

/* DualMethod(lpd,m,am) should be true for methods that require different
 * interpretation of the optimization result code */

# define DualMethod(lpd,m,am) \
     (((lpd)->prob_type == PROBLEM_LP || (lpd)->prob_type == PROBLEM_RELAXEDL) && \
 	((m) == METHOD_DUAL || ((m) == METHOD_NET && (am) == METHOD_DUAL)) )

# define CPX_HAS_RELAXEDLP

# if (CPLEX >= 7)  /* CPLEX 7 only */

#  define SetCPXAlg(cpx_env, method)  { \
    CPXsetintparam(cpx_env, CPX_PARAM_LPMETHOD, method);\
    CPXsetintparam(cpx_env, CPX_PARAM_STARTALG, method);\
}

# elif (CPLEX >= 6)  /* CPLEX 6 only */

#  define CPX_ALG_NET                     CPX_NODEALG_HYBNETOPT

#  define HAS_NO_BARCROSSOVER  /* does not have `no crossover' for barrier */

# define SetCPXAlg(cpx_env, method)  { \
    CPXsetintparam(cpx_env, CPX_PARAM_STARTALG, method);\
}

# endif

#endif /* CPLEX >= 8 */

#if CPLEX > 12 || (CPLEX == 12  &&  CPLEXMINOR >= 3)
#define IfAtLeast123(X) (X)
#else
#define IfAtLeast123(X) 0
#endif

#define SuccessState(d) ( \
	(d)->sol_state == CPX_STAT_OPTIMAL || \
	(d)->sol_state == CPX_STAT_OPTIMAL_INFEAS || \
        (d)->sol_state == CPX_STAT_OPTIMAL_FACE_UNBOUNDED || \
	IfAtLeast123((d)->sol_state == CPX_STAT_FIRSTORDER) || \
        MIPSuccessState(d))

#define MIPSuccessState(d) ( \
	(d)->sol_state == CPXMIP_OPTIMAL || \
	(d)->sol_state == CPXMIP_OPTIMAL_TOL || \
	(d)->sol_state == CPXMIP_OPTIMAL_INFEAS)

#define FailState(d) ( \
	(d)->sol_state == CPX_STAT_INFEASIBLE || \
	(d)->sol_state == CPXMIP_INFEASIBLE)

#if CPLEX >= 8
#define UnboundedState(d) ( \
	(d)->sol_state == CPX_STAT_UNBOUNDED || \
        (d)->sol_state == CPXMIP_UNBOUNDED)

#define MaybeFailState(d) ( \
	(d)->sol_state == CPX_STAT_INForUNBD || \
        (d)->sol_state == CPXMIP_INForUNBD || \
	(d)->sol_state == CPXERR_PRESLV_INForUNBD )

#define LPAbortedState(d) ( \
	(d)->sol_state == CPX_STAT_ABORT_IT_LIM || \
	(d)->sol_state == CPX_STAT_ABORT_TIME_LIM || \
	IfAtLeast123((d)->sol_state == CPX_STAT_ABORT_DETTIME_LIM) || \
	(d)->sol_state == CPX_STAT_ABORT_OBJ_LIM || \
	(d)->sol_state == CPX_STAT_ABORT_USER || \
        (d)->sol_state == CPX_STAT_NUM_BEST || \
	(d)->sol_state == CPX_STAT_ABORT_PRIM_OBJ_LIM || \
	(d)->sol_state == CPX_STAT_ABORT_DUAL_OBJ_LIM )

#else

/* aborted with no feasible (primal/dual) solution */
#define LPAbortedNoSolState(d) ( \
	(d)->sol_state == CPX_IT_LIM_INFEAS || \
	(d)->sol_state == CPX_TIME_LIM_INFEAS || \
	(d)->sol_state == CPX_NUM_BEST_INFEAS || \
	(d)->sol_state == CPX_ABORT_INFEAS  )

/* aborted with feasible solution */
#define LPAbortedSolState(d) ( \
	(d)->sol_state == CPX_OBJ_LIM || \
	(d)->sol_state == CPX_IT_LIM_FEAS || \
	(d)->sol_state == CPX_TIME_LIM_FEAS || \
	(d)->sol_state == CPX_NUM_BEST_FEAS || \
	(d)->sol_state == CPX_NUM_BEST_PRIM_DUAL_FEAS || \
	(d)->sol_state == CPX_ABORT_FEAS )

#define LPAbortedState(d) ( \
        LPAbortedSolState(d) || LPAbortedNoSolState(d) )

#define UnboundedState(d) ( \
	(d)->sol_state == CPX_STAT_UNBOUNDED )

#if CPLEX > 6 || ( CPLEX == 6 && CPLEXMINOR > 0 )
#define MaybeFailState(d) ( \
	(d)->sol_state == CPX_STAT_INForUNBD || \
	(d)->sol_state == CPXERR_PRESLV_INForUNBD )
#else
#define MaybeFailState(d) 0
#endif

#endif

#if CPLEX >= 7
# define MIPSemiFailState(d) ( \
	(d)->sol_state == CPXMIP_NODE_LIM_INFEAS || \
	(d)->sol_state == CPXMIP_TIME_LIM_INFEAS || \
	IfAtLeast123((d)->sol_state == CPXMIP_DETTIME_LIM_INFEAS) || \
	(d)->sol_state == CPXMIP_FAIL_INFEAS || \
	(d)->sol_state == CPXMIP_MEM_LIM_INFEAS || \
	(d)->sol_state == CPXMIP_ABORT_INFEAS || \
	(d)->sol_state == CPXMIP_FAIL_INFEAS_NO_TREE)

# define MIPSemiSuccessState(d) ( \
	(d)->sol_state == CPXMIP_SOL_LIM || \
	(d)->sol_state == CPXMIP_NODE_LIM_FEAS || \
	(d)->sol_state == CPXMIP_TIME_LIM_FEAS || \
	IfAtLeast123((d)->sol_state == CPXMIP_DETTIME_LIM_FEAS) || \
	(d)->sol_state == CPXMIP_FAIL_FEAS || \
	(d)->sol_state == CPXMIP_MEM_LIM_FEAS || \
	(d)->sol_state == CPXMIP_ABORT_FEAS || \
	(d)->sol_state == CPXMIP_FAIL_FEAS_NO_TREE)
#else
# define MIPSemiFailState(d) ( \
	(d)->sol_state == CPXMIP_NODE_LIM_INFEAS || \
	(d)->sol_state == CPXMIP_TIME_LIM_INFEAS || \
	(d)->sol_state == CPXMIP_FAIL_INFEAS || \
	(d)->sol_state == CPXMIP_MEM_LIM_INFEAS || \
	(d)->sol_state == CPXMIP_ABORT_INFEAS || \
	(d)->sol_state == CPXMIP_FAIL_INFEAS_NO_TREE || \
	(d)->sol_state == CPXMIP_NODE_FILE_LIM_INFEAS)

# define MIPSemiSuccessState(d) ( \
	(d)->sol_state == CPXMIP_SOL_LIM || \
	(d)->sol_state == CPXMIP_NODE_LIM_FEAS || \
	(d)->sol_state == CPXMIP_TIME_LIM_FEAS || \
	(d)->sol_state == CPXMIP_FAIL_FEAS || \
	(d)->sol_state == CPXMIP_MEM_LIM_FEAS || \
	(d)->sol_state == CPXMIP_ABORT_FEAS || \
	(d)->sol_state == CPXMIP_FAIL_FEAS_NO_TREE || \
	(d)->sol_state == CPXMIP_NODE_FILE_LIM_FEAS)
#endif


#define SetPreSolve(state) \
{\
   Log1(CPXsetintparam(cpx_env, CPX_PARAM_PREIND, %d), state); \
   CPXsetintparam(cpx_env, CPX_PARAM_PREIND, state); \
}

#define Update_Model(LP)

# define Get_LP_Objval(A1,A2)		CPXgetobjval(cpx_env,(A1)->lp,A2)
# define Get_Best_Objbound(A1, A2)      CPXgetbestobjval(cpx_env,A1,A2)

#define Get_MIPCutOff(d, v) \
       ((d)->sense == SENSE_MIN ? CPXgetdblparam(cpx_env, CPX_PARAM_CUTUP, v) : CPXgetdblparam(cpx_env, CPX_PARAM_CUTLO, v))

#define UsingBarrierNoCrossOver(d) (CPXgetmethod(cpx_env, (d)) == CPX_ALG_BARRIER)
#define Get_Bar_Primal_Obj(d, obj) CPXgetdblquality(cpx_env, (d), obj, CPX_PRIMAL_OBJ)
#define Get_Bar_Dual_Obj(d, obj) CPXgetdblquality(cpx_env, (d), obj, CPX_DUAL_OBJ)

#define HAS_QUADRATIC

#define HAS_LIMITED_MIP_RESULTS
