//===========================================================================//
// 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.                                                      //
//===========================================================================//

#ifndef UTIL_MACROS_DECOMP_INCLUDED
#define UTIL_MACROS_DECOMP_INCLUDED

// =========================================================================
#define UTIL_USE_TIMERS

// =========================================================================
#include "Decomp.h"
#include "CoinError.hpp"
#include "CoinPackedVector.hpp"
#include "CoinHelperFunctions.hpp"

// =========================================================================
class DecompApp;

// =========================================================================
#ifdef UTIL_USE_TIMERS
#include "UtilTimer.h"
//NOTE: this is not thread safe!
static UtilTimer              globalTimer;
static std::map<std::string, UtilTimer> globalTimerFuncMap;
#endif

// =========================================================================
#define UtilException(msg,methodN,classN) \
   CoinError(msg,methodN,classN,__FILE__,__LINE__)
#define UtilExceptionMemory(methodN,classN) \
   UtilException("Out of memory",methodN,classN)


// =========================================================================
// Debug Macros
// =========================================================================
#ifdef UTIL_USE_TIMERS
// ------------------------------------------------------------------------- //
inline void UtilPrintFuncBegin(std::ostream*       os,
                               const std::string& classTag,
                               const std::string& funcName,
                               const int      logLevel,
                               const int      logLimit)
{
   const size_t nDashes = 30;
   std::string      funcKey       = classTag + funcName;
   UtilTimer& thisFuncTimer = globalTimerFuncMap[funcKey];
   thisFuncTimer.reset();

   if (logLevel >= logLimit) {
      size_t i;
      std::string funcBegin = "<--- " + funcName + " ";

      for (i = funcBegin.size(); i < nDashes; i++) {
         funcBegin += "-";
      }

      (*os) << std::left << std::setw(9) << classTag << ": "
            << std::setprecision(3) << std::setw(8) << globalTimer.getRealTime()
            << " [CPU: " << std::setprecision(3) << std::setw(8)
            << globalTimer.getCpuTime() << "] " << funcBegin << "\n";
   }
}

// ------------------------------------------------------------------------- //
inline void UtilPrintFuncEnd(std::ostream*       os,
                             const std::string& classTag,
                             const std::string& funcName,
                             const int      logLevel,
                             const int      logLimit)
{
   const size_t nDashes = 30;
   std::string      funcKey       = classTag + funcName;
   UtilTimer& thisFuncTimer = globalTimerFuncMap[funcKey];

   if (logLevel >= logLimit) {
      size_t i;
      std::string funcEnd = " --- " + funcName + " ";

      for (i = funcEnd.size(); i < nDashes; i++) {
         funcEnd += "-";
      }

      funcEnd += ">";
      (*os) << std::left << std::setw(9) << classTag << ": "
            << std::setprecision(3) << std::setw(8) << globalTimer.getRealTime()
            << " [CPU: " << std::setprecision(4) << std::setw(8)
            << globalTimer.getCpuTime() << "] " << funcEnd << " funcT = "
            << std::setprecision(3) << std::setw(8) << thisFuncTimer.getCpuTime()
            << "\n";
   }
}

#else

// ------------------------------------------------------------------------- //
inline void UtilPrintFuncBegin(ostream*       os,
                               const string& classTag,
                               const string& funcName,
                               const int      logLevel,
                               const int      logLimit)
{
   const int nDashes = 30;

   if (logLevel >= logLimit) {
      int i;
      string funcBegin = "<--- " + funcName + " ";

      for (i = funcBegin.size(); i < nDashes; i++) {
         funcBegin += "-";
      }

      (*os) << left << setw(9) << classTag << ": " << funcBegin << "\n";
   }
}

// ------------------------------------------------------------------------- //
inline void UtilPrintFuncEnd(ostream*       os,
                             const string& classTag,
                             const string& funcName,
                             const int      logLevel,
                             const int      logLimit)
{
   const int nDashes = 30;

   if (logLevel >= logLimit) {
      int i;
      string funcEnd = " --- " + funcName + " ";

      for (i = funcEnd.size(); i < nDashes; i++) {
         funcEnd += "-";
      }

      funcEnd += ">";
      (*os) << left << setw(9) << classTag << ": " << funcEnd << "\n";
   }
}

#endif

// =========================================================================
// COIN Macros
// TODO: anything that depends on COIN should probably not be in util lib
// =========================================================================

// ------------------------------------------------------------------------- //
/**
 * Calculate gap: |(ub-lb)|/|lb|
 */
inline double UtilCalculateGap(const double boundLB,
                               const double boundUB)
{
   double gap = DecompInf;

   if (boundLB > -DecompInf && boundUB < DecompInf) {
      if (boundLB != 0.0) {
         gap = fabs(boundUB - boundLB) / fabs(boundLB);
      } else {
         gap = fabs(boundUB);
      }
   }

   return gap;
}


// ------------------------------------------------------------------------- //
CoinPackedVector* UtilPackedVectorFromDense(const int      len,
      const double* dense,
      const double   etol);
void UtilPackedVectorFromDense(const int          len,
                               const double*      dense,
                               const double       etol,
                               CoinPackedVector& v);

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



#endif
