blob: 8fba2688bc06d49b0cb776dbd5919e0576bcf2b4 [file] [log] [blame]
// -*- C++ -*-
// Copyright (C) 2011-2021 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the terms
// of the GNU General Public License as published by the Free Software
// Foundation; either version 3, or (at your option) any later
// version.
// This library 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
// General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
/**
* @file testsuite_random.h
*/
#ifndef _GLIBCXX_TESTSUITE_RANDOM_H
#define _GLIBCXX_TESTSUITE_RANDOM_H
#include <cmath>
#include <initializer_list>
#include <testsuite_hooks.h>
namespace __gnu_test
{
// Adapted for libstdc++ from GNU gsl-1.14/randist/test.c
// Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007, 2010
// James Theiler, Brian Gough
template<unsigned long BINS = 100,
unsigned long N = 100000,
typename Distribution, typename Pdf>
void
testDiscreteDist(Distribution& f, Pdf pdf)
{
double count[BINS], p[BINS];
for (unsigned long i = 0; i < BINS; i++)
count[i] = 0;
for (unsigned long i = 0; i < N; i++)
{
auto r = f();
if (r >= 0 && (unsigned long)r < BINS)
count[r]++;
}
for (unsigned long i = 0; i < BINS; i++)
p[i] = pdf(i);
for (unsigned long i = 0; i < BINS; i++)
{
bool status_i;
double d = std::abs(count[i] - N * p[i]);
if (p[i] != 0)
{
double s = d / std::sqrt(N * p[i]);
status_i = (s > 5) && (d > 1);
}
else
status_i = (count[i] != 0);
VERIFY( !status_i );
}
}
inline double
bernoulli_pdf(int k, double p)
{
if (k == 0)
return 1 - p;
else if (k == 1)
return p;
else
return 0.0;
}
#ifdef _GLIBCXX_USE_C99_MATH_TR1
inline double
binomial_pdf(int k, int n, double p)
{
if (k < 0 || k > n)
return 0.0;
else
{
double q;
if (p == 0.0)
q = (k == 0) ? 1.0 : 0.0;
else if (p == 1.0)
q = (k == n) ? 1.0 : 0.0;
else
{
double ln_Cnk = (std::lgamma(n + 1.0) - std::lgamma(k + 1.0)
- std::lgamma(n - k + 1.0));
q = ln_Cnk + k * std::log(p) + (n - k) * std::log1p(-p);
q = std::exp(q);
}
return q;
}
}
#endif
inline double
discrete_pdf(int k, std::initializer_list<double> wl)
{
if (!wl.size())
{
static std::initializer_list<double> one = { 1.0 };
wl = one;
}
if (k < 0 || (std::size_t)k >= wl.size())
return 0.0;
else
{
double sum = 0.0;
for (auto it = wl.begin(); it != wl.end(); ++it)
sum += *it;
return wl.begin()[k] / sum;
}
}
inline double
geometric_pdf(int k, double p)
{
if (k < 0)
return 0.0;
else if (k == 0)
return p;
else
return p * std::pow(1 - p, k);
}
#ifdef _GLIBCXX_USE_C99_MATH_TR1
inline double
negative_binomial_pdf(int k, int n, double p)
{
if (k < 0)
return 0.0;
else
{
double f = std::lgamma(k + (double)n);
double a = std::lgamma(n);
double b = std::lgamma(k + 1.0);
return std::exp(f - a - b) * std::pow(p, n) * std::pow(1 - p, k);
}
}
inline double
poisson_pdf(int k, double mu)
{
if (k < 0)
return 0.0;
else
{
double lf = std::lgamma(k + 1.0);
return std::exp(std::log(mu) * k - lf - mu);
}
}
#endif
inline double
uniform_int_pdf(int k, int a, int b)
{
if (k < 0 || k < a || k > b)
return 0.0;
else
return 1.0 / (b - a + 1.0);
}
#ifdef _GLIBCXX_USE_C99_MATH_TR1
inline double
lbincoef(int n, int k)
{
return std::lgamma(double(1 + n))
- std::lgamma(double(1 + k))
- std::lgamma(double(1 + n - k));
}
inline double
hypergeometric_pdf(int k, int N, int K, int n)
{
if (k < 0 || k < std::max(0, n - (N - K)) || k > std::min(K, n))
return 0.0;
else
return lbincoef(K, k) + lbincoef(N - K, n - k) - lbincoef(N, n);
}
#endif
// Check whether TOKEN can construct a std::random_device successfully.
inline bool
random_device_available(const std::string& token) noexcept
{
try {
std::random_device dev(token);
return true;
} catch (...) {
return false;
}
}
} // namespace __gnu_test
#endif // #ifndef _GLIBCXX_TESTSUITE_RANDOM_H