/*******************************************************************************
 *  Math.h
 *
 *  (C) 2007 AG Aktives Sehen <agas@uni-koblenz.de>
 *           Universitaet Koblenz-Landau
 *
 *  Additional information:
 *  $Id: $
 *******************************************************************************/

#ifndef Math_H
#define Math_H

#include <vector>
#include "Point2D.h"

/**
 * @class  Math
 * @brief  Generic math and statistics functions
 * @author David Gossow (RX)
 */
class Math
{
  public:

    struct WeightedValue
    {
      float value;
      float weight;
    };

    static const double Pi = 3.14159265358979323846;

    /** @return mean value */
    template<class ContainerT>
    static double mean ( const ContainerT& values );

    /** @return variance of given values */
    template<class ContainerT>
    static double variance ( const ContainerT& values );

    /** @return mean angle of given values
     *  @note   there are always two possible choices for the mean angle. This function returns the one with the smallest deviation
     *  @note   Works for angles in [-Pi..Pi], negative angles are treated
     */
    static float meanAngle ( const std::vector<float>& angles );

    static float meanAngleWeighted ( const std::vector< WeightedValue >& weightedAngles );

    /** @return variance for given mean */
    static float angleVariance ( float meanAngle, const std::vector<float>& angles );

    /** @return minimal angle needed to turn from angle 1 to angle 2 [-Pi..Pi] */
    static float minTurnAngle ( float angle1, float angle2 );

    static Point2D center ( std::vector<Point2D>& points );

    static float deg2Rad ( float deg ) { return deg / 180.0*Pi; }

    static float rad2Deg ( float rad ) { return rad / Pi*180.0; }

    static double randomGauss ( float variance = 1.0 );

    static double random01 ( unsigned long init = 0 );

    /** @return ratio between one dimension seen under old viewangle and dimension under new viewangle*/
    static double angleToPercent ( double newAngle, double oldAngle ) { return tan ( ( Pi / 180.0 ) * newAngle / 2 ) / tan ( ( Pi / 180.0 ) * oldAngle / 2 ); };

    /** @return angle under which the ratio between dimension seen under old viewangle and new viewangle equals percent*/
    static double percentToAngle ( double percent, double angle ) { return 2* atan ( tan ( ( Pi / 180.0 ) * angle / 2 ) * percent ) * ( 180 / Pi ); };

    /** @return horizontal view angle corresponding to diagonal view angle and aspect ratio (e.g. 4.0/3.0)*/
    static double horizontalViewAngle ( double diagonalAngle, double aspectRatio ) { return verticalViewAngle ( diagonalAngle, 1.0 / aspectRatio ); };

    /** @return vertical view angle corresponding to diagonal view angle and aspect ratio (e.g. 4.0/3.0)*/
    static double verticalViewAngle ( double diagonalAngle, double aspectRatio )
    {
      return percentToAngle ( 1.0 / sqrt ( pow ( aspectRatio, 2 ) + 1.0 ), diagonalAngle );
    };

    template<class ValueT>
    static inline ValueT min ( ValueT a, ValueT b ) { return a < b ? a : b; }

    template<class ValueT>
    static inline ValueT max ( ValueT a, ValueT b ) { return a > b ? a : b; }

  private:

    /** @brief The constructor */
    Math();

    /** @brief The destructor */
    ~Math();

};

template<class ContainerT>
double Math::mean ( const ContainerT& values )
{
  typename ContainerT::const_iterator it;
  it = values.begin();
  double sum = 0;
  while ( it != values.end() )
  {
    sum += *it;
    it++;
  }
  return sum / double ( values.size() );
}


template<class ContainerT>
double Math::variance ( const ContainerT& values )
{
  double mean = mean ( values );
  typename ContainerT::const_iterator it;
  it = values.begin();
  double sum = 0;
  while ( it != values.end() )
  {
    double diff = *it - mean;
    sum += diff * diff;
    it++;
  }
  return sum / double ( values.size() );
}


#endif