2021-11-20 03:58:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef _LIBCPP___RANDOM_NEGATIVE_BINOMIAL_DISTRIBUTION_H
|
|
|
|
#define _LIBCPP___RANDOM_NEGATIVE_BINOMIAL_DISTRIBUTION_H
|
|
|
|
|
|
|
|
#include <__config>
|
|
|
|
#include <__random/bernoulli_distribution.h>
|
|
|
|
#include <__random/gamma_distribution.h>
|
2021-12-02 08:55:26 +08:00
|
|
|
#include <__random/is_valid.h>
|
2021-11-20 03:58:51 +08:00
|
|
|
#include <__random/poisson_distribution.h>
|
|
|
|
#include <iosfwd>
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
2022-02-02 09:16:40 +08:00
|
|
|
# pragma GCC system_header
|
2021-11-20 03:58:51 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
_LIBCPP_PUSH_MACROS
|
|
|
|
#include <__undef_macros>
|
|
|
|
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
|
|
|
|
template<class _IntType = int>
|
|
|
|
class _LIBCPP_TEMPLATE_VIS negative_binomial_distribution
|
|
|
|
{
|
[libc++] Support int8_t and uint8_t in integer distributions as an extension
In D125283, we ensured that integer distributions would not compile when
used with arbitrary unsupported types. This effectively enforced what
the Standard mentions here: http://eel.is/c++draft/rand#req.genl-1.5.
However, this also had the effect of breaking some users that were
using integer distributions with unsupported types like int8_t. Since we
already support using __int128_t in those distributions, it is reasonable
to also support smaller types like int8_t and its unsigned variant. This
commit implements that, adds tests and documents the extension. Note that
we voluntarily don't add support for instantiating these distributions
with bool and char, since those are not integer types. However, it is
trivial to replace uses of these random distributions on char using int8_t.
It is also interesting to note that in the process of adding tests
for smaller types, I discovered that our distributions sometimes don't
provide as faithful a distribution when instantiated with smaller types,
so I had to relax a couple of tests. In particular, we do a really bad
job at implementing the negative binomial, geometric and poisson distributions
for small types. I think this all boils down to the algorithm we use in
std::poisson_distribution, however I am running out of time to investigate
that and changing the algorithm would be an ABI break (which might be
reasonable).
As part of this patch, I also added a mitigation for a very likely
integer overflow bug we were hitting in our tests in negative_binomial_distribution.
I also filed http://llvm.org/PR56656 to track fixing the problematic
distributions with int8_t and uint8_t.
Supersedes D125283.
Differential Revision: https://reviews.llvm.org/D126823
2022-06-02 03:25:14 +08:00
|
|
|
static_assert(__libcpp_random_is_valid_inttype<_IntType>::value, "IntType must be a supported integer type");
|
2021-11-20 03:58:51 +08:00
|
|
|
public:
|
|
|
|
// types
|
|
|
|
typedef _IntType result_type;
|
|
|
|
|
|
|
|
class _LIBCPP_TEMPLATE_VIS param_type
|
|
|
|
{
|
|
|
|
result_type __k_;
|
|
|
|
double __p_;
|
|
|
|
public:
|
|
|
|
typedef negative_binomial_distribution distribution_type;
|
|
|
|
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
explicit param_type(result_type __k = 1, double __p = 0.5)
|
|
|
|
: __k_(__k), __p_(__p) {}
|
|
|
|
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
result_type k() const {return __k_;}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
double p() const {return __p_;}
|
|
|
|
|
|
|
|
friend _LIBCPP_INLINE_VISIBILITY
|
|
|
|
bool operator==(const param_type& __x, const param_type& __y)
|
|
|
|
{return __x.__k_ == __y.__k_ && __x.__p_ == __y.__p_;}
|
|
|
|
friend _LIBCPP_INLINE_VISIBILITY
|
|
|
|
bool operator!=(const param_type& __x, const param_type& __y)
|
|
|
|
{return !(__x == __y);}
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
param_type __p_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// constructor and reset functions
|
|
|
|
#ifndef _LIBCPP_CXX03_LANG
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
negative_binomial_distribution() : negative_binomial_distribution(1) {}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
explicit negative_binomial_distribution(result_type __k, double __p = 0.5)
|
|
|
|
: __p_(__k, __p) {}
|
|
|
|
#else
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
explicit negative_binomial_distribution(result_type __k = 1,
|
|
|
|
double __p = 0.5)
|
|
|
|
: __p_(__k, __p) {}
|
|
|
|
#endif
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
explicit negative_binomial_distribution(const param_type& __p) : __p_(__p) {}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
void reset() {}
|
|
|
|
|
|
|
|
// generating functions
|
|
|
|
template<class _URNG>
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
result_type operator()(_URNG& __g)
|
|
|
|
{return (*this)(__g, __p_);}
|
|
|
|
template<class _URNG> result_type operator()(_URNG& __g, const param_type& __p);
|
|
|
|
|
|
|
|
// property functions
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
result_type k() const {return __p_.k();}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
double p() const {return __p_.p();}
|
|
|
|
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
param_type param() const {return __p_;}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
void param(const param_type& __p) {__p_ = __p;}
|
|
|
|
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
result_type min() const {return 0;}
|
|
|
|
_LIBCPP_INLINE_VISIBILITY
|
|
|
|
result_type max() const {return numeric_limits<result_type>::max();}
|
|
|
|
|
|
|
|
friend _LIBCPP_INLINE_VISIBILITY
|
|
|
|
bool operator==(const negative_binomial_distribution& __x,
|
|
|
|
const negative_binomial_distribution& __y)
|
|
|
|
{return __x.__p_ == __y.__p_;}
|
|
|
|
friend _LIBCPP_INLINE_VISIBILITY
|
|
|
|
bool operator!=(const negative_binomial_distribution& __x,
|
|
|
|
const negative_binomial_distribution& __y)
|
|
|
|
{return !(__x == __y);}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _IntType>
|
|
|
|
template<class _URNG>
|
|
|
|
_IntType
|
|
|
|
negative_binomial_distribution<_IntType>::operator()(_URNG& __urng, const param_type& __pr)
|
|
|
|
{
|
2022-01-18 00:04:01 +08:00
|
|
|
static_assert(__libcpp_random_is_valid_urng<_URNG>::value, "");
|
2021-11-20 03:58:51 +08:00
|
|
|
result_type __k = __pr.k();
|
|
|
|
double __p = __pr.p();
|
[libc++] Support int8_t and uint8_t in integer distributions as an extension
In D125283, we ensured that integer distributions would not compile when
used with arbitrary unsupported types. This effectively enforced what
the Standard mentions here: http://eel.is/c++draft/rand#req.genl-1.5.
However, this also had the effect of breaking some users that were
using integer distributions with unsupported types like int8_t. Since we
already support using __int128_t in those distributions, it is reasonable
to also support smaller types like int8_t and its unsigned variant. This
commit implements that, adds tests and documents the extension. Note that
we voluntarily don't add support for instantiating these distributions
with bool and char, since those are not integer types. However, it is
trivial to replace uses of these random distributions on char using int8_t.
It is also interesting to note that in the process of adding tests
for smaller types, I discovered that our distributions sometimes don't
provide as faithful a distribution when instantiated with smaller types,
so I had to relax a couple of tests. In particular, we do a really bad
job at implementing the negative binomial, geometric and poisson distributions
for small types. I think this all boils down to the algorithm we use in
std::poisson_distribution, however I am running out of time to investigate
that and changing the algorithm would be an ABI break (which might be
reasonable).
As part of this patch, I also added a mitigation for a very likely
integer overflow bug we were hitting in our tests in negative_binomial_distribution.
I also filed http://llvm.org/PR56656 to track fixing the problematic
distributions with int8_t and uint8_t.
Supersedes D125283.
Differential Revision: https://reviews.llvm.org/D126823
2022-06-02 03:25:14 +08:00
|
|
|
// When the number of bits in _IntType is small, we are too likely to
|
|
|
|
// overflow __f below to use this technique.
|
|
|
|
if (__k <= 21 * __p && sizeof(_IntType) > 1)
|
2021-11-20 03:58:51 +08:00
|
|
|
{
|
|
|
|
bernoulli_distribution __gen(__p);
|
|
|
|
result_type __f = 0;
|
|
|
|
result_type __s = 0;
|
|
|
|
while (__s < __k)
|
|
|
|
{
|
|
|
|
if (__gen(__urng))
|
|
|
|
++__s;
|
|
|
|
else
|
|
|
|
++__f;
|
|
|
|
}
|
[libc++] Support int8_t and uint8_t in integer distributions as an extension
In D125283, we ensured that integer distributions would not compile when
used with arbitrary unsupported types. This effectively enforced what
the Standard mentions here: http://eel.is/c++draft/rand#req.genl-1.5.
However, this also had the effect of breaking some users that were
using integer distributions with unsupported types like int8_t. Since we
already support using __int128_t in those distributions, it is reasonable
to also support smaller types like int8_t and its unsigned variant. This
commit implements that, adds tests and documents the extension. Note that
we voluntarily don't add support for instantiating these distributions
with bool and char, since those are not integer types. However, it is
trivial to replace uses of these random distributions on char using int8_t.
It is also interesting to note that in the process of adding tests
for smaller types, I discovered that our distributions sometimes don't
provide as faithful a distribution when instantiated with smaller types,
so I had to relax a couple of tests. In particular, we do a really bad
job at implementing the negative binomial, geometric and poisson distributions
for small types. I think this all boils down to the algorithm we use in
std::poisson_distribution, however I am running out of time to investigate
that and changing the algorithm would be an ABI break (which might be
reasonable).
As part of this patch, I also added a mitigation for a very likely
integer overflow bug we were hitting in our tests in negative_binomial_distribution.
I also filed http://llvm.org/PR56656 to track fixing the problematic
distributions with int8_t and uint8_t.
Supersedes D125283.
Differential Revision: https://reviews.llvm.org/D126823
2022-06-02 03:25:14 +08:00
|
|
|
_LIBCPP_ASSERT(__f >= 0, "std::negative_binomial_distribution should never produce negative values. "
|
|
|
|
"This is almost certainly a signed integer overflow issue on __f.");
|
2021-11-20 03:58:51 +08:00
|
|
|
return __f;
|
|
|
|
}
|
|
|
|
return poisson_distribution<result_type>(gamma_distribution<double>
|
|
|
|
(__k, (1-__p)/__p)(__urng))(__urng);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _CharT, class _Traits, class _IntType>
|
2022-08-13 19:23:16 +08:00
|
|
|
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
|
2021-11-20 03:58:51 +08:00
|
|
|
operator<<(basic_ostream<_CharT, _Traits>& __os,
|
|
|
|
const negative_binomial_distribution<_IntType>& __x)
|
|
|
|
{
|
|
|
|
__save_flags<_CharT, _Traits> __lx(__os);
|
|
|
|
typedef basic_ostream<_CharT, _Traits> _OStream;
|
|
|
|
__os.flags(_OStream::dec | _OStream::left | _OStream::fixed |
|
|
|
|
_OStream::scientific);
|
|
|
|
_CharT __sp = __os.widen(' ');
|
|
|
|
__os.fill(__sp);
|
|
|
|
return __os << __x.k() << __sp << __x.p();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _CharT, class _Traits, class _IntType>
|
2022-08-13 19:23:16 +08:00
|
|
|
_LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
|
2021-11-20 03:58:51 +08:00
|
|
|
operator>>(basic_istream<_CharT, _Traits>& __is,
|
|
|
|
negative_binomial_distribution<_IntType>& __x)
|
|
|
|
{
|
|
|
|
typedef negative_binomial_distribution<_IntType> _Eng;
|
|
|
|
typedef typename _Eng::result_type result_type;
|
|
|
|
typedef typename _Eng::param_type param_type;
|
|
|
|
__save_flags<_CharT, _Traits> __lx(__is);
|
|
|
|
typedef basic_istream<_CharT, _Traits> _Istream;
|
|
|
|
__is.flags(_Istream::dec | _Istream::skipws);
|
|
|
|
result_type __k;
|
|
|
|
double __p;
|
|
|
|
__is >> __k >> __p;
|
|
|
|
if (!__is.fail())
|
|
|
|
__x.param(param_type(__k, __p));
|
|
|
|
return __is;
|
|
|
|
}
|
|
|
|
|
|
|
|
_LIBCPP_END_NAMESPACE_STD
|
|
|
|
|
|
|
|
_LIBCPP_POP_MACROS
|
|
|
|
|
|
|
|
#endif // _LIBCPP___RANDOM_NEGATIVE_BINOMIAL_DISTRIBUTION_H
|