// $Id: MathAux.h 1089 2020-04-08 12:11:55Z ge $
/// \file MathAux.h
/// \brief contains auxiliary math functions
///
/// $Revision: 1089 $
/// \author Gerald Weber <gweberbh@gmail.com>
#ifndef GBC_MATHAUX_H
#define GBC_MATHAUX_H "$Id: MathAux.h 1089 2020-04-08 12:11:55Z ge $"
#include <vector>
#include <valarray>
#include <cmath>
namespace gbc
  {
  template<class _VectorTp>
  /// \brief Generates a linear spaced vector in the same way as SciLab's linspace
  inline  _VectorTp linear_space(typename _VectorTp::value_type start, typename _VectorTp::value_type end, long int steps)
    {
    _VectorTp vec(steps);
    long int i;
    typename _VectorTp::value_type inc=(end-start)/(steps-1);
    for (i=0; i < steps; i++) vec[i]=start+inc*i;
    return vec;
    }

  template<class _VectorTp>
  inline typename _VectorTp::value_type average(const _VectorTp& vec)
    {
    size_t n=vec.size(), i;
    typename _VectorTp::value_type sum=typename _VectorTp::value_type();
    for (i=0; i < n; i++) sum+=vec[i];
    return sum/n;
    }

  template<class _VectorTp>
  inline typename _VectorTp::value_type sqr_sum(const _VectorTp& vec)
    {
    size_t n=vec.size(), i;
    typename _VectorTp::value_type sum=typename _VectorTp::value_type();
    for (i=0; i < n; i++) sum+=pow(vec[i],2.0);
    return sum;
    }

  template<class _VectorTp>
  typename _VectorTp::value_type standard_deviation(const _VectorTp& vec)
    {
    typename _VectorTp::value_type av=average(vec);
    size_t n=vec.size(), i;
    typename _VectorTp::value_type sum=typename _VectorTp::value_type();
    for (i=0; i < n; i++) sum+=pow(vec[i]-av,2.0);
    return sqrt(sum/n);
    }

  template<class _MapTp,class _VectorTp>
  /// \brief converts a map into a vector.
  ///
  /// This function resizes the vector to the same size as
  /// the map and then assigns all values of the map sequentially
  /// to the vector in the same order as the map keys.
  inline  void convert(const _MapTp& mp, ///< map to be converted
                       _VectorTp& vec)   ///< new vector with values of the map
    {
    if (vec.size() != mp.size()) vec.resize(mp.size());
    typename _MapTp::const_iterator mit;
    size_t n;
    for(mit=mp.begin(), n=0; mit != mp.end(); ++mit, ++n)
      vec[n]=mit->second;
    }

  template<class _MapTp,class _VectorTp1, class _VectorTp2>
  /// \brief converts a map into two vectors.
  ///
  /// This function resizes the vectors to the same size as
  /// the map. Then it assigns all keys to the
  /// first vector and all values to the second.
  ///
  /// \attention It is important that the keys are of a type that 
  /// can be assigned to a vector.
  inline  void convert(const _MapTp& mp,   ///< map to be converted 
                       _VectorTp1& keys,   ///< new vector with keys of the map 
                       _VectorTp2& values) ///< new vector with values of the map
    {
    if (keys.size()   != mp.size()) keys.resize(mp.size());
    if (values.size() != mp.size()) values.resize(mp.size());
    typename _MapTp::const_iterator mit;
    size_t n;
    for(mit=mp.begin(), n=0; mit != mp.end(); ++mit, ++n)
      {keys[n]=mit->first; values[n]=mit->second;}
    }

  template<class _Tp>
  inline std::ostream& operator<<(std::ostream &out, const std::valarray<_Tp>& v)
    {
    size_t i,n=v.size();
    for (i=0; i < n; i++) out << v[i] << " ";
    return out;
    }

  //Each time we generate two random variables y1 and y2, we return y2 and save y1 for the next call
  //see also  gasdev in press87
  inline double gauss_ran(double sigma=1.0, double mu=0.0)
    {
    static double y1=0.0;
    static bool available=false;
    if (available) {available=false; return y1*sigma+mu;}
    else
      {
      double x1=rand()/static_cast<double>(RAND_MAX);
      double x2=rand()/static_cast<double>(RAND_MAX);
             y1=sqrt(-2.0*log(x1))*sin(2.0*M_PI*x2);
      double y2=sqrt(-2.0*log(x1))*cos(2.0*M_PI*x2);
      available=true;
      return y2*sigma+mu;
      }
    }


  //Returns a uniformly distributed random number 
  //sigma=1.0 and mu=0.5 returns a number between -0.5 and 0.5
  //sigma=1.0 and mu=0   returns a number between 0 and 1
  inline double linear_ran(double sigma=1.0, double mu=0)
    {
    return sigma*(rand()/static_cast<double>(RAND_MAX)-mu);
    }
    
  };
#endif
