/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  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 file 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2002-10-11
**  Copyright: 2002-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>
#include <QGpCoreTools.h>

#include "GaussDistribution.h"

namespace QGpCoreMath {

  /*!
     precision is the maximum tolerated difference accepted for probabilities
     mean and sigma are set to 0
  */
  GaussDistribution::GaussDistribution(double precision)
  {
    TRACE;
    _precision=precision;
    _mean=0;
    _inv_stddev=0;
  }

  /*!
     Initialize the distribution by its basic parameters:
     \li \a mean or x0
     \li \a stddev or sigma
     \li \a precision is the maximum tolerated difference accepted for probabilities
  */
  GaussDistribution::GaussDistribution(double mean, double stddev,double precision)
  {
    TRACE;
    _mean=mean;
    _inv_stddev=1.0/stddev;
    _precision=precision;
  }

  GaussDistribution::~GaussDistribution()
  {
    TRACE;
  }

  bool GaussDistribution::operator==(const GaussDistribution& o) const
  {
    TRACE;
    return _mean==o._mean && _inv_stddev==o._inv_stddev;
  }

  /*!
    \fn double GaussDistribution::prob(double a, double b)

    Return the probability of interval [a,b].
    calculate the integral from -inf to a and from -inf to b
  */

  /*!
    \fn double GaussDistribution::part(double a)

    Calculate the integral from -inf to a
  */

  /*!
    \fn double GaussDistribution::partSigma(double a)

    Calculate the integral from -inf to mean+a*stddev
  */

  /*!
    Return the density of probability for value val
  */
  double GaussDistribution::rho(double val) const
  {
    TRACE;
    double delta=val-_mean;
    // 1/sqrt(2*M_PI)=0.398942280402
    return 0.398942280402*_inv_stddev*exp(-delta*delta*0.5*_inv_stddev*_inv_stddev);
  }

  /*!
    Calculate the prob integral between -std::numeric_limits<double>::infinity() and z by Taylor serie
  */
  double GaussDistribution::errorFunction(double z) const
  {
    TRACE;
    /*
      returns the value of intergral(-inf,z)[exp(-z^2/2)/sqrt(2*pi)]
      =1/2+1/sqrt(2*pi)*Somme(k=0,+inf)[((-1)^k*z^(2*k+1))/((2*k+1)*2^k*k!)]
      we calculate it by recurrence: Somme=Somme[n(k)/(2*k+1)]
      where n(k)=-n(k-1)*z^2/(2*k) with n(0)=z
    */
    if(z<-5) return 0;
    if(z>5) return 1;
    double serie=0,dserie=std::numeric_limits<double>::infinity(),n=z;
    int k=0;
    while(dserie>_precision || dserie<-_precision)
      {
        dserie=n/((k << 1)+1);
        serie+=dserie;
        k++;
        n=-n*z*z/(2*k);
      }
    return 0.5+0.398942280402*serie;
  }

} // namespace QGpCoreMath
