/*
 *  Open BEAGLE
 *  Copyright (C) 2001-2004 by Christian Gagne and Marc Parizeau
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *  Laboratoire de Vision et Systemes Numeriques
 *  Departement de genie electrique et de genie informatique
 *  Universite Laval, Quebec, Canada, G1V 4J7
 *  http://vision.gel.ulaval.ca
 *
 */

/*!
 *  \file   beagle/macros.hpp
 *  \brief  Contains some general Beagle macros and global functions.
 *  \author Christian Gagne
 *  \author Marc Parizeau
 *  $Revision: 1.2 $
 *  $Date: 2004/01/15 21:08:57 $
 */

/*!
 *  \defgroup Utils Utilities
 *  \ingroup OOF
 *  \brief Miscelaneous utility classes and functions, part of the Object Oriented Foundations.
 */
 
#ifndef Beagle_macros_hpp
#define Beagle_macros_hpp

#include <string>
#include <iomanip>
#include <iostream>
#include <sstream>

#include <beagle/config.hpp>

#ifdef BEAGLE_HAVE_NUMERIC_LIMITS
#include <limits>
#endif // BEAGLE_HAVE_NUMERIC_LIMITS

                  
/*
 *  Define a spare bool type if it is not defined by the compiler.
 */
#ifndef BEAGLE_HAVE_BOOL
enum boolvals {false=0, true=1};
typedef enum boolvals bool;
#endif // BEAGLE_HAVE_BOOL


/*
 *  Define an dumb explicit keyword if it is not defined by the compiler.
 */
#ifndef BEAGLE_HAVE_EXPLICIT
#define explicit
#endif // BEAGLE_HAVE_EXPLICIT


/*!
 *  \def    Beagle_MinM(X1,X2)
 *  \brief  Calculate the minimum between two values.
 *  \param  X1 First value to compare.
 *  \param  X2 Second value to compare.
 *  \return Minimum of the two values.
 *  \deprecated Use inline function minOf instead.
 */
#define Beagle_MinM(X1,X2) ( ((X1)<(X2)) ? (X1) : (X2) )


/*!
 *  \def    Beagle_MaxM(X1,X2)
 *  \brief  Calculate the maximum between two values.
 *  \param  X1 First value to compare.
 *  \param  X2 Second value to compare.
 *  \return Maximum of the two values.
 *  \deprecated Use inline function maxOf instead.
 */
#define Beagle_MaxM(X1,X2) ( ((X1)>(X2)) ? (X1) : (X2) )

/*!
 *  \def    Beagle_Pow2M(X1,X2)
 *  \brief  Calculate the square of a value.
 *  \param  X Value to compute the square.
 *  \return Square of value.
 *  \deprecated Use inline function pow2Of instead.
 */
#define Beagle_Pow2M(X) ( (X)*(X) )


namespace Beagle {


/*!
 *  \brief Test if a floating-point number is in a not-a-number state.
 *  \param inFloat Value to evaluate not-a-number state.
 *  \return True if the floating-point value is not-a-number, otherwise false.
 *  \ingroup Utils
 */
template <class T>
inline bool isNaN(const T& inFloat)
{
  return ((inFloat == inFloat) == false);
}


/*!
 *  \brief Test if a floating-point number is infinite.
 *  \param inFloat Value to evaluate finiteness.
 *  \return True if the floating-point value is equal to infinity, otherwise false.
 *  \ingroup Utils
 */
template <class T>
inline bool isInfinity(const T& inFloat)
{
#ifdef BEAGLE_HAVE_NUMERIC_LIMITS
  if(std::numeric_limits<T>::has_infinity) {
    return ((inFloat == std::numeric_limits<T>::infinity()) ||
            (inFloat == -std::numeric_limits<T>::infinity()));
  }
#endif // BEAGLE_HAVE_NUMERIC_LIMITS
  T lZero(0.0);
  T lInfinity(1.0 / lZero);
  return ((inFloat == lInfinity) || (inFloat == -lInfinity));
}


/*!
 *  \brief Test if a floating-point number is finite, that is not NaN or infinite.
 *  \param inFloat Value to evaluate finiteness.
 *  \return True if the floating-point value is finite, otherwise false.
 *  \ingroup Utils
 */
template <class T>
inline bool isFinite(const T& inFloat)
{
  return ((isNaN<T>(inFloat) == false) && (isInfinity(inFloat) == false));
}


/*!
 *  \brief Evaluate the maximum of two values.
 *  \param inValue1 First value from which we want the maximum.
 *  \param inValue2 Second value from which we want the maximum.
 *  \return Maximum value between inValue1 and inValue2.
 *  \ingroup Utils
 */
template <class T>
inline T maxOf(const T& inValue1, const T& inValue2)
{
  if(isNaN(inValue1)) return inValue2;
  else if(isNaN(inValue2)) return inValue1;
  return ((inValue2 < inValue1) ? inValue1 : inValue2);
}


/*!
 *  \brief Evaluate the minimum of two values.
 *  \param inValue1 First value from which we want the minimum.
 *  \param inValue2 Second value from which we want the minimum.
 *  \return Minimum value between inValue1 and inValue2.
 *  \ingroup Utils
 */
template <class T>
inline T minOf(const T& inValue1, const T& inValue2)
{
  if(isNaN(inValue1)) return inValue2;
  else if(isNaN(inValue2)) return inValue1;
  return ((inValue1 < inValue2) ? inValue1 : inValue2);
}


/*!
 *  \brief Evaluate square of a number (X^2).
 *  \param inValue Value to evaluate the square.
 *  \return Square of the value.
 *  \ingroup Utils
 */
template <class T>
inline T pow2Of(const T& inValue)
{
  return (inValue * inValue);
}


/*!
 *  \brief  Convert an integer into a std::string.
 *  \param  inInteger Integer to convert into a string.
 *  \return std::string containing conversion of the integer.
 *  \ingroup Utils
 */
inline std::string int2str(long inInteger)
{
  std::ostringstream lStringOS;
  lStringOS << inInteger;
  return lStringOS.str();
}


/*!
 *  \brief  Convert a std::string into a integer.
 *  \param  inString String to convert into an integer.
 *  \return Long containing the conversion of the string.
 *  \ingroup Utils
 */
inline long str2int(const std::string& inString)
{
  std::istringstream lStringIS(inString);
  long lInteger;
  lStringIS >> lInteger;
  return lInteger;
}


/*!
 *  \brief  Convert an unsigned integer into a std::string.
 *  \param  inInteger Integer to convert into a string.
 *  \return std::string containing conversion of the integer.
 *  \ingroup Utils
 */
inline std::string uint2str(unsigned long inInteger)
{
  std::ostringstream lStringOS;
  lStringOS << inInteger;
  return lStringOS.str();
}


/*!
 *  \brief  Convert a std::string into an unsigned integer.
 *  \param  inString String to convert into an integer.
 *  \return Unsigned long containing the conversion of the string.
 *  \ingroup Utils
 */
inline unsigned long str2uint(const std::string& inString)
{
  std::istringstream lStringIS(inString);
  unsigned long lInteger;
  lStringIS >> lInteger;
  return lInteger;
}


/*!
 *  \brief  Convert a double into a std::string.
 *  \param  inDouble Double to convert into a string.
 *  \return std::string containing conversion of the double.
 *  \ingroup Utils
 */
inline std::string dbl2str(double inDouble)
{
  std::ostringstream lStringOS;
  if(isNaN(inDouble)) lStringOS << "nan";
  else if(isInfinity(inDouble)) {
    if(inDouble < 0.0) lStringOS << "-inf";
    else lStringOS << "inf";
  }
  else lStringOS << inDouble;
  return lStringOS.str();
}


/*!
 *  \brief  Convert a double into a std::string.
 *  \param  inDouble Double to convert into a string.
 *  \param  inWidth Width of the written double.
 *  \return std::string containing conversion of the double.
 *  \ingroup Utils
 */
inline std::string dbl2str(double inDouble, unsigned int inWidth)
{
  using namespace std;
  ostringstream lStringOS;
  if(inWidth > 1) lStringOS.precision(inWidth-1);
  lStringOS.setf(ostringstream::left);
  lStringOS.width(inWidth);
  if(isNaN(inDouble)) lStringOS << "nan" << std::flush;
  else if(isInfinity(inDouble)) {
    if(inDouble < 0.0) lStringOS << "-inf" << std::flush;
    else lStringOS << "inf" << std::flush;
  }
  else lStringOS << inDouble << std::flush;
  std::string lString = lStringOS.str();
  if(lString.size() > inWidth) {
    std::string::size_type lPosE = lString.find('e', 0);
    if(lPosE < lString.size()) {
      std::string lStringE(lString, lPosE, lString.size());
      if(lStringE.size() < inWidth)
        lString.replace((inWidth-lStringE.size()), inWidth, lStringE);
    }
  }
  if(lString.size() != inWidth) lString.resize(inWidth, ' ');
  return lString;
}


/*!
 *  \brief  Convert a std::string into a double.
 *  \param  inString String to convert into a double.
 *  \return Double containing the conversion of the string.
 *  \ingroup Utils
 */
inline double str2dbl(const std::string& inString)
{
  if(inString == "nan") {
    double lZero(0.0);
    return (lZero / lZero);
  }
  else if(inString == "inf") {
#ifdef BEAGLE_HAVE_NUMERIC_LIMITS
    if(std::numeric_limits<double>::has_infinity) {
      return std::numeric_limits<double>::infinity();
    }
#endif // BEAGLE_HAVE_NUMERIC_LIMITS
    double lZero(0.0);
    return (1.0 / lZero);
  }
  else if(inString == "-inf") {
#ifdef BEAGLE_HAVE_NUMERIC_LIMITS
    if(std::numeric_limits<double>::has_infinity) {
      return -std::numeric_limits<double>::infinity();
    }
#endif // BEAGLE_HAVE_NUMERIC_LIMITS
    double lZero(0.0);
    return (-1.0 / lZero);
  }   

  std::istringstream lStringIS(inString);
  double lDouble;
  lStringIS >> lDouble;
  return lDouble;
}


/*!
 *  \brief Convert a integer to its ordinal form , i.e. 1 -> 1st, 22 -> 22nd.
 *  \param inNumber Integer to convert.
 *  \return Ordinal form of the number.
 *  \author Matthew Walker
 *  \author Christian Gagne
 *  \ingroup Utils
 */
inline std::string uint2ordinal(unsigned int inNumber) {
  std::string lSuffix = "th";
  switch(inNumber % 10) {
    case 1: {lSuffix = "st"; break;}
    case 2: {lSuffix = "nd"; break;}
    case 3: {lSuffix = "rd"; break;}
  }
  switch(inNumber % 100) {
    case 11:
    case 12:
    case 13: {lSuffix = "th"; break;}
  }
  std::ostringstream outStream;
  outStream << inNumber << lSuffix;
  return outStream.str();
}


/*!
 *  \brief Wrap a string to fit a given line witdh.
 *  \param ioString String to wrap.
 *  \param inLineWidth Line width of the converted string.
 *  \ingroup Utils
 */
inline void wrapString(std::string& ioString, unsigned int inLineWidth=80)
{
  std::string::size_type lPos = 0;
  while(lPos+inLineWidth < ioString.size()) {
    std::string::size_type lFound = ioString.find_last_of(" \n\t",lPos+inLineWidth-1);
    if(lFound < lPos) {
      ioString.insert(lPos+inLineWidth-1, "\n");
      lPos += inLineWidth;
    }
    else {
      ioString[lFound] = '\n';
      lPos = lFound+1;
    }
  }
}

}


#endif // Beagle_macros_hpp
