// -*- C++ -*-

/*!
  \file numerical/random/poisson/PoissonPdf.h
  \brief Probability density function for the Poisson distribution.
*/

#if !defined(__numerical_PoissonPdf_h__)
#define __numerical_PoissonPdf_h__

#include "../../specialFunctions/Gamma.h"

#include <functional>

#include <cassert>
#include <cmath>

namespace numerical {

//! Probability density function for the Poisson distribution.
/*!
  \param T The number type.  By default it is double.

  This functor computes
  \f[
  \mathrm{pdf}(\mu, n) = \frac{e^{-\mu} \mu^n}{n!}.
  \f]
  It does this by computing the quantity.
  \f[
  \mathrm{exp}(- \mu + n \log(\mu) - \mathrm{logarithmOfGamma}(n + 1))
  \f]
  Hence it uses the LogarithmOfGamma functor.

  This functor also computes the derivative (with respect to the mean)
  of the Poisson PDF with computeDerivative() .
  \f[
  \frac{\mathrm{d} \mathrm{pdf}}{\mathrm{d} \mu}(\mu, n) =
  \left( \frac{n}{\mu} - 1 \right) pdf(\mu, n)
  \f]
  Note the limiting cases:
  \f[
  \frac{\mathrm{d} \mathrm{pdf}}{\mathrm{d} \mu}(0, n) =
  \begin{array}{ll}
  -1 & \mathrm{for } n = 0 \\
  0 & \mathrm{for } n \neq 0
  \end{array}
  \f]

  \note This functor returns a valid result even when the answer causes
  underflow.  (The underflow will occur in evaluating the exponential
  function.)  In this case, the results is truncated to zero.  Note that this
  underflow will usually incur a performance penalty.

  Below is a table showing the relative error in evaluating this function
  for \f$n = \lfloor \mu \rfloor\f$.
  <!--Generated by the unit test code.  PoissonPdf.txt-->
  <table>
  <tr> <th> \f$\mu\f$ <th> \f$\mathrm{pdf}(\mu, n)\f$ <th> Relative Error
  <tr> <td> 1e-008 <td> 1 <td> 0
  <tr> <td> 1e-007 <td> 1 <td> 0
  <tr> <td> 1e-006 <td> 0.999999 <td> 0
  <tr> <td> 1e-005 <td> 0.99999 <td> 0
  <tr> <td> 0.0001 <td> 0.9999 <td> 0
  <tr> <td> 0.001 <td> 0.999 <td> 0
  <tr> <td> 0.01 <td> 0.99005 <td> 0
  <tr> <td> 0.1 <td> 0.904837 <td> -1.22699e-016
  <tr> <td> 1 <td> 0.367879 <td> 4.52685e-016
  <tr> <td> 10 <td> 0.12511 <td> -8.75861e-013
  <tr> <td> 100 <td> 0.039861 <td> -1.04836e-010
  <tr> <td> 1000 <td> 0.0126146 <td> -1.78729e-010
  <tr> <td> 10000 <td> 0.00398939 <td> -1.82296e-010
  <tr> <td> 100000 <td> 0.00126157 <td> -1.49168e-010
  <tr> <td> 1e+006 <td> 0.000398942 <td> -8.87817e-010
  </table>
*/
template < typename T = double >
class PoissonPdf :
   public std::binary_function<T, int, T> {
   //
   // Private types.
   //
private:

   typedef std::binary_function<T, int, T> Base;

   //
   // Public types.
   //
public:

   //! The first argument type.
   typedef typename Base::first_argument_type first_argument_type;
   //! The second argument type.
   typedef typename Base::second_argument_type second_argument_type;
   //! The result type.
   typedef typename Base::result_type result_type;
   //! The number type.
   typedef T Number;

   //
   // Member data.
   //
private:

   LogarithmOfGamma<Number> _logarithmOfGamma;

public:

   //! Default constructor.
   PoissonPdf() :
      _logarithmOfGamma() {}

   //! Copy constructor.
   PoissonPdf(const PoissonPdf& other) :
      _logarithmOfGamma(other._logarithmOfGamma) {}

   //! Assignment operator.
   PoissonPdf&
   operator=(const PoissonPdf& other) {
      if (this != &other) {
         _logarithmOfGamma = other._logarithmOfGamma;
      }
      return *this;
   }

   //! Trivial destructor.
   ~PoissonPdf() {}

   //! Return the Poisson probability density function.
   /*!
     \pre mean >= 0 and n >= 0.
   */
   result_type
   operator()(first_argument_type mean, second_argument_type n);

   //! Return the derivative (with respect to the mean) of the Poisson probability density function.
   /*!
     \pre mean >= 0 and n >= 0.
   */
   result_type
   computeDerivative(first_argument_type mean, second_argument_type n);
};


} // namespace numerical

#define __numerical_random_PoissonPdf_ipp__
#include "PoissonPdf.ipp"
#undef __numerical_random_PoissonPdf_ipp__

#endif
