/*
 *  Copyright (C) 2005  Andreas Volz
 *
 *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 *  File: MathUtil.h
 *  Andreas Volz <linux@brachttal.net>
 *   
 */

#ifndef MATHUTIL_H
#define MATHUTIL_H 1

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <cmath>

namespace Animorph {

/// "Wrap" an angle in range -pi...pi by adding the correct multible of 2 pi
float wrapPi (float theta);

/// convert angel from radian to degree
inline double rad2deg (double x)
{
  return x * 180.0 / M_PI;
}

/// convert angel from degree to radian
inline double deg2rad (double x)
{
  return x * M_PI / 180.0;
}

/***********************
 * some math templates *
 ***********************/

/// return true if 'value' between 'range1' and 'range2' - including bounds!
template <typename T>
bool between (T value, T range1, T range2)
{
  if ( ((value >= range1) && (value <=range2)) ||
       ((value >= range2) && (value <=range1)) )
  {
    return true;
  }
  return false;
}

/// fuzzy a value (very primitive implementation!)
template <typename T, typename T2>
T2 pseudoFuzzy (T total, T part, T2 fuzzyable)
{
  T2 ratio;
  T null_value (0);

  if (part != null_value)
  {
    ratio = total / part;
    return fuzzyable / ratio;
  }
  else
  {
    return 0;
  }
}

/// return true in 'unexact' in 'tolerance' == 'unexact'
template <typename T>
bool inTolerance (T exact, T unexact, T tolerance)
{
  return between (unexact, exact - tolerance, exact + tolerance);
}

/// compute the absolute value of a value
template <typename T>
T abs (T value)
{  
  T null_value (0);

  if (value < null_value)
    value = -value;  

  return value;
}

/// return the distance of two float values
template <typename T>
T dist (T value1, T value2)
{
  T d = value1 - value2;
  T null_value (0);
  
  return abs <T> (d);
}

/// return the float value exact between both values
template <typename T>
T middle (T value1, T value2)
{
  if (value1 < value2)
  {
    return value1 + (dist (value1, value2) * 0.5);
  }
  else if (value1 > value2)
  {
    return value1 - (dist (value1, value2) * 0.5);
  }

  // both have same value, return one of them
  return value1;
}

/// limit value to bounds from limit_val (from both sides!)
template <typename T>
T limit (T value, T limit_val)
{
  if (value > limit_val)
    value = limit_val;
  else if (value < -limit_val)
    value = -limit_val;
  
  return value;
}

/// test three values about its monotonie attributes
/*!
 * \param value0 first value
 * \param value1 second value
 * \param value2 third value
 * \return 0 if strictly monotonic increasing
 * \return 1 if strictly monotonic decreasing
 * \return -1 if not monotonic
 */
template <typename T>
int hasMonotonie (T value0, T value1, T value2)
{
  // strictly monotonic increasing
  if ((value0 > value1) && (value1 > value2))
    return 0;

  // strictly monotonic decreasing
  if ((value0 < value1) && (value1 < value2))
    return 1;

  return -1;
}

}

#endif  // MATHUTIL_H
