/*
 * This file belongs to the Galois project, a C++ library for exploiting parallelism.
 * The code is being released under the terms of the 3-Clause BSD License (a
 * copy is located in LICENSE.txt at the top-level directory).
 *
 * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
 * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
 * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
 * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
 * DEALING OR USAGE OF TRADE.  NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
 * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
 * shall University be liable for incidental, special, indirect, direct or
 * consequential damages or loss of profits, interruption of business, or
 * related expenses which may arise from use of Software or Documentation,
 * including but not limited to those resulting from defects in Software and/or
 * Documentation, or loss or inaccuracy of data of any kind.
 */

/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can be found
 * in the LICENSE file.
 */

/// \file
/// \brief Defines general utility functions and adds an abstraction layer for
/// platform-dependent functionality

#ifndef __defined_libdai_util_h
#define __defined_libdai_util_h

#include <string>
#include <vector>
#include <set>
#include <map>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <cerrno>
#include <gmpxx.h>

#include <dai/exceptions.h>

#if defined(WINDOWS)
#include <boost/tr1/unordered_map.hpp> // only present in boost 1.37 and higher
#elif defined(CYGWIN)
#include <boost/tr1/unordered_map.hpp> // only present in boost 1.37 and higher
#elif defined(MACOSX)
#include <boost/tr1/unordered_map.hpp> // only present in boost 1.37 and higher
#else
#include <tr1/unordered_map> // only present in modern GCC distributions
#endif

/// An alias to the BOOST_FOREACH macro from the boost::foreach library
#define diaforeach BOOST_FOREACH

#ifdef DAI_DEBUG
/// \brief "Print variable". Prints the text of an expression, followed by its
/// value (only if DAI_DEBUG is defined)
/**
 *  Useful debugging macro to see what your code is doing.
 *  Example: \code DAI_PV(3+4) \endcode
 *  Output: \code 3+4= 7 \endcode
 */
#define DAI_PV(x)                                                              \
  do {                                                                         \
    std::cerr << #x "= " << (x) << std::endl;                                  \
  } while (0)
/// "Debugging message": Prints a message (only if DAI_DEBUG is defined)
#define DAI_DMSG(str)                                                          \
  do {                                                                         \
    std::cerr << str << std::endl;                                             \
  } while (0)
#else
#define DAI_PV(x)                                                              \
  do {                                                                         \
  } while (0)
#define DAI_DMSG(str)                                                          \
  do {                                                                         \
  } while (0)
#endif

/// Macro to write message \a stmt to \c std::cerr if \a props.verbose >= \a n
#define DAI_IFVERB(n, stmt)                                                    \
  if (props.verbose >= n) {                                                    \
    std::cerr << stmt;                                                         \
  }

#ifdef WINDOWS
/// Returns inverse hyperbolic tangent of argument
double atanh(double x);

/// Returns log(1+x)
double log1p(double x);

/// Define INFINITY
#define INFINITY (std::numeric_limits<Real>::infinity())
#endif

namespace dai {

/// Real number (alias for \c double, which could be changed to <tt>long
/// double</tt> if necessary)
typedef double Real;

/// Arbitrary precision integer number
typedef mpz_class BigInt;

/// Safe down-cast of big integer to size_t
inline size_t BigInt_size_t(const BigInt& N) {
  DAI_ASSERT(N <= std::numeric_limits<std::size_t>::max());
  return N.get_ui();
}

/// Returns true if argument is NAN (Not A Number)
bool isnan(Real x);

/// Returns logarithm of \a x
inline Real log(Real x) { return std::log(x); }

/// Returns logarithm of \a x, or 0 if \a x == 0
inline Real log0(Real x) { return x ? std::log(x) : 0; }

/// Returns exponent of \a x
inline Real exp(Real x) { return std::exp(x); }

/// Returns \a to the power \a y
inline Real pow(Real x, Real y) {
  errno       = 0;
  Real result = std::pow(x, y);
  DAI_DEBASSERT(errno == 0);
  return result;
}

/// hash_map is an alias for \c std::tr1::unordered_map.
/** We use the (experimental) TR1 unordered_map implementation included in
 * modern GCC distributions or in boost versions 1.37 and higher.
 */
template <typename T, typename U, typename H = boost::hash<T>>
class hash_map : public std::tr1::unordered_map<T, U, H> {};

/// Returns wall clock time in seconds
double toc();

/// Returns absolute value of \a t
template <class T>
inline T abs(const T& t) {
  return (t < 0) ? (-t) : t;
}

/// Sets the random seed
void rnd_seed(size_t seed);

/// Returns a real number, distributed uniformly on [0,1)
Real rnd_uniform();

/// Returns a real number from a standard-normal distribution
Real rnd_stdnormal();

/// Returns a random integer in interval [\a min, \a max]
int rnd_int(int min, int max);

/// Returns a random integer in the half-open interval [0, \a n)
inline int rnd(int n) { return rnd_int(0, n - 1); }

/// Converts a variable of type \a T to a \c std::string by using a \c
/// boost::lexical_cast
template <class T>
std::string toString(const T& x) {
  return boost::lexical_cast<std::string>(x);
}

/// Converts a variable of type std::string to \a T by using a \c
/// boost::lexical_cast
template <class T>
T fromString(const std::string& x) {
  return boost::lexical_cast<T>(x);
}

/// Writes a \c std::vector<> to a \c std::ostream
template <class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& x) {
  os << "(";
  for (typename std::vector<T>::const_iterator it = x.begin(); it != x.end();
       it++)
    os << (it != x.begin() ? ", " : "") << *it;
  os << ")";
  return os;
}

/// Writes a \c std::set<> to a \c std::ostream
template <class T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& x) {
  os << "{";
  for (typename std::set<T>::const_iterator it = x.begin(); it != x.end(); it++)
    os << (it != x.begin() ? ", " : "") << *it;
  os << "}";
  return os;
}

/// Writes a \c std::map<> to a \c std::ostream
template <class T1, class T2>
std::ostream& operator<<(std::ostream& os, const std::map<T1, T2>& x) {
  os << "{";
  for (typename std::map<T1, T2>::const_iterator it = x.begin(); it != x.end();
       it++)
    os << (it != x.begin() ? ", " : "") << it->first << "->" << it->second;
  os << "}";
  return os;
}

/// Writes a \c std::pair<> to a \c std::ostream
template <class T1, class T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& x) {
  os << "(" << x.first << ", " << x.second << ")";
  return os;
}

/// Concatenates two vectors
template <class T>
std::vector<T> concat(const std::vector<T>& u, const std::vector<T>& v) {
  std::vector<T> w;
  w.reserve(u.size() + v.size());
  for (size_t i = 0; i < u.size(); i++)
    w.push_back(u[i]);
  for (size_t i = 0; i < v.size(); i++)
    w.push_back(v[i]);
  return w;
}

/// Split a string into tokens delimited by one of the characters in \a delim
/** \param s the string to be split into tokens
 *  \param singleDelim if \c true, any single delimiter forms a boundary between
 * two tokens; if \c false, a maximal group of consecutive delimiters forms a
 * boundary between two tokens \param delim delimiter characters
 */
std::vector<std::string> tokenizeString(const std::string& s, bool singleDelim,
                                        const std::string& delim = "\t\n");

/// Enumerates different ways of normalizing a probability measure.
/**
 *  - NORMPROB means that the sum of all entries should be 1;
 *  - NORMLINF means that the maximum absolute value of all entries should be 1.
 */
typedef enum { NORMPROB, NORMLINF } ProbNormType;

/// Enumerates different distance measures between probability measures.
/**
 *  - DISTL1 is the \f$\ell_1\f$ distance (sum of absolute values of pointwise
 * difference);
 *  - DISTLINF is the \f$\ell_\infty\f$ distance (maximum absolute value of
 * pointwise difference);
 *  - DISTTV is the total variation distance (half of the \f$\ell_1\f$
 * distance);
 *  - DISTKL is the Kullback-Leibler distance (\f$\sum_i p_i (\log p_i - \log
 * q_i)\f$).
 *  - DISTHEL is the Hellinger distance (\f$\frac{1}{2}\sum_i
 * (\sqrt{p_i}-\sqrt{q_i})^2\f$).
 */
typedef enum { DISTL1, DISTLINF, DISTTV, DISTKL, DISTHEL } ProbDistType;

} // end of namespace dai

#endif
