5f070ba298
This makes std::random_device usable on VxWorks when running on older x86 hardware. Since the r10-728 fix for PR libstdc++/85494 the library will use the new code unconditionally on x86, but the cpuid checks for RDSEED and RDRAND can fail at runtime, depending on the hardware where the code is executing. If the OS does not provide /dev/urandom then this means the std::random_device constructor always fails. In previous releases if /dev/urandom is unavailable then std::mt19937 was used unconditionally. This patch adds a fallback for the case where the runtime cpuid checks for x86 hardware instructions fail, and no /dev/urandom is available. When this happens a std::linear_congruential_engine object will be used, with a seed based on hashing the engine's address and the current time. Distinct std::random_device objects will use different seeds, unless an object is created and destroyed and a new object created at the same memory location within the clock tick. This is not great, but is better than always throwing from the constructor, and better than always using std::mt19937 with the same seed (as GCC 9 and earlier do). libstdc++-v3/ChangeLog: * src/c++11/random.cc (USE_LCG): Define when a pseudo-random fallback is needed. [USE_LCG] (bad_seed, construct_lcg_at, destroy_lcg_at, __lcg): New helper functions and callback. (random_device::_M_init): Add 'prng' and 'all' enumerators. Replace switch with fallthrough with a series of 'if' statements. [USE_LCG]: Construct an lcg_type engine and use __lcg when cpuid checks fail. (random_device::_M_init_pretr1) [USE_MT19937]: Accept "prng" token. (random_device::_M_getval): Check for callback unconditionally and always pass _M_file pointer. * testsuite/26_numerics/random/random_device/85494.cc: Remove effective-target check. Use new random_device_available helper. * testsuite/26_numerics/random/random_device/94087.cc: Likewise. * testsuite/26_numerics/random/random_device/cons/default-cow.cc: Remove effective-target check. * testsuite/26_numerics/random/random_device/cons/default.cc: Likewise. * testsuite/26_numerics/random/random_device/cons/token.cc: Use new random_device_available helper. Test "prng" token. * testsuite/util/testsuite_random.h (random_device_available): New helper function.
215 lines
4.5 KiB
C++
215 lines
4.5 KiB
C++
// -*- 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
|