xref: /freebsd-src/contrib/llvm-project/libcxx/include/__random/poisson_distribution.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
14824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
24824e7fdSDimitry Andric //
34824e7fdSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44824e7fdSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
54824e7fdSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64824e7fdSDimitry Andric //
74824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
84824e7fdSDimitry Andric 
94824e7fdSDimitry Andric #ifndef _LIBCPP___RANDOM_POISSON_DISTRIBUTION_H
104824e7fdSDimitry Andric #define _LIBCPP___RANDOM_POISSON_DISTRIBUTION_H
114824e7fdSDimitry Andric 
124824e7fdSDimitry Andric #include <__config>
130eae32dcSDimitry Andric #include <__random/clamp_to_integral.h>
144824e7fdSDimitry Andric #include <__random/exponential_distribution.h>
1581ad6265SDimitry Andric #include <__random/is_valid.h>
164824e7fdSDimitry Andric #include <__random/normal_distribution.h>
174824e7fdSDimitry Andric #include <__random/uniform_real_distribution.h>
184824e7fdSDimitry Andric #include <cmath>
194824e7fdSDimitry Andric #include <iosfwd>
204824e7fdSDimitry Andric #include <limits>
214824e7fdSDimitry Andric 
224824e7fdSDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
234824e7fdSDimitry Andric #  pragma GCC system_header
244824e7fdSDimitry Andric #endif
254824e7fdSDimitry Andric 
264824e7fdSDimitry Andric _LIBCPP_PUSH_MACROS
274824e7fdSDimitry Andric #include <__undef_macros>
284824e7fdSDimitry Andric 
294824e7fdSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
304824e7fdSDimitry Andric 
314824e7fdSDimitry Andric template<class _IntType = int>
324824e7fdSDimitry Andric class _LIBCPP_TEMPLATE_VIS poisson_distribution
334824e7fdSDimitry Andric {
34fcaf7f86SDimitry Andric     static_assert(__libcpp_random_is_valid_inttype<_IntType>::value, "IntType must be a supported integer type");
354824e7fdSDimitry Andric public:
364824e7fdSDimitry Andric     // types
374824e7fdSDimitry Andric     typedef _IntType result_type;
384824e7fdSDimitry Andric 
394824e7fdSDimitry Andric     class _LIBCPP_TEMPLATE_VIS param_type
404824e7fdSDimitry Andric     {
414824e7fdSDimitry Andric         double __mean_;
424824e7fdSDimitry Andric         double __s_;
434824e7fdSDimitry Andric         double __d_;
444824e7fdSDimitry Andric         double __l_;
454824e7fdSDimitry Andric         double __omega_;
464824e7fdSDimitry Andric         double __c0_;
474824e7fdSDimitry Andric         double __c1_;
484824e7fdSDimitry Andric         double __c2_;
494824e7fdSDimitry Andric         double __c3_;
504824e7fdSDimitry Andric         double __c_;
514824e7fdSDimitry Andric 
524824e7fdSDimitry Andric     public:
534824e7fdSDimitry Andric         typedef poisson_distribution distribution_type;
544824e7fdSDimitry Andric 
554824e7fdSDimitry Andric         explicit param_type(double __mean = 1.0);
564824e7fdSDimitry Andric 
574824e7fdSDimitry Andric         _LIBCPP_INLINE_VISIBILITY
584824e7fdSDimitry Andric         double mean() const {return __mean_;}
594824e7fdSDimitry Andric 
604824e7fdSDimitry Andric         friend _LIBCPP_INLINE_VISIBILITY
614824e7fdSDimitry Andric             bool operator==(const param_type& __x, const param_type& __y)
624824e7fdSDimitry Andric             {return __x.__mean_ == __y.__mean_;}
634824e7fdSDimitry Andric         friend _LIBCPP_INLINE_VISIBILITY
644824e7fdSDimitry Andric             bool operator!=(const param_type& __x, const param_type& __y)
654824e7fdSDimitry Andric             {return !(__x == __y);}
664824e7fdSDimitry Andric 
674824e7fdSDimitry Andric         friend class poisson_distribution;
684824e7fdSDimitry Andric     };
694824e7fdSDimitry Andric 
704824e7fdSDimitry Andric private:
714824e7fdSDimitry Andric     param_type __p_;
724824e7fdSDimitry Andric 
734824e7fdSDimitry Andric public:
744824e7fdSDimitry Andric     // constructors and reset functions
754824e7fdSDimitry Andric #ifndef _LIBCPP_CXX03_LANG
764824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
774824e7fdSDimitry Andric     poisson_distribution() : poisson_distribution(1.0) {}
784824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
794824e7fdSDimitry Andric     explicit poisson_distribution(double __mean)
804824e7fdSDimitry Andric         : __p_(__mean) {}
814824e7fdSDimitry Andric #else
824824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
834824e7fdSDimitry Andric     explicit poisson_distribution(double __mean = 1.0)
844824e7fdSDimitry Andric         : __p_(__mean) {}
854824e7fdSDimitry Andric #endif
864824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
874824e7fdSDimitry Andric     explicit poisson_distribution(const param_type& __p) : __p_(__p) {}
884824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
894824e7fdSDimitry Andric     void reset() {}
904824e7fdSDimitry Andric 
914824e7fdSDimitry Andric     // generating functions
924824e7fdSDimitry Andric     template<class _URNG>
934824e7fdSDimitry Andric         _LIBCPP_INLINE_VISIBILITY
944824e7fdSDimitry Andric         result_type operator()(_URNG& __g)
954824e7fdSDimitry Andric         {return (*this)(__g, __p_);}
964824e7fdSDimitry Andric     template<class _URNG> result_type operator()(_URNG& __g, const param_type& __p);
974824e7fdSDimitry Andric 
984824e7fdSDimitry Andric     // property functions
994824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
1004824e7fdSDimitry Andric     double mean() const {return __p_.mean();}
1014824e7fdSDimitry Andric 
1024824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
1034824e7fdSDimitry Andric     param_type param() const {return __p_;}
1044824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
1054824e7fdSDimitry Andric     void param(const param_type& __p) {__p_ = __p;}
1064824e7fdSDimitry Andric 
1074824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
1084824e7fdSDimitry Andric     result_type min() const {return 0;}
1094824e7fdSDimitry Andric     _LIBCPP_INLINE_VISIBILITY
1104824e7fdSDimitry Andric     result_type max() const {return numeric_limits<result_type>::max();}
1114824e7fdSDimitry Andric 
1124824e7fdSDimitry Andric     friend _LIBCPP_INLINE_VISIBILITY
1134824e7fdSDimitry Andric         bool operator==(const poisson_distribution& __x,
1144824e7fdSDimitry Andric                         const poisson_distribution& __y)
1154824e7fdSDimitry Andric         {return __x.__p_ == __y.__p_;}
1164824e7fdSDimitry Andric     friend _LIBCPP_INLINE_VISIBILITY
1174824e7fdSDimitry Andric         bool operator!=(const poisson_distribution& __x,
1184824e7fdSDimitry Andric                         const poisson_distribution& __y)
1194824e7fdSDimitry Andric         {return !(__x == __y);}
1204824e7fdSDimitry Andric };
1214824e7fdSDimitry Andric 
1224824e7fdSDimitry Andric template<class _IntType>
1234824e7fdSDimitry Andric poisson_distribution<_IntType>::param_type::param_type(double __mean)
1244824e7fdSDimitry Andric     // According to the standard `inf` is a valid input, but it causes the
1254824e7fdSDimitry Andric     // distribution to hang, so we replace it with the maximum representable
1264824e7fdSDimitry Andric     // mean.
1274824e7fdSDimitry Andric     : __mean_(isinf(__mean) ? numeric_limits<double>::max() : __mean)
1284824e7fdSDimitry Andric {
1294824e7fdSDimitry Andric     if (__mean_ < 10)
1304824e7fdSDimitry Andric     {
1314824e7fdSDimitry Andric         __s_ = 0;
1324824e7fdSDimitry Andric         __d_ = 0;
1334824e7fdSDimitry Andric         __l_ = _VSTD::exp(-__mean_);
1344824e7fdSDimitry Andric         __omega_ = 0;
1354824e7fdSDimitry Andric         __c3_ = 0;
1364824e7fdSDimitry Andric         __c2_ = 0;
1374824e7fdSDimitry Andric         __c1_ = 0;
1384824e7fdSDimitry Andric         __c0_ = 0;
1394824e7fdSDimitry Andric         __c_ = 0;
1404824e7fdSDimitry Andric     }
1414824e7fdSDimitry Andric     else
1424824e7fdSDimitry Andric     {
1434824e7fdSDimitry Andric         __s_ = _VSTD::sqrt(__mean_);
1444824e7fdSDimitry Andric         __d_ = 6 * __mean_ * __mean_;
1454824e7fdSDimitry Andric         __l_ = _VSTD::trunc(__mean_ - 1.1484);
1464824e7fdSDimitry Andric         __omega_ = .3989423 / __s_;
1474824e7fdSDimitry Andric         double __b1_ = .4166667E-1 / __mean_;
1484824e7fdSDimitry Andric         double __b2_ = .3 * __b1_ * __b1_;
1494824e7fdSDimitry Andric         __c3_ = .1428571 * __b1_ * __b2_;
1504824e7fdSDimitry Andric         __c2_ = __b2_ - 15. * __c3_;
1514824e7fdSDimitry Andric         __c1_ = __b1_ - 6. * __b2_ + 45. * __c3_;
1524824e7fdSDimitry Andric         __c0_ = 1. - __b1_ + 3. * __b2_ - 15. * __c3_;
1534824e7fdSDimitry Andric         __c_ = .1069 / __mean_;
1544824e7fdSDimitry Andric     }
1554824e7fdSDimitry Andric }
1564824e7fdSDimitry Andric 
1574824e7fdSDimitry Andric template <class _IntType>
1584824e7fdSDimitry Andric template<class _URNG>
1594824e7fdSDimitry Andric _IntType
1604824e7fdSDimitry Andric poisson_distribution<_IntType>::operator()(_URNG& __urng, const param_type& __pr)
1614824e7fdSDimitry Andric {
16281ad6265SDimitry Andric     static_assert(__libcpp_random_is_valid_urng<_URNG>::value, "");
1634824e7fdSDimitry Andric     double __tx;
1644824e7fdSDimitry Andric     uniform_real_distribution<double> __urd;
1654824e7fdSDimitry Andric     if (__pr.__mean_ < 10)
1664824e7fdSDimitry Andric     {
1674824e7fdSDimitry Andric          __tx = 0;
1684824e7fdSDimitry Andric         for (double __p = __urd(__urng); __p > __pr.__l_; ++__tx)
1694824e7fdSDimitry Andric             __p *= __urd(__urng);
1704824e7fdSDimitry Andric     }
1714824e7fdSDimitry Andric     else
1724824e7fdSDimitry Andric     {
1734824e7fdSDimitry Andric         double __difmuk;
1744824e7fdSDimitry Andric         double __g = __pr.__mean_ + __pr.__s_ * normal_distribution<double>()(__urng);
1754824e7fdSDimitry Andric         double __u;
1764824e7fdSDimitry Andric         if (__g > 0)
1774824e7fdSDimitry Andric         {
1784824e7fdSDimitry Andric             __tx = _VSTD::trunc(__g);
1794824e7fdSDimitry Andric             if (__tx >= __pr.__l_)
1804824e7fdSDimitry Andric                 return _VSTD::__clamp_to_integral<result_type>(__tx);
1814824e7fdSDimitry Andric             __difmuk = __pr.__mean_ - __tx;
1824824e7fdSDimitry Andric             __u = __urd(__urng);
1834824e7fdSDimitry Andric             if (__pr.__d_ * __u >= __difmuk * __difmuk * __difmuk)
1844824e7fdSDimitry Andric                 return _VSTD::__clamp_to_integral<result_type>(__tx);
1854824e7fdSDimitry Andric         }
1864824e7fdSDimitry Andric         exponential_distribution<double> __edist;
1874824e7fdSDimitry Andric         for (bool __using_exp_dist = false; true; __using_exp_dist = true)
1884824e7fdSDimitry Andric         {
1894824e7fdSDimitry Andric             double __e;
1904824e7fdSDimitry Andric             if (__using_exp_dist || __g <= 0)
1914824e7fdSDimitry Andric             {
1924824e7fdSDimitry Andric                 double __t;
1934824e7fdSDimitry Andric                 do
1944824e7fdSDimitry Andric                 {
1954824e7fdSDimitry Andric                     __e = __edist(__urng);
1964824e7fdSDimitry Andric                     __u = __urd(__urng);
1974824e7fdSDimitry Andric                     __u += __u - 1;
1984824e7fdSDimitry Andric                     __t = 1.8 + (__u < 0 ? -__e : __e);
1994824e7fdSDimitry Andric                 } while (__t <= -.6744);
2004824e7fdSDimitry Andric                 __tx = _VSTD::trunc(__pr.__mean_ + __pr.__s_ * __t);
2014824e7fdSDimitry Andric                 __difmuk = __pr.__mean_ - __tx;
2024824e7fdSDimitry Andric                 __using_exp_dist = true;
2034824e7fdSDimitry Andric             }
2044824e7fdSDimitry Andric             double __px;
2054824e7fdSDimitry Andric             double __py;
2064824e7fdSDimitry Andric             if (__tx < 10 && __tx >= 0)
2074824e7fdSDimitry Andric             {
2084824e7fdSDimitry Andric                 const double __fac[] = {1, 1, 2, 6, 24, 120, 720, 5040,
2094824e7fdSDimitry Andric                                              40320, 362880};
2104824e7fdSDimitry Andric                 __px = -__pr.__mean_;
2114824e7fdSDimitry Andric                 __py = _VSTD::pow(__pr.__mean_, (double)__tx) / __fac[static_cast<int>(__tx)];
2124824e7fdSDimitry Andric             }
2134824e7fdSDimitry Andric             else
2144824e7fdSDimitry Andric             {
2154824e7fdSDimitry Andric                 double __del = .8333333E-1 / __tx;
2164824e7fdSDimitry Andric                 __del -= 4.8 * __del * __del * __del;
2174824e7fdSDimitry Andric                 double __v = __difmuk / __tx;
2184824e7fdSDimitry Andric                 if (_VSTD::abs(__v) > 0.25)
2194824e7fdSDimitry Andric                     __px = __tx * _VSTD::log(1 + __v) - __difmuk - __del;
2204824e7fdSDimitry Andric                 else
2214824e7fdSDimitry Andric                     __px = __tx * __v * __v * (((((((.1250060 * __v + -.1384794) *
2224824e7fdSDimitry Andric                            __v + .1421878) * __v + -.1661269) * __v + .2000118) *
2234824e7fdSDimitry Andric                            __v + -.2500068) * __v + .3333333) * __v + -.5) - __del;
2244824e7fdSDimitry Andric                 __py = .3989423 / _VSTD::sqrt(__tx);
2254824e7fdSDimitry Andric             }
2264824e7fdSDimitry Andric             double __r = (0.5 - __difmuk) / __pr.__s_;
2274824e7fdSDimitry Andric             double __r2 = __r * __r;
2284824e7fdSDimitry Andric             double __fx = -0.5 * __r2;
2294824e7fdSDimitry Andric             double __fy = __pr.__omega_ * (((__pr.__c3_ * __r2 + __pr.__c2_) *
2304824e7fdSDimitry Andric                                         __r2 + __pr.__c1_) * __r2 + __pr.__c0_);
2314824e7fdSDimitry Andric             if (__using_exp_dist)
2324824e7fdSDimitry Andric             {
2334824e7fdSDimitry Andric                 if (__pr.__c_ * _VSTD::abs(__u) <= __py * _VSTD::exp(__px + __e) -
2344824e7fdSDimitry Andric                                                    __fy * _VSTD::exp(__fx + __e))
2354824e7fdSDimitry Andric                     break;
2364824e7fdSDimitry Andric             }
2374824e7fdSDimitry Andric             else
2384824e7fdSDimitry Andric             {
2394824e7fdSDimitry Andric                 if (__fy - __u * __fy <= __py * _VSTD::exp(__px - __fx))
2404824e7fdSDimitry Andric                     break;
2414824e7fdSDimitry Andric             }
2424824e7fdSDimitry Andric         }
2434824e7fdSDimitry Andric     }
2444824e7fdSDimitry Andric     return _VSTD::__clamp_to_integral<result_type>(__tx);
2454824e7fdSDimitry Andric }
2464824e7fdSDimitry Andric 
2474824e7fdSDimitry Andric template <class _CharT, class _Traits, class _IntType>
248*bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
2494824e7fdSDimitry Andric operator<<(basic_ostream<_CharT, _Traits>& __os,
2504824e7fdSDimitry Andric            const poisson_distribution<_IntType>& __x)
2514824e7fdSDimitry Andric {
2524824e7fdSDimitry Andric     __save_flags<_CharT, _Traits> __lx(__os);
2534824e7fdSDimitry Andric     typedef basic_ostream<_CharT, _Traits> _OStream;
2544824e7fdSDimitry Andric     __os.flags(_OStream::dec | _OStream::left | _OStream::fixed |
2554824e7fdSDimitry Andric                _OStream::scientific);
2564824e7fdSDimitry Andric     return __os << __x.mean();
2574824e7fdSDimitry Andric }
2584824e7fdSDimitry Andric 
2594824e7fdSDimitry Andric template <class _CharT, class _Traits, class _IntType>
260*bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
2614824e7fdSDimitry Andric operator>>(basic_istream<_CharT, _Traits>& __is,
2624824e7fdSDimitry Andric            poisson_distribution<_IntType>& __x)
2634824e7fdSDimitry Andric {
2644824e7fdSDimitry Andric     typedef poisson_distribution<_IntType> _Eng;
2654824e7fdSDimitry Andric     typedef typename _Eng::param_type param_type;
2664824e7fdSDimitry Andric     __save_flags<_CharT, _Traits> __lx(__is);
2674824e7fdSDimitry Andric     typedef basic_istream<_CharT, _Traits> _Istream;
2684824e7fdSDimitry Andric     __is.flags(_Istream::dec | _Istream::skipws);
2694824e7fdSDimitry Andric     double __mean;
2704824e7fdSDimitry Andric     __is >> __mean;
2714824e7fdSDimitry Andric     if (!__is.fail())
2724824e7fdSDimitry Andric         __x.param(param_type(__mean));
2734824e7fdSDimitry Andric     return __is;
2744824e7fdSDimitry Andric }
2754824e7fdSDimitry Andric 
2764824e7fdSDimitry Andric _LIBCPP_END_NAMESPACE_STD
2774824e7fdSDimitry Andric 
2784824e7fdSDimitry Andric _LIBCPP_POP_MACROS
2794824e7fdSDimitry Andric 
2804824e7fdSDimitry Andric #endif // _LIBCPP___RANDOM_POISSON_DISTRIBUTION_H
281