Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
230
TMessagesProj/jni/voip/webrtc/absl/random/internal/chi_square.cc
Normal file
230
TMessagesProj/jni/voip/webrtc/absl/random/internal/chi_square.cc
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/chi_square.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "absl/random/internal/distribution_test_util.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found.
|
||||
inline double fma(double x, double y, double z) { return (x * y) + z; }
|
||||
#endif
|
||||
|
||||
// Use Horner's method to evaluate a polynomial.
|
||||
template <typename T, unsigned N>
|
||||
inline T EvaluatePolynomial(T x, const T (&poly)[N]) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
using std::fma;
|
||||
#endif
|
||||
T p = poly[N - 1];
|
||||
for (unsigned i = 2; i <= N; i++) {
|
||||
p = fma(p, x, poly[N - i]);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static constexpr int kLargeDOF = 150;
|
||||
|
||||
// Returns the probability of a normal z-value.
|
||||
//
|
||||
// Adapted from the POZ function in:
|
||||
// Ibbetson D, Algorithm 209
|
||||
// Collected Algorithms of the CACM 1963 p. 616
|
||||
//
|
||||
double POZ(double z) {
|
||||
static constexpr double kP1[] = {
|
||||
0.797884560593, -0.531923007300, 0.319152932694,
|
||||
-0.151968751364, 0.059054035642, -0.019198292004,
|
||||
0.005198775019, -0.001075204047, 0.000124818987,
|
||||
};
|
||||
static constexpr double kP2[] = {
|
||||
0.999936657524, 0.000535310849, -0.002141268741, 0.005353579108,
|
||||
-0.009279453341, 0.011630447319, -0.010557625006, 0.006549791214,
|
||||
-0.002034254874, -0.000794620820, 0.001390604284, -0.000676904986,
|
||||
-0.000019538132, 0.000152529290, -0.000045255659,
|
||||
};
|
||||
|
||||
const double kZMax = 6.0; // Maximum meaningful z-value.
|
||||
if (z == 0.0) {
|
||||
return 0.5;
|
||||
}
|
||||
double x;
|
||||
double y = 0.5 * std::fabs(z);
|
||||
if (y >= (kZMax * 0.5)) {
|
||||
x = 1.0;
|
||||
} else if (y < 1.0) {
|
||||
double w = y * y;
|
||||
x = EvaluatePolynomial(w, kP1) * y * 2.0;
|
||||
} else {
|
||||
y -= 2.0;
|
||||
x = EvaluatePolynomial(y, kP2);
|
||||
}
|
||||
return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5);
|
||||
}
|
||||
|
||||
// Approximates the survival function of the normal distribution.
|
||||
//
|
||||
// Algorithm 26.2.18, from:
|
||||
// [Abramowitz and Stegun, Handbook of Mathematical Functions,p.932]
|
||||
// http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf
|
||||
//
|
||||
double normal_survival(double z) {
|
||||
// Maybe replace with the alternate formulation.
|
||||
// 0.5 * erfc((x - mean)/(sqrt(2) * sigma))
|
||||
static constexpr double kR[] = {
|
||||
1.0, 0.196854, 0.115194, 0.000344, 0.019527,
|
||||
};
|
||||
double r = EvaluatePolynomial(z, kR);
|
||||
r *= r;
|
||||
return 0.5 / (r * r);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Calculates the critical chi-square value given degrees-of-freedom and a
|
||||
// p-value, usually using bisection. Also known by the name CRITCHI.
|
||||
double ChiSquareValue(int dof, double p) {
|
||||
static constexpr double kChiEpsilon =
|
||||
0.000001; // Accuracy of the approximation.
|
||||
static constexpr double kChiMax = 99999.0; // Maximum chi-squared value.
|
||||
|
||||
const double p_value = 1.0 - p;
|
||||
if (dof < 1 || p_value > 1.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (dof > kLargeDOF) {
|
||||
// For large degrees of freedom, use the normal approximation by
|
||||
// Wilson, E. B. and Hilferty, M. M. (1931)
|
||||
// chi^2 - mean
|
||||
// Z = --------------
|
||||
// stddev
|
||||
const double z = InverseNormalSurvival(p_value);
|
||||
const double mean = 1 - 2.0 / (9 * dof);
|
||||
const double variance = 2.0 / (9 * dof);
|
||||
// Cannot use this method if the variance is 0.
|
||||
if (variance != 0) {
|
||||
double term = z * std::sqrt(variance) + mean;
|
||||
return dof * (term * term * term);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_value <= 0.0) return kChiMax;
|
||||
|
||||
// Otherwise search for the p value by bisection
|
||||
double min_chisq = 0.0;
|
||||
double max_chisq = kChiMax;
|
||||
double current = dof / std::sqrt(p_value);
|
||||
while ((max_chisq - min_chisq) > kChiEpsilon) {
|
||||
if (ChiSquarePValue(current, dof) < p_value) {
|
||||
max_chisq = current;
|
||||
} else {
|
||||
min_chisq = current;
|
||||
}
|
||||
current = (max_chisq + min_chisq) * 0.5;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Calculates the p-value (probability) of a given chi-square value
|
||||
// and degrees of freedom.
|
||||
//
|
||||
// Adapted from the POCHISQ function from:
|
||||
// Hill, I. D. and Pike, M. C. Algorithm 299
|
||||
// Collected Algorithms of the CACM 1963 p. 243
|
||||
//
|
||||
double ChiSquarePValue(double chi_square, int dof) {
|
||||
static constexpr double kLogSqrtPi =
|
||||
0.5723649429247000870717135; // Log[Sqrt[Pi]]
|
||||
static constexpr double kInverseSqrtPi =
|
||||
0.5641895835477562869480795; // 1/(Sqrt[Pi])
|
||||
|
||||
// For large degrees of freedom, use the normal approximation by
|
||||
// Wilson, E. B. and Hilferty, M. M. (1931)
|
||||
// Via Wikipedia:
|
||||
// By the Central Limit Theorem, because the chi-square distribution is the
|
||||
// sum of k independent random variables with finite mean and variance, it
|
||||
// converges to a normal distribution for large k.
|
||||
if (dof > kLargeDOF) {
|
||||
// Re-scale everything.
|
||||
const double chi_square_scaled = std::pow(chi_square / dof, 1.0 / 3);
|
||||
const double mean = 1 - 2.0 / (9 * dof);
|
||||
const double variance = 2.0 / (9 * dof);
|
||||
// If variance is 0, this method cannot be used.
|
||||
if (variance != 0) {
|
||||
const double z = (chi_square_scaled - mean) / std::sqrt(variance);
|
||||
if (z > 0) {
|
||||
return normal_survival(z);
|
||||
} else if (z < 0) {
|
||||
return 1.0 - normal_survival(-z);
|
||||
} else {
|
||||
return 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The chi square function is >= 0 for any degrees of freedom.
|
||||
// In other words, probability that the chi square function >= 0 is 1.
|
||||
if (chi_square <= 0.0) return 1.0;
|
||||
|
||||
// If the degrees of freedom is zero, the chi square function is always 0 by
|
||||
// definition. In other words, the probability that the chi square function
|
||||
// is > 0 is zero (chi square values <= 0 have been filtered above).
|
||||
if (dof < 1) return 0;
|
||||
|
||||
auto capped_exp = [](double x) { return x < -20 ? 0.0 : std::exp(x); };
|
||||
static constexpr double kBigX = 20;
|
||||
|
||||
double a = 0.5 * chi_square;
|
||||
const bool even = !(dof & 1); // True if dof is an even number.
|
||||
const double y = capped_exp(-a);
|
||||
double s = even ? y : (2.0 * POZ(-std::sqrt(chi_square)));
|
||||
|
||||
if (dof <= 2) {
|
||||
return s;
|
||||
}
|
||||
|
||||
chi_square = 0.5 * (dof - 1.0);
|
||||
double z = (even ? 1.0 : 0.5);
|
||||
if (a > kBigX) {
|
||||
double e = (even ? 0.0 : kLogSqrtPi);
|
||||
double c = std::log(a);
|
||||
while (z <= chi_square) {
|
||||
e = std::log(z) + e;
|
||||
s += capped_exp(c * z - a - e);
|
||||
z += 1.0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
double e = (even ? 1.0 : (kInverseSqrtPi / std::sqrt(a)));
|
||||
double c = 0.0;
|
||||
while (z <= chi_square) {
|
||||
e = e * (a / z);
|
||||
c = c + e;
|
||||
z += 1.0;
|
||||
}
|
||||
return c * y + s;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
|
||||
#define ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
|
||||
|
||||
// The chi-square statistic.
|
||||
//
|
||||
// Useful for evaluating if `D` independent random variables are behaving as
|
||||
// expected, or if two distributions are similar. (`D` is the degrees of
|
||||
// freedom).
|
||||
//
|
||||
// Each bucket should have an expected count of 10 or more for the chi square to
|
||||
// be meaningful.
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
constexpr const char kChiSquared[] = "chi-squared";
|
||||
|
||||
// Returns the measured chi square value, using a single expected value. This
|
||||
// assumes that the values in [begin, end) are uniformly distributed.
|
||||
template <typename Iterator>
|
||||
double ChiSquareWithExpected(Iterator begin, Iterator end, double expected) {
|
||||
// Compute the sum and the number of buckets.
|
||||
assert(expected >= 10); // require at least 10 samples per bucket.
|
||||
double chi_square = 0;
|
||||
for (auto it = begin; it != end; it++) {
|
||||
double d = static_cast<double>(*it) - expected;
|
||||
chi_square += d * d;
|
||||
}
|
||||
chi_square = chi_square / expected;
|
||||
return chi_square;
|
||||
}
|
||||
|
||||
// Returns the measured chi square value, taking the actual value of each bucket
|
||||
// from the first set of iterators, and the expected value of each bucket from
|
||||
// the second set of iterators.
|
||||
template <typename Iterator, typename Expected>
|
||||
double ChiSquare(Iterator it, Iterator end, Expected eit, Expected eend) {
|
||||
double chi_square = 0;
|
||||
for (; it != end && eit != eend; ++it, ++eit) {
|
||||
if (*it > 0) {
|
||||
assert(*eit > 0);
|
||||
}
|
||||
double e = static_cast<double>(*eit);
|
||||
double d = static_cast<double>(*it - *eit);
|
||||
if (d != 0) {
|
||||
assert(e > 0);
|
||||
chi_square += (d * d) / e;
|
||||
}
|
||||
}
|
||||
assert(it == end && eit == eend);
|
||||
return chi_square;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// The following methods can be used for an arbitrary significance level.
|
||||
//
|
||||
|
||||
// Calculates critical chi-square values to produce the given p-value using a
|
||||
// bisection search for a value within epsilon, relying on the monotonicity of
|
||||
// ChiSquarePValue().
|
||||
double ChiSquareValue(int dof, double p);
|
||||
|
||||
// Calculates the p-value (probability) of a given chi-square value.
|
||||
double ChiSquarePValue(double chi_square, int dof);
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/chi_square.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/base/macros.h"
|
||||
|
||||
using absl::random_internal::ChiSquare;
|
||||
using absl::random_internal::ChiSquarePValue;
|
||||
using absl::random_internal::ChiSquareValue;
|
||||
using absl::random_internal::ChiSquareWithExpected;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(ChiSquare, Value) {
|
||||
struct {
|
||||
int line;
|
||||
double chi_square;
|
||||
int df;
|
||||
double confidence;
|
||||
} const specs[] = {
|
||||
// Testing lookup at 1% confidence
|
||||
{__LINE__, 0, 0, 0.01},
|
||||
{__LINE__, 0.00016, 1, 0.01},
|
||||
{__LINE__, 1.64650, 8, 0.01},
|
||||
{__LINE__, 5.81221, 16, 0.01},
|
||||
{__LINE__, 156.4319, 200, 0.01},
|
||||
{__LINE__, 1121.3784, 1234, 0.01},
|
||||
{__LINE__, 53557.1629, 54321, 0.01},
|
||||
{__LINE__, 651662.6647, 654321, 0.01},
|
||||
|
||||
// Testing lookup at 99% confidence
|
||||
{__LINE__, 0, 0, 0.99},
|
||||
{__LINE__, 6.635, 1, 0.99},
|
||||
{__LINE__, 20.090, 8, 0.99},
|
||||
{__LINE__, 32.000, 16, 0.99},
|
||||
{__LINE__, 249.4456, 200, 0.99},
|
||||
{__LINE__, 1131.1573, 1023, 0.99},
|
||||
{__LINE__, 1352.5038, 1234, 0.99},
|
||||
{__LINE__, 55090.7356, 54321, 0.99},
|
||||
{__LINE__, 656985.1514, 654321, 0.99},
|
||||
|
||||
// Testing lookup at 99.9% confidence
|
||||
{__LINE__, 16.2659, 3, 0.999},
|
||||
{__LINE__, 22.4580, 6, 0.999},
|
||||
{__LINE__, 267.5409, 200, 0.999},
|
||||
{__LINE__, 1168.5033, 1023, 0.999},
|
||||
{__LINE__, 55345.1741, 54321, 0.999},
|
||||
{__LINE__, 657861.7284, 654321, 0.999},
|
||||
{__LINE__, 51.1772, 24, 0.999},
|
||||
{__LINE__, 59.7003, 30, 0.999},
|
||||
{__LINE__, 37.6984, 15, 0.999},
|
||||
{__LINE__, 29.5898, 10, 0.999},
|
||||
{__LINE__, 27.8776, 9, 0.999},
|
||||
|
||||
// Testing lookup at random confidences
|
||||
{__LINE__, 0.000157088, 1, 0.01},
|
||||
{__LINE__, 5.31852, 2, 0.93},
|
||||
{__LINE__, 1.92256, 4, 0.25},
|
||||
{__LINE__, 10.7709, 13, 0.37},
|
||||
{__LINE__, 26.2514, 17, 0.93},
|
||||
{__LINE__, 36.4799, 29, 0.84},
|
||||
{__LINE__, 25.818, 31, 0.27},
|
||||
{__LINE__, 63.3346, 64, 0.50},
|
||||
{__LINE__, 196.211, 128, 0.9999},
|
||||
{__LINE__, 215.21, 243, 0.10},
|
||||
{__LINE__, 285.393, 256, 0.90},
|
||||
{__LINE__, 984.504, 1024, 0.1923},
|
||||
{__LINE__, 2043.85, 2048, 0.4783},
|
||||
{__LINE__, 48004.6, 48273, 0.194},
|
||||
};
|
||||
for (const auto& spec : specs) {
|
||||
SCOPED_TRACE(spec.line);
|
||||
// Verify all values are have at most a 1% relative error.
|
||||
const double val = ChiSquareValue(spec.df, spec.confidence);
|
||||
const double err = std::max(5e-6, spec.chi_square / 5e3); // 1 part in 5000
|
||||
EXPECT_NEAR(spec.chi_square, val, err) << spec.line;
|
||||
}
|
||||
|
||||
// Relaxed test for extreme values, from
|
||||
// http://www.ciphersbyritter.com/JAVASCRP/NORMCHIK.HTM#ChiSquare
|
||||
EXPECT_NEAR(49.2680, ChiSquareValue(100, 1e-6), 5); // 0.000'005 mark
|
||||
EXPECT_NEAR(123.499, ChiSquareValue(200, 1e-6), 5); // 0.000'005 mark
|
||||
|
||||
EXPECT_NEAR(149.449, ChiSquareValue(100, 0.999), 0.01);
|
||||
EXPECT_NEAR(161.318, ChiSquareValue(100, 0.9999), 0.01);
|
||||
EXPECT_NEAR(172.098, ChiSquareValue(100, 0.99999), 0.01);
|
||||
|
||||
EXPECT_NEAR(381.426, ChiSquareValue(300, 0.999), 0.05);
|
||||
EXPECT_NEAR(399.756, ChiSquareValue(300, 0.9999), 0.1);
|
||||
EXPECT_NEAR(416.126, ChiSquareValue(300, 0.99999), 0.2);
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, PValue) {
|
||||
struct {
|
||||
int line;
|
||||
double pval;
|
||||
double chi_square;
|
||||
int df;
|
||||
} static const specs[] = {
|
||||
{__LINE__, 1, 0, 0},
|
||||
{__LINE__, 0, 0.001, 0},
|
||||
{__LINE__, 1.000, 0, 453},
|
||||
{__LINE__, 0.134471, 7972.52, 7834},
|
||||
{__LINE__, 0.203922, 28.32, 23},
|
||||
{__LINE__, 0.737171, 48274, 48472},
|
||||
{__LINE__, 0.444146, 583.1234, 579},
|
||||
{__LINE__, 0.294814, 138.2, 130},
|
||||
{__LINE__, 0.0816532, 12.63, 7},
|
||||
{__LINE__, 0, 682.32, 67},
|
||||
{__LINE__, 0.49405, 999, 999},
|
||||
{__LINE__, 1.000, 0, 9999},
|
||||
{__LINE__, 0.997477, 0.00001, 1},
|
||||
{__LINE__, 0, 5823.21, 5040},
|
||||
};
|
||||
for (const auto& spec : specs) {
|
||||
SCOPED_TRACE(spec.line);
|
||||
const double pval = ChiSquarePValue(spec.chi_square, spec.df);
|
||||
EXPECT_NEAR(spec.pval, pval, 1e-3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, CalcChiSquare) {
|
||||
struct {
|
||||
int line;
|
||||
std::vector<int> expected;
|
||||
std::vector<int> actual;
|
||||
} const specs[] = {
|
||||
{__LINE__,
|
||||
{56, 234, 76, 1, 546, 1, 87, 345, 1, 234},
|
||||
{2, 132, 4, 43, 234, 8, 345, 8, 236, 56}},
|
||||
{__LINE__,
|
||||
{123, 36, 234, 367, 345, 2, 456, 567, 234, 567},
|
||||
{123, 56, 2345, 8, 345, 8, 2345, 23, 48, 267}},
|
||||
{__LINE__,
|
||||
{123, 234, 345, 456, 567, 678, 789, 890, 98, 76},
|
||||
{123, 234, 345, 456, 567, 678, 789, 890, 98, 76}},
|
||||
{__LINE__, {3, 675, 23, 86, 2, 8, 2}, {456, 675, 23, 86, 23, 65, 2}},
|
||||
{__LINE__, {1}, {23}},
|
||||
};
|
||||
for (const auto& spec : specs) {
|
||||
SCOPED_TRACE(spec.line);
|
||||
double chi_square = 0;
|
||||
for (int i = 0; i < spec.expected.size(); ++i) {
|
||||
const double diff = spec.actual[i] - spec.expected[i];
|
||||
chi_square += (diff * diff) / spec.expected[i];
|
||||
}
|
||||
EXPECT_NEAR(chi_square,
|
||||
ChiSquare(std::begin(spec.actual), std::end(spec.actual),
|
||||
std::begin(spec.expected), std::end(spec.expected)),
|
||||
1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, CalcChiSquareInt64) {
|
||||
const int64_t data[3] = {910293487, 910292491, 910216780};
|
||||
// $ python -c "import scipy.stats
|
||||
// > print scipy.stats.chisquare([910293487, 910292491, 910216780])[0]"
|
||||
// 4.25410123524
|
||||
double sum = std::accumulate(std::begin(data), std::end(data), double{0});
|
||||
size_t n = std::distance(std::begin(data), std::end(data));
|
||||
double a = ChiSquareWithExpected(std::begin(data), std::end(data), sum / n);
|
||||
EXPECT_NEAR(4.254101, a, 1e-6);
|
||||
|
||||
// ... Or with known values.
|
||||
double b =
|
||||
ChiSquareWithExpected(std::begin(data), std::end(data), 910267586.0);
|
||||
EXPECT_NEAR(4.254101, b, 1e-6);
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, TableData) {
|
||||
// Test data from
|
||||
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
|
||||
// 0.90 0.95 0.975 0.99 0.999
|
||||
const double data[100][5] = {
|
||||
/* 1*/ {2.706, 3.841, 5.024, 6.635, 10.828},
|
||||
/* 2*/ {4.605, 5.991, 7.378, 9.210, 13.816},
|
||||
/* 3*/ {6.251, 7.815, 9.348, 11.345, 16.266},
|
||||
/* 4*/ {7.779, 9.488, 11.143, 13.277, 18.467},
|
||||
/* 5*/ {9.236, 11.070, 12.833, 15.086, 20.515},
|
||||
/* 6*/ {10.645, 12.592, 14.449, 16.812, 22.458},
|
||||
/* 7*/ {12.017, 14.067, 16.013, 18.475, 24.322},
|
||||
/* 8*/ {13.362, 15.507, 17.535, 20.090, 26.125},
|
||||
/* 9*/ {14.684, 16.919, 19.023, 21.666, 27.877},
|
||||
/*10*/ {15.987, 18.307, 20.483, 23.209, 29.588},
|
||||
/*11*/ {17.275, 19.675, 21.920, 24.725, 31.264},
|
||||
/*12*/ {18.549, 21.026, 23.337, 26.217, 32.910},
|
||||
/*13*/ {19.812, 22.362, 24.736, 27.688, 34.528},
|
||||
/*14*/ {21.064, 23.685, 26.119, 29.141, 36.123},
|
||||
/*15*/ {22.307, 24.996, 27.488, 30.578, 37.697},
|
||||
/*16*/ {23.542, 26.296, 28.845, 32.000, 39.252},
|
||||
/*17*/ {24.769, 27.587, 30.191, 33.409, 40.790},
|
||||
/*18*/ {25.989, 28.869, 31.526, 34.805, 42.312},
|
||||
/*19*/ {27.204, 30.144, 32.852, 36.191, 43.820},
|
||||
/*20*/ {28.412, 31.410, 34.170, 37.566, 45.315},
|
||||
/*21*/ {29.615, 32.671, 35.479, 38.932, 46.797},
|
||||
/*22*/ {30.813, 33.924, 36.781, 40.289, 48.268},
|
||||
/*23*/ {32.007, 35.172, 38.076, 41.638, 49.728},
|
||||
/*24*/ {33.196, 36.415, 39.364, 42.980, 51.179},
|
||||
/*25*/ {34.382, 37.652, 40.646, 44.314, 52.620},
|
||||
/*26*/ {35.563, 38.885, 41.923, 45.642, 54.052},
|
||||
/*27*/ {36.741, 40.113, 43.195, 46.963, 55.476},
|
||||
/*28*/ {37.916, 41.337, 44.461, 48.278, 56.892},
|
||||
/*29*/ {39.087, 42.557, 45.722, 49.588, 58.301},
|
||||
/*30*/ {40.256, 43.773, 46.979, 50.892, 59.703},
|
||||
/*31*/ {41.422, 44.985, 48.232, 52.191, 61.098},
|
||||
/*32*/ {42.585, 46.194, 49.480, 53.486, 62.487},
|
||||
/*33*/ {43.745, 47.400, 50.725, 54.776, 63.870},
|
||||
/*34*/ {44.903, 48.602, 51.966, 56.061, 65.247},
|
||||
/*35*/ {46.059, 49.802, 53.203, 57.342, 66.619},
|
||||
/*36*/ {47.212, 50.998, 54.437, 58.619, 67.985},
|
||||
/*37*/ {48.363, 52.192, 55.668, 59.893, 69.347},
|
||||
/*38*/ {49.513, 53.384, 56.896, 61.162, 70.703},
|
||||
/*39*/ {50.660, 54.572, 58.120, 62.428, 72.055},
|
||||
/*40*/ {51.805, 55.758, 59.342, 63.691, 73.402},
|
||||
/*41*/ {52.949, 56.942, 60.561, 64.950, 74.745},
|
||||
/*42*/ {54.090, 58.124, 61.777, 66.206, 76.084},
|
||||
/*43*/ {55.230, 59.304, 62.990, 67.459, 77.419},
|
||||
/*44*/ {56.369, 60.481, 64.201, 68.710, 78.750},
|
||||
/*45*/ {57.505, 61.656, 65.410, 69.957, 80.077},
|
||||
/*46*/ {58.641, 62.830, 66.617, 71.201, 81.400},
|
||||
/*47*/ {59.774, 64.001, 67.821, 72.443, 82.720},
|
||||
/*48*/ {60.907, 65.171, 69.023, 73.683, 84.037},
|
||||
/*49*/ {62.038, 66.339, 70.222, 74.919, 85.351},
|
||||
/*50*/ {63.167, 67.505, 71.420, 76.154, 86.661},
|
||||
/*51*/ {64.295, 68.669, 72.616, 77.386, 87.968},
|
||||
/*52*/ {65.422, 69.832, 73.810, 78.616, 89.272},
|
||||
/*53*/ {66.548, 70.993, 75.002, 79.843, 90.573},
|
||||
/*54*/ {67.673, 72.153, 76.192, 81.069, 91.872},
|
||||
/*55*/ {68.796, 73.311, 77.380, 82.292, 93.168},
|
||||
/*56*/ {69.919, 74.468, 78.567, 83.513, 94.461},
|
||||
/*57*/ {71.040, 75.624, 79.752, 84.733, 95.751},
|
||||
/*58*/ {72.160, 76.778, 80.936, 85.950, 97.039},
|
||||
/*59*/ {73.279, 77.931, 82.117, 87.166, 98.324},
|
||||
/*60*/ {74.397, 79.082, 83.298, 88.379, 99.607},
|
||||
/*61*/ {75.514, 80.232, 84.476, 89.591, 100.888},
|
||||
/*62*/ {76.630, 81.381, 85.654, 90.802, 102.166},
|
||||
/*63*/ {77.745, 82.529, 86.830, 92.010, 103.442},
|
||||
/*64*/ {78.860, 83.675, 88.004, 93.217, 104.716},
|
||||
/*65*/ {79.973, 84.821, 89.177, 94.422, 105.988},
|
||||
/*66*/ {81.085, 85.965, 90.349, 95.626, 107.258},
|
||||
/*67*/ {82.197, 87.108, 91.519, 96.828, 108.526},
|
||||
/*68*/ {83.308, 88.250, 92.689, 98.028, 109.791},
|
||||
/*69*/ {84.418, 89.391, 93.856, 99.228, 111.055},
|
||||
/*70*/ {85.527, 90.531, 95.023, 100.425, 112.317},
|
||||
/*71*/ {86.635, 91.670, 96.189, 101.621, 113.577},
|
||||
/*72*/ {87.743, 92.808, 97.353, 102.816, 114.835},
|
||||
/*73*/ {88.850, 93.945, 98.516, 104.010, 116.092},
|
||||
/*74*/ {89.956, 95.081, 99.678, 105.202, 117.346},
|
||||
/*75*/ {91.061, 96.217, 100.839, 106.393, 118.599},
|
||||
/*76*/ {92.166, 97.351, 101.999, 107.583, 119.850},
|
||||
/*77*/ {93.270, 98.484, 103.158, 108.771, 121.100},
|
||||
/*78*/ {94.374, 99.617, 104.316, 109.958, 122.348},
|
||||
/*79*/ {95.476, 100.749, 105.473, 111.144, 123.594},
|
||||
/*80*/ {96.578, 101.879, 106.629, 112.329, 124.839},
|
||||
/*81*/ {97.680, 103.010, 107.783, 113.512, 126.083},
|
||||
/*82*/ {98.780, 104.139, 108.937, 114.695, 127.324},
|
||||
/*83*/ {99.880, 105.267, 110.090, 115.876, 128.565},
|
||||
/*84*/ {100.980, 106.395, 111.242, 117.057, 129.804},
|
||||
/*85*/ {102.079, 107.522, 112.393, 118.236, 131.041},
|
||||
/*86*/ {103.177, 108.648, 113.544, 119.414, 132.277},
|
||||
/*87*/ {104.275, 109.773, 114.693, 120.591, 133.512},
|
||||
/*88*/ {105.372, 110.898, 115.841, 121.767, 134.746},
|
||||
/*89*/ {106.469, 112.022, 116.989, 122.942, 135.978},
|
||||
/*90*/ {107.565, 113.145, 118.136, 124.116, 137.208},
|
||||
/*91*/ {108.661, 114.268, 119.282, 125.289, 138.438},
|
||||
/*92*/ {109.756, 115.390, 120.427, 126.462, 139.666},
|
||||
/*93*/ {110.850, 116.511, 121.571, 127.633, 140.893},
|
||||
/*94*/ {111.944, 117.632, 122.715, 128.803, 142.119},
|
||||
/*95*/ {113.038, 118.752, 123.858, 129.973, 143.344},
|
||||
/*96*/ {114.131, 119.871, 125.000, 131.141, 144.567},
|
||||
/*97*/ {115.223, 120.990, 126.141, 132.309, 145.789},
|
||||
/*98*/ {116.315, 122.108, 127.282, 133.476, 147.010},
|
||||
/*99*/ {117.407, 123.225, 128.422, 134.642, 148.230},
|
||||
/*100*/ {118.498, 124.342, 129.561, 135.807, 149.449} /**/};
|
||||
|
||||
// 0.90 0.95 0.975 0.99 0.999
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(data); i++) {
|
||||
const double E = 0.0001;
|
||||
EXPECT_NEAR(ChiSquarePValue(data[i][0], i + 1), 0.10, E)
|
||||
<< i << " " << data[i][0];
|
||||
EXPECT_NEAR(ChiSquarePValue(data[i][1], i + 1), 0.05, E)
|
||||
<< i << " " << data[i][1];
|
||||
EXPECT_NEAR(ChiSquarePValue(data[i][2], i + 1), 0.025, E)
|
||||
<< i << " " << data[i][2];
|
||||
EXPECT_NEAR(ChiSquarePValue(data[i][3], i + 1), 0.01, E)
|
||||
<< i << " " << data[i][3];
|
||||
EXPECT_NEAR(ChiSquarePValue(data[i][4], i + 1), 0.001, E)
|
||||
<< i << " " << data[i][4];
|
||||
|
||||
const double F = 0.1;
|
||||
EXPECT_NEAR(ChiSquareValue(i + 1, 0.90), data[i][0], F) << i;
|
||||
EXPECT_NEAR(ChiSquareValue(i + 1, 0.95), data[i][1], F) << i;
|
||||
EXPECT_NEAR(ChiSquareValue(i + 1, 0.975), data[i][2], F) << i;
|
||||
EXPECT_NEAR(ChiSquareValue(i + 1, 0.99), data[i][3], F) << i;
|
||||
EXPECT_NEAR(ChiSquareValue(i + 1, 0.999), data[i][4], F) << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, ChiSquareTwoIterator) {
|
||||
// Test data from http://www.stat.yale.edu/Courses/1997-98/101/chigf.htm
|
||||
// Null-hypothesis: This data is normally distributed.
|
||||
const int counts[10] = {6, 6, 18, 33, 38, 38, 28, 21, 9, 3};
|
||||
const double expected[10] = {4.6, 8.8, 18.4, 30.0, 38.2,
|
||||
38.2, 30.0, 18.4, 8.8, 4.6};
|
||||
double chi_square = ChiSquare(std::begin(counts), std::end(counts),
|
||||
std::begin(expected), std::end(expected));
|
||||
EXPECT_NEAR(chi_square, 2.69, 0.001);
|
||||
|
||||
// Degrees of freedom: 10 bins. two estimated parameters. = 10 - 2 - 1.
|
||||
const int dof = 7;
|
||||
// The critical value of 7, 95% => 14.067 (see above test)
|
||||
double p_value_05 = ChiSquarePValue(14.067, dof);
|
||||
EXPECT_NEAR(p_value_05, 0.05, 0.001); // 95%-ile p-value
|
||||
|
||||
double p_actual = ChiSquarePValue(chi_square, dof);
|
||||
EXPECT_GT(p_actual, 0.05); // Accept the null hypothesis.
|
||||
}
|
||||
|
||||
TEST(ChiSquareTest, DiceRolls) {
|
||||
// Assume we are testing 102 fair dice rolls.
|
||||
// Null-hypothesis: This data is fairly distributed.
|
||||
//
|
||||
// The dof value of 4, @95% = 9.488 (see above test)
|
||||
// The dof value of 5, @95% = 11.070
|
||||
const int rolls[6] = {22, 11, 17, 14, 20, 18};
|
||||
double sum = std::accumulate(std::begin(rolls), std::end(rolls), double{0});
|
||||
size_t n = std::distance(std::begin(rolls), std::end(rolls));
|
||||
|
||||
double a = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), sum / n);
|
||||
EXPECT_NEAR(a, 4.70588, 1e-5);
|
||||
EXPECT_LT(a, ChiSquareValue(4, 0.95));
|
||||
|
||||
double p_a = ChiSquarePValue(a, 4);
|
||||
EXPECT_NEAR(p_a, 0.318828, 1e-5); // Accept the null hypothesis.
|
||||
|
||||
double b = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), 17.0);
|
||||
EXPECT_NEAR(b, 4.70588, 1e-5);
|
||||
EXPECT_LT(b, ChiSquareValue(5, 0.95));
|
||||
|
||||
double p_b = ChiSquarePValue(b, 5);
|
||||
EXPECT_NEAR(p_b, 0.4528180, 1e-5); // Accept the null hypothesis.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
|
||||
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/fast_type_id.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/utility/utility.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// DistributionCaller provides an opportunity to overload the general
|
||||
// mechanism for calling a distribution, allowing for mock-RNG classes
|
||||
// to intercept such calls.
|
||||
template <typename URBG>
|
||||
struct DistributionCaller {
|
||||
static_assert(!std::is_pointer<URBG>::value,
|
||||
"You must pass a reference, not a pointer.");
|
||||
// SFINAE to detect whether the URBG type includes a member matching
|
||||
// bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
|
||||
//
|
||||
// These live inside BitGenRef so that they have friend access
|
||||
// to MockingBitGen. (see similar methods in DistributionCaller).
|
||||
template <template <class...> class Trait, class AlwaysVoid, class... Args>
|
||||
struct detector : std::false_type {};
|
||||
template <template <class...> class Trait, class... Args>
|
||||
struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
|
||||
: std::true_type {};
|
||||
|
||||
template <class T>
|
||||
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
|
||||
std::declval<::absl::base_internal::FastTypeIdType>(),
|
||||
std::declval<void*>(), std::declval<void*>()));
|
||||
|
||||
using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type;
|
||||
|
||||
// Default implementation of distribution caller.
|
||||
template <typename DistrT, typename... Args>
|
||||
static typename DistrT::result_type Impl(std::false_type, URBG* urbg,
|
||||
Args&&... args) {
|
||||
DistrT dist(std::forward<Args>(args)...);
|
||||
return dist(*urbg);
|
||||
}
|
||||
|
||||
// Mock implementation of distribution caller.
|
||||
// The underlying KeyT must match the KeyT constructed by MockOverloadSet.
|
||||
template <typename DistrT, typename... Args>
|
||||
static typename DistrT::result_type Impl(std::true_type, URBG* urbg,
|
||||
Args&&... args) {
|
||||
using ResultT = typename DistrT::result_type;
|
||||
using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
|
||||
using KeyT = ResultT(DistrT, ArgTupleT);
|
||||
|
||||
ArgTupleT arg_tuple(std::forward<Args>(args)...);
|
||||
ResultT result;
|
||||
if (!urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
|
||||
&result)) {
|
||||
auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
|
||||
result = dist(*urbg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Default implementation of distribution caller.
|
||||
template <typename DistrT, typename... Args>
|
||||
static typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
|
||||
return Impl<DistrT, Args...>(HasInvokeMock{}, urbg,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
|
||||
|
|
@ -0,0 +1,418 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/distribution_test_util.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found.
|
||||
inline double fma(double x, double y, double z) { return (x * y) + z; }
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
DistributionMoments ComputeDistributionMoments(
|
||||
absl::Span<const double> data_points) {
|
||||
DistributionMoments result;
|
||||
|
||||
// Compute m1
|
||||
for (double x : data_points) {
|
||||
result.n++;
|
||||
result.mean += x;
|
||||
}
|
||||
result.mean /= static_cast<double>(result.n);
|
||||
|
||||
// Compute m2, m3, m4
|
||||
for (double x : data_points) {
|
||||
double v = x - result.mean;
|
||||
result.variance += v * v;
|
||||
result.skewness += v * v * v;
|
||||
result.kurtosis += v * v * v * v;
|
||||
}
|
||||
result.variance /= static_cast<double>(result.n - 1);
|
||||
|
||||
result.skewness /= static_cast<double>(result.n);
|
||||
result.skewness /= std::pow(result.variance, 1.5);
|
||||
|
||||
result.kurtosis /= static_cast<double>(result.n);
|
||||
result.kurtosis /= std::pow(result.variance, 2.0);
|
||||
return result;
|
||||
|
||||
// When validating the min/max count, the following confidence intervals may
|
||||
// be of use:
|
||||
// 3.291 * stddev = 99.9% CI
|
||||
// 2.576 * stddev = 99% CI
|
||||
// 1.96 * stddev = 95% CI
|
||||
// 1.65 * stddev = 90% CI
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments) {
|
||||
return os << absl::StrFormat("mean=%f, stddev=%f, skewness=%f, kurtosis=%f",
|
||||
moments.mean, std::sqrt(moments.variance),
|
||||
moments.skewness, moments.kurtosis);
|
||||
}
|
||||
|
||||
double InverseNormalSurvival(double x) {
|
||||
// inv_sf(u) = -sqrt(2) * erfinv(2u-1)
|
||||
static constexpr double kSqrt2 = 1.4142135623730950488;
|
||||
return -kSqrt2 * absl::random_internal::erfinv(2 * x - 1.0);
|
||||
}
|
||||
|
||||
bool Near(absl::string_view msg, double actual, double expected, double bound) {
|
||||
assert(bound > 0.0);
|
||||
double delta = fabs(expected - actual);
|
||||
if (delta < bound) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string formatted = absl::StrCat(
|
||||
msg, " actual=", actual, " expected=", expected, " err=", delta / bound);
|
||||
ABSL_RAW_LOG(INFO, "%s", formatted.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(absl-team): Replace with an "ABSL_HAVE_SPECIAL_MATH" and try
|
||||
// to use std::beta(). As of this writing P0226R1 is not implemented
|
||||
// in libc++: http://libcxx.llvm.org/cxx1z_status.html
|
||||
double beta(double p, double q) {
|
||||
// Beta(x, y) = Gamma(x) * Gamma(y) / Gamma(x+y)
|
||||
double lbeta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
|
||||
return std::exp(lbeta);
|
||||
}
|
||||
|
||||
// Approximation to inverse of the Error Function in double precision.
|
||||
// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf)
|
||||
double erfinv(double x) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
using std::fma;
|
||||
#endif
|
||||
|
||||
double w = 0.0;
|
||||
double p = 0.0;
|
||||
w = -std::log((1.0 - x) * (1.0 + x));
|
||||
if (w < 6.250000) {
|
||||
w = w - 3.125000;
|
||||
p = -3.6444120640178196996e-21;
|
||||
p = fma(p, w, -1.685059138182016589e-19);
|
||||
p = fma(p, w, 1.2858480715256400167e-18);
|
||||
p = fma(p, w, 1.115787767802518096e-17);
|
||||
p = fma(p, w, -1.333171662854620906e-16);
|
||||
p = fma(p, w, 2.0972767875968561637e-17);
|
||||
p = fma(p, w, 6.6376381343583238325e-15);
|
||||
p = fma(p, w, -4.0545662729752068639e-14);
|
||||
p = fma(p, w, -8.1519341976054721522e-14);
|
||||
p = fma(p, w, 2.6335093153082322977e-12);
|
||||
p = fma(p, w, -1.2975133253453532498e-11);
|
||||
p = fma(p, w, -5.4154120542946279317e-11);
|
||||
p = fma(p, w, 1.051212273321532285e-09);
|
||||
p = fma(p, w, -4.1126339803469836976e-09);
|
||||
p = fma(p, w, -2.9070369957882005086e-08);
|
||||
p = fma(p, w, 4.2347877827932403518e-07);
|
||||
p = fma(p, w, -1.3654692000834678645e-06);
|
||||
p = fma(p, w, -1.3882523362786468719e-05);
|
||||
p = fma(p, w, 0.0001867342080340571352);
|
||||
p = fma(p, w, -0.00074070253416626697512);
|
||||
p = fma(p, w, -0.0060336708714301490533);
|
||||
p = fma(p, w, 0.24015818242558961693);
|
||||
p = fma(p, w, 1.6536545626831027356);
|
||||
} else if (w < 16.000000) {
|
||||
w = std::sqrt(w) - 3.250000;
|
||||
p = 2.2137376921775787049e-09;
|
||||
p = fma(p, w, 9.0756561938885390979e-08);
|
||||
p = fma(p, w, -2.7517406297064545428e-07);
|
||||
p = fma(p, w, 1.8239629214389227755e-08);
|
||||
p = fma(p, w, 1.5027403968909827627e-06);
|
||||
p = fma(p, w, -4.013867526981545969e-06);
|
||||
p = fma(p, w, 2.9234449089955446044e-06);
|
||||
p = fma(p, w, 1.2475304481671778723e-05);
|
||||
p = fma(p, w, -4.7318229009055733981e-05);
|
||||
p = fma(p, w, 6.8284851459573175448e-05);
|
||||
p = fma(p, w, 2.4031110387097893999e-05);
|
||||
p = fma(p, w, -0.0003550375203628474796);
|
||||
p = fma(p, w, 0.00095328937973738049703);
|
||||
p = fma(p, w, -0.0016882755560235047313);
|
||||
p = fma(p, w, 0.0024914420961078508066);
|
||||
p = fma(p, w, -0.0037512085075692412107);
|
||||
p = fma(p, w, 0.005370914553590063617);
|
||||
p = fma(p, w, 1.0052589676941592334);
|
||||
p = fma(p, w, 3.0838856104922207635);
|
||||
} else {
|
||||
w = std::sqrt(w) - 5.000000;
|
||||
p = -2.7109920616438573243e-11;
|
||||
p = fma(p, w, -2.5556418169965252055e-10);
|
||||
p = fma(p, w, 1.5076572693500548083e-09);
|
||||
p = fma(p, w, -3.7894654401267369937e-09);
|
||||
p = fma(p, w, 7.6157012080783393804e-09);
|
||||
p = fma(p, w, -1.4960026627149240478e-08);
|
||||
p = fma(p, w, 2.9147953450901080826e-08);
|
||||
p = fma(p, w, -6.7711997758452339498e-08);
|
||||
p = fma(p, w, 2.2900482228026654717e-07);
|
||||
p = fma(p, w, -9.9298272942317002539e-07);
|
||||
p = fma(p, w, 4.5260625972231537039e-06);
|
||||
p = fma(p, w, -1.9681778105531670567e-05);
|
||||
p = fma(p, w, 7.5995277030017761139e-05);
|
||||
p = fma(p, w, -0.00021503011930044477347);
|
||||
p = fma(p, w, -0.00013871931833623122026);
|
||||
p = fma(p, w, 1.0103004648645343977);
|
||||
p = fma(p, w, 4.8499064014085844221);
|
||||
}
|
||||
return p * x;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Direct implementation of AS63, BETAIN()
|
||||
// https://www.jstor.org/stable/2346797?seq=3#page_scan_tab_contents.
|
||||
//
|
||||
// BETAIN(x, p, q, beta)
|
||||
// x: the value of the upper limit x.
|
||||
// p: the value of the parameter p.
|
||||
// q: the value of the parameter q.
|
||||
// beta: the value of ln B(p, q)
|
||||
//
|
||||
double BetaIncompleteImpl(const double x, const double p, const double q,
|
||||
const double beta) {
|
||||
if (p < (p + q) * x) {
|
||||
// Incomplete beta function is symmetrical, so return the complement.
|
||||
return 1. - BetaIncompleteImpl(1.0 - x, q, p, beta);
|
||||
}
|
||||
|
||||
double psq = p + q;
|
||||
const double kErr = 1e-14;
|
||||
const double xc = 1. - x;
|
||||
const double pre =
|
||||
std::exp(p * std::log(x) + (q - 1.) * std::log(xc) - beta) / p;
|
||||
|
||||
double term = 1.;
|
||||
double ai = 1.;
|
||||
double result = 1.;
|
||||
int ns = static_cast<int>(q + xc * psq);
|
||||
|
||||
// Use the soper reduction formula.
|
||||
double rx = (ns == 0) ? x : x / xc;
|
||||
double temp = q - ai;
|
||||
for (;;) {
|
||||
term = term * temp * rx / (p + ai);
|
||||
result = result + term;
|
||||
temp = std::fabs(term);
|
||||
if (temp < kErr && temp < kErr * result) {
|
||||
return result * pre;
|
||||
}
|
||||
ai = ai + 1.;
|
||||
--ns;
|
||||
if (ns >= 0) {
|
||||
temp = q - ai;
|
||||
if (ns == 0) {
|
||||
rx = x;
|
||||
}
|
||||
} else {
|
||||
temp = psq;
|
||||
psq = psq + 1.;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: See also TOMS Algorithm 708.
|
||||
// http://www.netlib.org/toms/index.html
|
||||
//
|
||||
// NOTE: The NWSC library also includes BRATIO / ISUBX (p87)
|
||||
// https://archive.org/details/DTIC_ADA261511/page/n75
|
||||
}
|
||||
|
||||
// Direct implementation of AS109, XINBTA(p, q, beta, alpha)
|
||||
// https://www.jstor.org/stable/2346798?read-now=1&seq=4#page_scan_tab_contents
|
||||
// https://www.jstor.org/stable/2346887?seq=1#page_scan_tab_contents
|
||||
//
|
||||
// XINBTA(p, q, beta, alpha)
|
||||
// p: the value of the parameter p.
|
||||
// q: the value of the parameter q.
|
||||
// beta: the value of ln B(p, q)
|
||||
// alpha: the value of the lower tail area.
|
||||
//
|
||||
double BetaIncompleteInvImpl(const double p, const double q, const double beta,
|
||||
const double alpha) {
|
||||
if (alpha < 0.5) {
|
||||
// Inverse Incomplete beta function is symmetrical, return the complement.
|
||||
return 1. - BetaIncompleteInvImpl(q, p, beta, 1. - alpha);
|
||||
}
|
||||
const double kErr = 1e-14;
|
||||
double value = kErr;
|
||||
|
||||
// Compute the initial estimate.
|
||||
{
|
||||
double r = std::sqrt(-std::log(alpha * alpha));
|
||||
double y =
|
||||
r - fma(r, 0.27061, 2.30753) / fma(r, fma(r, 0.04481, 0.99229), 1.0);
|
||||
if (p > 1. && q > 1.) {
|
||||
r = (y * y - 3.) / 6.;
|
||||
double s = 1. / (p + p - 1.);
|
||||
double t = 1. / (q + q - 1.);
|
||||
double h = 2. / s + t;
|
||||
double w =
|
||||
y * std::sqrt(h + r) / h - (t - s) * (r + 5. / 6. - t / (3. * h));
|
||||
value = p / (p + q * std::exp(w + w));
|
||||
} else {
|
||||
r = q + q;
|
||||
double t = 1.0 / (9. * q);
|
||||
double u = 1.0 - t + y * std::sqrt(t);
|
||||
t = r * (u * u * u);
|
||||
if (t <= 0) {
|
||||
value = 1.0 - std::exp((std::log((1.0 - alpha) * q) + beta) / q);
|
||||
} else {
|
||||
t = (4.0 * p + r - 2.0) / t;
|
||||
if (t <= 1) {
|
||||
value = std::exp((std::log(alpha * p) + beta) / p);
|
||||
} else {
|
||||
value = 1.0 - 2.0 / (t + 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Solve for x using a modified newton-raphson method using the function
|
||||
// BetaIncomplete.
|
||||
{
|
||||
value = std::max(value, kErr);
|
||||
value = std::min(value, 1.0 - kErr);
|
||||
|
||||
const double r = 1.0 - p;
|
||||
const double t = 1.0 - q;
|
||||
double y;
|
||||
double yprev = 0;
|
||||
double sq = 1;
|
||||
double prev = 1;
|
||||
for (;;) {
|
||||
if (value < 0 || value > 1.0) {
|
||||
// Error case; value went infinite.
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (value == 0 || value == 1) {
|
||||
y = value;
|
||||
} else {
|
||||
y = BetaIncompleteImpl(value, p, q, beta);
|
||||
if (!std::isfinite(y)) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
y = (y - alpha) *
|
||||
std::exp(beta + r * std::log(value) + t * std::log(1.0 - value));
|
||||
if (y * yprev <= 0) {
|
||||
prev = std::max(sq, std::numeric_limits<double>::min());
|
||||
}
|
||||
double g = 1.0;
|
||||
for (;;) {
|
||||
const double adj = g * y;
|
||||
const double adj_sq = adj * adj;
|
||||
if (adj_sq >= prev) {
|
||||
g = g / 3.0;
|
||||
continue;
|
||||
}
|
||||
const double tx = value - adj;
|
||||
if (tx < 0 || tx > 1) {
|
||||
g = g / 3.0;
|
||||
continue;
|
||||
}
|
||||
if (prev < kErr) {
|
||||
return value;
|
||||
}
|
||||
if (y * y < kErr) {
|
||||
return value;
|
||||
}
|
||||
if (tx == value) {
|
||||
return value;
|
||||
}
|
||||
if (tx == 0 || tx == 1) {
|
||||
g = g / 3.0;
|
||||
continue;
|
||||
}
|
||||
value = tx;
|
||||
yprev = y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTES: See also: Asymptotic inversion of the incomplete beta function.
|
||||
// https://core.ac.uk/download/pdf/82140723.pdf
|
||||
//
|
||||
// NOTE: See the Boost library documentation as well:
|
||||
// https://www.boost.org/doc/libs/1_52_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_beta/ibeta_function.html
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
double BetaIncomplete(const double x, const double p, const double q) {
|
||||
// Error cases.
|
||||
if (p < 0 || q < 0 || x < 0 || x > 1.0) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (x == 0 || x == 1) {
|
||||
return x;
|
||||
}
|
||||
// ln(Beta(p, q))
|
||||
double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
|
||||
return BetaIncompleteImpl(x, p, q, beta);
|
||||
}
|
||||
|
||||
double BetaIncompleteInv(const double p, const double q, const double alpha) {
|
||||
// Error cases.
|
||||
if (p < 0 || q < 0 || alpha < 0 || alpha > 1.0) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (alpha == 0 || alpha == 1) {
|
||||
return alpha;
|
||||
}
|
||||
// ln(Beta(p, q))
|
||||
double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q);
|
||||
return BetaIncompleteInvImpl(p, q, beta, alpha);
|
||||
}
|
||||
|
||||
// Given `num_trials` trials each with probability `p` of success, the
|
||||
// probability of no failures is `p^k`. To ensure the probability of a failure
|
||||
// is no more than `p_fail`, it must be that `p^k == 1 - p_fail`. This function
|
||||
// computes `p` from that equation.
|
||||
double RequiredSuccessProbability(const double p_fail, const int num_trials) {
|
||||
double p = std::exp(std::log(1.0 - p_fail) / static_cast<double>(num_trials));
|
||||
ABSL_ASSERT(p > 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
double ZScore(double expected_mean, const DistributionMoments& moments) {
|
||||
return (moments.mean - expected_mean) /
|
||||
(std::sqrt(moments.variance) /
|
||||
std::sqrt(static_cast<double>(moments.n)));
|
||||
}
|
||||
|
||||
double MaxErrorTolerance(double acceptance_probability) {
|
||||
double one_sided_pvalue = 0.5 * (1.0 - acceptance_probability);
|
||||
const double max_err = InverseNormalSurvival(one_sided_pvalue);
|
||||
ABSL_ASSERT(max_err > 0);
|
||||
return max_err;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
|
||||
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <ostream>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
// NOTE: The functions in this file are test only, and are should not be used in
|
||||
// non-test code.
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf
|
||||
|
||||
// Compute the 1st to 4th standard moments:
|
||||
// mean, variance, skewness, and kurtosis.
|
||||
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
|
||||
struct DistributionMoments {
|
||||
size_t n = 0;
|
||||
double mean = 0.0;
|
||||
double variance = 0.0;
|
||||
double skewness = 0.0;
|
||||
double kurtosis = 0.0;
|
||||
};
|
||||
DistributionMoments ComputeDistributionMoments(
|
||||
absl::Span<const double> data_points);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments);
|
||||
|
||||
// Computes the Z-score for a set of data with the given distribution moments
|
||||
// compared against `expected_mean`.
|
||||
double ZScore(double expected_mean, const DistributionMoments& moments);
|
||||
|
||||
// Returns the probability of success required for a single trial to ensure that
|
||||
// after `num_trials` trials, the probability of at least one failure is no more
|
||||
// than `p_fail`.
|
||||
double RequiredSuccessProbability(double p_fail, int num_trials);
|
||||
|
||||
// Computes the maximum distance from the mean tolerable, for Z-Tests that are
|
||||
// expected to pass with `acceptance_probability`. Will terminate if the
|
||||
// resulting tolerance is zero (due to passing in 0.0 for
|
||||
// `acceptance_probability` or rounding errors).
|
||||
//
|
||||
// For example,
|
||||
// MaxErrorTolerance(0.001) = 0.0
|
||||
// MaxErrorTolerance(0.5) = ~0.47
|
||||
// MaxErrorTolerance(1.0) = inf
|
||||
double MaxErrorTolerance(double acceptance_probability);
|
||||
|
||||
// Approximation to inverse of the Error Function in double precision.
|
||||
// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf)
|
||||
double erfinv(double x);
|
||||
|
||||
// Beta(p, q) = Gamma(p) * Gamma(q) / Gamma(p+q)
|
||||
double beta(double p, double q);
|
||||
|
||||
// The inverse of the normal survival function.
|
||||
double InverseNormalSurvival(double x);
|
||||
|
||||
// Returns whether actual is "near" expected, based on the bound.
|
||||
bool Near(absl::string_view msg, double actual, double expected, double bound);
|
||||
|
||||
// Implements the incomplete regularized beta function, AS63, BETAIN.
|
||||
// https://www.jstor.org/stable/2346797
|
||||
//
|
||||
// BetaIncomplete(x, p, q), where
|
||||
// `x` is the value of the upper limit
|
||||
// `p` is beta parameter p, `q` is beta parameter q.
|
||||
//
|
||||
// NOTE: This is a test-only function which is only accurate to within, at most,
|
||||
// 1e-13 of the actual value.
|
||||
//
|
||||
double BetaIncomplete(double x, double p, double q);
|
||||
|
||||
// Implements the inverse of the incomplete regularized beta function, AS109,
|
||||
// XINBTA.
|
||||
// https://www.jstor.org/stable/2346798
|
||||
// https://www.jstor.org/stable/2346887
|
||||
//
|
||||
// BetaIncompleteInv(p, q, beta, alpha)
|
||||
// `p` is beta parameter p, `q` is beta parameter q.
|
||||
// `alpha` is the value of the lower tail area.
|
||||
//
|
||||
// NOTE: This is a test-only function and, when successful, is only accurate to
|
||||
// within ~1e-6 of the actual value; there are some cases where it diverges from
|
||||
// the actual value by much more than that. The function uses Newton's method,
|
||||
// and thus the runtime is highly variable.
|
||||
double BetaIncompleteInv(double p, double q, double alpha);
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/distribution_test_util.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(TestUtil, InverseErf) {
|
||||
const struct {
|
||||
const double z;
|
||||
const double value;
|
||||
} kErfInvTable[] = {
|
||||
{0.0000001, 8.86227e-8},
|
||||
{0.00001, 8.86227e-6},
|
||||
{0.5, 0.4769362762044},
|
||||
{0.6, 0.5951160814499},
|
||||
{0.99999, 3.1234132743},
|
||||
{0.9999999, 3.7665625816},
|
||||
{0.999999944, 3.8403850690566985}, // = log((1-x) * (1+x)) =~ 16.004
|
||||
{0.999999999, 4.3200053849134452},
|
||||
};
|
||||
|
||||
for (const auto& data : kErfInvTable) {
|
||||
auto value = absl::random_internal::erfinv(data.z);
|
||||
|
||||
// Log using the Wolfram-alpha function name & parameters.
|
||||
EXPECT_NEAR(value, data.value, 1e-8)
|
||||
<< " InverseErf[" << data.z << "] (expected=" << data.value << ") -> "
|
||||
<< value;
|
||||
}
|
||||
}
|
||||
|
||||
const struct {
|
||||
const double p;
|
||||
const double q;
|
||||
const double x;
|
||||
const double alpha;
|
||||
} kBetaTable[] = {
|
||||
{0.5, 0.5, 0.01, 0.06376856085851985},
|
||||
{0.5, 0.5, 0.1, 0.2048327646991335},
|
||||
{0.5, 0.5, 1, 1},
|
||||
{1, 0.5, 0, 0},
|
||||
{1, 0.5, 0.01, 0.005012562893380045},
|
||||
{1, 0.5, 0.1, 0.0513167019494862},
|
||||
{1, 0.5, 0.5, 0.2928932188134525},
|
||||
{1, 1, 0.5, 0.5},
|
||||
{2, 2, 0.1, 0.028},
|
||||
{2, 2, 0.2, 0.104},
|
||||
{2, 2, 0.3, 0.216},
|
||||
{2, 2, 0.4, 0.352},
|
||||
{2, 2, 0.5, 0.5},
|
||||
{2, 2, 0.6, 0.648},
|
||||
{2, 2, 0.7, 0.784},
|
||||
{2, 2, 0.8, 0.896},
|
||||
{2, 2, 0.9, 0.972},
|
||||
{5.5, 5, 0.5, 0.4361908850559777},
|
||||
{10, 0.5, 0.9, 0.1516409096346979},
|
||||
{10, 5, 0.5, 0.08978271484375},
|
||||
{10, 5, 1, 1},
|
||||
{10, 10, 0.5, 0.5},
|
||||
{20, 5, 0.8, 0.4598773297575791},
|
||||
{20, 10, 0.6, 0.2146816102371739},
|
||||
{20, 10, 0.8, 0.9507364826957875},
|
||||
{20, 20, 0.5, 0.5},
|
||||
{20, 20, 0.6, 0.8979413687105918},
|
||||
{30, 10, 0.7, 0.2241297491808366},
|
||||
{30, 10, 0.8, 0.7586405487192086},
|
||||
{40, 20, 0.7, 0.7001783247477069},
|
||||
{1, 0.5, 0.1, 0.0513167019494862},
|
||||
{1, 0.5, 0.2, 0.1055728090000841},
|
||||
{1, 0.5, 0.3, 0.1633399734659245},
|
||||
{1, 0.5, 0.4, 0.2254033307585166},
|
||||
{1, 2, 0.2, 0.36},
|
||||
{1, 3, 0.2, 0.488},
|
||||
{1, 4, 0.2, 0.5904},
|
||||
{1, 5, 0.2, 0.67232},
|
||||
{2, 2, 0.3, 0.216},
|
||||
{3, 2, 0.3, 0.0837},
|
||||
{4, 2, 0.3, 0.03078},
|
||||
{5, 2, 0.3, 0.010935},
|
||||
|
||||
// These values test small & large points along the range of the Beta
|
||||
// function.
|
||||
//
|
||||
// When selecting test points, remember that if BetaIncomplete(x, p, q)
|
||||
// returns the same value to within the limits of precision over a large
|
||||
// domain of the input, x, then BetaIncompleteInv(alpha, p, q) may return an
|
||||
// essentially arbitrary value where BetaIncomplete(x, p, q) =~ alpha.
|
||||
|
||||
// BetaRegularized[x, 0.00001, 0.00001],
|
||||
// For x in {~0.001 ... ~0.999}, => ~0.5
|
||||
{1e-5, 1e-5, 1e-5, 0.4999424388184638311},
|
||||
{1e-5, 1e-5, (1.0 - 1e-8), 0.5000920948389232964},
|
||||
|
||||
// BetaRegularized[x, 0.00001, 10000].
|
||||
// For x in {~epsilon ... 1.0}, => ~1
|
||||
{1e-5, 1e5, 1e-6, 0.9999817708130066936},
|
||||
{1e-5, 1e5, (1.0 - 1e-7), 1.0},
|
||||
|
||||
// BetaRegularized[x, 10000, 0.00001].
|
||||
// For x in {0 .. 1-epsilon}, => ~0
|
||||
{1e5, 1e-5, 1e-6, 0},
|
||||
{1e5, 1e-5, (1.0 - 1e-6), 1.8229186993306369e-5},
|
||||
};
|
||||
|
||||
TEST(BetaTest, BetaIncomplete) {
|
||||
for (const auto& data : kBetaTable) {
|
||||
auto value = absl::random_internal::BetaIncomplete(data.x, data.p, data.q);
|
||||
|
||||
// Log using the Wolfram-alpha function name & parameters.
|
||||
EXPECT_NEAR(value, data.alpha, 1e-12)
|
||||
<< " BetaRegularized[" << data.x << ", " << data.p << ", " << data.q
|
||||
<< "] (expected=" << data.alpha << ") -> " << value;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BetaTest, BetaIncompleteInv) {
|
||||
for (const auto& data : kBetaTable) {
|
||||
auto value =
|
||||
absl::random_internal::BetaIncompleteInv(data.p, data.q, data.alpha);
|
||||
|
||||
// Log using the Wolfram-alpha function name & parameters.
|
||||
EXPECT_NEAR(value, data.x, 1e-6)
|
||||
<< " InverseBetaRegularized[" << data.alpha << ", " << data.p << ", "
|
||||
<< data.q << "] (expected=" << data.x << ") -> " << value;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MaxErrorTolerance, MaxErrorTolerance) {
|
||||
std::vector<std::pair<double, double>> cases = {
|
||||
{0.0000001, 8.86227e-8 * 1.41421356237},
|
||||
{0.00001, 8.86227e-6 * 1.41421356237},
|
||||
{0.5, 0.4769362762044 * 1.41421356237},
|
||||
{0.6, 0.5951160814499 * 1.41421356237},
|
||||
{0.99999, 3.1234132743 * 1.41421356237},
|
||||
{0.9999999, 3.7665625816 * 1.41421356237},
|
||||
{0.999999944, 3.8403850690566985 * 1.41421356237},
|
||||
{0.999999999, 4.3200053849134452 * 1.41421356237}};
|
||||
for (auto entry : cases) {
|
||||
EXPECT_NEAR(absl::random_internal::MaxErrorTolerance(entry.first),
|
||||
entry.second, 1e-8);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ZScore, WithSameMean) {
|
||||
absl::random_internal::DistributionMoments m;
|
||||
m.n = 100;
|
||||
m.mean = 5;
|
||||
m.variance = 1;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(5, m), 0, 1e-12);
|
||||
|
||||
m.n = 1;
|
||||
m.mean = 0;
|
||||
m.variance = 1;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(0, m), 0, 1e-12);
|
||||
|
||||
m.n = 10000;
|
||||
m.mean = -5;
|
||||
m.variance = 100;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(-5, m), 0, 1e-12);
|
||||
}
|
||||
|
||||
TEST(ZScore, DifferentMean) {
|
||||
absl::random_internal::DistributionMoments m;
|
||||
m.n = 100;
|
||||
m.mean = 5;
|
||||
m.variance = 1;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(4, m), 10, 1e-12);
|
||||
|
||||
m.n = 1;
|
||||
m.mean = 0;
|
||||
m.variance = 1;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(-1, m), 1, 1e-12);
|
||||
|
||||
m.n = 10000;
|
||||
m.mean = -5;
|
||||
m.variance = 100;
|
||||
EXPECT_NEAR(absl::random_internal::ZScore(-4, m), -10, 1e-12);
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
|
||||
#define ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/endian.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// This class conforms to the C++ Standard "Seed Sequence" concept
|
||||
// [rand.req.seedseq].
|
||||
//
|
||||
// An "ExplicitSeedSeq" is meant to provide a conformant interface for
|
||||
// forwarding pre-computed seed material to the constructor of a class
|
||||
// conforming to the "Uniform Random Bit Generator" concept. This class makes no
|
||||
// attempt to mutate the state provided by its constructor, and returns it
|
||||
// directly via ExplicitSeedSeq::generate().
|
||||
//
|
||||
// If this class is asked to generate more seed material than was provided to
|
||||
// the constructor, then the remaining bytes will be filled with deterministic,
|
||||
// nonrandom data.
|
||||
class ExplicitSeedSeq {
|
||||
public:
|
||||
using result_type = uint32_t;
|
||||
|
||||
ExplicitSeedSeq() : state_() {}
|
||||
|
||||
// Copy and move both allowed.
|
||||
ExplicitSeedSeq(const ExplicitSeedSeq& other) = default;
|
||||
ExplicitSeedSeq& operator=(const ExplicitSeedSeq& other) = default;
|
||||
ExplicitSeedSeq(ExplicitSeedSeq&& other) = default;
|
||||
ExplicitSeedSeq& operator=(ExplicitSeedSeq&& other) = default;
|
||||
|
||||
template <typename Iterator>
|
||||
ExplicitSeedSeq(Iterator begin, Iterator end) {
|
||||
for (auto it = begin; it != end; it++) {
|
||||
state_.push_back(*it & 0xffffffff);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ExplicitSeedSeq(std::initializer_list<T> il)
|
||||
: ExplicitSeedSeq(il.begin(), il.end()) {}
|
||||
|
||||
size_t size() const { return state_.size(); }
|
||||
|
||||
template <typename OutIterator>
|
||||
void param(OutIterator out) const {
|
||||
std::copy(std::begin(state_), std::end(state_), out);
|
||||
}
|
||||
|
||||
template <typename OutIterator>
|
||||
void generate(OutIterator begin, OutIterator end) {
|
||||
for (size_t index = 0; begin != end; begin++) {
|
||||
*begin = state_.empty() ? 0 : state_[index++];
|
||||
if (index >= state_.size()) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint32_t> state_;
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/explicit_seed_seq.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/random/seed_sequences.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using ::absl::random_internal::ExplicitSeedSeq;
|
||||
|
||||
template <typename Sseq>
|
||||
bool ConformsToInterface() {
|
||||
// Check that the SeedSequence can be default-constructed.
|
||||
{
|
||||
Sseq default_constructed_seq;
|
||||
}
|
||||
// Check that the SeedSequence can be constructed with two iterators.
|
||||
{
|
||||
uint32_t init_array[] = {1, 3, 5, 7, 9};
|
||||
Sseq iterator_constructed_seq(init_array, &init_array[5]);
|
||||
}
|
||||
// Check that the SeedSequence can be std::initializer_list-constructed.
|
||||
{
|
||||
Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13};
|
||||
}
|
||||
// Check that param() and size() return state provided to constructor.
|
||||
{
|
||||
uint32_t init_array[] = {1, 2, 3, 4, 5};
|
||||
Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]);
|
||||
EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));
|
||||
|
||||
uint32_t state_array[ABSL_ARRAYSIZE(init_array)];
|
||||
seq.param(state_array);
|
||||
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) {
|
||||
EXPECT_EQ(state_array[i], i + 1);
|
||||
}
|
||||
}
|
||||
// Check for presence of generate() method.
|
||||
{
|
||||
Sseq seq;
|
||||
uint32_t seeds[5];
|
||||
|
||||
seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(SeedSequences, CheckInterfaces) {
|
||||
// Control case
|
||||
EXPECT_TRUE(ConformsToInterface<std::seed_seq>());
|
||||
|
||||
// Abseil classes
|
||||
EXPECT_TRUE(ConformsToInterface<ExplicitSeedSeq>());
|
||||
}
|
||||
|
||||
TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) {
|
||||
const size_t kNumBlocks = 128;
|
||||
|
||||
uint32_t outputs[kNumBlocks];
|
||||
ExplicitSeedSeq seq;
|
||||
seq.generate(outputs, &outputs[kNumBlocks]);
|
||||
|
||||
for (uint32_t& seed : outputs) {
|
||||
EXPECT_EQ(seed, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) {
|
||||
const size_t kNumBlocks = 128;
|
||||
|
||||
uint32_t seed_material[kNumBlocks];
|
||||
std::random_device urandom{"/dev/urandom"};
|
||||
for (uint32_t& seed : seed_material) {
|
||||
seed = urandom();
|
||||
}
|
||||
ExplicitSeedSeq seq(seed_material, &seed_material[kNumBlocks]);
|
||||
|
||||
// Check that output is same as seed-material provided to constructor.
|
||||
{
|
||||
const size_t kNumGenerated = kNumBlocks / 2;
|
||||
uint32_t outputs[kNumGenerated];
|
||||
seq.generate(outputs, &outputs[kNumGenerated]);
|
||||
for (size_t i = 0; i < kNumGenerated; i++) {
|
||||
EXPECT_EQ(outputs[i], seed_material[i]);
|
||||
}
|
||||
}
|
||||
// Check that SeedSequence is stateless between invocations: Despite the last
|
||||
// invocation of generate() only consuming half of the input-entropy, the same
|
||||
// entropy will be recycled for the next invocation.
|
||||
{
|
||||
const size_t kNumGenerated = kNumBlocks;
|
||||
uint32_t outputs[kNumGenerated];
|
||||
seq.generate(outputs, &outputs[kNumGenerated]);
|
||||
for (size_t i = 0; i < kNumGenerated; i++) {
|
||||
EXPECT_EQ(outputs[i], seed_material[i]);
|
||||
}
|
||||
}
|
||||
// Check that when more seed-material is asked for than is provided, nonzero
|
||||
// values are still written.
|
||||
{
|
||||
const size_t kNumGenerated = kNumBlocks * 2;
|
||||
uint32_t outputs[kNumGenerated];
|
||||
seq.generate(outputs, &outputs[kNumGenerated]);
|
||||
for (size_t i = 0; i < kNumGenerated; i++) {
|
||||
EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExplicitSeedSeq, CopyAndMoveConstructors) {
|
||||
using testing::Each;
|
||||
using testing::Eq;
|
||||
using testing::Not;
|
||||
using testing::Pointwise;
|
||||
|
||||
uint32_t entropy[4];
|
||||
std::random_device urandom("/dev/urandom");
|
||||
for (uint32_t& entry : entropy) {
|
||||
entry = urandom();
|
||||
}
|
||||
ExplicitSeedSeq seq_from_entropy(std::begin(entropy), std::end(entropy));
|
||||
// Copy constructor.
|
||||
{
|
||||
ExplicitSeedSeq seq_copy(seq_from_entropy);
|
||||
EXPECT_EQ(seq_copy.size(), seq_from_entropy.size());
|
||||
|
||||
std::vector<uint32_t> seeds_1(1000, 0);
|
||||
std::vector<uint32_t> seeds_2(1000, 1);
|
||||
|
||||
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
|
||||
seq_copy.generate(seeds_2.begin(), seeds_2.end());
|
||||
|
||||
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
|
||||
}
|
||||
// Assignment operator.
|
||||
{
|
||||
for (uint32_t& entry : entropy) {
|
||||
entry = urandom();
|
||||
}
|
||||
ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy));
|
||||
|
||||
std::vector<uint32_t> seeds_1(1000, 0);
|
||||
std::vector<uint32_t> seeds_2(1000, 0);
|
||||
|
||||
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
|
||||
another_seq.generate(seeds_2.begin(), seeds_2.end());
|
||||
|
||||
// Assert precondition: Sequences generated by seed-sequences are not equal.
|
||||
EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2)));
|
||||
|
||||
// Apply the assignment-operator.
|
||||
// GCC 12 has a false-positive -Wstringop-overflow warning here.
|
||||
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
another_seq = seq_from_entropy;
|
||||
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// Re-generate seeds.
|
||||
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
|
||||
another_seq.generate(seeds_2.begin(), seeds_2.end());
|
||||
|
||||
// Seeds generated by seed-sequences should now be equal.
|
||||
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
|
||||
}
|
||||
// Move constructor.
|
||||
{
|
||||
// Get seeds from seed-sequence constructed from entropy.
|
||||
std::vector<uint32_t> seeds_1(1000, 0);
|
||||
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
|
||||
|
||||
// Apply move-constructor move the sequence to another instance.
|
||||
absl::random_internal::ExplicitSeedSeq moved_seq(
|
||||
std::move(seq_from_entropy));
|
||||
std::vector<uint32_t> seeds_2(1000, 1);
|
||||
moved_seq.generate(seeds_2.begin(), seeds_2.end());
|
||||
// Verify that seeds produced by moved-instance are the same as original.
|
||||
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
|
||||
|
||||
// Verify that the moved-from instance now behaves like a
|
||||
// default-constructed instance.
|
||||
EXPECT_EQ(seq_from_entropy.size(), 0);
|
||||
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
|
||||
EXPECT_THAT(seeds_1, Each(Eq(0)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExplicitSeedSeq, StdURBGGoldenTests) {
|
||||
// Verify that for std::- URBG instances the results are stable across
|
||||
// platforms (these should have deterministic output).
|
||||
{
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
std::minstd_rand rng(seed_sequence);
|
||||
|
||||
std::minstd_rand::result_type values[4] = {rng(), rng(), rng(), rng()};
|
||||
EXPECT_THAT(values,
|
||||
testing::ElementsAre(579252, 43785881, 464353103, 1501811174));
|
||||
}
|
||||
|
||||
{
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
std::mt19937 rng(seed_sequence);
|
||||
|
||||
std::mt19937::result_type values[4] = {rng(), rng(), rng(), rng()};
|
||||
EXPECT_THAT(values, testing::ElementsAre(138416803, 151130212, 33817739,
|
||||
138416803));
|
||||
}
|
||||
|
||||
{
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
std::mt19937_64 rng(seed_sequence);
|
||||
|
||||
std::mt19937_64::result_type values[4] = {rng(), rng(), rng(), rng()};
|
||||
EXPECT_THAT(values,
|
||||
testing::ElementsAre(19738651785169348, 1464811352364190456,
|
||||
18054685302720800, 19738651785169348));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
|
||||
#define ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/internal/traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
// Returns true if the input value is zero or a power of two. Useful for
|
||||
// determining if the range of output values in a URBG
|
||||
template <typename UIntType>
|
||||
constexpr bool IsPowerOfTwoOrZero(UIntType n) {
|
||||
return (n == 0) || ((n & (n - 1)) == 0);
|
||||
}
|
||||
|
||||
// Computes the length of the range of values producible by the URBG, or returns
|
||||
// zero if that would encompass the entire range of representable values in
|
||||
// URBG::result_type.
|
||||
template <typename URBG>
|
||||
constexpr typename URBG::result_type RangeSize() {
|
||||
using result_type = typename URBG::result_type;
|
||||
static_assert((URBG::max)() != (URBG::min)(), "URBG range cannot be 0.");
|
||||
return ((URBG::max)() == (std::numeric_limits<result_type>::max)() &&
|
||||
(URBG::min)() == std::numeric_limits<result_type>::lowest())
|
||||
? result_type{0}
|
||||
: ((URBG::max)() - (URBG::min)() + result_type{1});
|
||||
}
|
||||
|
||||
// Computes the floor of the log. (i.e., std::floor(std::log2(N));
|
||||
template <typename UIntType>
|
||||
constexpr UIntType IntegerLog2(UIntType n) {
|
||||
return (n <= 1) ? 0 : 1 + IntegerLog2(n >> 1);
|
||||
}
|
||||
|
||||
// Returns the number of bits of randomness returned through
|
||||
// `PowerOfTwoVariate(urbg)`.
|
||||
template <typename URBG>
|
||||
constexpr size_t NumBits() {
|
||||
return static_cast<size_t>(
|
||||
RangeSize<URBG>() == 0
|
||||
? std::numeric_limits<typename URBG::result_type>::digits
|
||||
: IntegerLog2(RangeSize<URBG>()));
|
||||
}
|
||||
|
||||
// Given a shift value `n`, constructs a mask with exactly the low `n` bits set.
|
||||
// If `n == 0`, all bits are set.
|
||||
template <typename UIntType>
|
||||
constexpr UIntType MaskFromShift(size_t n) {
|
||||
return ((n % std::numeric_limits<UIntType>::digits) == 0)
|
||||
? ~UIntType{0}
|
||||
: (UIntType{1} << n) - UIntType{1};
|
||||
}
|
||||
|
||||
// Tags used to dispatch FastUniformBits::generate to the simple or more complex
|
||||
// entropy extraction algorithm.
|
||||
struct SimplifiedLoopTag {};
|
||||
struct RejectionLoopTag {};
|
||||
|
||||
// FastUniformBits implements a fast path to acquire uniform independent bits
|
||||
// from a type which conforms to the [rand.req.urbg] concept.
|
||||
// Parameterized by:
|
||||
// `UIntType`: the result (output) type
|
||||
//
|
||||
// The std::independent_bits_engine [rand.adapt.ibits] adaptor can be
|
||||
// instantiated from an existing generator through a copy or a move. It does
|
||||
// not, however, facilitate the production of pseudorandom bits from an un-owned
|
||||
// generator that will outlive the std::independent_bits_engine instance.
|
||||
template <typename UIntType = uint64_t>
|
||||
class FastUniformBits {
|
||||
public:
|
||||
using result_type = UIntType;
|
||||
|
||||
static constexpr result_type(min)() { return 0; }
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
template <typename URBG>
|
||||
result_type operator()(URBG& g); // NOLINT(runtime/references)
|
||||
|
||||
private:
|
||||
static_assert(IsUnsigned<UIntType>::value,
|
||||
"Class-template FastUniformBits<> must be parameterized using "
|
||||
"an unsigned type.");
|
||||
|
||||
// Generate() generates a random value, dispatched on whether
|
||||
// the underlying URBG must use rejection sampling to generate a value,
|
||||
// or whether a simplified loop will suffice.
|
||||
template <typename URBG>
|
||||
result_type Generate(URBG& g, // NOLINT(runtime/references)
|
||||
SimplifiedLoopTag);
|
||||
|
||||
template <typename URBG>
|
||||
result_type Generate(URBG& g, // NOLINT(runtime/references)
|
||||
RejectionLoopTag);
|
||||
};
|
||||
|
||||
template <typename UIntType>
|
||||
template <typename URBG>
|
||||
typename FastUniformBits<UIntType>::result_type
|
||||
FastUniformBits<UIntType>::operator()(URBG& g) { // NOLINT(runtime/references)
|
||||
// kRangeMask is the mask used when sampling variates from the URBG when the
|
||||
// width of the URBG range is not a power of 2.
|
||||
// Y = (2 ^ kRange) - 1
|
||||
static_assert((URBG::max)() > (URBG::min)(),
|
||||
"URBG::max and URBG::min may not be equal.");
|
||||
|
||||
using tag = absl::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()),
|
||||
SimplifiedLoopTag, RejectionLoopTag>;
|
||||
return Generate(g, tag{});
|
||||
}
|
||||
|
||||
template <typename UIntType>
|
||||
template <typename URBG>
|
||||
typename FastUniformBits<UIntType>::result_type
|
||||
FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
|
||||
SimplifiedLoopTag) {
|
||||
// The simplified version of FastUniformBits works only on URBGs that have
|
||||
// a range that is a power of 2. In this case we simply loop and shift without
|
||||
// attempting to balance the bits across calls.
|
||||
static_assert(IsPowerOfTwoOrZero(RangeSize<URBG>()),
|
||||
"incorrect Generate tag for URBG instance");
|
||||
|
||||
static constexpr size_t kResultBits =
|
||||
std::numeric_limits<result_type>::digits;
|
||||
static constexpr size_t kUrbgBits = NumBits<URBG>();
|
||||
static constexpr size_t kIters =
|
||||
(kResultBits / kUrbgBits) + (kResultBits % kUrbgBits != 0);
|
||||
static constexpr size_t kShift = (kIters == 1) ? 0 : kUrbgBits;
|
||||
static constexpr auto kMin = (URBG::min)();
|
||||
|
||||
result_type r = static_cast<result_type>(g() - kMin);
|
||||
for (size_t n = 1; n < kIters; ++n) {
|
||||
r = static_cast<result_type>(r << kShift) +
|
||||
static_cast<result_type>(g() - kMin);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename UIntType>
|
||||
template <typename URBG>
|
||||
typename FastUniformBits<UIntType>::result_type
|
||||
FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
|
||||
RejectionLoopTag) {
|
||||
static_assert(!IsPowerOfTwoOrZero(RangeSize<URBG>()),
|
||||
"incorrect Generate tag for URBG instance");
|
||||
using urbg_result_type = typename URBG::result_type;
|
||||
|
||||
// See [rand.adapt.ibits] for more details on the constants calculated below.
|
||||
//
|
||||
// It is preferable to use roughly the same number of bits from each generator
|
||||
// call, however this is only possible when the number of bits provided by the
|
||||
// URBG is a divisor of the number of bits in `result_type`. In all other
|
||||
// cases, the number of bits used cannot always be the same, but it can be
|
||||
// guaranteed to be off by at most 1. Thus we run two loops, one with a
|
||||
// smaller bit-width size (`kSmallWidth`) and one with a larger width size
|
||||
// (satisfying `kLargeWidth == kSmallWidth + 1`). The loops are run
|
||||
// `kSmallIters` and `kLargeIters` times respectively such
|
||||
// that
|
||||
//
|
||||
// `kResultBits == kSmallIters * kSmallBits
|
||||
// + kLargeIters * kLargeBits`
|
||||
//
|
||||
// where `kResultBits` is the total number of bits in `result_type`.
|
||||
//
|
||||
static constexpr size_t kResultBits =
|
||||
std::numeric_limits<result_type>::digits; // w
|
||||
static constexpr urbg_result_type kUrbgRange = RangeSize<URBG>(); // R
|
||||
static constexpr size_t kUrbgBits = NumBits<URBG>(); // m
|
||||
|
||||
// compute the initial estimate of the bits used.
|
||||
// [rand.adapt.ibits] 2 (c)
|
||||
static constexpr size_t kA = // ceil(w/m)
|
||||
(kResultBits / kUrbgBits) + ((kResultBits % kUrbgBits) != 0); // n'
|
||||
|
||||
static constexpr size_t kABits = kResultBits / kA; // w0'
|
||||
static constexpr urbg_result_type kARejection =
|
||||
((kUrbgRange >> kABits) << kABits); // y0'
|
||||
|
||||
// refine the selection to reduce the rejection frequency.
|
||||
static constexpr size_t kTotalIters =
|
||||
((kUrbgRange - kARejection) <= (kARejection / kA)) ? kA : (kA + 1); // n
|
||||
|
||||
// [rand.adapt.ibits] 2 (b)
|
||||
static constexpr size_t kSmallIters =
|
||||
kTotalIters - (kResultBits % kTotalIters); // n0
|
||||
static constexpr size_t kSmallBits = kResultBits / kTotalIters; // w0
|
||||
static constexpr urbg_result_type kSmallRejection =
|
||||
((kUrbgRange >> kSmallBits) << kSmallBits); // y0
|
||||
|
||||
static constexpr size_t kLargeBits = kSmallBits + 1; // w0+1
|
||||
static constexpr urbg_result_type kLargeRejection =
|
||||
((kUrbgRange >> kLargeBits) << kLargeBits); // y1
|
||||
|
||||
//
|
||||
// Because `kLargeBits == kSmallBits + 1`, it follows that
|
||||
//
|
||||
// `kResultBits == kSmallIters * kSmallBits + kLargeIters`
|
||||
//
|
||||
// and therefore
|
||||
//
|
||||
// `kLargeIters == kTotalWidth % kSmallWidth`
|
||||
//
|
||||
// Intuitively, each iteration with the large width accounts for one unit
|
||||
// of the remainder when `kTotalWidth` is divided by `kSmallWidth`. As
|
||||
// mentioned above, if the URBG width is a divisor of `kTotalWidth`, then
|
||||
// there would be no need for any large iterations (i.e., one loop would
|
||||
// suffice), and indeed, in this case, `kLargeIters` would be zero.
|
||||
static_assert(kResultBits == kSmallIters * kSmallBits +
|
||||
(kTotalIters - kSmallIters) * kLargeBits,
|
||||
"Error in looping constant calculations.");
|
||||
|
||||
// The small shift is essentially small bits, but due to the potential
|
||||
// of generating a smaller result_type from a larger urbg type, the actual
|
||||
// shift might be 0.
|
||||
static constexpr size_t kSmallShift = kSmallBits % kResultBits;
|
||||
static constexpr auto kSmallMask =
|
||||
MaskFromShift<urbg_result_type>(kSmallShift);
|
||||
static constexpr size_t kLargeShift = kLargeBits % kResultBits;
|
||||
static constexpr auto kLargeMask =
|
||||
MaskFromShift<urbg_result_type>(kLargeShift);
|
||||
|
||||
static constexpr auto kMin = (URBG::min)();
|
||||
|
||||
result_type s = 0;
|
||||
for (size_t n = 0; n < kSmallIters; ++n) {
|
||||
urbg_result_type v;
|
||||
do {
|
||||
v = g() - kMin;
|
||||
} while (v >= kSmallRejection);
|
||||
|
||||
s = (s << kSmallShift) + static_cast<result_type>(v & kSmallMask);
|
||||
}
|
||||
|
||||
for (size_t n = kSmallIters; n < kTotalIters; ++n) {
|
||||
urbg_result_type v;
|
||||
do {
|
||||
v = g() - kMin;
|
||||
} while (v >= kLargeRejection);
|
||||
|
||||
s = (s << kLargeShift) + static_cast<result_type>(v & kLargeMask);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/fast_uniform_bits.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
template <typename IntType>
|
||||
class FastUniformBitsTypedTest : public ::testing::Test {};
|
||||
|
||||
using IntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
|
||||
|
||||
TYPED_TEST_SUITE(FastUniformBitsTypedTest, IntTypes);
|
||||
|
||||
TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
|
||||
using Limits = std::numeric_limits<TypeParam>;
|
||||
using FastBits = FastUniformBits<TypeParam>;
|
||||
|
||||
EXPECT_EQ(0, (FastBits::min)());
|
||||
EXPECT_EQ((Limits::max)(), (FastBits::max)());
|
||||
|
||||
constexpr int kIters = 10000;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
FastBits fast;
|
||||
for (int i = 0; i < kIters; i++) {
|
||||
const auto v = fast(gen);
|
||||
EXPECT_LE(v, (FastBits::max)());
|
||||
EXPECT_GE(v, (FastBits::min)());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo>
|
||||
struct FakeUrbg {
|
||||
using result_type = UIntType;
|
||||
|
||||
FakeUrbg() = default;
|
||||
explicit FakeUrbg(bool r) : reject(r) {}
|
||||
|
||||
static constexpr result_type(max)() { return Hi; }
|
||||
static constexpr result_type(min)() { return Lo; }
|
||||
result_type operator()() {
|
||||
// when reject is set, return Hi half the time.
|
||||
return ((++calls % 2) == 1 && reject) ? Hi : Val;
|
||||
}
|
||||
|
||||
bool reject = false;
|
||||
size_t calls = 0;
|
||||
};
|
||||
|
||||
TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) {
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{0}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{1}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{2}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{3}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{4}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{16}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{17}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint8_t>::max)()));
|
||||
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{0}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{1}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{2}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{3}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{4}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{16}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{17}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint16_t>::max)()));
|
||||
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{0}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{1}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{2}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{3}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{32}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{17}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint32_t>::max)()));
|
||||
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{0}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{1}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{2}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{3}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{4}));
|
||||
EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{64}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{17}));
|
||||
EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint64_t>::max)()));
|
||||
}
|
||||
|
||||
TEST(FastUniformBitsTest, IntegerLog2) {
|
||||
EXPECT_EQ(0, IntegerLog2(uint16_t{0}));
|
||||
EXPECT_EQ(0, IntegerLog2(uint16_t{1}));
|
||||
EXPECT_EQ(1, IntegerLog2(uint16_t{2}));
|
||||
EXPECT_EQ(1, IntegerLog2(uint16_t{3}));
|
||||
EXPECT_EQ(2, IntegerLog2(uint16_t{4}));
|
||||
EXPECT_EQ(2, IntegerLog2(uint16_t{5}));
|
||||
EXPECT_EQ(2, IntegerLog2(uint16_t{7}));
|
||||
EXPECT_EQ(3, IntegerLog2(uint16_t{8}));
|
||||
EXPECT_EQ(63, IntegerLog2((std::numeric_limits<uint64_t>::max)()));
|
||||
}
|
||||
|
||||
TEST(FastUniformBitsTest, RangeSize) {
|
||||
EXPECT_EQ(2, (RangeSize<FakeUrbg<uint8_t, 0, 1>>()));
|
||||
EXPECT_EQ(3, (RangeSize<FakeUrbg<uint8_t, 0, 2>>()));
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 0, 3>>()));
|
||||
// EXPECT_EQ(0, (RangeSize<FakeUrbg<uint8_t, 2, 2>>()));
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 2, 5>>()));
|
||||
EXPECT_EQ(5, (RangeSize<FakeUrbg<uint8_t, 2, 6>>()));
|
||||
EXPECT_EQ(9, (RangeSize<FakeUrbg<uint8_t, 2, 10>>()));
|
||||
EXPECT_EQ(
|
||||
0, (RangeSize<
|
||||
FakeUrbg<uint8_t, 0, (std::numeric_limits<uint8_t>::max)()>>()));
|
||||
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 0, 3>>()));
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 2, 5>>()));
|
||||
EXPECT_EQ(5, (RangeSize<FakeUrbg<uint16_t, 2, 6>>()));
|
||||
EXPECT_EQ(18, (RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()));
|
||||
EXPECT_EQ(
|
||||
0, (RangeSize<
|
||||
FakeUrbg<uint16_t, 0, (std::numeric_limits<uint16_t>::max)()>>()));
|
||||
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 0, 3>>()));
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 2, 5>>()));
|
||||
EXPECT_EQ(5, (RangeSize<FakeUrbg<uint32_t, 2, 6>>()));
|
||||
EXPECT_EQ(18, (RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()));
|
||||
EXPECT_EQ(0, (RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()));
|
||||
EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()));
|
||||
EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()));
|
||||
EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()));
|
||||
EXPECT_EQ(
|
||||
0, (RangeSize<
|
||||
FakeUrbg<uint32_t, 0, (std::numeric_limits<uint32_t>::max)()>>()));
|
||||
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 0, 3>>()));
|
||||
EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 2, 5>>()));
|
||||
EXPECT_EQ(5, (RangeSize<FakeUrbg<uint64_t, 2, 6>>()));
|
||||
EXPECT_EQ(18, (RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()));
|
||||
EXPECT_EQ(0x100000000, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()));
|
||||
EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()));
|
||||
EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()));
|
||||
EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()));
|
||||
EXPECT_EQ(0, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffff>>()));
|
||||
EXPECT_EQ(0xffffffffffffffff,
|
||||
(RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffff>>()));
|
||||
EXPECT_EQ(0xfffffffffffffffe,
|
||||
(RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffe>>()));
|
||||
EXPECT_EQ(0xfffffffffffffffd,
|
||||
(RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffe>>()));
|
||||
EXPECT_EQ(
|
||||
0, (RangeSize<
|
||||
FakeUrbg<uint64_t, 0, (std::numeric_limits<uint64_t>::max)()>>()));
|
||||
}
|
||||
|
||||
// The constants need to be chosen so that an infinite rejection loop doesn't
|
||||
// happen...
|
||||
using Urng1_5bit = FakeUrbg<uint8_t, 0, 2, 0>; // ~1.5 bits (range 3)
|
||||
using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>;
|
||||
using Urng22bits = FakeUrbg<uint32_t, 0, 0x3fffff, 0x301020>;
|
||||
using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>; // ~31.9 bits
|
||||
using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>;
|
||||
using Urng33bits =
|
||||
FakeUrbg<uint64_t, 1, 0x1ffffffff, 0x013301033>; // ~32.9 bits
|
||||
using Urng63bits = FakeUrbg<uint64_t, 1, 0xfffffffffffffffe,
|
||||
0xfedcba9012345678>; // ~63.9 bits
|
||||
using Urng64bits =
|
||||
FakeUrbg<uint64_t, 0, 0xffffffffffffffff, 0x123456780fedcba9>;
|
||||
|
||||
TEST(FastUniformBitsTest, OutputsUpTo32Bits) {
|
||||
// Tests that how values are composed; the single-bit deltas should be spread
|
||||
// across each invocation.
|
||||
Urng1_5bit urng1_5;
|
||||
Urng4bits urng4;
|
||||
Urng22bits urng22;
|
||||
Urng31bits urng31;
|
||||
Urng32bits urng32;
|
||||
Urng33bits urng33;
|
||||
Urng63bits urng63;
|
||||
Urng64bits urng64;
|
||||
|
||||
// 8-bit types
|
||||
{
|
||||
FastUniformBits<uint8_t> fast8;
|
||||
EXPECT_EQ(0x0, fast8(urng1_5));
|
||||
EXPECT_EQ(0x11, fast8(urng4));
|
||||
EXPECT_EQ(0x20, fast8(urng22));
|
||||
EXPECT_EQ(0x2, fast8(urng31));
|
||||
EXPECT_EQ(0x1, fast8(urng32));
|
||||
EXPECT_EQ(0x32, fast8(urng33));
|
||||
EXPECT_EQ(0x77, fast8(urng63));
|
||||
EXPECT_EQ(0xa9, fast8(urng64));
|
||||
}
|
||||
|
||||
// 16-bit types
|
||||
{
|
||||
FastUniformBits<uint16_t> fast16;
|
||||
EXPECT_EQ(0x0, fast16(urng1_5));
|
||||
EXPECT_EQ(0x1111, fast16(urng4));
|
||||
EXPECT_EQ(0x1020, fast16(urng22));
|
||||
EXPECT_EQ(0x0f02, fast16(urng31));
|
||||
EXPECT_EQ(0x0f01, fast16(urng32));
|
||||
EXPECT_EQ(0x1032, fast16(urng33));
|
||||
EXPECT_EQ(0x5677, fast16(urng63));
|
||||
EXPECT_EQ(0xcba9, fast16(urng64));
|
||||
}
|
||||
|
||||
// 32-bit types
|
||||
{
|
||||
FastUniformBits<uint32_t> fast32;
|
||||
EXPECT_EQ(0x0, fast32(urng1_5));
|
||||
EXPECT_EQ(0x11111111, fast32(urng4));
|
||||
EXPECT_EQ(0x08301020, fast32(urng22));
|
||||
EXPECT_EQ(0x0f020f02, fast32(urng31));
|
||||
EXPECT_EQ(0x74010f01, fast32(urng32));
|
||||
EXPECT_EQ(0x13301032, fast32(urng33));
|
||||
EXPECT_EQ(0x12345677, fast32(urng63));
|
||||
EXPECT_EQ(0x0fedcba9, fast32(urng64));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FastUniformBitsTest, Outputs64Bits) {
|
||||
// Tests that how values are composed; the single-bit deltas should be spread
|
||||
// across each invocation.
|
||||
FastUniformBits<uint64_t> fast64;
|
||||
|
||||
{
|
||||
FakeUrbg<uint8_t, 0, 1, 0> urng0;
|
||||
FakeUrbg<uint8_t, 0, 1, 1> urng1;
|
||||
Urng4bits urng4;
|
||||
Urng22bits urng22;
|
||||
Urng31bits urng31;
|
||||
Urng32bits urng32;
|
||||
Urng33bits urng33;
|
||||
Urng63bits urng63;
|
||||
Urng64bits urng64;
|
||||
|
||||
// somewhat degenerate cases only create a single bit.
|
||||
EXPECT_EQ(0x0, fast64(urng0));
|
||||
EXPECT_EQ(64, urng0.calls);
|
||||
EXPECT_EQ(0xffffffffffffffff, fast64(urng1));
|
||||
EXPECT_EQ(64, urng1.calls);
|
||||
|
||||
// less degenerate cases.
|
||||
EXPECT_EQ(0x1111111111111111, fast64(urng4));
|
||||
EXPECT_EQ(16, urng4.calls);
|
||||
EXPECT_EQ(0x01020c0408301020, fast64(urng22));
|
||||
EXPECT_EQ(3, urng22.calls);
|
||||
EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
|
||||
EXPECT_EQ(3, urng31.calls);
|
||||
EXPECT_EQ(0x74010f0174010f01, fast64(urng32));
|
||||
EXPECT_EQ(2, urng32.calls);
|
||||
EXPECT_EQ(0x808194040cb01032, fast64(urng33));
|
||||
EXPECT_EQ(3, urng33.calls);
|
||||
EXPECT_EQ(0x1234567712345677, fast64(urng63));
|
||||
EXPECT_EQ(2, urng63.calls);
|
||||
EXPECT_EQ(0x123456780fedcba9, fast64(urng64));
|
||||
EXPECT_EQ(1, urng64.calls);
|
||||
}
|
||||
|
||||
// The 1.5 bit case is somewhat interesting in that the algorithm refinement
|
||||
// causes one extra small sample. Comments here reference the names used in
|
||||
// [rand.adapt.ibits] that correspond to this case.
|
||||
{
|
||||
Urng1_5bit urng1_5;
|
||||
|
||||
// w = 64
|
||||
// R = 3
|
||||
// m = 1
|
||||
// n' = 64
|
||||
// w0' = 1
|
||||
// y0' = 2
|
||||
// n = (1 <= 0) > 64 : 65 = 65
|
||||
// n0 = 65 - (64%65) = 1
|
||||
// n1 = 64
|
||||
// w0 = 0
|
||||
// y0 = 3
|
||||
// w1 = 1
|
||||
// y1 = 2
|
||||
EXPECT_EQ(0x0, fast64(urng1_5));
|
||||
EXPECT_EQ(65, urng1_5.calls);
|
||||
}
|
||||
|
||||
// Validate rejections for non-power-of-2 cases.
|
||||
{
|
||||
Urng1_5bit urng1_5(true);
|
||||
Urng31bits urng31(true);
|
||||
Urng33bits urng33(true);
|
||||
Urng63bits urng63(true);
|
||||
|
||||
// For 1.5 bits, there would be 1+2*64, except the first
|
||||
// value was accepted and shifted off the end.
|
||||
EXPECT_EQ(0, fast64(urng1_5));
|
||||
EXPECT_EQ(128, urng1_5.calls);
|
||||
EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
|
||||
EXPECT_EQ(6, urng31.calls);
|
||||
EXPECT_EQ(0x808194040cb01032, fast64(urng33));
|
||||
EXPECT_EQ(6, urng33.calls);
|
||||
EXPECT_EQ(0x1234567712345677, fast64(urng63));
|
||||
EXPECT_EQ(4, urng63.calls);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FastUniformBitsTest, URBG32bitRegression) {
|
||||
// Validate with deterministic 32-bit std::minstd_rand
|
||||
// to ensure that operator() performs as expected.
|
||||
|
||||
EXPECT_EQ(2147483646, RangeSize<std::minstd_rand>());
|
||||
EXPECT_EQ(30, IntegerLog2(RangeSize<std::minstd_rand>()));
|
||||
|
||||
std::minstd_rand gen(1);
|
||||
FastUniformBits<uint64_t> fast64;
|
||||
|
||||
EXPECT_EQ(0x05e47095f8791f45, fast64(gen));
|
||||
EXPECT_EQ(0x028be17e3c07c122, fast64(gen));
|
||||
EXPECT_EQ(0x55d2847c1626e8c2, fast64(gen));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_FASTMATH_H_
|
||||
#define ABSL_RANDOM_INTERNAL_FASTMATH_H_
|
||||
|
||||
// This file contains fast math functions (bitwise ops as well as some others)
|
||||
// which are implementation details of various absl random number distributions.
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/numeric/bits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// Compute log2(n) using integer operations.
|
||||
// While std::log2 is more accurate than std::log(n) / std::log(2), for
|
||||
// very large numbers--those close to std::numeric_limits<uint64_t>::max() - 2,
|
||||
// for instance--std::log2 rounds up rather than down, which introduces
|
||||
// definite skew in the results.
|
||||
inline int IntLog2Floor(uint64_t n) {
|
||||
return (n <= 1) ? 0 : (63 - countl_zero(n));
|
||||
}
|
||||
inline int IntLog2Ceil(uint64_t n) {
|
||||
return (n <= 1) ? 0 : (64 - countl_zero(n - 1));
|
||||
}
|
||||
|
||||
inline double StirlingLogFactorial(double n) {
|
||||
assert(n >= 1);
|
||||
// Using Stirling's approximation.
|
||||
constexpr double kLog2PI = 1.83787706640934548356;
|
||||
const double logn = std::log(n);
|
||||
const double ninv = 1.0 / static_cast<double>(n);
|
||||
return n * logn - n + 0.5 * (kLog2PI + logn) + (1.0 / 12.0) * ninv -
|
||||
(1.0 / 360.0) * ninv * ninv * ninv;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_FASTMATH_H_
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/fastmath.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#if defined(__native_client__) || defined(__EMSCRIPTEN__)
|
||||
// NACL has a less accurate implementation of std::log2 than most of
|
||||
// the other platforms. For some values which should have integral results,
|
||||
// sometimes NACL returns slightly larger values.
|
||||
//
|
||||
// The MUSL libc used by emscripten also has a similar bug.
|
||||
#define ABSL_RANDOM_INACCURATE_LOG2
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(FastMathTest, IntLog2FloorTest) {
|
||||
using absl::random_internal::IntLog2Floor;
|
||||
constexpr uint64_t kZero = 0;
|
||||
EXPECT_EQ(0, IntLog2Floor(0)); // boundary. return 0.
|
||||
EXPECT_EQ(0, IntLog2Floor(1));
|
||||
EXPECT_EQ(1, IntLog2Floor(2));
|
||||
EXPECT_EQ(63, IntLog2Floor(~kZero));
|
||||
|
||||
// A boundary case: Converting 0xffffffffffffffff requires > 53
|
||||
// bits of precision, so the conversion to double rounds up,
|
||||
// and the result of std::log2(x) > IntLog2Floor(x).
|
||||
EXPECT_LT(IntLog2Floor(~kZero), static_cast<int>(std::log2(~kZero)));
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i;
|
||||
EXPECT_EQ(i, IntLog2Floor(i_pow_2));
|
||||
EXPECT_EQ(i, static_cast<int>(std::log2(i_pow_2)));
|
||||
|
||||
uint64_t y = i_pow_2;
|
||||
for (int j = i - 1; j > 0; --j) {
|
||||
y = y | (i_pow_2 >> j);
|
||||
EXPECT_EQ(i, IntLog2Floor(y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FastMathTest, IntLog2CeilTest) {
|
||||
using absl::random_internal::IntLog2Ceil;
|
||||
constexpr uint64_t kZero = 0;
|
||||
EXPECT_EQ(0, IntLog2Ceil(0)); // boundary. return 0.
|
||||
EXPECT_EQ(0, IntLog2Ceil(1));
|
||||
EXPECT_EQ(1, IntLog2Ceil(2));
|
||||
EXPECT_EQ(64, IntLog2Ceil(~kZero));
|
||||
|
||||
// A boundary case: Converting 0xffffffffffffffff requires > 53
|
||||
// bits of precision, so the conversion to double rounds up,
|
||||
// and the result of std::log2(x) > IntLog2Floor(x).
|
||||
EXPECT_LE(IntLog2Ceil(~kZero), static_cast<int>(std::log2(~kZero)));
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i;
|
||||
EXPECT_EQ(i, IntLog2Ceil(i_pow_2));
|
||||
#ifndef ABSL_RANDOM_INACCURATE_LOG2
|
||||
EXPECT_EQ(i, static_cast<int>(std::ceil(std::log2(i_pow_2))));
|
||||
#endif
|
||||
|
||||
uint64_t y = i_pow_2;
|
||||
for (int j = i - 1; j > 0; --j) {
|
||||
y = y | (i_pow_2 >> j);
|
||||
EXPECT_EQ(i + 1, IntLog2Ceil(y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FastMathTest, StirlingLogFactorial) {
|
||||
using absl::random_internal::StirlingLogFactorial;
|
||||
|
||||
EXPECT_NEAR(StirlingLogFactorial(1.0), 0, 1e-3);
|
||||
EXPECT_NEAR(StirlingLogFactorial(1.50), 0.284683, 1e-3);
|
||||
EXPECT_NEAR(StirlingLogFactorial(2.0), 0.69314718056, 1e-4);
|
||||
|
||||
for (int i = 2; i < 50; i++) {
|
||||
double d = static_cast<double>(i);
|
||||
EXPECT_NEAR(StirlingLogFactorial(d), std::lgamma(d + 1), 3e-5);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generates gaussian_distribution.cc
|
||||
//
|
||||
// $ blaze run :gaussian_distribution_gentables > gaussian_distribution.cc
|
||||
//
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/random/gaussian_distribution.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
template <typename T, size_t N>
|
||||
void FormatArrayContents(std::ostream* os, T (&data)[N]) {
|
||||
if (!std::numeric_limits<T>::is_exact) {
|
||||
// Note: T is either an integer or a float.
|
||||
// float requires higher precision to ensure that values are
|
||||
// reproduced exactly.
|
||||
// Trivia: C99 has hexadecimal floating point literals, but C++11 does not.
|
||||
// Using them would remove all concern of precision loss.
|
||||
os->precision(std::numeric_limits<T>::max_digits10 + 2);
|
||||
}
|
||||
*os << " {";
|
||||
std::string separator = "";
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
*os << separator << data[i];
|
||||
if ((i + 1) % 3 != 0) {
|
||||
separator = ", ";
|
||||
} else {
|
||||
separator = ",\n ";
|
||||
}
|
||||
}
|
||||
*os << "}";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TableGenerator : public gaussian_distribution_base {
|
||||
public:
|
||||
TableGenerator();
|
||||
void Print(std::ostream* os);
|
||||
|
||||
using gaussian_distribution_base::kMask;
|
||||
using gaussian_distribution_base::kR;
|
||||
using gaussian_distribution_base::kV;
|
||||
|
||||
private:
|
||||
Tables tables_;
|
||||
};
|
||||
|
||||
// Ziggurat gaussian initialization. For an explanation of the algorithm, see
|
||||
// the Marsaglia paper, "The Ziggurat Method for Generating Random Variables".
|
||||
// http://www.jstatsoft.org/v05/i08/
|
||||
//
|
||||
// Further details are available in the Doornik paper
|
||||
// https://www.doornik.com/research/ziggurat.pdf
|
||||
//
|
||||
TableGenerator::TableGenerator() {
|
||||
// The constants here should match the values in gaussian_distribution.h
|
||||
static constexpr int kC = kMask + 1;
|
||||
|
||||
static_assert((ABSL_ARRAYSIZE(tables_.x) == kC + 1),
|
||||
"xArray must be length kMask + 2");
|
||||
|
||||
static_assert((ABSL_ARRAYSIZE(tables_.x) == ABSL_ARRAYSIZE(tables_.f)),
|
||||
"fx and x arrays must be identical length");
|
||||
|
||||
auto f = [](double x) { return std::exp(-0.5 * x * x); };
|
||||
auto f_inv = [](double x) { return std::sqrt(-2.0 * std::log(x)); };
|
||||
|
||||
tables_.x[0] = kV / f(kR);
|
||||
tables_.f[0] = f(tables_.x[0]);
|
||||
|
||||
tables_.x[1] = kR;
|
||||
tables_.f[1] = f(tables_.x[1]);
|
||||
|
||||
tables_.x[kC] = 0.0;
|
||||
tables_.f[kC] = f(tables_.x[kC]); // 1.0
|
||||
|
||||
for (int i = 2; i < kC; i++) {
|
||||
double v = (kV / tables_.x[i - 1]) + tables_.f[i - 1];
|
||||
tables_.x[i] = f_inv(v);
|
||||
tables_.f[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
void TableGenerator::Print(std::ostream* os) {
|
||||
*os << "// BEGIN GENERATED CODE; DO NOT EDIT\n"
|
||||
"// clang-format off\n"
|
||||
"\n"
|
||||
"#include \"absl/random/gaussian_distribution.h\"\n"
|
||||
"\n"
|
||||
"namespace absl {\n"
|
||||
"ABSL_NAMESPACE_BEGIN\n"
|
||||
"namespace random_internal {\n"
|
||||
"\n"
|
||||
"const gaussian_distribution_base::Tables\n"
|
||||
" gaussian_distribution_base::zg_ = {\n";
|
||||
FormatArrayContents(os, tables_.x);
|
||||
*os << ",\n";
|
||||
FormatArrayContents(os, tables_.f);
|
||||
*os << "};\n"
|
||||
"\n"
|
||||
"} // namespace random_internal\n"
|
||||
"ABSL_NAMESPACE_END\n"
|
||||
"} // namespace absl\n"
|
||||
"\n"
|
||||
"// clang-format on\n"
|
||||
"// END GENERATED CODE";
|
||||
*os << std::endl;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
int main(int, char**) {
|
||||
std::cerr << "\nCopy the output to gaussian_distribution.cc" << std::endl;
|
||||
absl::random_internal::TableGenerator generator;
|
||||
generator.Print(&std::cout);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
|
||||
#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
|
||||
|
||||
// This file contains some implementation details which are used by one or more
|
||||
// of the absl random number distributions.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/random/internal/fastmath.h"
|
||||
#include "absl/random/internal/traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// Tristate tag types controlling the output of GenerateRealFromBits.
|
||||
struct GeneratePositiveTag {};
|
||||
struct GenerateNegativeTag {};
|
||||
struct GenerateSignedTag {};
|
||||
|
||||
// GenerateRealFromBits generates a single real value from a single 64-bit
|
||||
// `bits` with template fields controlling the output.
|
||||
//
|
||||
// The `SignedTag` parameter controls whether positive, negative,
|
||||
// or either signed/unsigned may be returned.
|
||||
// When SignedTag == GeneratePositiveTag, range is U(0, 1)
|
||||
// When SignedTag == GenerateNegativeTag, range is U(-1, 0)
|
||||
// When SignedTag == GenerateSignedTag, range is U(-1, 1)
|
||||
//
|
||||
// When the `IncludeZero` parameter is true, the function may return 0 for some
|
||||
// inputs, otherwise it never returns 0.
|
||||
//
|
||||
// When a value in U(0,1) is required, use:
|
||||
// GenerateRealFromBits<double, PositiveValueT, true>;
|
||||
//
|
||||
// When a value in U(-1,1) is required, use:
|
||||
// GenerateRealFromBits<double, SignedValueT, false>;
|
||||
//
|
||||
// This generates more distinct values than the mathematical equivalent
|
||||
// `U(0, 1) * 2.0 - 1.0`.
|
||||
//
|
||||
// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
|
||||
// GenerateRealFromBits<double>(..., -1); => U(0, 0.5)
|
||||
// GenerateRealFromBits<double>(..., 1); => U(0, 2)
|
||||
//
|
||||
template <typename RealType, // Real type, either float or double.
|
||||
typename SignedTag = GeneratePositiveTag, // Whether a positive,
|
||||
// negative, or signed
|
||||
// value is generated.
|
||||
bool IncludeZero = true>
|
||||
inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) {
|
||||
using real_type = RealType;
|
||||
using uint_type = absl::conditional_t<std::is_same<real_type, float>::value,
|
||||
uint32_t, uint64_t>;
|
||||
|
||||
static_assert(
|
||||
(std::is_same<double, real_type>::value ||
|
||||
std::is_same<float, real_type>::value),
|
||||
"GenerateRealFromBits must be parameterized by either float or double.");
|
||||
|
||||
static_assert(sizeof(uint_type) == sizeof(real_type),
|
||||
"Mismatched unsigned and real types.");
|
||||
|
||||
static_assert((std::numeric_limits<real_type>::is_iec559 &&
|
||||
std::numeric_limits<real_type>::radix == 2),
|
||||
"RealType representation is not IEEE 754 binary.");
|
||||
|
||||
static_assert((std::is_same<SignedTag, GeneratePositiveTag>::value ||
|
||||
std::is_same<SignedTag, GenerateNegativeTag>::value ||
|
||||
std::is_same<SignedTag, GenerateSignedTag>::value),
|
||||
"");
|
||||
|
||||
static constexpr int kExp = std::numeric_limits<real_type>::digits - 1;
|
||||
static constexpr uint_type kMask = (static_cast<uint_type>(1) << kExp) - 1u;
|
||||
static constexpr int kUintBits = sizeof(uint_type) * 8;
|
||||
|
||||
int exp = exp_bias + int{std::numeric_limits<real_type>::max_exponent - 2};
|
||||
|
||||
// Determine the sign bit.
|
||||
// Depending on the SignedTag, this may use the left-most bit
|
||||
// or it may be a constant value.
|
||||
uint_type sign = std::is_same<SignedTag, GenerateNegativeTag>::value
|
||||
? (static_cast<uint_type>(1) << (kUintBits - 1))
|
||||
: 0;
|
||||
if (std::is_same<SignedTag, GenerateSignedTag>::value) {
|
||||
if (std::is_same<uint_type, uint64_t>::value) {
|
||||
sign = bits & uint64_t{0x8000000000000000};
|
||||
}
|
||||
if (std::is_same<uint_type, uint32_t>::value) {
|
||||
const uint64_t tmp = bits & uint64_t{0x8000000000000000};
|
||||
sign = static_cast<uint32_t>(tmp >> 32);
|
||||
}
|
||||
// adjust the bits and the exponent to account for removing
|
||||
// the leading bit.
|
||||
bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF};
|
||||
exp++;
|
||||
}
|
||||
if (IncludeZero) {
|
||||
if (bits == 0u) return 0;
|
||||
}
|
||||
|
||||
// Number of leading zeros is mapped to the exponent: 2^-clz
|
||||
// bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0
|
||||
int clz = countl_zero(bits);
|
||||
bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits.
|
||||
exp -= clz; // set the exponent.
|
||||
bits >>= (63 - kExp);
|
||||
|
||||
// Construct the 32-bit or 64-bit IEEE 754 floating-point value from
|
||||
// the individual fields: sign, exp, mantissa(bits).
|
||||
uint_type val = sign | (static_cast<uint_type>(exp) << kExp) |
|
||||
(static_cast<uint_type>(bits) & kMask);
|
||||
|
||||
// bit_cast to the output-type
|
||||
real_type result;
|
||||
memcpy(static_cast<void*>(&result), static_cast<const void*>(&val),
|
||||
sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/generate_real.h"
|
||||
|
||||
#include <cfloat>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
|
||||
ABSL_FLAG(int64_t, absl_random_test_trials, 50000,
|
||||
"Number of trials for the probability tests.");
|
||||
|
||||
using absl::random_internal::GenerateNegativeTag;
|
||||
using absl::random_internal::GeneratePositiveTag;
|
||||
using absl::random_internal::GenerateRealFromBits;
|
||||
using absl::random_internal::GenerateSignedTag;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Positive_NoZero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GeneratePositiveTag, false>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Positive_Zero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Negative_NoZero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GenerateNegativeTag, false>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Negative_Zero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GenerateNegativeTag, true>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Signed_NoZero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GenerateSignedTag, false>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), -5.421010862e-20f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Signed_Zero_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GenerateSignedTag, true>(a);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 0);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), 0);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloat_Signed_Bias_Test) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GenerateSignedTag, true>(a, 1);
|
||||
};
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 0);
|
||||
EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 2 * 0.9999999404f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), 0);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), 2 * -1.084202172e-19f);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToFloatTest) {
|
||||
auto ToFloat = [](uint64_t a) -> float {
|
||||
return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f);
|
||||
|
||||
EXPECT_EQ(ToFloat(0x8000000000000000), 0.5f);
|
||||
EXPECT_EQ(ToFloat(0x8000000000000001), 0.5f);
|
||||
EXPECT_EQ(ToFloat(0x800000FFFFFFFFFF), 0.5f);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
|
||||
|
||||
EXPECT_GT(ToFloat(0x0000000000000001), 0.0f);
|
||||
|
||||
EXPECT_NE(ToFloat(0x7FFFFF0000000000), ToFloat(0x7FFFFEFFFFFFFFFF));
|
||||
|
||||
EXPECT_LT(ToFloat(0xFFFFFFFFFFFFFFFF), 1.0f);
|
||||
int32_t two_to_24 = 1 << 24;
|
||||
EXPECT_EQ(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24),
|
||||
two_to_24 - 1);
|
||||
EXPECT_NE(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24 * 2),
|
||||
two_to_24 * 2 - 1);
|
||||
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFF0000000000));
|
||||
EXPECT_NE(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFEFFFFFFFFFF));
|
||||
EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF8000000000));
|
||||
EXPECT_NE(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF7FFFFFFFFF));
|
||||
EXPECT_EQ(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFC000000000));
|
||||
EXPECT_NE(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFBFFFFFFFFF));
|
||||
|
||||
// For values where every bit counts, the values scale as multiples of the
|
||||
// input.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
EXPECT_EQ(i * ToFloat(0x0000000000000001), ToFloat(i));
|
||||
}
|
||||
|
||||
// For each i: value generated from (1 << i).
|
||||
float exp_values[64];
|
||||
exp_values[63] = 0.5f;
|
||||
for (int i = 62; i >= 0; --i) exp_values[i] = 0.5f * exp_values[i + 1];
|
||||
constexpr uint64_t one = 1;
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
EXPECT_EQ(ToFloat(one << i), exp_values[i]);
|
||||
for (int j = 1; j < FLT_MANT_DIG && i - j >= 0; ++j) {
|
||||
EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]);
|
||||
EXPECT_EQ(ToFloat((one << i) + (one << (i - j))),
|
||||
exp_values[i] + exp_values[i - j]);
|
||||
}
|
||||
for (int j = FLT_MANT_DIG; i - j >= 0; ++j) {
|
||||
EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]);
|
||||
EXPECT_EQ(ToFloat((one << i) + (one << (i - j))), exp_values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Positive_NoZero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GeneratePositiveTag, false>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Positive_Zero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Negative_NoZero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateNegativeTag, false>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Negative_Zero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateNegativeTag, true>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Signed_NoZero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_Signed_Zero_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateSignedTag, true>(a);
|
||||
};
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), 0);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDouble_GenerateSignedTag_Bias_Test) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateSignedTag, true>(a, -1);
|
||||
};
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2);
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978 / 2);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), 0);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19 / 2);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2);
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDoubleTest) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x7fffffffffffffef), 0.499999999999999944489);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
|
||||
|
||||
// For values > 0.5, RandU64ToDouble discards up to 11 bits. (64-53).
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x80000000000007FF), 0.5);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
|
||||
EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF));
|
||||
|
||||
EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFF), 1.0);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF800));
|
||||
EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF7FF));
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00));
|
||||
EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFBFF));
|
||||
EXPECT_EQ(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFE00));
|
||||
EXPECT_NE(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFDFF));
|
||||
|
||||
EXPECT_EQ(ToDouble(0x1000000000000001), 0.0625);
|
||||
EXPECT_EQ(ToDouble(0x2000000000000001), 0.125);
|
||||
EXPECT_EQ(ToDouble(0x3000000000000001), 0.1875);
|
||||
EXPECT_EQ(ToDouble(0x4000000000000001), 0.25);
|
||||
EXPECT_EQ(ToDouble(0x5000000000000001), 0.3125);
|
||||
EXPECT_EQ(ToDouble(0x6000000000000001), 0.375);
|
||||
EXPECT_EQ(ToDouble(0x7000000000000001), 0.4375);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x9000000000000001), 0.5625);
|
||||
EXPECT_EQ(ToDouble(0xa000000000000001), 0.625);
|
||||
EXPECT_EQ(ToDouble(0xb000000000000001), 0.6875);
|
||||
EXPECT_EQ(ToDouble(0xc000000000000001), 0.75);
|
||||
EXPECT_EQ(ToDouble(0xd000000000000001), 0.8125);
|
||||
EXPECT_EQ(ToDouble(0xe000000000000001), 0.875);
|
||||
EXPECT_EQ(ToDouble(0xf000000000000001), 0.9375);
|
||||
|
||||
// Large powers of 2.
|
||||
int64_t two_to_53 = int64_t{1} << 53;
|
||||
EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53),
|
||||
two_to_53 - 1);
|
||||
EXPECT_NE(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53 * 2),
|
||||
two_to_53 * 2 - 1);
|
||||
|
||||
// For values where every bit counts, the values scale as multiples of the
|
||||
// input.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
EXPECT_EQ(i * ToDouble(0x0000000000000001), ToDouble(i));
|
||||
}
|
||||
|
||||
// For each i: value generated from (1 << i).
|
||||
double exp_values[64];
|
||||
exp_values[63] = 0.5;
|
||||
for (int i = 62; i >= 0; --i) exp_values[i] = 0.5 * exp_values[i + 1];
|
||||
constexpr uint64_t one = 1;
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
EXPECT_EQ(ToDouble(one << i), exp_values[i]);
|
||||
for (int j = 1; j < DBL_MANT_DIG && i - j >= 0; ++j) {
|
||||
EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]);
|
||||
EXPECT_EQ(ToDouble((one << i) + (one << (i - j))),
|
||||
exp_values[i] + exp_values[i - j]);
|
||||
}
|
||||
for (int j = DBL_MANT_DIG; i - j >= 0; ++j) {
|
||||
EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]);
|
||||
EXPECT_EQ(ToDouble((one << i) + (one << (i - j))), exp_values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, U64ToDoubleSignedTest) {
|
||||
auto ToDouble = [](uint64_t a) {
|
||||
return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
|
||||
};
|
||||
|
||||
EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
|
||||
|
||||
EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20);
|
||||
EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19);
|
||||
|
||||
const double e_plus = ToDouble(0x0000000000000001);
|
||||
const double e_minus = ToDouble(0x8000000000000001);
|
||||
EXPECT_EQ(e_plus, 1.084202172485504434e-19);
|
||||
EXPECT_EQ(e_minus, -1.084202172485504434e-19);
|
||||
|
||||
EXPECT_EQ(ToDouble(0x3fffffffffffffef), 0.499999999999999944489);
|
||||
EXPECT_EQ(ToDouble(0xbfffffffffffffef), -0.499999999999999944489);
|
||||
|
||||
// For values > 0.5, RandU64ToDouble discards up to 10 bits. (63-53).
|
||||
EXPECT_EQ(ToDouble(0x4000000000000000), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x4000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x40000000000003FF), 0.5);
|
||||
|
||||
EXPECT_EQ(ToDouble(0xC000000000000000), -0.5);
|
||||
EXPECT_EQ(ToDouble(0xC000000000000001), -0.5);
|
||||
EXPECT_EQ(ToDouble(0xC0000000000003FF), -0.5);
|
||||
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFe), 0.999999999999999888978);
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999999999888978);
|
||||
|
||||
EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF));
|
||||
|
||||
EXPECT_LT(ToDouble(0x7FFFFFFFFFFFFFFF), 1.0);
|
||||
EXPECT_GT(ToDouble(0x7FFFFFFFFFFFFFFF), 0.9999999999);
|
||||
|
||||
EXPECT_GT(ToDouble(0xFFFFFFFFFFFFFFFe), -1.0);
|
||||
EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999);
|
||||
|
||||
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFFC00));
|
||||
EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00));
|
||||
EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFF3FF));
|
||||
EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFF3FF));
|
||||
|
||||
EXPECT_EQ(ToDouble(0x1000000000000001), 0.125);
|
||||
EXPECT_EQ(ToDouble(0x2000000000000001), 0.25);
|
||||
EXPECT_EQ(ToDouble(0x3000000000000001), 0.375);
|
||||
EXPECT_EQ(ToDouble(0x4000000000000001), 0.5);
|
||||
EXPECT_EQ(ToDouble(0x5000000000000001), 0.625);
|
||||
EXPECT_EQ(ToDouble(0x6000000000000001), 0.75);
|
||||
EXPECT_EQ(ToDouble(0x7000000000000001), 0.875);
|
||||
EXPECT_EQ(ToDouble(0x7800000000000001), 0.9375);
|
||||
EXPECT_EQ(ToDouble(0x7c00000000000001), 0.96875);
|
||||
EXPECT_EQ(ToDouble(0x7e00000000000001), 0.984375);
|
||||
EXPECT_EQ(ToDouble(0x7f00000000000001), 0.9921875);
|
||||
|
||||
// 0x8000000000000000 ~= 0
|
||||
EXPECT_EQ(ToDouble(0x9000000000000001), -0.125);
|
||||
EXPECT_EQ(ToDouble(0xa000000000000001), -0.25);
|
||||
EXPECT_EQ(ToDouble(0xb000000000000001), -0.375);
|
||||
EXPECT_EQ(ToDouble(0xc000000000000001), -0.5);
|
||||
EXPECT_EQ(ToDouble(0xd000000000000001), -0.625);
|
||||
EXPECT_EQ(ToDouble(0xe000000000000001), -0.75);
|
||||
EXPECT_EQ(ToDouble(0xf000000000000001), -0.875);
|
||||
|
||||
// Large powers of 2.
|
||||
int64_t two_to_53 = int64_t{1} << 53;
|
||||
EXPECT_EQ(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53),
|
||||
two_to_53 - 1);
|
||||
EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53),
|
||||
-(two_to_53 - 1));
|
||||
|
||||
EXPECT_NE(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53 * 2),
|
||||
two_to_53 * 2 - 1);
|
||||
|
||||
// For values where every bit counts, the values scale as multiples of the
|
||||
// input.
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
EXPECT_EQ(i * e_plus, ToDouble(i)) << i;
|
||||
EXPECT_EQ(i * e_minus, ToDouble(0x8000000000000000 | i)) << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GenerateRealTest, ExhaustiveFloat) {
|
||||
auto ToFloat = [](uint64_t a) {
|
||||
return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
|
||||
};
|
||||
|
||||
// Rely on RandU64ToFloat generating values from greatest to least when
|
||||
// supplied with uint64_t values from greatest (0xfff...) to least (0x0).
|
||||
// Thus, this algorithm stores the previous value, and if the new value is at
|
||||
// greater than or equal to the previous value, then there is a collision in
|
||||
// the generation algorithm.
|
||||
//
|
||||
// Use the computation below to convert the random value into a result:
|
||||
// double res = a() * (1.0f - sample) + b() * sample;
|
||||
float last_f = 1.0, last_g = 2.0;
|
||||
uint64_t f_collisions = 0, g_collisions = 0;
|
||||
uint64_t f_unique = 0, g_unique = 0;
|
||||
uint64_t total = 0;
|
||||
auto count = [&](const float r) {
|
||||
total++;
|
||||
// `f` is mapped to the range [0, 1) (default)
|
||||
const float f = 0.0f * (1.0f - r) + 1.0f * r;
|
||||
if (f >= last_f) {
|
||||
f_collisions++;
|
||||
} else {
|
||||
f_unique++;
|
||||
last_f = f;
|
||||
}
|
||||
// `g` is mapped to the range [1, 2)
|
||||
const float g = 1.0f * (1.0f - r) + 2.0f * r;
|
||||
if (g >= last_g) {
|
||||
g_collisions++;
|
||||
} else {
|
||||
g_unique++;
|
||||
last_g = g;
|
||||
}
|
||||
};
|
||||
|
||||
size_t limit = absl::GetFlag(FLAGS_absl_random_test_trials);
|
||||
|
||||
// Generate all uint64_t which have unique floating point values.
|
||||
// Counting down from 0xFFFFFFFFFFFFFFFFu ... 0x0u
|
||||
uint64_t x = ~uint64_t(0);
|
||||
for (; x != 0 && limit > 0;) {
|
||||
constexpr int kDig = (64 - FLT_MANT_DIG);
|
||||
// Set a decrement value & the next point at which to change
|
||||
// the decrement value. By default these are 1, 0.
|
||||
uint64_t dec = 1;
|
||||
uint64_t chk = 0;
|
||||
|
||||
// Adjust decrement and check value based on how many leading 0
|
||||
// bits are set in the current value.
|
||||
const int clz = absl::countl_zero(x);
|
||||
if (clz < kDig) {
|
||||
dec <<= (kDig - clz);
|
||||
chk = (~uint64_t(0)) >> (clz + 1);
|
||||
}
|
||||
for (; x > chk && limit > 0; x -= dec) {
|
||||
count(ToFloat(x));
|
||||
--limit;
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(FLT_MANT_DIG == 24,
|
||||
"The float type is expected to have a 24 bit mantissa.");
|
||||
|
||||
if (limit != 0) {
|
||||
// There are between 2^28 and 2^29 unique values in the range [0, 1). For
|
||||
// the low values of x, there are 2^24 -1 unique values. Once x > 2^24,
|
||||
// there are 40 * 2^24 unique values. Thus:
|
||||
// (2 + 4 + 8 ... + 2^23) + 40 * 2^23
|
||||
EXPECT_LT(1 << 28, f_unique);
|
||||
EXPECT_EQ((1 << 24) + 40 * (1 << 23) - 1, f_unique);
|
||||
EXPECT_EQ(total, f_unique);
|
||||
EXPECT_EQ(0, f_collisions);
|
||||
|
||||
// Expect at least 2^23 unique values for the range [1, 2)
|
||||
EXPECT_LE(1 << 23, g_unique);
|
||||
EXPECT_EQ(total - g_unique, g_collisions);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
|
||||
#define ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <ios>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// The null_state_saver does nothing.
|
||||
template <typename T>
|
||||
class null_state_saver {
|
||||
public:
|
||||
using stream_type = T;
|
||||
using flags_type = std::ios_base::fmtflags;
|
||||
|
||||
null_state_saver(T&, flags_type) {}
|
||||
~null_state_saver() {}
|
||||
};
|
||||
|
||||
// ostream_state_saver is a RAII object to save and restore the common
|
||||
// basic_ostream flags used when implementing `operator <<()` on any of
|
||||
// the absl random distributions.
|
||||
template <typename OStream>
|
||||
class ostream_state_saver {
|
||||
public:
|
||||
using ostream_type = OStream;
|
||||
using flags_type = std::ios_base::fmtflags;
|
||||
using fill_type = typename ostream_type::char_type;
|
||||
using precision_type = std::streamsize;
|
||||
|
||||
ostream_state_saver(ostream_type& os, // NOLINT(runtime/references)
|
||||
flags_type flags, fill_type fill)
|
||||
: os_(os),
|
||||
flags_(os.flags(flags)),
|
||||
fill_(os.fill(fill)),
|
||||
precision_(os.precision()) {
|
||||
// Save state in initialized variables.
|
||||
}
|
||||
|
||||
~ostream_state_saver() {
|
||||
// Restore saved state.
|
||||
os_.precision(precision_);
|
||||
os_.fill(fill_);
|
||||
os_.flags(flags_);
|
||||
}
|
||||
|
||||
private:
|
||||
ostream_type& os_;
|
||||
const flags_type flags_;
|
||||
const fill_type fill_;
|
||||
const precision_type precision_;
|
||||
};
|
||||
|
||||
#if defined(__NDK_MAJOR__) && __NDK_MAJOR__ < 16
|
||||
#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 1
|
||||
#else
|
||||
#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 0
|
||||
#endif
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
ostream_state_saver<std::basic_ostream<CharT, Traits>> make_ostream_state_saver(
|
||||
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
|
||||
std::ios_base::fmtflags flags = std::ios_base::dec | std::ios_base::left |
|
||||
#if ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT
|
||||
std::ios_base::fixed |
|
||||
#endif
|
||||
std::ios_base::scientific) {
|
||||
using result_type = ostream_state_saver<std::basic_ostream<CharT, Traits>>;
|
||||
return result_type(os, flags, os.widen(' '));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value,
|
||||
null_state_saver<T>>
|
||||
make_ostream_state_saver(T& is, // NOLINT(runtime/references)
|
||||
std::ios_base::fmtflags flags = std::ios_base::dec) {
|
||||
using result_type = null_state_saver<T>;
|
||||
return result_type(is, flags);
|
||||
}
|
||||
|
||||
// stream_precision_helper<type>::kPrecision returns the base 10 precision
|
||||
// required to stream and reconstruct a real type exact binary value through
|
||||
// a binary->decimal->binary transition.
|
||||
template <typename T>
|
||||
struct stream_precision_helper {
|
||||
// max_digits10 may be 0 on MSVC; if so, use digits10 + 3.
|
||||
static constexpr int kPrecision =
|
||||
(std::numeric_limits<T>::max_digits10 > std::numeric_limits<T>::digits10)
|
||||
? std::numeric_limits<T>::max_digits10
|
||||
: (std::numeric_limits<T>::digits10 + 3);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct stream_precision_helper<float> {
|
||||
static constexpr int kPrecision = 9;
|
||||
};
|
||||
template <>
|
||||
struct stream_precision_helper<double> {
|
||||
static constexpr int kPrecision = 17;
|
||||
};
|
||||
template <>
|
||||
struct stream_precision_helper<long double> {
|
||||
static constexpr int kPrecision = 36; // assuming fp128
|
||||
};
|
||||
|
||||
// istream_state_saver is a RAII object to save and restore the common
|
||||
// std::basic_istream<> flags used when implementing `operator >>()` on any of
|
||||
// the absl random distributions.
|
||||
template <typename IStream>
|
||||
class istream_state_saver {
|
||||
public:
|
||||
using istream_type = IStream;
|
||||
using flags_type = std::ios_base::fmtflags;
|
||||
|
||||
istream_state_saver(istream_type& is, // NOLINT(runtime/references)
|
||||
flags_type flags)
|
||||
: is_(is), flags_(is.flags(flags)) {}
|
||||
|
||||
~istream_state_saver() { is_.flags(flags_); }
|
||||
|
||||
private:
|
||||
istream_type& is_;
|
||||
flags_type flags_;
|
||||
};
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
istream_state_saver<std::basic_istream<CharT, Traits>> make_istream_state_saver(
|
||||
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
|
||||
std::ios_base::fmtflags flags = std::ios_base::dec |
|
||||
std::ios_base::scientific |
|
||||
std::ios_base::skipws) {
|
||||
using result_type = istream_state_saver<std::basic_istream<CharT, Traits>>;
|
||||
return result_type(is, flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value,
|
||||
null_state_saver<T>>
|
||||
make_istream_state_saver(T& is, // NOLINT(runtime/references)
|
||||
std::ios_base::fmtflags flags = std::ios_base::dec) {
|
||||
using result_type = null_state_saver<T>;
|
||||
return result_type(is, flags);
|
||||
}
|
||||
|
||||
// stream_format_type<T> is a helper struct to convert types which
|
||||
// basic_iostream cannot output as decimal numbers into types which
|
||||
// basic_iostream can output as decimal numbers. Specifically:
|
||||
// * signed/unsigned char-width types are converted to int.
|
||||
// * TODO(lar): __int128 => uint128, except there is no operator << yet.
|
||||
//
|
||||
template <typename T>
|
||||
struct stream_format_type
|
||||
: public std::conditional<(sizeof(T) == sizeof(char)), int, T> {};
|
||||
|
||||
// stream_u128_helper allows us to write out either absl::uint128 or
|
||||
// __uint128_t types in the same way, which enables their use as internal
|
||||
// state of PRNG engines.
|
||||
template <typename T>
|
||||
struct stream_u128_helper;
|
||||
|
||||
template <>
|
||||
struct stream_u128_helper<absl::uint128> {
|
||||
template <typename IStream>
|
||||
inline absl::uint128 read(IStream& in) {
|
||||
uint64_t h = 0;
|
||||
uint64_t l = 0;
|
||||
in >> h >> l;
|
||||
return absl::MakeUint128(h, l);
|
||||
}
|
||||
|
||||
template <typename OStream>
|
||||
inline void write(absl::uint128 val, OStream& out) {
|
||||
uint64_t h = absl::Uint128High64(val);
|
||||
uint64_t l = absl::Uint128Low64(val);
|
||||
out << h << out.fill() << l;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef ABSL_HAVE_INTRINSIC_INT128
|
||||
template <>
|
||||
struct stream_u128_helper<__uint128_t> {
|
||||
template <typename IStream>
|
||||
inline __uint128_t read(IStream& in) {
|
||||
uint64_t h = 0;
|
||||
uint64_t l = 0;
|
||||
in >> h >> l;
|
||||
return (static_cast<__uint128_t>(h) << 64) | l;
|
||||
}
|
||||
|
||||
template <typename OStream>
|
||||
inline void write(__uint128_t val, OStream& out) {
|
||||
uint64_t h = static_cast<uint64_t>(val >> 64u);
|
||||
uint64_t l = static_cast<uint64_t>(val);
|
||||
out << h << out.fill() << l;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename FloatType, typename IStream>
|
||||
inline FloatType read_floating_point(IStream& is) {
|
||||
static_assert(std::is_floating_point<FloatType>::value, "");
|
||||
FloatType dest;
|
||||
is >> dest;
|
||||
// Parsing a double value may report a subnormal value as an error
|
||||
// despite being able to represent it.
|
||||
// See https://stackoverflow.com/q/52410931/3286653
|
||||
// It may also report an underflow when parsing DOUBLE_MIN as an
|
||||
// ERANGE error, as the parsed value may be smaller than DOUBLE_MIN
|
||||
// and rounded up.
|
||||
// See: https://stackoverflow.com/q/42005462
|
||||
if (is.fail() &&
|
||||
(std::fabs(dest) == (std::numeric_limits<FloatType>::min)() ||
|
||||
std::fpclassify(dest) == FP_SUBNORMAL)) {
|
||||
is.clear(is.rdstate() & (~std::ios_base::failbit));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/iostream_state_saver.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::make_istream_state_saver;
|
||||
using absl::random_internal::make_ostream_state_saver;
|
||||
using absl::random_internal::stream_precision_helper;
|
||||
|
||||
template <typename T>
|
||||
typename absl::enable_if_t<std::is_integral<T>::value, T> //
|
||||
StreamRoundTrip(T t) {
|
||||
std::stringstream ss;
|
||||
{
|
||||
auto saver = make_ostream_state_saver(ss);
|
||||
ss.precision(stream_precision_helper<T>::kPrecision);
|
||||
ss << t;
|
||||
}
|
||||
T result = 0;
|
||||
{
|
||||
auto saver = make_istream_state_saver(ss);
|
||||
ss >> result;
|
||||
}
|
||||
EXPECT_FALSE(ss.fail()) //
|
||||
<< ss.str() << " " //
|
||||
<< (ss.good() ? "good " : "") //
|
||||
<< (ss.bad() ? "bad " : "") //
|
||||
<< (ss.eof() ? "eof " : "") //
|
||||
<< (ss.fail() ? "fail " : "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename absl::enable_if_t<std::is_floating_point<T>::value, T> //
|
||||
StreamRoundTrip(T t) {
|
||||
std::stringstream ss;
|
||||
{
|
||||
auto saver = make_ostream_state_saver(ss);
|
||||
ss.precision(stream_precision_helper<T>::kPrecision);
|
||||
ss << t;
|
||||
}
|
||||
T result = 0;
|
||||
{
|
||||
auto saver = make_istream_state_saver(ss);
|
||||
result = absl::random_internal::read_floating_point<T>(ss);
|
||||
}
|
||||
EXPECT_FALSE(ss.fail()) //
|
||||
<< ss.str() << " " //
|
||||
<< (ss.good() ? "good " : "") //
|
||||
<< (ss.bad() ? "bad " : "") //
|
||||
<< (ss.eof() ? "eof " : "") //
|
||||
<< (ss.fail() ? "fail " : "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(IOStreamStateSaver, BasicSaverState) {
|
||||
std::stringstream ss;
|
||||
ss.precision(2);
|
||||
ss.fill('x');
|
||||
ss.flags(std::ios_base::dec | std::ios_base::right);
|
||||
|
||||
{
|
||||
auto saver = make_ostream_state_saver(ss);
|
||||
ss.precision(10);
|
||||
EXPECT_NE('x', ss.fill());
|
||||
EXPECT_EQ(10, ss.precision());
|
||||
EXPECT_NE(std::ios_base::dec | std::ios_base::right, ss.flags());
|
||||
|
||||
ss << 1.23;
|
||||
}
|
||||
|
||||
EXPECT_EQ('x', ss.fill());
|
||||
EXPECT_EQ(2, ss.precision());
|
||||
EXPECT_EQ(std::ios_base::dec | std::ios_base::right, ss.flags());
|
||||
}
|
||||
|
||||
TEST(IOStreamStateSaver, RoundTripInts) {
|
||||
const uint64_t kUintValues[] = {
|
||||
0,
|
||||
1,
|
||||
static_cast<uint64_t>(-1),
|
||||
2,
|
||||
static_cast<uint64_t>(-2),
|
||||
|
||||
1 << 7,
|
||||
1 << 8,
|
||||
1 << 16,
|
||||
1ull << 32,
|
||||
1ull << 50,
|
||||
1ull << 62,
|
||||
1ull << 63,
|
||||
|
||||
(1 << 7) - 1,
|
||||
(1 << 8) - 1,
|
||||
(1 << 16) - 1,
|
||||
(1ull << 32) - 1,
|
||||
(1ull << 50) - 1,
|
||||
(1ull << 62) - 1,
|
||||
(1ull << 63) - 1,
|
||||
|
||||
static_cast<uint64_t>(-(1 << 8)),
|
||||
static_cast<uint64_t>(-(1 << 16)),
|
||||
static_cast<uint64_t>(-(1ll << 32)),
|
||||
static_cast<uint64_t>(-(1ll << 50)),
|
||||
static_cast<uint64_t>(-(1ll << 62)),
|
||||
|
||||
static_cast<uint64_t>(-(1 << 8) - 1),
|
||||
static_cast<uint64_t>(-(1 << 16) - 1),
|
||||
static_cast<uint64_t>(-(1ll << 32) - 1),
|
||||
static_cast<uint64_t>(-(1ll << 50) - 1),
|
||||
static_cast<uint64_t>(-(1ll << 62) - 1),
|
||||
};
|
||||
|
||||
for (const uint64_t u : kUintValues) {
|
||||
EXPECT_EQ(u, StreamRoundTrip<uint64_t>(u));
|
||||
|
||||
int64_t x = static_cast<int64_t>(u);
|
||||
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
|
||||
|
||||
double d = static_cast<double>(x);
|
||||
EXPECT_EQ(d, StreamRoundTrip<double>(d));
|
||||
|
||||
float f = d;
|
||||
EXPECT_EQ(f, StreamRoundTrip<float>(f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IOStreamStateSaver, RoundTripFloats) {
|
||||
static_assert(
|
||||
stream_precision_helper<float>::kPrecision >= 9,
|
||||
"stream_precision_helper<float>::kPrecision should be at least 9");
|
||||
|
||||
const float kValues[] = {
|
||||
1,
|
||||
std::nextafter(1.0f, 0.0f), // 1 - epsilon
|
||||
std::nextafter(1.0f, 2.0f), // 1 + epsilon
|
||||
|
||||
1.0e+1f,
|
||||
1.0e-1f,
|
||||
1.0e+2f,
|
||||
1.0e-2f,
|
||||
1.0e+10f,
|
||||
1.0e-10f,
|
||||
|
||||
0.00000051110000111311111111f,
|
||||
-0.00000051110000111211111111f,
|
||||
|
||||
1.234678912345678912345e+6f,
|
||||
1.234678912345678912345e-6f,
|
||||
1.234678912345678912345e+30f,
|
||||
1.234678912345678912345e-30f,
|
||||
1.234678912345678912345e+38f,
|
||||
1.0234678912345678912345e-38f,
|
||||
|
||||
// Boundary cases.
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::lowest(),
|
||||
std::numeric_limits<float>::epsilon(),
|
||||
std::nextafter(std::numeric_limits<float>::min(),
|
||||
1.0f), // min + epsilon
|
||||
std::numeric_limits<float>::min(), // smallest normal
|
||||
// There are some errors dealing with denorms on apple platforms.
|
||||
std::numeric_limits<float>::denorm_min(), // smallest denorm
|
||||
std::numeric_limits<float>::min() / 2,
|
||||
std::nextafter(std::numeric_limits<float>::min(),
|
||||
0.0f), // denorm_max
|
||||
std::nextafter(std::numeric_limits<float>::denorm_min(), 1.0f),
|
||||
};
|
||||
|
||||
for (const float f : kValues) {
|
||||
EXPECT_EQ(f, StreamRoundTrip<float>(f));
|
||||
EXPECT_EQ(-f, StreamRoundTrip<float>(-f));
|
||||
|
||||
double d = f;
|
||||
EXPECT_EQ(d, StreamRoundTrip<double>(d));
|
||||
EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
|
||||
|
||||
// Avoid undefined behavior (overflow/underflow).
|
||||
if (f <= static_cast<float>(std::numeric_limits<int64_t>::max()) &&
|
||||
f >= static_cast<float>(std::numeric_limits<int64_t>::lowest())) {
|
||||
int64_t x = static_cast<int64_t>(f);
|
||||
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IOStreamStateSaver, RoundTripDoubles) {
|
||||
static_assert(
|
||||
stream_precision_helper<double>::kPrecision >= 17,
|
||||
"stream_precision_helper<double>::kPrecision should be at least 17");
|
||||
|
||||
const double kValues[] = {
|
||||
1,
|
||||
std::nextafter(1.0, 0.0), // 1 - epsilon
|
||||
std::nextafter(1.0, 2.0), // 1 + epsilon
|
||||
|
||||
1.0e+1,
|
||||
1.0e-1,
|
||||
1.0e+2,
|
||||
1.0e-2,
|
||||
1.0e+10,
|
||||
1.0e-10,
|
||||
|
||||
0.00000051110000111311111111,
|
||||
-0.00000051110000111211111111,
|
||||
|
||||
1.234678912345678912345e+6,
|
||||
1.234678912345678912345e-6,
|
||||
1.234678912345678912345e+30,
|
||||
1.234678912345678912345e-30,
|
||||
1.234678912345678912345e+38,
|
||||
1.0234678912345678912345e-38,
|
||||
|
||||
1.0e+100,
|
||||
1.0e-100,
|
||||
1.234678912345678912345e+308,
|
||||
1.0234678912345678912345e-308,
|
||||
2.22507385850720138e-308,
|
||||
|
||||
// Boundary cases.
|
||||
std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::lowest(),
|
||||
std::numeric_limits<double>::epsilon(),
|
||||
std::nextafter(std::numeric_limits<double>::min(),
|
||||
1.0), // min + epsilon
|
||||
std::numeric_limits<double>::min(), // smallest normal
|
||||
// There are some errors dealing with denorms on apple platforms.
|
||||
std::numeric_limits<double>::denorm_min(), // smallest denorm
|
||||
std::numeric_limits<double>::min() / 2,
|
||||
std::nextafter(std::numeric_limits<double>::min(),
|
||||
0.0), // denorm_max
|
||||
std::nextafter(std::numeric_limits<double>::denorm_min(), 1.0f),
|
||||
};
|
||||
|
||||
for (const double d : kValues) {
|
||||
EXPECT_EQ(d, StreamRoundTrip<double>(d));
|
||||
EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
|
||||
|
||||
// Avoid undefined behavior (overflow/underflow).
|
||||
if (d <= std::numeric_limits<float>::max() &&
|
||||
d >= std::numeric_limits<float>::lowest()) {
|
||||
float f = static_cast<float>(d);
|
||||
EXPECT_EQ(f, StreamRoundTrip<float>(f));
|
||||
}
|
||||
|
||||
// Avoid undefined behavior (overflow/underflow).
|
||||
if (d <= static_cast<double>(std::numeric_limits<int64_t>::max()) &&
|
||||
d >= static_cast<double>(std::numeric_limits<int64_t>::lowest())) {
|
||||
int64_t x = static_cast<int64_t>(d);
|
||||
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IOStreamStateSaver, RoundTripLongDoubles) {
|
||||
// Technically, C++ only guarantees that long double is at least as large as a
|
||||
// double. Practically it varies from 64-bits to 128-bits.
|
||||
//
|
||||
// So it is best to consider long double a best-effort extended precision
|
||||
// type.
|
||||
|
||||
static_assert(
|
||||
stream_precision_helper<long double>::kPrecision >= 36,
|
||||
"stream_precision_helper<long double>::kPrecision should be at least 36");
|
||||
|
||||
using real_type = long double;
|
||||
const real_type kValues[] = {
|
||||
1,
|
||||
std::nextafter(1.0, 0.0), // 1 - epsilon
|
||||
std::nextafter(1.0, 2.0), // 1 + epsilon
|
||||
|
||||
1.0e+1,
|
||||
1.0e-1,
|
||||
1.0e+2,
|
||||
1.0e-2,
|
||||
1.0e+10,
|
||||
1.0e-10,
|
||||
|
||||
0.00000051110000111311111111,
|
||||
-0.00000051110000111211111111,
|
||||
|
||||
1.2346789123456789123456789123456789e+6,
|
||||
1.2346789123456789123456789123456789e-6,
|
||||
1.2346789123456789123456789123456789e+30,
|
||||
1.2346789123456789123456789123456789e-30,
|
||||
1.2346789123456789123456789123456789e+38,
|
||||
1.2346789123456789123456789123456789e-38,
|
||||
1.2346789123456789123456789123456789e+308,
|
||||
1.2346789123456789123456789123456789e-308,
|
||||
|
||||
1.0e+100,
|
||||
1.0e-100,
|
||||
1.234678912345678912345e+308,
|
||||
1.0234678912345678912345e-308,
|
||||
|
||||
// Boundary cases.
|
||||
std::numeric_limits<real_type>::max(),
|
||||
std::numeric_limits<real_type>::lowest(),
|
||||
std::numeric_limits<real_type>::epsilon(),
|
||||
std::nextafter(std::numeric_limits<real_type>::min(),
|
||||
real_type(1)), // min + epsilon
|
||||
std::numeric_limits<real_type>::min(), // smallest normal
|
||||
// There are some errors dealing with denorms on apple platforms.
|
||||
std::numeric_limits<real_type>::denorm_min(), // smallest denorm
|
||||
std::numeric_limits<real_type>::min() / 2,
|
||||
std::nextafter(std::numeric_limits<real_type>::min(),
|
||||
0.0), // denorm_max
|
||||
std::nextafter(std::numeric_limits<real_type>::denorm_min(), 1.0f),
|
||||
};
|
||||
|
||||
int index = -1;
|
||||
for (const long double dd : kValues) {
|
||||
index++;
|
||||
EXPECT_EQ(dd, StreamRoundTrip<real_type>(dd)) << index;
|
||||
EXPECT_EQ(-dd, StreamRoundTrip<real_type>(-dd)) << index;
|
||||
|
||||
// Avoid undefined behavior (overflow/underflow).
|
||||
if (dd <= std::numeric_limits<double>::max() &&
|
||||
dd >= std::numeric_limits<double>::lowest()) {
|
||||
double d = static_cast<double>(dd);
|
||||
EXPECT_EQ(d, StreamRoundTrip<double>(d));
|
||||
}
|
||||
|
||||
// Avoid undefined behavior (overflow/underflow).
|
||||
if (dd <= static_cast<long double>(std::numeric_limits<int64_t>::max()) &&
|
||||
dd >=
|
||||
static_cast<long double>(std::numeric_limits<int64_t>::lowest())) {
|
||||
int64_t x = static_cast<int64_t>(dd);
|
||||
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StrToDTest, DoubleMin) {
|
||||
const char kV[] = "2.22507385850720138e-308";
|
||||
char* end;
|
||||
double x = std::strtod(kV, &end);
|
||||
EXPECT_EQ(std::numeric_limits<double>::min(), x);
|
||||
// errno may equal ERANGE.
|
||||
}
|
||||
|
||||
TEST(StrToDTest, DoubleDenormMin) {
|
||||
const char kV[] = "4.94065645841246544e-324";
|
||||
char* end;
|
||||
double x = std::strtod(kV, &end);
|
||||
EXPECT_EQ(std::numeric_limits<double>::denorm_min(), x);
|
||||
// errno may equal ERANGE.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
|
||||
#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/fast_type_id.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// A no-op validator meeting the ValidatorT requirements for MockHelpers.
|
||||
//
|
||||
// Custom validators should follow a similar structure, passing the type to
|
||||
// MockHelpers::MockFor<KeyT>(m, CustomValidatorT()).
|
||||
struct NoOpValidator {
|
||||
// Default validation: do nothing.
|
||||
template <typename ResultT, typename... Args>
|
||||
static void Validate(ResultT, Args&&...) {}
|
||||
};
|
||||
|
||||
// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and
|
||||
// BitGenRef to enable the mocking capability for absl distribution functions.
|
||||
//
|
||||
// MockingBitGen registers mocks based on the typeid of a mock signature, KeyT,
|
||||
// which is used to generate a unique id.
|
||||
//
|
||||
// KeyT is a signature of the form:
|
||||
// result_type(discriminator_type, std::tuple<args...>)
|
||||
// The mocked function signature will be composed from KeyT as:
|
||||
// result_type(args...)
|
||||
//
|
||||
class MockHelpers {
|
||||
using IdType = ::absl::base_internal::FastTypeIdType;
|
||||
|
||||
// Given a key signature type used to index the mock, extract the components.
|
||||
// KeyT is expected to have the form:
|
||||
// result_type(discriminator_type, arg_tuple_type)
|
||||
template <typename KeyT>
|
||||
struct KeySignature;
|
||||
|
||||
template <typename ResultT, typename DiscriminatorT, typename ArgTupleT>
|
||||
struct KeySignature<ResultT(DiscriminatorT, ArgTupleT)> {
|
||||
using result_type = ResultT;
|
||||
using discriminator_type = DiscriminatorT;
|
||||
using arg_tuple_type = ArgTupleT;
|
||||
};
|
||||
|
||||
// Detector for InvokeMock.
|
||||
template <class T>
|
||||
using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
|
||||
std::declval<IdType>(), std::declval<void*>(), std::declval<void*>()));
|
||||
|
||||
// Empty implementation of InvokeMock.
|
||||
template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
|
||||
typename... Args>
|
||||
static absl::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Non-empty implementation of InvokeMock.
|
||||
template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG,
|
||||
typename = invoke_mock_t<URBG>, typename... Args>
|
||||
static absl::optional<ReturnT> InvokeMockImpl(int, URBG* urbg,
|
||||
Args&&... args) {
|
||||
ArgTupleT arg_tuple(std::forward<Args>(args)...);
|
||||
ReturnT result;
|
||||
if (urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple,
|
||||
&result)) {
|
||||
return result;
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
public:
|
||||
// InvokeMock is private; this provides access for some specialized use cases.
|
||||
template <typename URBG>
|
||||
static inline bool PrivateInvokeMock(URBG* urbg, IdType type,
|
||||
void* args_tuple, void* result) {
|
||||
return urbg->InvokeMock(type, args_tuple, result);
|
||||
}
|
||||
|
||||
// Invoke a mock for the KeyT (may or may not be a signature).
|
||||
//
|
||||
// KeyT is used to generate a typeid-based lookup key for the mock.
|
||||
// KeyT is a signature of the form:
|
||||
// result_type(discriminator_type, std::tuple<args...>)
|
||||
// The mocked function signature will be composed from KeyT as:
|
||||
// result_type(args...)
|
||||
//
|
||||
// An instance of arg_tuple_type must be constructable from Args..., since
|
||||
// the underlying mechanism requires a pointer to an argument tuple.
|
||||
template <typename KeyT, typename URBG, typename... Args>
|
||||
static auto MaybeInvokeMock(URBG* urbg, Args&&... args)
|
||||
-> absl::optional<typename KeySignature<KeyT>::result_type> {
|
||||
// Use function overloading to dispatch to the implementation since
|
||||
// more modern patterns (e.g. require + constexpr) are not supported in all
|
||||
// compiler configurations.
|
||||
return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type,
|
||||
typename KeySignature<KeyT>::arg_tuple_type, URBG>(
|
||||
0, urbg, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Acquire a mock for the KeyT (may or may not be a signature), set up to use
|
||||
// the ValidatorT to verify that the result is in the range of the RNG
|
||||
// function.
|
||||
//
|
||||
// KeyT is used to generate a typeid-based lookup for the mock.
|
||||
// KeyT is a signature of the form:
|
||||
// result_type(discriminator_type, std::tuple<args...>)
|
||||
// The mocked function signature will be composed from KeyT as:
|
||||
// result_type(args...)
|
||||
// ValidatorT::Validate will be called after the result of the RNG. The
|
||||
// signature is expected to be of the form:
|
||||
// ValidatorT::Validate(result, args...)
|
||||
template <typename KeyT, typename ValidatorT, typename MockURBG>
|
||||
static auto MockFor(MockURBG& m, ValidatorT)
|
||||
-> decltype(m.template RegisterMock<
|
||||
typename KeySignature<KeyT>::result_type,
|
||||
typename KeySignature<KeyT>::arg_tuple_type>(
|
||||
m, std::declval<IdType>(), ValidatorT())) {
|
||||
return m.template RegisterMock<typename KeySignature<KeyT>::result_type,
|
||||
typename KeySignature<KeyT>::arg_tuple_type>(
|
||||
m, ::absl::base_internal::FastTypeId<KeyT>(), ValidatorT());
|
||||
}
|
||||
|
||||
// Acquire a mock for the KeyT (may or may not be a signature).
|
||||
//
|
||||
// KeyT is used to generate a typeid-based lookup for the mock.
|
||||
// KeyT is a signature of the form:
|
||||
// result_type(discriminator_type, std::tuple<args...>)
|
||||
// The mocked function signature will be composed from KeyT as:
|
||||
// result_type(args...)
|
||||
template <typename KeyT, typename MockURBG>
|
||||
static decltype(auto) MockFor(MockURBG& m) {
|
||||
return MockFor<KeyT>(m, NoOpValidator());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
|
||||
#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/random/internal/mock_helpers.h"
|
||||
#include "absl/random/mocking_bit_gen.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
template <typename DistrT, typename ValidatorT, typename Fn>
|
||||
struct MockSingleOverload;
|
||||
|
||||
// MockSingleOverload
|
||||
//
|
||||
// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros.
|
||||
// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
|
||||
// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
|
||||
// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
|
||||
// arguments to MockingBitGen::Register.
|
||||
//
|
||||
// The underlying KeyT must match the KeyT constructed by DistributionCaller.
|
||||
template <typename DistrT, typename ValidatorT, typename Ret, typename... Args>
|
||||
struct MockSingleOverload<DistrT, ValidatorT, Ret(MockingBitGen&, Args...)> {
|
||||
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
|
||||
"Overload signature must have return type matching the "
|
||||
"distribution result_type.");
|
||||
using KeyT = Ret(DistrT, std::tuple<Args...>);
|
||||
|
||||
template <typename MockURBG>
|
||||
auto gmock_Call(MockURBG& gen, const ::testing::Matcher<Args>&... matchers)
|
||||
-> decltype(MockHelpers::MockFor<KeyT>(gen, ValidatorT())
|
||||
.gmock_Call(matchers...)) {
|
||||
static_assert(std::is_base_of<MockingBitGen, MockURBG>::value,
|
||||
"Mocking requires an absl::MockingBitGen");
|
||||
return MockHelpers::MockFor<KeyT>(gen, ValidatorT())
|
||||
.gmock_Call(matchers...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DistrT, typename ValidatorT, typename Ret, typename Arg,
|
||||
typename... Args>
|
||||
struct MockSingleOverload<DistrT, ValidatorT,
|
||||
Ret(Arg, MockingBitGen&, Args...)> {
|
||||
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
|
||||
"Overload signature must have return type matching the "
|
||||
"distribution result_type.");
|
||||
using KeyT = Ret(DistrT, std::tuple<Arg, Args...>);
|
||||
|
||||
template <typename MockURBG>
|
||||
auto gmock_Call(const ::testing::Matcher<Arg>& matcher, MockURBG& gen,
|
||||
const ::testing::Matcher<Args>&... matchers)
|
||||
-> decltype(MockHelpers::MockFor<KeyT>(gen, ValidatorT())
|
||||
.gmock_Call(matcher, matchers...)) {
|
||||
static_assert(std::is_base_of<MockingBitGen, MockURBG>::value,
|
||||
"Mocking requires an absl::MockingBitGen");
|
||||
return MockHelpers::MockFor<KeyT>(gen, ValidatorT())
|
||||
.gmock_Call(matcher, matchers...);
|
||||
}
|
||||
};
|
||||
|
||||
// MockOverloadSetWithValidator
|
||||
//
|
||||
// MockOverloadSetWithValidator is a wrapper around MockOverloadSet which takes
|
||||
// an additional Validator parameter, allowing for customization of the mock
|
||||
// behavior.
|
||||
//
|
||||
// `ValidatorT::Validate(result, args...)` will be called after the mock
|
||||
// distribution returns a value in `result`, allowing for validation against the
|
||||
// args.
|
||||
template <typename DistrT, typename ValidatorT, typename... Fns>
|
||||
struct MockOverloadSetWithValidator;
|
||||
|
||||
template <typename DistrT, typename ValidatorT, typename Sig>
|
||||
struct MockOverloadSetWithValidator<DistrT, ValidatorT, Sig>
|
||||
: public MockSingleOverload<DistrT, ValidatorT, Sig> {
|
||||
using MockSingleOverload<DistrT, ValidatorT, Sig>::gmock_Call;
|
||||
};
|
||||
|
||||
template <typename DistrT, typename ValidatorT, typename FirstSig,
|
||||
typename... Rest>
|
||||
struct MockOverloadSetWithValidator<DistrT, ValidatorT, FirstSig, Rest...>
|
||||
: public MockSingleOverload<DistrT, ValidatorT, FirstSig>,
|
||||
public MockOverloadSetWithValidator<DistrT, ValidatorT, Rest...> {
|
||||
using MockSingleOverload<DistrT, ValidatorT, FirstSig>::gmock_Call;
|
||||
using MockOverloadSetWithValidator<DistrT, ValidatorT, Rest...>::gmock_Call;
|
||||
};
|
||||
|
||||
// MockOverloadSet
|
||||
//
|
||||
// MockOverloadSet takes a distribution and a collection of signatures and
|
||||
// performs overload resolution amongst all the overloads. This makes
|
||||
// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution
|
||||
// correctly.
|
||||
template <typename DistrT, typename... Signatures>
|
||||
using MockOverloadSet =
|
||||
MockOverloadSetWithValidator<DistrT, NoOpValidator, Signatures...>;
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
#endif // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2024 The Abseil Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_MOCK_VALIDATORS_H_
|
||||
#define ABSL_RANDOM_INTERNAL_MOCK_VALIDATORS_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/random/internal/iostream_state_saver.h"
|
||||
#include "absl/random/internal/uniform_helper.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
template <typename NumType>
|
||||
class UniformDistributionValidator {
|
||||
public:
|
||||
// Handle absl::Uniform<NumType>(gen, absl::IntervalTag, lo, hi).
|
||||
template <typename TagType>
|
||||
static void Validate(NumType x, TagType tag, NumType lo, NumType hi) {
|
||||
// For invalid ranges, absl::Uniform() simply returns one of the bounds.
|
||||
if (x == lo && lo == hi) return;
|
||||
|
||||
ValidateImpl(std::is_floating_point<NumType>{}, x, tag, lo, hi);
|
||||
}
|
||||
|
||||
// Handle absl::Uniform<NumType>(gen, lo, hi).
|
||||
static void Validate(NumType x, NumType lo, NumType hi) {
|
||||
Validate(x, IntervalClosedOpenTag(), lo, hi);
|
||||
}
|
||||
|
||||
// Handle absl::Uniform<NumType>(gen).
|
||||
static void Validate(NumType) {
|
||||
// absl::Uniform<NumType>(gen) spans the entire range of `NumType`, so any
|
||||
// value is okay. This overload exists because the validation logic attempts
|
||||
// to call it anyway rather than adding extra SFINAE.
|
||||
}
|
||||
|
||||
private:
|
||||
static absl::string_view TagLbBound(IntervalClosedOpenTag) { return "["; }
|
||||
static absl::string_view TagLbBound(IntervalOpenOpenTag) { return "("; }
|
||||
static absl::string_view TagLbBound(IntervalClosedClosedTag) { return "["; }
|
||||
static absl::string_view TagLbBound(IntervalOpenClosedTag) { return "("; }
|
||||
static absl::string_view TagUbBound(IntervalClosedOpenTag) { return ")"; }
|
||||
static absl::string_view TagUbBound(IntervalOpenOpenTag) { return ")"; }
|
||||
static absl::string_view TagUbBound(IntervalClosedClosedTag) { return "]"; }
|
||||
static absl::string_view TagUbBound(IntervalOpenClosedTag) { return "]"; }
|
||||
|
||||
template <typename TagType>
|
||||
static void ValidateImpl(std::true_type /* is_floating_point */, NumType x,
|
||||
TagType tag, NumType lo, NumType hi) {
|
||||
UniformDistributionWrapper<NumType> dist(tag, lo, hi);
|
||||
NumType lb = dist.a();
|
||||
NumType ub = dist.b();
|
||||
// uniform_real_distribution is always closed-open, so the upper bound is
|
||||
// always non-inclusive.
|
||||
ABSL_INTERNAL_CHECK(lb <= x && x < ub,
|
||||
absl::StrCat(x, " is not in ", TagLbBound(tag), lo,
|
||||
", ", hi, TagUbBound(tag)));
|
||||
}
|
||||
|
||||
template <typename TagType>
|
||||
static void ValidateImpl(std::false_type /* is_floating_point */, NumType x,
|
||||
TagType tag, NumType lo, NumType hi) {
|
||||
using stream_type =
|
||||
typename random_internal::stream_format_type<NumType>::type;
|
||||
|
||||
UniformDistributionWrapper<NumType> dist(tag, lo, hi);
|
||||
NumType lb = dist.a();
|
||||
NumType ub = dist.b();
|
||||
ABSL_INTERNAL_CHECK(
|
||||
lb <= x && x <= ub,
|
||||
absl::StrCat(stream_type{x}, " is not in ", TagLbBound(tag),
|
||||
stream_type{lo}, ", ", stream_type{hi}, TagUbBound(tag)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_MOCK_VALIDATORS_H_
|
||||
|
|
@ -0,0 +1,804 @@
|
|||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/nanobenchmark.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm> // sort
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring> // memcpy
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen_engine.h"
|
||||
|
||||
// OS
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define ABSL_OS_WIN
|
||||
#include <windows.h> // NOLINT
|
||||
|
||||
#elif defined(__ANDROID__)
|
||||
#define ABSL_OS_ANDROID
|
||||
|
||||
#elif defined(__linux__)
|
||||
#define ABSL_OS_LINUX
|
||||
#include <sched.h> // NOLINT
|
||||
#include <sys/syscall.h> // NOLINT
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_ARCH_X86_64) && !defined(ABSL_OS_WIN)
|
||||
#include <cpuid.h> // NOLINT
|
||||
#endif
|
||||
|
||||
// __ppc_get_timebase_freq
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
#include <sys/platform/ppc.h> // NOLINT
|
||||
#endif
|
||||
|
||||
// clock_gettime
|
||||
#if defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
|
||||
#include <time.h> // NOLINT
|
||||
#endif
|
||||
|
||||
// ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method.
|
||||
#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __declspec(noinline)
|
||||
#else
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal_nanobenchmark {
|
||||
namespace {
|
||||
|
||||
// For code folding.
|
||||
namespace platform {
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
|
||||
// TODO(janwas): Merge with the one in randen_hwaes.cc?
|
||||
void Cpuid(const uint32_t level, const uint32_t count,
|
||||
uint32_t* ABSL_RANDOM_INTERNAL_RESTRICT abcd) {
|
||||
#if defined(ABSL_OS_WIN)
|
||||
int regs[4];
|
||||
__cpuidex(regs, level, count);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
abcd[i] = regs[i];
|
||||
}
|
||||
#else
|
||||
uint32_t a, b, c, d;
|
||||
__cpuid_count(level, count, a, b, c, d);
|
||||
abcd[0] = a;
|
||||
abcd[1] = b;
|
||||
abcd[2] = c;
|
||||
abcd[3] = d;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string BrandString() {
|
||||
char brand_string[49];
|
||||
uint32_t abcd[4];
|
||||
|
||||
// Check if brand string is supported (it is on all reasonable Intel/AMD)
|
||||
Cpuid(0x80000000U, 0, abcd);
|
||||
if (abcd[0] < 0x80000004U) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
Cpuid(0x80000002U + i, 0, abcd);
|
||||
memcpy(brand_string + i * 16, &abcd, sizeof(abcd));
|
||||
}
|
||||
brand_string[48] = 0;
|
||||
return brand_string;
|
||||
}
|
||||
|
||||
// Returns the frequency quoted inside the brand string. This does not
|
||||
// account for throttling nor Turbo Boost.
|
||||
double NominalClockRate() {
|
||||
const std::string& brand_string = BrandString();
|
||||
// Brand strings include the maximum configured frequency. These prefixes are
|
||||
// defined by Intel CPUID documentation.
|
||||
const char* prefixes[3] = {"MHz", "GHz", "THz"};
|
||||
const double multipliers[3] = {1E6, 1E9, 1E12};
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
const size_t pos_prefix = brand_string.find(prefixes[i]);
|
||||
if (pos_prefix != std::string::npos) {
|
||||
const size_t pos_space = brand_string.rfind(' ', pos_prefix - 1);
|
||||
if (pos_space != std::string::npos) {
|
||||
const std::string digits =
|
||||
brand_string.substr(pos_space + 1, pos_prefix - pos_space - 1);
|
||||
return std::stod(digits) * multipliers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif // ABSL_ARCH_X86_64
|
||||
} // namespace platform
|
||||
|
||||
// Prevents the compiler from eliding the computations that led to "output".
|
||||
template <class T>
|
||||
inline void PreventElision(T&& output) {
|
||||
#ifndef ABSL_OS_WIN
|
||||
// Works by indicating to the compiler that "output" is being read and
|
||||
// modified. The +r constraint avoids unnecessary writes to memory, but only
|
||||
// works for built-in types (typically FuncOutput).
|
||||
asm volatile("" : "+r"(output) : : "memory");
|
||||
#else
|
||||
// MSVC does not support inline assembly anymore (and never supported GCC's
|
||||
// RTL constraints). Self-assignment with #pragma optimize("off") might be
|
||||
// expected to prevent elision, but it does not with MSVC 2015. Type-punning
|
||||
// with volatile pointers generates inefficient code on MSVC 2017.
|
||||
static std::atomic<T> dummy(T{});
|
||||
dummy.store(output, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace timer {
|
||||
|
||||
// Start/Stop return absolute timestamps and must be placed immediately before
|
||||
// and after the region to measure. We provide separate Start/Stop functions
|
||||
// because they use different fences.
|
||||
//
|
||||
// Background: RDTSC is not 'serializing'; earlier instructions may complete
|
||||
// after it, and/or later instructions may complete before it. 'Fences' ensure
|
||||
// regions' elapsed times are independent of such reordering. The only
|
||||
// documented unprivileged serializing instruction is CPUID, which acts as a
|
||||
// full fence (no reordering across it in either direction). Unfortunately
|
||||
// the latency of CPUID varies wildly (perhaps made worse by not initializing
|
||||
// its EAX input). Because it cannot reliably be deducted from the region's
|
||||
// elapsed time, it must not be included in the region to measure (i.e.
|
||||
// between the two RDTSC).
|
||||
//
|
||||
// The newer RDTSCP is sometimes described as serializing, but it actually
|
||||
// only serves as a half-fence with release semantics. Although all
|
||||
// instructions in the region will complete before the final timestamp is
|
||||
// captured, subsequent instructions may leak into the region and increase the
|
||||
// elapsed time. Inserting another fence after the final RDTSCP would prevent
|
||||
// such reordering without affecting the measured region.
|
||||
//
|
||||
// Fortunately, such a fence exists. The LFENCE instruction is only documented
|
||||
// to delay later loads until earlier loads are visible. However, Intel's
|
||||
// reference manual says it acts as a full fence (waiting until all earlier
|
||||
// instructions have completed, and delaying later instructions until it
|
||||
// completes). AMD assigns the same behavior to MFENCE.
|
||||
//
|
||||
// We need a fence before the initial RDTSC to prevent earlier instructions
|
||||
// from leaking into the region, and arguably another after RDTSC to avoid
|
||||
// region instructions from completing before the timestamp is recorded.
|
||||
// When surrounded by fences, the additional RDTSCP half-fence provides no
|
||||
// benefit, so the initial timestamp can be recorded via RDTSC, which has
|
||||
// lower overhead than RDTSCP because it does not read TSC_AUX. In summary,
|
||||
// we define Start = LFENCE/RDTSC/LFENCE; Stop = RDTSCP/LFENCE.
|
||||
//
|
||||
// Using Start+Start leads to higher variance and overhead than Stop+Stop.
|
||||
// However, Stop+Stop includes an LFENCE in the region measurements, which
|
||||
// adds a delay dependent on earlier loads. The combination of Start+Stop
|
||||
// is faster than Start+Start and more consistent than Stop+Stop because
|
||||
// the first LFENCE already delayed subsequent loads before the measured
|
||||
// region. This combination seems not to have been considered in prior work:
|
||||
// http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c
|
||||
//
|
||||
// Note: performance counters can measure 'exact' instructions-retired or
|
||||
// (unhalted) cycle counts. The RDPMC instruction is not serializing and also
|
||||
// requires fences. Unfortunately, it is not accessible on all OSes and we
|
||||
// prefer to avoid kernel-mode drivers. Performance counters are also affected
|
||||
// by several under/over-count errata, so we use the TSC instead.
|
||||
|
||||
// Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds,
|
||||
// divide by InvariantTicksPerSecond.
|
||||
inline uint64_t Start64() {
|
||||
uint64_t t;
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
|
||||
#elif defined(ABSL_ARCH_X86_64)
|
||||
#if defined(ABSL_OS_WIN)
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
t = __rdtsc();
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
asm volatile(
|
||||
"lfence\n\t"
|
||||
"rdtsc\n\t"
|
||||
"shl $32, %%rdx\n\t"
|
||||
"or %%rdx, %0\n\t"
|
||||
"lfence"
|
||||
: "=a"(t)
|
||||
:
|
||||
// "memory" avoids reordering. rdx = TSC >> 32.
|
||||
// "cc" = flags modified by SHL.
|
||||
: "rdx", "memory", "cc");
|
||||
#endif
|
||||
#else
|
||||
// Fall back to OS - unsure how to reliably query cntvct_el0 frequency.
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
t = ts.tv_sec * 1000000000LL + ts.tv_nsec;
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint64_t Stop64() {
|
||||
uint64_t t;
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
#if defined(ABSL_OS_WIN)
|
||||
_ReadWriteBarrier();
|
||||
unsigned aux;
|
||||
t = __rdtscp(&aux);
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
// Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
|
||||
asm volatile(
|
||||
"rdtscp\n\t"
|
||||
"shl $32, %%rdx\n\t"
|
||||
"or %%rdx, %0\n\t"
|
||||
"lfence"
|
||||
: "=a"(t)
|
||||
:
|
||||
// "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32.
|
||||
// "cc" = flags modified by SHL.
|
||||
: "rcx", "rdx", "memory", "cc");
|
||||
#endif
|
||||
#else
|
||||
t = Start64();
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
// Returns a 32-bit timestamp with about 4 cycles less overhead than
|
||||
// Start64. Only suitable for measuring very short regions because the
|
||||
// timestamp overflows about once a second.
|
||||
inline uint32_t Start32() {
|
||||
uint32_t t;
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
#if defined(ABSL_OS_WIN)
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
t = static_cast<uint32_t>(__rdtsc());
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
asm volatile(
|
||||
"lfence\n\t"
|
||||
"rdtsc\n\t"
|
||||
"lfence"
|
||||
: "=a"(t)
|
||||
:
|
||||
// "memory" avoids reordering. rdx = TSC >> 32.
|
||||
: "rdx", "memory");
|
||||
#endif
|
||||
#else
|
||||
t = static_cast<uint32_t>(Start64());
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint32_t Stop32() {
|
||||
uint32_t t;
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
#if defined(ABSL_OS_WIN)
|
||||
_ReadWriteBarrier();
|
||||
unsigned aux;
|
||||
t = static_cast<uint32_t>(__rdtscp(&aux));
|
||||
_ReadWriteBarrier();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
// Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
|
||||
asm volatile(
|
||||
"rdtscp\n\t"
|
||||
"lfence"
|
||||
: "=a"(t)
|
||||
:
|
||||
// "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32.
|
||||
: "rcx", "rdx", "memory");
|
||||
#endif
|
||||
#else
|
||||
t = static_cast<uint32_t>(Stop64());
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace timer
|
||||
|
||||
namespace robust_statistics {
|
||||
|
||||
// Sorts integral values in ascending order (e.g. for Mode). About 3x faster
|
||||
// than std::sort for input distributions with very few unique values.
|
||||
template <class T>
|
||||
void CountingSort(T* values, size_t num_values) {
|
||||
// Unique values and their frequency (similar to flat_map).
|
||||
using Unique = std::pair<T, int>;
|
||||
std::vector<Unique> unique;
|
||||
for (size_t i = 0; i < num_values; ++i) {
|
||||
const T value = values[i];
|
||||
const auto pos =
|
||||
std::find_if(unique.begin(), unique.end(),
|
||||
[value](const Unique u) { return u.first == value; });
|
||||
if (pos == unique.end()) {
|
||||
unique.push_back(std::make_pair(value, 1));
|
||||
} else {
|
||||
++pos->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort in ascending order of value (pair.first).
|
||||
std::sort(unique.begin(), unique.end());
|
||||
|
||||
// Write that many copies of each unique value to the array.
|
||||
T* ABSL_RANDOM_INTERNAL_RESTRICT p = values;
|
||||
for (const auto& value_count : unique) {
|
||||
std::fill_n(p, value_count.second, value_count.first);
|
||||
p += value_count.second;
|
||||
}
|
||||
ABSL_RAW_CHECK(p == values + num_values, "Did not produce enough output");
|
||||
}
|
||||
|
||||
// @return i in [idx_begin, idx_begin + half_count) that minimizes
|
||||
// sorted[i + half_count] - sorted[i].
|
||||
template <typename T>
|
||||
size_t MinRange(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted,
|
||||
const size_t idx_begin, const size_t half_count) {
|
||||
T min_range = (std::numeric_limits<T>::max)();
|
||||
size_t min_idx = 0;
|
||||
|
||||
for (size_t idx = idx_begin; idx < idx_begin + half_count; ++idx) {
|
||||
ABSL_RAW_CHECK(sorted[idx] <= sorted[idx + half_count], "Not sorted");
|
||||
const T range = sorted[idx + half_count] - sorted[idx];
|
||||
if (range < min_range) {
|
||||
min_range = range;
|
||||
min_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
return min_idx;
|
||||
}
|
||||
|
||||
// Returns an estimate of the mode by calling MinRange on successively
|
||||
// halved intervals. "sorted" must be in ascending order. This is the
|
||||
// Half Sample Mode estimator proposed by Bickel in "On a fast, robust
|
||||
// estimator of the mode", with complexity O(N log N). The mode is less
|
||||
// affected by outliers in highly-skewed distributions than the median.
|
||||
// The averaging operation below assumes "T" is an unsigned integer type.
|
||||
template <typename T>
|
||||
T ModeOfSorted(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted,
|
||||
const size_t num_values) {
|
||||
size_t idx_begin = 0;
|
||||
size_t half_count = num_values / 2;
|
||||
while (half_count > 1) {
|
||||
idx_begin = MinRange(sorted, idx_begin, half_count);
|
||||
half_count >>= 1;
|
||||
}
|
||||
|
||||
const T x = sorted[idx_begin + 0];
|
||||
if (half_count == 0) {
|
||||
return x;
|
||||
}
|
||||
ABSL_RAW_CHECK(half_count == 1, "Should stop at half_count=1");
|
||||
const T average = (x + sorted[idx_begin + 1] + 1) / 2;
|
||||
return average;
|
||||
}
|
||||
|
||||
// Returns the mode. Side effect: sorts "values".
|
||||
template <typename T>
|
||||
T Mode(T* values, const size_t num_values) {
|
||||
CountingSort(values, num_values);
|
||||
return ModeOfSorted(values, num_values);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
T Mode(T (&values)[N]) {
|
||||
return Mode(&values[0], N);
|
||||
}
|
||||
|
||||
// Returns the median value. Side effect: sorts "values".
|
||||
template <typename T>
|
||||
T Median(T* values, const size_t num_values) {
|
||||
ABSL_RAW_CHECK(num_values != 0, "Empty input");
|
||||
std::sort(values, values + num_values);
|
||||
const size_t half = num_values / 2;
|
||||
// Odd count: return middle
|
||||
if (num_values % 2) {
|
||||
return values[half];
|
||||
}
|
||||
// Even count: return average of middle two.
|
||||
return (values[half] + values[half - 1] + 1) / 2;
|
||||
}
|
||||
|
||||
// Returns a robust measure of variability.
|
||||
template <typename T>
|
||||
T MedianAbsoluteDeviation(const T* values, const size_t num_values,
|
||||
const T median) {
|
||||
ABSL_RAW_CHECK(num_values != 0, "Empty input");
|
||||
std::vector<T> abs_deviations;
|
||||
abs_deviations.reserve(num_values);
|
||||
for (size_t i = 0; i < num_values; ++i) {
|
||||
const int64_t abs = std::abs(int64_t(values[i]) - int64_t(median));
|
||||
abs_deviations.push_back(static_cast<T>(abs));
|
||||
}
|
||||
return Median(abs_deviations.data(), num_values);
|
||||
}
|
||||
|
||||
} // namespace robust_statistics
|
||||
|
||||
// Ticks := platform-specific timer values (CPU cycles on x86). Must be
|
||||
// unsigned to guarantee wraparound on overflow. 32 bit timers are faster to
|
||||
// read than 64 bit.
|
||||
using Ticks = uint32_t;
|
||||
|
||||
// Returns timer overhead / minimum measurable difference.
|
||||
Ticks TimerResolution() {
|
||||
// Nested loop avoids exceeding stack/L1 capacity.
|
||||
Ticks repetitions[Params::kTimerSamples];
|
||||
for (size_t rep = 0; rep < Params::kTimerSamples; ++rep) {
|
||||
Ticks samples[Params::kTimerSamples];
|
||||
for (size_t i = 0; i < Params::kTimerSamples; ++i) {
|
||||
const Ticks t0 = timer::Start32();
|
||||
const Ticks t1 = timer::Stop32();
|
||||
samples[i] = t1 - t0;
|
||||
}
|
||||
repetitions[rep] = robust_statistics::Mode(samples);
|
||||
}
|
||||
return robust_statistics::Mode(repetitions);
|
||||
}
|
||||
|
||||
static const Ticks timer_resolution = TimerResolution();
|
||||
|
||||
// Estimates the expected value of "lambda" values with a variable number of
|
||||
// samples until the variability "rel_mad" is less than "max_rel_mad".
|
||||
template <class Lambda>
|
||||
Ticks SampleUntilStable(const double max_rel_mad, double* rel_mad,
|
||||
const Params& p, const Lambda& lambda) {
|
||||
auto measure_duration = [&lambda]() -> Ticks {
|
||||
const Ticks t0 = timer::Start32();
|
||||
lambda();
|
||||
const Ticks t1 = timer::Stop32();
|
||||
return t1 - t0;
|
||||
};
|
||||
|
||||
// Choose initial samples_per_eval based on a single estimated duration.
|
||||
Ticks est = measure_duration();
|
||||
static const double ticks_per_second = InvariantTicksPerSecond();
|
||||
const size_t ticks_per_eval = ticks_per_second * p.seconds_per_eval;
|
||||
size_t samples_per_eval = ticks_per_eval / est;
|
||||
samples_per_eval = (std::max)(samples_per_eval, p.min_samples_per_eval);
|
||||
|
||||
std::vector<Ticks> samples;
|
||||
samples.reserve(1 + samples_per_eval);
|
||||
samples.push_back(est);
|
||||
|
||||
// Percentage is too strict for tiny differences, so also allow a small
|
||||
// absolute "median absolute deviation".
|
||||
const Ticks max_abs_mad = (timer_resolution + 99) / 100;
|
||||
*rel_mad = 0.0; // ensure initialized
|
||||
|
||||
for (size_t eval = 0; eval < p.max_evals; ++eval, samples_per_eval *= 2) {
|
||||
samples.reserve(samples.size() + samples_per_eval);
|
||||
for (size_t i = 0; i < samples_per_eval; ++i) {
|
||||
const Ticks r = measure_duration();
|
||||
samples.push_back(r);
|
||||
}
|
||||
|
||||
if (samples.size() >= p.min_mode_samples) {
|
||||
est = robust_statistics::Mode(samples.data(), samples.size());
|
||||
} else {
|
||||
// For "few" (depends also on the variance) samples, Median is safer.
|
||||
est = robust_statistics::Median(samples.data(), samples.size());
|
||||
}
|
||||
ABSL_RAW_CHECK(est != 0, "Estimator returned zero duration");
|
||||
|
||||
// Median absolute deviation (mad) is a robust measure of 'variability'.
|
||||
const Ticks abs_mad = robust_statistics::MedianAbsoluteDeviation(
|
||||
samples.data(), samples.size(), est);
|
||||
*rel_mad = static_cast<double>(static_cast<int>(abs_mad)) / est;
|
||||
|
||||
if (*rel_mad <= max_rel_mad || abs_mad <= max_abs_mad) {
|
||||
if (p.verbose) {
|
||||
ABSL_RAW_LOG(INFO,
|
||||
"%6zu samples => %5u (abs_mad=%4u, rel_mad=%4.2f%%)\n",
|
||||
samples.size(), est, abs_mad, *rel_mad * 100.0);
|
||||
}
|
||||
return est;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.verbose) {
|
||||
ABSL_RAW_LOG(WARNING,
|
||||
"rel_mad=%4.2f%% still exceeds %4.2f%% after %6zu samples.\n",
|
||||
*rel_mad * 100.0, max_rel_mad * 100.0, samples.size());
|
||||
}
|
||||
return est;
|
||||
}
|
||||
|
||||
using InputVec = std::vector<FuncInput>;
|
||||
|
||||
// Returns vector of unique input values.
|
||||
InputVec UniqueInputs(const FuncInput* inputs, const size_t num_inputs) {
|
||||
InputVec unique(inputs, inputs + num_inputs);
|
||||
std::sort(unique.begin(), unique.end());
|
||||
unique.erase(std::unique(unique.begin(), unique.end()), unique.end());
|
||||
return unique;
|
||||
}
|
||||
|
||||
// Returns how often we need to call func for sufficient precision, or zero
|
||||
// on failure (e.g. the elapsed time is too long for a 32-bit tick count).
|
||||
size_t NumSkip(const Func func, const void* arg, const InputVec& unique,
|
||||
const Params& p) {
|
||||
// Min elapsed ticks for any input.
|
||||
Ticks min_duration = ~0u;
|
||||
|
||||
for (const FuncInput input : unique) {
|
||||
// Make sure a 32-bit timer is sufficient.
|
||||
const uint64_t t0 = timer::Start64();
|
||||
PreventElision(func(arg, input));
|
||||
const uint64_t t1 = timer::Stop64();
|
||||
const uint64_t elapsed = t1 - t0;
|
||||
if (elapsed >= (1ULL << 30)) {
|
||||
ABSL_RAW_LOG(WARNING,
|
||||
"Measurement failed: need 64-bit timer for input=%zu\n",
|
||||
static_cast<size_t>(input));
|
||||
return 0;
|
||||
}
|
||||
|
||||
double rel_mad;
|
||||
const Ticks total = SampleUntilStable(
|
||||
p.target_rel_mad, &rel_mad, p,
|
||||
[func, arg, input]() { PreventElision(func(arg, input)); });
|
||||
min_duration = (std::min)(min_duration, total - timer_resolution);
|
||||
}
|
||||
|
||||
// Number of repetitions required to reach the target resolution.
|
||||
const size_t max_skip = p.precision_divisor;
|
||||
// Number of repetitions given the estimated duration.
|
||||
const size_t num_skip =
|
||||
min_duration == 0 ? 0 : (max_skip + min_duration - 1) / min_duration;
|
||||
if (p.verbose) {
|
||||
ABSL_RAW_LOG(INFO, "res=%u max_skip=%zu min_dur=%u num_skip=%zu\n",
|
||||
timer_resolution, max_skip, min_duration, num_skip);
|
||||
}
|
||||
return num_skip;
|
||||
}
|
||||
|
||||
// Replicates inputs until we can omit "num_skip" occurrences of an input.
|
||||
InputVec ReplicateInputs(const FuncInput* inputs, const size_t num_inputs,
|
||||
const size_t num_unique, const size_t num_skip,
|
||||
const Params& p) {
|
||||
InputVec full;
|
||||
if (num_unique == 1) {
|
||||
full.assign(p.subset_ratio * num_skip, inputs[0]);
|
||||
return full;
|
||||
}
|
||||
|
||||
full.reserve(p.subset_ratio * num_skip * num_inputs);
|
||||
for (size_t i = 0; i < p.subset_ratio * num_skip; ++i) {
|
||||
full.insert(full.end(), inputs, inputs + num_inputs);
|
||||
}
|
||||
absl::random_internal::randen_engine<uint32_t> rng;
|
||||
std::shuffle(full.begin(), full.end(), rng);
|
||||
return full;
|
||||
}
|
||||
|
||||
// Copies the "full" to "subset" in the same order, but with "num_skip"
|
||||
// randomly selected occurrences of "input_to_skip" removed.
|
||||
void FillSubset(const InputVec& full, const FuncInput input_to_skip,
|
||||
const size_t num_skip, InputVec* subset) {
|
||||
const size_t count = std::count(full.begin(), full.end(), input_to_skip);
|
||||
// Generate num_skip random indices: which occurrence to skip.
|
||||
std::vector<uint32_t> omit;
|
||||
// Replacement for std::iota, not yet available in MSVC builds.
|
||||
omit.reserve(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
omit.push_back(i);
|
||||
}
|
||||
// omit[] is the same on every call, but that's OK because they identify the
|
||||
// Nth instance of input_to_skip, so the position within full[] differs.
|
||||
absl::random_internal::randen_engine<uint32_t> rng;
|
||||
std::shuffle(omit.begin(), omit.end(), rng);
|
||||
omit.resize(num_skip);
|
||||
std::sort(omit.begin(), omit.end());
|
||||
|
||||
uint32_t occurrence = ~0u; // 0 after preincrement
|
||||
size_t idx_omit = 0; // cursor within omit[]
|
||||
size_t idx_subset = 0; // cursor within *subset
|
||||
for (const FuncInput next : full) {
|
||||
if (next == input_to_skip) {
|
||||
++occurrence;
|
||||
// Haven't removed enough already
|
||||
if (idx_omit < num_skip) {
|
||||
// This one is up for removal
|
||||
if (occurrence == omit[idx_omit]) {
|
||||
++idx_omit;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx_subset < subset->size()) {
|
||||
(*subset)[idx_subset++] = next;
|
||||
}
|
||||
}
|
||||
ABSL_RAW_CHECK(idx_subset == subset->size(), "idx_subset not at end");
|
||||
ABSL_RAW_CHECK(idx_omit == omit.size(), "idx_omit not at end");
|
||||
ABSL_RAW_CHECK(occurrence == count - 1, "occurrence not at end");
|
||||
}
|
||||
|
||||
// Returns total ticks elapsed for all inputs.
|
||||
Ticks TotalDuration(const Func func, const void* arg, const InputVec* inputs,
|
||||
const Params& p, double* max_rel_mad) {
|
||||
double rel_mad;
|
||||
const Ticks duration =
|
||||
SampleUntilStable(p.target_rel_mad, &rel_mad, p, [func, arg, inputs]() {
|
||||
for (const FuncInput input : *inputs) {
|
||||
PreventElision(func(arg, input));
|
||||
}
|
||||
});
|
||||
*max_rel_mad = (std::max)(*max_rel_mad, rel_mad);
|
||||
return duration;
|
||||
}
|
||||
|
||||
// (Nearly) empty Func for measuring timer overhead/resolution.
|
||||
ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE FuncOutput
|
||||
EmptyFunc(const void* arg, const FuncInput input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
// Returns overhead of accessing inputs[] and calling a function; this will
|
||||
// be deducted from future TotalDuration return values.
|
||||
Ticks Overhead(const void* arg, const InputVec* inputs, const Params& p) {
|
||||
double rel_mad;
|
||||
// Zero tolerance because repeatability is crucial and EmptyFunc is fast.
|
||||
return SampleUntilStable(0.0, &rel_mad, p, [arg, inputs]() {
|
||||
for (const FuncInput input : *inputs) {
|
||||
PreventElision(EmptyFunc(arg, input));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void PinThreadToCPU(int cpu) {
|
||||
// We might migrate to another CPU before pinning below, but at least cpu
|
||||
// will be one of the CPUs on which this thread ran.
|
||||
#if defined(ABSL_OS_WIN)
|
||||
if (cpu < 0) {
|
||||
cpu = static_cast<int>(GetCurrentProcessorNumber());
|
||||
ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed");
|
||||
if (cpu >= 64) {
|
||||
// NOTE: On wine, at least, GetCurrentProcessorNumber() sometimes returns
|
||||
// a value > 64, which is out of range. When this happens, log a message
|
||||
// and don't set a cpu affinity.
|
||||
ABSL_RAW_LOG(ERROR, "Invalid CPU number: %d", cpu);
|
||||
return;
|
||||
}
|
||||
} else if (cpu >= 64) {
|
||||
// User specified an explicit CPU affinity > the valid range.
|
||||
ABSL_RAW_LOG(FATAL, "Invalid CPU number: %d", cpu);
|
||||
}
|
||||
const DWORD_PTR prev = SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpu);
|
||||
ABSL_RAW_CHECK(prev != 0, "SetAffinity failed");
|
||||
#elif defined(ABSL_OS_LINUX) && !defined(ABSL_OS_ANDROID)
|
||||
if (cpu < 0) {
|
||||
cpu = sched_getcpu();
|
||||
ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed");
|
||||
}
|
||||
const pid_t pid = 0; // current thread
|
||||
cpu_set_t set;
|
||||
CPU_ZERO(&set);
|
||||
CPU_SET(cpu, &set);
|
||||
const int err = sched_setaffinity(pid, sizeof(set), &set);
|
||||
ABSL_RAW_CHECK(err == 0, "SetAffinity failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns tick rate. Invariant means the tick counter frequency is independent
|
||||
// of CPU throttling or sleep. May be expensive, caller should cache the result.
|
||||
double InvariantTicksPerSecond() {
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
return __ppc_get_timebase_freq();
|
||||
#elif defined(ABSL_ARCH_X86_64)
|
||||
// We assume the TSC is invariant; it is on all recent Intel/AMD CPUs.
|
||||
return platform::NominalClockRate();
|
||||
#else
|
||||
// Fall back to clock_gettime nanoseconds.
|
||||
return 1E9;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t MeasureImpl(const Func func, const void* arg, const size_t num_skip,
|
||||
const InputVec& unique, const InputVec& full,
|
||||
const Params& p, Result* results) {
|
||||
const float mul = 1.0f / static_cast<int>(num_skip);
|
||||
|
||||
InputVec subset(full.size() - num_skip);
|
||||
const Ticks overhead = Overhead(arg, &full, p);
|
||||
const Ticks overhead_skip = Overhead(arg, &subset, p);
|
||||
if (overhead < overhead_skip) {
|
||||
ABSL_RAW_LOG(WARNING, "Measurement failed: overhead %u < %u\n", overhead,
|
||||
overhead_skip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p.verbose) {
|
||||
ABSL_RAW_LOG(INFO, "#inputs=%5zu,%5zu overhead=%5u,%5u\n", full.size(),
|
||||
subset.size(), overhead, overhead_skip);
|
||||
}
|
||||
|
||||
double max_rel_mad = 0.0;
|
||||
const Ticks total = TotalDuration(func, arg, &full, p, &max_rel_mad);
|
||||
|
||||
for (size_t i = 0; i < unique.size(); ++i) {
|
||||
FillSubset(full, unique[i], num_skip, &subset);
|
||||
const Ticks total_skip = TotalDuration(func, arg, &subset, p, &max_rel_mad);
|
||||
|
||||
if (total < total_skip) {
|
||||
ABSL_RAW_LOG(WARNING, "Measurement failed: total %u < %u\n", total,
|
||||
total_skip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Ticks duration = (total - overhead) - (total_skip - overhead_skip);
|
||||
results[i].input = unique[i];
|
||||
results[i].ticks = duration * mul;
|
||||
results[i].variability = max_rel_mad;
|
||||
}
|
||||
|
||||
return unique.size();
|
||||
}
|
||||
|
||||
size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
|
||||
const size_t num_inputs, Result* results, const Params& p) {
|
||||
ABSL_RAW_CHECK(num_inputs != 0, "No inputs");
|
||||
|
||||
const InputVec unique = UniqueInputs(inputs, num_inputs);
|
||||
const size_t num_skip = NumSkip(func, arg, unique, p); // never 0
|
||||
if (num_skip == 0) return 0; // NumSkip already printed error message
|
||||
|
||||
const InputVec full =
|
||||
ReplicateInputs(inputs, num_inputs, unique.size(), num_skip, p);
|
||||
|
||||
// MeasureImpl may fail up to p.max_measure_retries times.
|
||||
for (size_t i = 0; i < p.max_measure_retries; i++) {
|
||||
auto result = MeasureImpl(func, arg, num_skip, unique, full, p, results);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// All retries failed. (Unusual)
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace random_internal_nanobenchmark
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
|
||||
#define ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
|
||||
|
||||
// Benchmarks functions of a single integer argument with realistic branch
|
||||
// prediction hit rates. Uses a robust estimator to summarize the measurements.
|
||||
// The precision is about 0.2%.
|
||||
//
|
||||
// Examples: see nanobenchmark_test.cc.
|
||||
//
|
||||
// Background: Microbenchmarks such as http://github.com/google/benchmark
|
||||
// can measure elapsed times on the order of a microsecond. Shorter functions
|
||||
// are typically measured by repeating them thousands of times and dividing
|
||||
// the total elapsed time by this count. Unfortunately, repetition (especially
|
||||
// with the same input parameter!) influences the runtime. In time-critical
|
||||
// code, it is reasonable to expect warm instruction/data caches and TLBs,
|
||||
// but a perfect record of which branches will be taken is unrealistic.
|
||||
// Unless the application also repeatedly invokes the measured function with
|
||||
// the same parameter, the benchmark is measuring something very different -
|
||||
// a best-case result, almost as if the parameter were made a compile-time
|
||||
// constant. This may lead to erroneous conclusions about branch-heavy
|
||||
// algorithms outperforming branch-free alternatives.
|
||||
//
|
||||
// Our approach differs in three ways. Adding fences to the timer functions
|
||||
// reduces variability due to instruction reordering, improving the timer
|
||||
// resolution to about 40 CPU cycles. However, shorter functions must still
|
||||
// be invoked repeatedly. For more realistic branch prediction performance,
|
||||
// we vary the input parameter according to a user-specified distribution.
|
||||
// Thus, instead of VaryInputs(Measure(Repeat(func))), we change the
|
||||
// loop nesting to Measure(Repeat(VaryInputs(func))). We also estimate the
|
||||
// central tendency of the measurement samples with the "half sample mode",
|
||||
// which is more robust to outliers and skewed data than the mean or median.
|
||||
|
||||
// NOTE: for compatibility with multiple translation units compiled with
|
||||
// distinct flags, avoid #including headers that define functions.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal_nanobenchmark {
|
||||
|
||||
// Input influencing the function being measured (e.g. number of bytes to copy).
|
||||
using FuncInput = size_t;
|
||||
|
||||
// "Proof of work" returned by Func to ensure the compiler does not elide it.
|
||||
using FuncOutput = uint64_t;
|
||||
|
||||
// Function to measure: either 1) a captureless lambda or function with two
|
||||
// arguments or 2) a lambda with capture, in which case the first argument
|
||||
// is reserved for use by MeasureClosure.
|
||||
using Func = FuncOutput (*)(const void*, FuncInput);
|
||||
|
||||
// Internal parameters that determine precision/resolution/measuring time.
|
||||
struct Params {
|
||||
// For measuring timer overhead/resolution. Used in a nested loop =>
|
||||
// quadratic time, acceptable because we know timer overhead is "low".
|
||||
// constexpr because this is used to define array bounds.
|
||||
static constexpr size_t kTimerSamples = 256;
|
||||
|
||||
// Best-case precision, expressed as a divisor of the timer resolution.
|
||||
// Larger => more calls to Func and higher precision.
|
||||
size_t precision_divisor = 1024;
|
||||
|
||||
// Ratio between full and subset input distribution sizes. Cannot be less
|
||||
// than 2; larger values increase measurement time but more faithfully
|
||||
// model the given input distribution.
|
||||
size_t subset_ratio = 2;
|
||||
|
||||
// Together with the estimated Func duration, determines how many times to
|
||||
// call Func before checking the sample variability. Larger values increase
|
||||
// measurement time, memory/cache use and precision.
|
||||
double seconds_per_eval = 4E-3;
|
||||
|
||||
// The minimum number of samples before estimating the central tendency.
|
||||
size_t min_samples_per_eval = 7;
|
||||
|
||||
// The mode is better than median for estimating the central tendency of
|
||||
// skewed/fat-tailed distributions, but it requires sufficient samples
|
||||
// relative to the width of half-ranges.
|
||||
size_t min_mode_samples = 64;
|
||||
|
||||
// Maximum permissible variability (= median absolute deviation / center).
|
||||
double target_rel_mad = 0.002;
|
||||
|
||||
// Abort after this many evals without reaching target_rel_mad. This
|
||||
// prevents infinite loops.
|
||||
size_t max_evals = 9;
|
||||
|
||||
// Retry the measure loop up to this many times.
|
||||
size_t max_measure_retries = 2;
|
||||
|
||||
// Whether to print additional statistics to stdout.
|
||||
bool verbose = true;
|
||||
};
|
||||
|
||||
// Measurement result for each unique input.
|
||||
struct Result {
|
||||
FuncInput input;
|
||||
|
||||
// Robust estimate (mode or median) of duration.
|
||||
float ticks;
|
||||
|
||||
// Measure of variability (median absolute deviation relative to "ticks").
|
||||
float variability;
|
||||
};
|
||||
|
||||
// Ensures the thread is running on the specified cpu, and no others.
|
||||
// Reduces noise due to desynchronized socket RDTSC and context switches.
|
||||
// If "cpu" is negative, pin to the currently running core.
|
||||
void PinThreadToCPU(const int cpu = -1);
|
||||
|
||||
// Returns tick rate, useful for converting measurements to seconds. Invariant
|
||||
// means the tick counter frequency is independent of CPU throttling or sleep.
|
||||
// This call may be expensive, callers should cache the result.
|
||||
double InvariantTicksPerSecond();
|
||||
|
||||
// Precisely measures the number of ticks elapsed when calling "func" with the
|
||||
// given inputs, shuffled to ensure realistic branch prediction hit rates.
|
||||
//
|
||||
// "func" returns a 'proof of work' to ensure its computations are not elided.
|
||||
// "arg" is passed to Func, or reserved for internal use by MeasureClosure.
|
||||
// "inputs" is an array of "num_inputs" (not necessarily unique) arguments to
|
||||
// "func". The values should be chosen to maximize coverage of "func". This
|
||||
// represents a distribution, so a value's frequency should reflect its
|
||||
// probability in the real application. Order does not matter; for example, a
|
||||
// uniform distribution over [0, 4) could be represented as {3,0,2,1}.
|
||||
// Returns how many Result were written to "results": one per unique input, or
|
||||
// zero if the measurement failed (an error message goes to stderr).
|
||||
size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
|
||||
const size_t num_inputs, Result* results,
|
||||
const Params& p = Params());
|
||||
|
||||
// Calls operator() of the given closure (lambda function).
|
||||
template <class Closure>
|
||||
static FuncOutput CallClosure(const void* f, const FuncInput input) {
|
||||
return (*reinterpret_cast<const Closure*>(f))(input);
|
||||
}
|
||||
|
||||
// Same as Measure, except "closure" is typically a lambda function of
|
||||
// FuncInput -> FuncOutput with a capture list.
|
||||
template <class Closure>
|
||||
static inline size_t MeasureClosure(const Closure& closure,
|
||||
const FuncInput* inputs,
|
||||
const size_t num_inputs, Result* results,
|
||||
const Params& p = Params()) {
|
||||
return Measure(reinterpret_cast<Func>(&CallClosure<Closure>),
|
||||
reinterpret_cast<const void*>(&closure), inputs, num_inputs,
|
||||
results, p);
|
||||
}
|
||||
|
||||
} // namespace random_internal_nanobenchmark
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/nanobenchmark.h"
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal_nanobenchmark {
|
||||
namespace {
|
||||
|
||||
uint64_t Div(const void*, FuncInput in) {
|
||||
// Here we're measuring the throughput because benchmark invocations are
|
||||
// independent.
|
||||
const int64_t d1 = 0xFFFFFFFFFFll / int64_t(in); // IDIV
|
||||
return d1;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void MeasureDiv(const FuncInput (&inputs)[N]) {
|
||||
Result results[N];
|
||||
Params params;
|
||||
params.max_evals = 6; // avoid test timeout
|
||||
const size_t num_results = Measure(&Div, nullptr, inputs, N, results, params);
|
||||
if (num_results == 0) {
|
||||
LOG(WARNING)
|
||||
<< "WARNING: Measurement failed, should not happen when using "
|
||||
"PinThreadToCPU unless the region to measure takes > 1 second.";
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < num_results; ++i) {
|
||||
LOG(INFO) << absl::StreamFormat("%5u: %6.2f ticks; MAD=%4.2f%%\n",
|
||||
results[i].input, results[i].ticks,
|
||||
results[i].variability * 100.0);
|
||||
CHECK_NE(results[i].ticks, 0.0f) << "Zero duration";
|
||||
}
|
||||
}
|
||||
|
||||
void RunAll(const int argc, char* argv[]) {
|
||||
// Avoid migrating between cores - important on multi-socket systems.
|
||||
int cpu = -1;
|
||||
if (argc == 2) {
|
||||
if (!absl::SimpleAtoi(argv[1], &cpu)) {
|
||||
LOG(FATAL) << "The optional argument must be a CPU number >= 0.";
|
||||
}
|
||||
}
|
||||
PinThreadToCPU(cpu);
|
||||
|
||||
// unpredictable == 1 but the compiler doesn't know that.
|
||||
const FuncInput unpredictable = argc != 999;
|
||||
static const FuncInput inputs[] = {unpredictable * 10, unpredictable * 100};
|
||||
|
||||
MeasureDiv(inputs);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace random_internal_nanobenchmark
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
absl::random_internal_nanobenchmark::RunAll(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
|
||||
#define ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/internal/pool_urbg.h"
|
||||
#include "absl/random/internal/salted_seed_seq.h"
|
||||
#include "absl/random/internal/seed_material.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RandenPoolSeedSeq is a custom seed sequence type where generate() fills the
|
||||
// provided buffer via the RandenPool entropy source.
|
||||
class RandenPoolSeedSeq {
|
||||
private:
|
||||
struct ContiguousTag {};
|
||||
struct BufferTag {};
|
||||
|
||||
// Generate random unsigned values directly into the buffer.
|
||||
template <typename Contiguous>
|
||||
void generate_impl(ContiguousTag, Contiguous begin, Contiguous end) {
|
||||
const size_t n = static_cast<size_t>(std::distance(begin, end));
|
||||
auto* a = &(*begin);
|
||||
RandenPool<uint8_t>::Fill(
|
||||
absl::MakeSpan(reinterpret_cast<uint8_t*>(a), sizeof(*a) * n));
|
||||
}
|
||||
|
||||
// Construct a buffer of size n and fill it with values, then copy
|
||||
// those values into the seed iterators.
|
||||
template <typename RandomAccessIterator>
|
||||
void generate_impl(BufferTag, RandomAccessIterator begin,
|
||||
RandomAccessIterator end) {
|
||||
const size_t n = std::distance(begin, end);
|
||||
absl::InlinedVector<uint32_t, 8> data(n, 0);
|
||||
RandenPool<uint32_t>::Fill(absl::MakeSpan(data.begin(), data.end()));
|
||||
std::copy(std::begin(data), std::end(data), begin);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = uint32_t;
|
||||
|
||||
size_t size() { return 0; }
|
||||
|
||||
template <typename OutIterator>
|
||||
void param(OutIterator) const {}
|
||||
|
||||
template <typename RandomAccessIterator>
|
||||
void generate(RandomAccessIterator begin, RandomAccessIterator end) {
|
||||
// RandomAccessIterator must be assignable from uint32_t
|
||||
if (begin != end) {
|
||||
using U = typename std::iterator_traits<RandomAccessIterator>::value_type;
|
||||
// ContiguousTag indicates the common case of a known contiguous buffer,
|
||||
// which allows directly filling the buffer. In C++20,
|
||||
// std::contiguous_iterator_tag provides a mechanism for testing this
|
||||
// capability, however until Abseil's support requirements allow us to
|
||||
// assume C++20, limit checks to a few common cases.
|
||||
using TagType = absl::conditional_t<
|
||||
(std::is_pointer<RandomAccessIterator>::value ||
|
||||
std::is_same<RandomAccessIterator,
|
||||
typename std::vector<U>::iterator>::value),
|
||||
ContiguousTag, BufferTag>;
|
||||
|
||||
generate_impl(TagType{}, begin, end);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Each instance of NonsecureURBGBase<URBG> will be seeded by variates produced
|
||||
// by a thread-unique URBG-instance.
|
||||
template <typename URBG, typename Seeder = RandenPoolSeedSeq>
|
||||
class NonsecureURBGBase {
|
||||
public:
|
||||
using result_type = typename URBG::result_type;
|
||||
|
||||
// Default constructor
|
||||
NonsecureURBGBase() : urbg_(ConstructURBG()) {}
|
||||
|
||||
// Copy disallowed, move allowed.
|
||||
NonsecureURBGBase(const NonsecureURBGBase&) = delete;
|
||||
NonsecureURBGBase& operator=(const NonsecureURBGBase&) = delete;
|
||||
NonsecureURBGBase(NonsecureURBGBase&&) = default;
|
||||
NonsecureURBGBase& operator=(NonsecureURBGBase&&) = default;
|
||||
|
||||
// Constructor using a seed
|
||||
template <class SSeq, typename = typename absl::enable_if_t<
|
||||
!std::is_same<SSeq, NonsecureURBGBase>::value>>
|
||||
explicit NonsecureURBGBase(SSeq&& seq)
|
||||
: urbg_(ConstructURBG(std::forward<SSeq>(seq))) {}
|
||||
|
||||
// Note: on MSVC, min() or max() can be interpreted as MIN() or MAX(), so we
|
||||
// enclose min() or max() in parens as (min)() and (max)().
|
||||
// Additionally, clang-format requires no space before this construction.
|
||||
|
||||
// NonsecureURBGBase::min()
|
||||
static constexpr result_type(min)() { return (URBG::min)(); }
|
||||
|
||||
// NonsecureURBGBase::max()
|
||||
static constexpr result_type(max)() { return (URBG::max)(); }
|
||||
|
||||
// NonsecureURBGBase::operator()()
|
||||
result_type operator()() { return urbg_(); }
|
||||
|
||||
// NonsecureURBGBase::discard()
|
||||
void discard(unsigned long long values) { // NOLINT(runtime/int)
|
||||
urbg_.discard(values);
|
||||
}
|
||||
|
||||
bool operator==(const NonsecureURBGBase& other) const {
|
||||
return urbg_ == other.urbg_;
|
||||
}
|
||||
|
||||
bool operator!=(const NonsecureURBGBase& other) const {
|
||||
return !(urbg_ == other.urbg_);
|
||||
}
|
||||
|
||||
private:
|
||||
static URBG ConstructURBG() {
|
||||
Seeder seeder;
|
||||
return URBG(seeder);
|
||||
}
|
||||
|
||||
template <typename SSeq>
|
||||
static URBG ConstructURBG(SSeq&& seq) { // NOLINT(runtime/references)
|
||||
auto salted_seq =
|
||||
random_internal::MakeSaltedSeedSeq(std::forward<SSeq>(seq));
|
||||
return URBG(salted_seq);
|
||||
}
|
||||
|
||||
URBG urbg_;
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/nonsecure_base.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/distributions.h"
|
||||
#include "absl/random/random.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using ExampleNonsecureURBG =
|
||||
absl::random_internal::NonsecureURBGBase<std::mt19937>;
|
||||
|
||||
template <typename T>
|
||||
void Use(const T&) {}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(NonsecureURBGBase, DefaultConstructorIsValid) {
|
||||
ExampleNonsecureURBG urbg;
|
||||
}
|
||||
|
||||
// Ensure that the recommended template-instantiations are valid.
|
||||
TEST(RecommendedTemplates, CanBeConstructed) {
|
||||
absl::BitGen default_generator;
|
||||
absl::InsecureBitGen insecure_generator;
|
||||
}
|
||||
|
||||
TEST(RecommendedTemplates, CanDiscardValues) {
|
||||
absl::BitGen default_generator;
|
||||
absl::InsecureBitGen insecure_generator;
|
||||
|
||||
default_generator.discard(5);
|
||||
insecure_generator.discard(5);
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, StandardInterface) {
|
||||
// Names after definition of [rand.req.urbg] in C++ standard.
|
||||
// e us a value of E
|
||||
// v is a lvalue of E
|
||||
// x, y are possibly const values of E
|
||||
// s is a value of T
|
||||
// q is a value satisfying requirements of seed_sequence
|
||||
// z is a value of type unsigned long long
|
||||
// os is a some specialization of basic_ostream
|
||||
// is is a some specialization of basic_istream
|
||||
|
||||
using E = absl::random_internal::NonsecureURBGBase<std::minstd_rand>;
|
||||
|
||||
using T = typename E::result_type;
|
||||
|
||||
static_assert(!std::is_copy_constructible<E>::value,
|
||||
"NonsecureURBGBase should not be copy constructible");
|
||||
|
||||
static_assert(!absl::is_copy_assignable<E>::value,
|
||||
"NonsecureURBGBase should not be copy assignable");
|
||||
|
||||
static_assert(std::is_move_constructible<E>::value,
|
||||
"NonsecureURBGBase should be move constructible");
|
||||
|
||||
static_assert(absl::is_move_assignable<E>::value,
|
||||
"NonsecureURBGBase should be move assignable");
|
||||
|
||||
static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
|
||||
"return type of operator() must be result_type");
|
||||
|
||||
{
|
||||
const E x, y;
|
||||
Use(x);
|
||||
Use(y);
|
||||
|
||||
static_assert(std::is_same<decltype(x == y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
|
||||
static_assert(std::is_same<decltype(x != y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
}
|
||||
|
||||
E e;
|
||||
std::seed_seq q{1, 2, 3};
|
||||
|
||||
E{};
|
||||
E{q};
|
||||
|
||||
// Copy constructor not supported.
|
||||
// E{x};
|
||||
|
||||
// result_type seed constructor not supported.
|
||||
// E{T{1}};
|
||||
|
||||
// Move constructors are supported.
|
||||
{
|
||||
E tmp(q);
|
||||
E m = std::move(tmp);
|
||||
E n(std::move(m));
|
||||
EXPECT_TRUE(e != n);
|
||||
}
|
||||
|
||||
// Comparisons work.
|
||||
{
|
||||
// MSVC emits error 2718 when using EXPECT_EQ(e, x)
|
||||
// * actual parameter with __declspec(align('#')) won't be aligned
|
||||
E a(q);
|
||||
E b(q);
|
||||
|
||||
EXPECT_TRUE(a != e);
|
||||
EXPECT_TRUE(a == b);
|
||||
|
||||
a();
|
||||
EXPECT_TRUE(a != b);
|
||||
}
|
||||
|
||||
// e.seed(s) not supported.
|
||||
|
||||
// [rand.req.eng] specifies the parameter as 'unsigned long long'
|
||||
// e.discard(unsigned long long) is supported.
|
||||
unsigned long long z = 1; // NOLINT(runtime/int)
|
||||
e.discard(z);
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, SeedSeqConstructorIsValid) {
|
||||
std::seed_seq seq;
|
||||
ExampleNonsecureURBG rbg(seq);
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) {
|
||||
ExampleNonsecureURBG rbg;
|
||||
|
||||
absl::Uniform(rbg, 0, 100);
|
||||
absl::Uniform(rbg, 0.5, 0.7);
|
||||
absl::Poisson<uint32_t>(rbg);
|
||||
absl::Exponential<float>(rbg);
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, CompatibleWithStdDistributions) {
|
||||
ExampleNonsecureURBG rbg;
|
||||
|
||||
// Cast to void to suppress [[nodiscard]] warnings
|
||||
static_cast<void>(std::uniform_int_distribution<uint32_t>(0, 100)(rbg));
|
||||
static_cast<void>(std::uniform_real_distribution<float>()(rbg));
|
||||
static_cast<void>(std::bernoulli_distribution(0.2)(rbg));
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) {
|
||||
const size_t kNumSamples = 128;
|
||||
|
||||
ExampleNonsecureURBG rbg1;
|
||||
ExampleNonsecureURBG rbg2;
|
||||
|
||||
for (size_t i = 0; i < kNumSamples; i++) {
|
||||
EXPECT_NE(rbg1(), rbg2());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NonsecureURBGBase, EqualSeedSequencesYieldEqualVariates) {
|
||||
std::seed_seq seq;
|
||||
|
||||
ExampleNonsecureURBG rbg1(seq);
|
||||
ExampleNonsecureURBG rbg2(seq);
|
||||
|
||||
// ExampleNonsecureURBG rbg3({1, 2, 3}); // Should not compile.
|
||||
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
EXPECT_EQ(rbg1(), rbg2());
|
||||
}
|
||||
|
||||
rbg1.discard(100);
|
||||
rbg2.discard(100);
|
||||
|
||||
// The sequences should continue after discarding
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
EXPECT_EQ(rbg1(), rbg2());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RandenPoolSeedSeqTest, SeederWorksForU32) {
|
||||
absl::random_internal::RandenPoolSeedSeq seeder;
|
||||
|
||||
uint32_t state[2] = {0, 0};
|
||||
seeder.generate(std::begin(state), std::end(state));
|
||||
EXPECT_FALSE(state[0] == 0 && state[1] == 0);
|
||||
}
|
||||
|
||||
TEST(RandenPoolSeedSeqTest, SeederWorksForU64) {
|
||||
absl::random_internal::RandenPoolSeedSeq seeder;
|
||||
|
||||
uint64_t state[2] = {0, 0};
|
||||
seeder.generate(std::begin(state), std::end(state));
|
||||
EXPECT_FALSE(state[0] == 0 && state[1] == 0);
|
||||
EXPECT_FALSE((state[0] >> 32) == 0 && (state[1] >> 32) == 0);
|
||||
}
|
||||
|
||||
TEST(RandenPoolSeedSeqTest, SeederWorksForS32) {
|
||||
absl::random_internal::RandenPoolSeedSeq seeder;
|
||||
|
||||
int32_t state[2] = {0, 0};
|
||||
seeder.generate(std::begin(state), std::end(state));
|
||||
EXPECT_FALSE(state[0] == 0 && state[1] == 0);
|
||||
}
|
||||
|
||||
TEST(RandenPoolSeedSeqTest, SeederWorksForVector) {
|
||||
absl::random_internal::RandenPoolSeedSeq seeder;
|
||||
|
||||
std::vector<uint32_t> state(2);
|
||||
seeder.generate(std::begin(state), std::end(state));
|
||||
EXPECT_FALSE(state[0] == 0 && state[1] == 0);
|
||||
}
|
||||
287
TMessagesProj/jni/voip/webrtc/absl/random/internal/pcg_engine.h
Normal file
287
TMessagesProj/jni/voip/webrtc/absl/random/internal/pcg_engine.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
|
||||
#define ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/random/internal/fastmath.h"
|
||||
#include "absl/random/internal/iostream_state_saver.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in
|
||||
// C++. PCG combines a linear congruential generator (LCG) with output state
|
||||
// mixing functions to generate each random variate. pcg_engine supports only a
|
||||
// single sequence (oneseq), and does not support streams.
|
||||
//
|
||||
// pcg_engine is parameterized by two types:
|
||||
// Params, which provides the multiplier and increment values;
|
||||
// Mix, which mixes the state into the result.
|
||||
//
|
||||
template <typename Params, typename Mix>
|
||||
class pcg_engine {
|
||||
static_assert(std::is_same<typename Params::state_type,
|
||||
typename Mix::state_type>::value,
|
||||
"Class-template absl::pcg_engine must be parameterized by "
|
||||
"Params and Mix with identical state_type");
|
||||
|
||||
static_assert(std::is_unsigned<typename Mix::result_type>::value,
|
||||
"Class-template absl::pcg_engine must be parameterized by "
|
||||
"an unsigned Mix::result_type");
|
||||
|
||||
using params_type = Params;
|
||||
using mix_type = Mix;
|
||||
using state_type = typename Mix::state_type;
|
||||
|
||||
public:
|
||||
// C++11 URBG interface:
|
||||
using result_type = typename Mix::result_type;
|
||||
|
||||
static constexpr result_type(min)() {
|
||||
return (std::numeric_limits<result_type>::min)();
|
||||
}
|
||||
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
explicit pcg_engine(uint64_t seed_value = 0) { seed(seed_value); }
|
||||
|
||||
template <class SeedSequence,
|
||||
typename = typename absl::enable_if_t<
|
||||
!std::is_same<SeedSequence, pcg_engine>::value>>
|
||||
explicit pcg_engine(SeedSequence&& seq) {
|
||||
seed(seq);
|
||||
}
|
||||
|
||||
pcg_engine(const pcg_engine&) = default;
|
||||
pcg_engine& operator=(const pcg_engine&) = default;
|
||||
pcg_engine(pcg_engine&&) = default;
|
||||
pcg_engine& operator=(pcg_engine&&) = default;
|
||||
|
||||
result_type operator()() {
|
||||
// Advance the LCG state, always using the new value to generate the output.
|
||||
state_ = lcg(state_);
|
||||
return Mix{}(state_);
|
||||
}
|
||||
|
||||
void seed(uint64_t seed_value = 0) {
|
||||
state_type tmp = seed_value;
|
||||
state_ = lcg(tmp + Params::increment());
|
||||
}
|
||||
|
||||
template <class SeedSequence>
|
||||
typename absl::enable_if_t<
|
||||
!std::is_convertible<SeedSequence, uint64_t>::value, void>
|
||||
seed(SeedSequence&& seq) {
|
||||
reseed(seq);
|
||||
}
|
||||
|
||||
void discard(uint64_t count) { state_ = advance(state_, count); }
|
||||
|
||||
bool operator==(const pcg_engine& other) const {
|
||||
return state_ == other.state_;
|
||||
}
|
||||
|
||||
bool operator!=(const pcg_engine& other) const { return !(*this == other); }
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend typename absl::enable_if_t<(sizeof(state_type) == 16),
|
||||
std::basic_ostream<CharT, Traits>&>
|
||||
operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
|
||||
const pcg_engine& engine) {
|
||||
auto saver = random_internal::make_ostream_state_saver(os);
|
||||
random_internal::stream_u128_helper<state_type> helper;
|
||||
helper.write(pcg_engine::params_type::multiplier(), os);
|
||||
os << os.fill();
|
||||
helper.write(pcg_engine::params_type::increment(), os);
|
||||
os << os.fill();
|
||||
helper.write(engine.state_, os);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend typename absl::enable_if_t<(sizeof(state_type) <= 8),
|
||||
std::basic_ostream<CharT, Traits>&>
|
||||
operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
|
||||
const pcg_engine& engine) {
|
||||
auto saver = random_internal::make_ostream_state_saver(os);
|
||||
os << pcg_engine::params_type::multiplier() << os.fill();
|
||||
os << pcg_engine::params_type::increment() << os.fill();
|
||||
os << engine.state_;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend typename absl::enable_if_t<(sizeof(state_type) == 16),
|
||||
std::basic_istream<CharT, Traits>&>
|
||||
operator>>(
|
||||
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
|
||||
pcg_engine& engine) { // NOLINT(runtime/references)
|
||||
random_internal::stream_u128_helper<state_type> helper;
|
||||
auto mult = helper.read(is);
|
||||
auto inc = helper.read(is);
|
||||
auto tmp = helper.read(is);
|
||||
if (mult != pcg_engine::params_type::multiplier() ||
|
||||
inc != pcg_engine::params_type::increment()) {
|
||||
// signal failure by setting the failbit.
|
||||
is.setstate(is.rdstate() | std::ios_base::failbit);
|
||||
}
|
||||
if (!is.fail()) {
|
||||
engine.state_ = tmp;
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend typename absl::enable_if_t<(sizeof(state_type) <= 8),
|
||||
std::basic_istream<CharT, Traits>&>
|
||||
operator>>(
|
||||
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
|
||||
pcg_engine& engine) { // NOLINT(runtime/references)
|
||||
state_type mult{}, inc{}, tmp{};
|
||||
is >> mult >> inc >> tmp;
|
||||
if (mult != pcg_engine::params_type::multiplier() ||
|
||||
inc != pcg_engine::params_type::increment()) {
|
||||
// signal failure by setting the failbit.
|
||||
is.setstate(is.rdstate() | std::ios_base::failbit);
|
||||
}
|
||||
if (!is.fail()) {
|
||||
engine.state_ = tmp;
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
private:
|
||||
state_type state_;
|
||||
|
||||
// Returns the linear-congruential generator next state.
|
||||
static inline constexpr state_type lcg(state_type s) {
|
||||
return s * Params::multiplier() + Params::increment();
|
||||
}
|
||||
|
||||
// Returns the linear-congruential arbitrary seek state.
|
||||
inline state_type advance(state_type s, uint64_t n) const {
|
||||
state_type mult = Params::multiplier();
|
||||
state_type inc = Params::increment();
|
||||
state_type m = 1;
|
||||
state_type i = 0;
|
||||
while (n > 0) {
|
||||
if (n & 1) {
|
||||
m *= mult;
|
||||
i = i * mult + inc;
|
||||
}
|
||||
inc = (mult + 1) * inc;
|
||||
mult *= mult;
|
||||
n >>= 1;
|
||||
}
|
||||
return m * s + i;
|
||||
}
|
||||
|
||||
template <class SeedSequence>
|
||||
void reseed(SeedSequence& seq) {
|
||||
using sequence_result_type = typename SeedSequence::result_type;
|
||||
constexpr size_t kBufferSize =
|
||||
sizeof(state_type) / sizeof(sequence_result_type);
|
||||
sequence_result_type buffer[kBufferSize];
|
||||
seq.generate(std::begin(buffer), std::end(buffer));
|
||||
// Convert the seed output to a single state value.
|
||||
state_type tmp = buffer[0];
|
||||
for (size_t i = 1; i < kBufferSize; i++) {
|
||||
tmp <<= (sizeof(sequence_result_type) * 8);
|
||||
tmp |= buffer[i];
|
||||
}
|
||||
state_ = lcg(tmp + params_type::increment());
|
||||
}
|
||||
};
|
||||
|
||||
// Parameterized implementation of the PCG 128-bit oneseq state.
|
||||
// This provides state_type, multiplier, and increment for pcg_engine.
|
||||
template <uint64_t kMultA, uint64_t kMultB, uint64_t kIncA, uint64_t kIncB>
|
||||
class pcg128_params {
|
||||
public:
|
||||
using state_type = absl::uint128;
|
||||
static inline constexpr state_type multiplier() {
|
||||
return absl::MakeUint128(kMultA, kMultB);
|
||||
}
|
||||
static inline constexpr state_type increment() {
|
||||
return absl::MakeUint128(kIncA, kIncB);
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which
|
||||
// accepts an input of state_type and mixes it into an output of result_type.
|
||||
struct pcg_xsl_rr_128_64 {
|
||||
using state_type = absl::uint128;
|
||||
using result_type = uint64_t;
|
||||
|
||||
inline uint64_t operator()(state_type state) {
|
||||
// This is equivalent to the xsl_rr_128_64 mixing function.
|
||||
uint64_t rotate = static_cast<uint64_t>(state >> 122u);
|
||||
state ^= state >> 64;
|
||||
uint64_t s = static_cast<uint64_t>(state);
|
||||
return rotr(s, static_cast<int>(rotate));
|
||||
}
|
||||
};
|
||||
|
||||
// Parameterized implementation of the PCG 64-bit oneseq state.
|
||||
// This provides state_type, multiplier, and increment for pcg_engine.
|
||||
template <uint64_t kMult, uint64_t kInc>
|
||||
class pcg64_params {
|
||||
public:
|
||||
using state_type = uint64_t;
|
||||
static inline constexpr state_type multiplier() { return kMult; }
|
||||
static inline constexpr state_type increment() { return kInc; }
|
||||
};
|
||||
|
||||
// Implementation of the PCG xsh_rr_64_32 64-bit mixing function, which accepts
|
||||
// an input of state_type and mixes it into an output of result_type.
|
||||
struct pcg_xsh_rr_64_32 {
|
||||
using state_type = uint64_t;
|
||||
using result_type = uint32_t;
|
||||
inline uint32_t operator()(uint64_t state) {
|
||||
return rotr(static_cast<uint32_t>(((state >> 18) ^ state) >> 27),
|
||||
state >> 59);
|
||||
}
|
||||
};
|
||||
|
||||
// Stable pcg_engine implementations:
|
||||
// This is a 64-bit generator using 128-bits of state.
|
||||
// The output sequence is equivalent to Melissa O'Neil's pcg64_oneseq.
|
||||
using pcg64_2018_engine = pcg_engine<
|
||||
random_internal::pcg128_params<0x2360ed051fc65da4ull, 0x4385df649fccf645ull,
|
||||
0x5851f42d4c957f2d, 0x14057b7ef767814f>,
|
||||
random_internal::pcg_xsl_rr_128_64>;
|
||||
|
||||
// This is a 32-bit generator using 64-bits of state.
|
||||
// This is equivalent to Melissa O'Neil's pcg32_oneseq.
|
||||
using pcg32_2018_engine = pcg_engine<
|
||||
random_internal::pcg64_params<0x5851f42d4c957f2dull, 0x14057b7ef767814full>,
|
||||
random_internal::pcg_xsh_rr_64_32>;
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
|
||||
|
|
@ -0,0 +1,638 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/pcg_engine.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/random/internal/explicit_seed_seq.h"
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#define UPDATE_GOLDEN 0
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::ExplicitSeedSeq;
|
||||
using absl::random_internal::pcg32_2018_engine;
|
||||
using absl::random_internal::pcg64_2018_engine;
|
||||
|
||||
template <typename EngineType>
|
||||
class PCGEngineTest : public ::testing::Test {};
|
||||
|
||||
using EngineTypes = ::testing::Types<pcg64_2018_engine, pcg32_2018_engine>;
|
||||
|
||||
TYPED_TEST_SUITE(PCGEngineTest, EngineTypes);
|
||||
|
||||
TYPED_TEST(PCGEngineTest, VerifyReseedChangesAllValues) {
|
||||
using engine_type = TypeParam;
|
||||
using result_type = typename engine_type::result_type;
|
||||
|
||||
const size_t kNumOutputs = 16;
|
||||
engine_type engine;
|
||||
|
||||
// MSVC emits error 2719 without the use of std::ref below.
|
||||
// * formal parameter with __declspec(align('#')) won't be aligned
|
||||
|
||||
{
|
||||
std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7};
|
||||
engine.seed(seq1);
|
||||
}
|
||||
result_type a[kNumOutputs];
|
||||
std::generate(std::begin(a), std::end(a), std::ref(engine));
|
||||
|
||||
{
|
||||
std::random_device rd;
|
||||
std::seed_seq seq2{rd(), rd(), rd()};
|
||||
engine.seed(seq2);
|
||||
}
|
||||
result_type b[kNumOutputs];
|
||||
std::generate(std::begin(b), std::end(b), std::ref(engine));
|
||||
|
||||
// Verify that two uncorrelated values have ~50% of there bits in common. Use
|
||||
// a 10% margin-of-error to reduce flakiness.
|
||||
size_t changed_bits = 0;
|
||||
size_t unchanged_bits = 0;
|
||||
size_t total_set = 0;
|
||||
size_t total_bits = 0;
|
||||
size_t equal_count = 0;
|
||||
for (size_t i = 0; i < kNumOutputs; ++i) {
|
||||
equal_count += (a[i] == b[i]) ? 1 : 0;
|
||||
std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
|
||||
changed_bits += bitset.count();
|
||||
unchanged_bits += bitset.size() - bitset.count();
|
||||
|
||||
std::bitset<sizeof(result_type) * 8> a_set(a[i]);
|
||||
std::bitset<sizeof(result_type) * 8> b_set(b[i]);
|
||||
total_set += a_set.count() + b_set.count();
|
||||
total_bits += 2 * 8 * sizeof(result_type);
|
||||
}
|
||||
// On average, half the bits are changed between two calls.
|
||||
EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
|
||||
EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
|
||||
|
||||
// verify using a quick normal-approximation to the binomial.
|
||||
EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
|
||||
<< "@" << total_set / static_cast<double>(total_bits);
|
||||
|
||||
// Also, A[i] == B[i] with probability (1/range) * N.
|
||||
// Give this a pretty wide latitude, though.
|
||||
const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
|
||||
EXPECT_LE(equal_count, 1.0 + kExpected);
|
||||
}
|
||||
|
||||
// Number of values that needs to be consumed to clean two sizes of buffer
|
||||
// and trigger third refresh. (slightly overestimates the actual state size).
|
||||
constexpr size_t kTwoBufferValues = 16;
|
||||
|
||||
TYPED_TEST(PCGEngineTest, VerifyDiscard) {
|
||||
using engine_type = TypeParam;
|
||||
|
||||
for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) {
|
||||
engine_type engine_used;
|
||||
for (size_t i = 0; i < num_used; ++i) {
|
||||
engine_used();
|
||||
}
|
||||
|
||||
for (size_t num_discard = 0; num_discard < kTwoBufferValues;
|
||||
++num_discard) {
|
||||
engine_type engine1 = engine_used;
|
||||
engine_type engine2 = engine_used;
|
||||
for (size_t i = 0; i < num_discard; ++i) {
|
||||
engine1();
|
||||
}
|
||||
engine2.discard(num_discard);
|
||||
for (size_t i = 0; i < kTwoBufferValues; ++i) {
|
||||
const auto r1 = engine1();
|
||||
const auto r2 = engine2();
|
||||
ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(PCGEngineTest, StreamOperatorsResult) {
|
||||
using engine_type = TypeParam;
|
||||
|
||||
std::wostringstream os;
|
||||
std::wistringstream is;
|
||||
engine_type engine;
|
||||
|
||||
EXPECT_EQ(&(os << engine), &os);
|
||||
EXPECT_EQ(&(is >> engine), &is);
|
||||
}
|
||||
|
||||
TYPED_TEST(PCGEngineTest, StreamSerialization) {
|
||||
using engine_type = TypeParam;
|
||||
|
||||
for (size_t discard = 0; discard < kTwoBufferValues; ++discard) {
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
engine_type engine(seed_sequence);
|
||||
engine.discard(discard);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << engine;
|
||||
|
||||
engine_type new_engine;
|
||||
stream >> new_engine;
|
||||
for (size_t i = 0; i < 64; ++i) {
|
||||
EXPECT_EQ(engine(), new_engine()) << " " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t kNumGoldenOutputs = 127;
|
||||
|
||||
// This test is checking if randen_engine is meets interface requirements
|
||||
// defined in [rand.req.urbg].
|
||||
TYPED_TEST(PCGEngineTest, RandomNumberEngineInterface) {
|
||||
using engine_type = TypeParam;
|
||||
|
||||
using E = engine_type;
|
||||
using T = typename E::result_type;
|
||||
|
||||
static_assert(std::is_copy_constructible<E>::value,
|
||||
"engine_type must be copy constructible");
|
||||
|
||||
static_assert(absl::is_copy_assignable<E>::value,
|
||||
"engine_type must be copy assignable");
|
||||
|
||||
static_assert(std::is_move_constructible<E>::value,
|
||||
"engine_type must be move constructible");
|
||||
|
||||
static_assert(absl::is_move_assignable<E>::value,
|
||||
"engine_type must be move assignable");
|
||||
|
||||
static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
|
||||
"return type of operator() must be result_type");
|
||||
|
||||
// Names after definition of [rand.req.urbg] in C++ standard.
|
||||
// e us a value of E
|
||||
// v is a lvalue of E
|
||||
// x, y are possibly const values of E
|
||||
// s is a value of T
|
||||
// q is a value satisfying requirements of seed_sequence
|
||||
// z is a value of type unsigned long long
|
||||
// os is a some specialization of basic_ostream
|
||||
// is is a some specialization of basic_istream
|
||||
|
||||
E e, v;
|
||||
const E x, y;
|
||||
T s = 1;
|
||||
std::seed_seq q{1, 2, 3};
|
||||
unsigned long long z = 1; // NOLINT(runtime/int)
|
||||
std::wostringstream os;
|
||||
std::wistringstream is;
|
||||
|
||||
E{};
|
||||
E{x};
|
||||
E{s};
|
||||
E{q};
|
||||
|
||||
e.seed();
|
||||
|
||||
// MSVC emits error 2718 when using EXPECT_EQ(e, x)
|
||||
// * actual parameter with __declspec(align('#')) won't be aligned
|
||||
EXPECT_TRUE(e == x);
|
||||
|
||||
e.seed(q);
|
||||
{
|
||||
E tmp(q);
|
||||
EXPECT_TRUE(e == tmp);
|
||||
}
|
||||
|
||||
e();
|
||||
{
|
||||
E tmp(q);
|
||||
EXPECT_TRUE(e != tmp);
|
||||
}
|
||||
|
||||
e.discard(z);
|
||||
|
||||
static_assert(std::is_same<decltype(x == y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
|
||||
static_assert(std::is_same<decltype(x != y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
}
|
||||
|
||||
TYPED_TEST(PCGEngineTest, RandenEngineSFINAETest) {
|
||||
using engine_type = TypeParam;
|
||||
using result_type = typename engine_type::result_type;
|
||||
|
||||
{
|
||||
engine_type engine(result_type(1));
|
||||
engine.seed(result_type(1));
|
||||
}
|
||||
|
||||
{
|
||||
result_type n = 1;
|
||||
engine_type engine(n);
|
||||
engine.seed(n);
|
||||
}
|
||||
|
||||
{
|
||||
engine_type engine(1);
|
||||
engine.seed(1);
|
||||
}
|
||||
|
||||
{
|
||||
int n = 1;
|
||||
engine_type engine(n);
|
||||
engine.seed(n);
|
||||
}
|
||||
|
||||
{
|
||||
std::seed_seq seed_seq;
|
||||
engine_type engine(seed_seq);
|
||||
engine.seed(seed_seq);
|
||||
}
|
||||
|
||||
{
|
||||
engine_type engine{std::seed_seq()};
|
||||
engine.seed(std::seed_seq());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Stability tests for pcg64_2018_engine
|
||||
// ------------------------------------------------------------------
|
||||
TEST(PCG642018EngineTest, VerifyGolden) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0x01070196e695f8f1, 0x703ec840c59f4493, 0xe54954914b3a44fa,
|
||||
0x96130ff204b9285e, 0x7d9fdef535ceb21a, 0x666feed42e1219a0,
|
||||
0x981f685721c8326f, 0xad80710d6eab4dda, 0xe202c480b037a029,
|
||||
0x5d3390eaedd907e2, 0x0756befb39c6b8aa, 0x1fb44ba6634d62a3,
|
||||
0x8d20423662426642, 0x34ea910167a39fb4, 0x93010b43a80d0ab6,
|
||||
0x663db08a98fc568a, 0x720b0a1335956fae, 0x2c35483e31e1d3ba,
|
||||
0x429f39776337409d, 0xb46d99e638687344, 0x105370b96aedcaee,
|
||||
0x3999e92f811cff71, 0xd230f8bcb591cfc9, 0x0dce3db2ba7bdea5,
|
||||
0xcf2f52c91eec99af, 0x2bc7c24a8b998a39, 0xbd8af1b0d599a19c,
|
||||
0x56bc45abc66059f5, 0x170a46dc170f7f1e, 0xc25daf5277b85fad,
|
||||
0xe629c2e0c948eadb, 0x1720a796915542ed, 0x22fb0caa4f909951,
|
||||
0x7e0c0f4175acd83d, 0xd9fcab37ff2a860c, 0xab2280fb2054bad1,
|
||||
0x58e8a06f37fa9e99, 0xc3a52a30b06528c7, 0x0175f773a13fc1bd,
|
||||
0x731cfc584b00e840, 0x404cc7b2648069cb, 0x5bc29153b0b7f783,
|
||||
0x771310a38cc999d1, 0x766a572f0a71a916, 0x90f450fb4fc48348,
|
||||
0xf080ea3e1c7b1a0d, 0x15471a4507d66a44, 0x7d58e55a78f3df69,
|
||||
0x0130a094576ac99c, 0x46669cb2d04b1d87, 0x17ab5bed20191840,
|
||||
0x95b177d260adff3e, 0x025fb624b6ee4c07, 0xb35de4330154a95f,
|
||||
0xe8510fff67e24c79, 0x132c3cbcd76ed2d3, 0x35e7cc145a093904,
|
||||
0x9f5b5b5f81583b79, 0x3ee749a533966233, 0x4af85886cdeda8cd,
|
||||
0x0ca5380ecb3ef3aa, 0x4f674eb7661d3192, 0x88a29aad00cd7733,
|
||||
0x70b627ca045ffac6, 0x5912b43ea887623d, 0x95dc9fc6f62cf221,
|
||||
0x926081a12a5c905b, 0x9c57d4cd7dfce651, 0x85ab2cbf23e3bb5d,
|
||||
0xc5cd669f63023152, 0x3067be0fad5d898e, 0x12b56f444cb53d05,
|
||||
0xbc2e5a640c3434fc, 0x9280bff0e4613fe1, 0x98819094c528743e,
|
||||
0x999d1c98d829df33, 0x9ff82a012dc89242, 0xf99183ed39c8be94,
|
||||
0xf0f59161cd421c55, 0x3c705730c2f6c48d, 0x66ad85c6e9278a61,
|
||||
0x2a3428e4a428d5d0, 0x79207d68fd04940d, 0xea7f2b402edc8430,
|
||||
0xa06b419ac857f63b, 0xcb1dd0e6fbc47e1c, 0x4f55229200ada6a4,
|
||||
0x9647b5e6359c927f, 0x30bf8f9197c7efe5, 0xa79519529cc384d0,
|
||||
0xbb22c4f339ad6497, 0xd7b9782f59d14175, 0x0dff12fff2ec0118,
|
||||
0xa331ad8305343a7c, 0x48dad7e3f17e0862, 0x324c6fb3fd3c9665,
|
||||
0xf0e4350e7933dfc4, 0x7ccda2f30b8b03b6, 0xa0afc6179005de40,
|
||||
0xee65da6d063b3a30, 0xb9506f42f2bfe87a, 0xc9a2e26b0ef5baa0,
|
||||
0x39fa9d4f495011d6, 0xbecc21a45d023948, 0x6bf484c6593f737f,
|
||||
0x8065e0070cadc3b7, 0x9ef617ed8d419799, 0xac692cf8c233dd15,
|
||||
0xd2ed87583c4ebb98, 0xad95ba1bebfedc62, 0x9b60b160a8264e43,
|
||||
0x0bc8c45f71fcf25b, 0x4a78035cdf1c9931, 0x4602dc106667e029,
|
||||
0xb335a3c250498ac8, 0x0256ebc4df20cab8, 0x0c61efd153f0c8d9,
|
||||
0xe5d0150a4f806f88, 0x99d6521d351e7d87, 0x8d4888c9f80f4325,
|
||||
0x106c5735c1ba868d, 0x73414881b880a878, 0x808a9a58a3064751,
|
||||
0x339a29f3746de3d5, 0x5410d7fa4f873896, 0xd84623c81d7b8a03,
|
||||
0x1f7c7e7a7f47f462,
|
||||
};
|
||||
|
||||
pcg64_2018_engine engine(0);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed();
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PCG642018EngineTest, VerifyGoldenSeeded) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0xb03988f1e39691ee, 0xbd2a1eb5ac31e97a, 0x8f00d6d433634d02,
|
||||
0x1823c28d483d5776, 0x000c3ee3e1aeb74a, 0xfa82ef27a4f3df9c,
|
||||
0xc6f382308654e454, 0x414afb1a238996c2, 0x4703a4bc252eb411,
|
||||
0x99d64f62c8f7f654, 0xbb07ebe11a34fa44, 0x79eb06a363c06131,
|
||||
0xf66ad3756f1c6b21, 0x130c01d5e869f457, 0x5ca2b9963aecbc81,
|
||||
0xfef7bebc1de27e6c, 0x1d174faa5ed2cdbf, 0xd75b7a773f2bb889,
|
||||
0xc35c872327a170a5, 0x46da6d88646a42fe, 0x4622985e0442dae2,
|
||||
0xbe3cbd67297f1f9b, 0xe7c37b4a4798bfd1, 0x173d5dfad15a25c3,
|
||||
0x0eb6849ba2961522, 0xb0ff7246e6700d73, 0x88cb9c42d3afa577,
|
||||
0xb609731dbd94d917, 0xd3941cda04b40081, 0x28d140f7409bea3a,
|
||||
0x3c96699a920a124a, 0xdb28be521958b2fd, 0x0a3f44db3d4c5124,
|
||||
0x7ac8e60ba13b70d2, 0x75f03a41ded5195a, 0xaed10ac7c4e4825d,
|
||||
0xb92a3b18aadb7adc, 0xda45e0081f2bca46, 0x74d39ab3753143fc,
|
||||
0xb686038018fac9ca, 0x4cc309fe99542dbb, 0xf3e1a4fcb311097c,
|
||||
0x58763d6fa698d69d, 0xd11c365dbecd8d60, 0x2c15d55725b1dee7,
|
||||
0x89805f254d85658c, 0x2374c44dfc62158b, 0x9a8350fa7995328d,
|
||||
0x198f838970cf91da, 0x96aff569562c0e53, 0xd76c8c52b7ec6e3f,
|
||||
0x23a01cd9ae4baa81, 0x3adb366b6d02a893, 0xb3313e2a4c5b333f,
|
||||
0x04c11230b96a5425, 0x1f7f7af04787d571, 0xaddb019365275ec7,
|
||||
0x5c960468ccb09f42, 0x8438db698c69a44a, 0x492be1e46111637e,
|
||||
0x9c6c01e18100c610, 0xbfe48e75b7d0aceb, 0xb5e0b89ec1ce6a00,
|
||||
0x9d280ecbc2fe8997, 0x290d9e991ba5fcab, 0xeec5bec7d9d2a4f0,
|
||||
0x726e81488f19150e, 0x1a6df7955a7e462c, 0x37a12d174ba46bb5,
|
||||
0x3cdcdffd96b1b5c5, 0x2c5d5ac10661a26e, 0xa742ed18f22e50c4,
|
||||
0x00e0ed88ff0d8a35, 0x3d3c1718cb1efc0b, 0x1d70c51ffbccbf11,
|
||||
0xfbbb895132a4092f, 0x619d27f2fb095f24, 0x69af68200985e5c4,
|
||||
0xbee4885f57373f8d, 0x10b7a6bfe0587e40, 0xa885e6cf2f7e5f0a,
|
||||
0x59f879464f767550, 0x24e805d69056990d, 0x860970b911095891,
|
||||
0xca3189954f84170d, 0x6652a5edd4590134, 0x5e1008cef76174bf,
|
||||
0xcbd417881f2bcfe5, 0xfd49fc9d706ecd17, 0xeebf540221ebd066,
|
||||
0x46af7679464504cb, 0xd4028486946956f1, 0xd4f41864b86c2103,
|
||||
0x7af090e751583372, 0x98cdaa09278cb642, 0xffd42b921215602f,
|
||||
0x1d05bec8466b1740, 0xf036fa78a0132044, 0x787880589d1ecc78,
|
||||
0x5644552cfef33230, 0x0a97e275fe06884b, 0x96d1b13333d470b5,
|
||||
0xc8b3cdad52d3b034, 0x091357b9db7376fd, 0xa5fe4232555edf8c,
|
||||
0x3371bc3b6ada76b5, 0x7deeb2300477c995, 0x6fc6d4244f2849c1,
|
||||
0x750e8cc797ca340a, 0x81728613cd79899f, 0x3467f4ee6f9aeb93,
|
||||
0x5ef0a905f58c640f, 0x432db85e5101c98a, 0x6488e96f46ac80c2,
|
||||
0x22fddb282625048c, 0x15b287a0bc2d4c5d, 0xa7e2343ef1f28bce,
|
||||
0xc87ee1aa89bed09e, 0x220610107812c5e9, 0xcbdab6fcd640f586,
|
||||
0x8d41047970928784, 0x1aa431509ec1ade0, 0xac3f0be53f518ddc,
|
||||
0x16f4428ad81d0cbb, 0x675b13c2736fc4bb, 0x6db073afdd87e32d,
|
||||
0x572f3ca2f1a078c6,
|
||||
};
|
||||
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
pcg64_2018_engine engine(seed_sequence);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed(seed_sequence);
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PCG642018EngineTest, VerifyGoldenFromDeserializedEngine) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0xdd425b47b4113dea, 0x1b07176479d444b0, 0x6b391027586f2e42,
|
||||
0xa166f2b15f4a2143, 0xffb6dbd7a179ee97, 0xb2c00035365bf0b1,
|
||||
0x8fbb518b45855521, 0xfc789a55ddf87c3b, 0x429531f0f17ff355,
|
||||
0xbe708560d603d283, 0x5bff415175c5cb6b, 0xe813491f4ad45394,
|
||||
0xa853f4506d55880d, 0x7e538453e568172e, 0xe101f1e098ddd0ec,
|
||||
0x6ee31266ee4c766d, 0xa8786d92d66b39d7, 0xfee622a2acf5e5b0,
|
||||
0x5fe8e82c102fa7b3, 0x01f10be4cdb53c9d, 0xbe0545366f857022,
|
||||
0x12e74f010a339bca, 0xb10d85ca40d5ce34, 0xe80d6feba5054875,
|
||||
0x2b7c1ee6d567d4ee, 0x2a9cd043bfd03b66, 0x5cfc531bd239f3f1,
|
||||
0x1c4734e4647d70f5, 0x85a8f60f006b5760, 0x6a4239ce76dca387,
|
||||
0x8da0f86d7339335c, 0xf055b0468551374d, 0x486e8567e9bea9a0,
|
||||
0x4cb531b8405192dd, 0xf813b1ee3157110b, 0x214c2a664a875d8e,
|
||||
0x74531237b29b35f7, 0xa6f0267bb77a771e, 0x64b552bff54184a4,
|
||||
0xa2d6f7af2d75b6fc, 0x460a10018e03b5ab, 0x76fd1fdcb81d0800,
|
||||
0x76f5f81805070d9d, 0x1fb75cb1a70b289a, 0x9dfd25a022c4b27f,
|
||||
0x9a31a14a80528e9e, 0x910dc565ddc25820, 0xd6aef8e2b0936c10,
|
||||
0xe1773c507fe70225, 0xe027fd7aadd632bc, 0xc1fecb427089c8b8,
|
||||
0xb5c74c69fa9dbf26, 0x71bf9b0e4670227d, 0x25f48fad205dcfdd,
|
||||
0x905248ec4d689c56, 0x5c2b7631b0de5c9d, 0x9f2ee0f8f485036c,
|
||||
0xfd6ce4ebb90bf7ea, 0xd435d20046085574, 0x6b7eadcb0625f986,
|
||||
0x679d7d44b48be89e, 0x49683b8e1cdc49de, 0x4366cf76e9a2f4ca,
|
||||
0x54026ec1cdad7bed, 0xa9a04385207f28d3, 0xc8e66de4eba074b2,
|
||||
0x40b08c42de0f4cc0, 0x1d4c5e0e93c5bbc0, 0x19b80792e470ae2d,
|
||||
0x6fcaaeaa4c2a5bd9, 0xa92cb07c4238438e, 0x8bb5c918a007e298,
|
||||
0x7cd671e944874cf4, 0x88166470b1ba3cac, 0xd013d476eaeeade6,
|
||||
0xcee416947189b3c3, 0x5d7c16ab0dce6088, 0xd3578a5c32b13d27,
|
||||
0x3875db5adc9cc973, 0xfbdaba01c5b5dc56, 0xffc4fdd391b231c3,
|
||||
0x2334520ecb164fec, 0x361c115e7b6de1fa, 0xeee58106cc3563d7,
|
||||
0x8b7f35a8db25ebb8, 0xb29d00211e2cafa6, 0x22a39fe4614b646b,
|
||||
0x92ca6de8b998506d, 0x40922fe3d388d1db, 0x9da47f1e540f802a,
|
||||
0x811dceebf16a25db, 0xf6524ae22e0e53a9, 0x52d9e780a16eb99d,
|
||||
0x4f504286bb830207, 0xf6654d4786bd5cc3, 0x00bd98316003a7e1,
|
||||
0xefda054a6ab8f5f3, 0x46cfb0f4c1872827, 0xc22b316965c0f3b2,
|
||||
0xd1a28087c7e7562a, 0xaa4f6a094b7f5cff, 0xfe2bc853a041f7da,
|
||||
0xe9d531402a83c3ba, 0xe545d8663d3ce4dd, 0xfa2dcd7d91a13fa8,
|
||||
0xda1a080e52a127b8, 0x19c98f1f809c3d84, 0x2cef109af4678c88,
|
||||
0x53462accab3b9132, 0x176b13a80415394e, 0xea70047ef6bc178b,
|
||||
0x57bca80506d6dcdf, 0xd853ba09ff09f5c4, 0x75f4df3a7ddd4775,
|
||||
0x209c367ade62f4fe, 0xa9a0bbc74d5f4682, 0x5dfe34bada86c21a,
|
||||
0xc2c05bbcd38566d1, 0x6de8088e348c916a, 0x6a7001c6000c2196,
|
||||
0xd9fb51865fc4a367, 0x12f320e444ece8ff, 0x6d56f7f793d65035,
|
||||
0x138f31b7a865f8aa, 0x58fc68b4026b9adf, 0xcd48954b79fb6436,
|
||||
0x27dfce4a0232af87,
|
||||
};
|
||||
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
std::seed_seq seed_sequence{1, 2, 3};
|
||||
pcg64_2018_engine engine(seed_sequence);
|
||||
std::ostringstream stream;
|
||||
stream << engine;
|
||||
auto str = stream.str();
|
||||
printf("%s\n\n", str.c_str());
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
pcg64_2018_engine engine;
|
||||
std::istringstream stream(
|
||||
"2549297995355413924 4865540595714422341 6364136223846793005 "
|
||||
"1442695040888963407 18088519957565336995 4845369368158826708");
|
||||
stream >> engine;
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Stability tests for pcg32_2018_engine
|
||||
// ------------------------------------------------------------------
|
||||
TEST(PCG322018EngineTest, VerifyGolden) {
|
||||
constexpr uint32_t kGolden[kNumGoldenOutputs] = {
|
||||
0x7a7ecbd9, 0x89fd6c06, 0xae646aa8, 0xcd3cf945, 0x6204b303, 0x198c8585,
|
||||
0x49fce611, 0xd1e9297a, 0x142d9440, 0xee75f56b, 0x473a9117, 0xe3a45903,
|
||||
0xbce807a1, 0xe54e5f4d, 0x497d6c51, 0x61829166, 0xa740474b, 0x031912a8,
|
||||
0x9de3defa, 0xd266dbf1, 0x0f38bebb, 0xec3c4f65, 0x07c5057d, 0xbbce03c8,
|
||||
0xfd2ac7a8, 0xffcf4773, 0x5b10affb, 0xede1c842, 0xe22b01b7, 0xda133c8c,
|
||||
0xaf89b0f4, 0x25d1b8bc, 0x9f625482, 0x7bfd6882, 0x2e2210c0, 0x2c8fb9a6,
|
||||
0x42cb3b83, 0x40ce0dab, 0x644a3510, 0x36230ef2, 0xe2cb6d43, 0x1012b343,
|
||||
0x746c6c9f, 0x36714cf8, 0xed1f5026, 0x8bbbf83e, 0xe98710f4, 0x8a2afa36,
|
||||
0x09035349, 0x6dc1a487, 0x682b634b, 0xc106794f, 0x7dd78beb, 0x628c262b,
|
||||
0x852fb232, 0xb153ac4c, 0x4f169d1b, 0xa69ab774, 0x4bd4b6f2, 0xdc351dd3,
|
||||
0x93ff3c8c, 0xa30819ab, 0xff07758c, 0x5ab13c62, 0xd16d7fb5, 0xc4950ffa,
|
||||
0xd309ae49, 0xb9677a87, 0x4464e317, 0x90dc44f1, 0xc694c1d4, 0x1d5e1168,
|
||||
0xadf37a2d, 0xda38990d, 0x1ec4bd33, 0x36ca25ce, 0xfa0dc76a, 0x968a9d43,
|
||||
0x6950ac39, 0xdd3276bc, 0x06d5a71e, 0x1f6f282d, 0x5c626c62, 0xdde3fc31,
|
||||
0x152194ce, 0xc35ed14c, 0xb1f7224e, 0x47f76bb8, 0xb34fdd08, 0x7011395e,
|
||||
0x162d2a49, 0x0d1bf09f, 0x9428a952, 0x03c5c344, 0xd3525616, 0x7816fff3,
|
||||
0x6bceb8a8, 0x8345a081, 0x366420fd, 0x182abeda, 0x70f82745, 0xaf15ded8,
|
||||
0xc7f52ca2, 0xa98db9c5, 0x919d99ba, 0x9c376c1c, 0xed8d34c2, 0x716ae9f5,
|
||||
0xef062fa5, 0xee3b6c56, 0x52325658, 0x61afa9c3, 0xfdaf02f0, 0x961cf3ab,
|
||||
0x9f291565, 0x4fbf3045, 0x0590c899, 0xde901385, 0x45005ffb, 0x509db162,
|
||||
0x262fa941, 0x4c421653, 0x4b17c21e, 0xea0d1530, 0xde803845, 0x61bfd515,
|
||||
0x438523ef,
|
||||
};
|
||||
|
||||
pcg32_2018_engine engine(0);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%08x, ", engine());
|
||||
if (i % 6 == 5) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed();
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PCG322018EngineTest, VerifyGoldenSeeded) {
|
||||
constexpr uint32_t kGolden[kNumGoldenOutputs] = {
|
||||
0x60b5a64c, 0x978502f9, 0x80a75f60, 0x241f1158, 0xa4cd1dbb, 0xe7284017,
|
||||
0x3b678da5, 0x5223ec99, 0xe4bdd5d9, 0x72190e6d, 0xe6e702c9, 0xff80c768,
|
||||
0xcf126ed3, 0x1fbd20ab, 0x60980489, 0xbc72bf89, 0x407ac6c0, 0x00bf3c51,
|
||||
0xf9087897, 0x172e4eb6, 0xe9e4f443, 0x1a6098bf, 0xbf44f8c2, 0xdd84a0e5,
|
||||
0xd9a52364, 0xc0e2e786, 0x061ae2ba, 0x9facb8e3, 0x6109432d, 0xd4e0a013,
|
||||
0xbd8eb9a6, 0x7e86c3b6, 0x629c0e68, 0x05337430, 0xb495b9f4, 0x11ccd65d,
|
||||
0xb578db25, 0x66f1246d, 0x6ef20a7f, 0x5e429812, 0x11772130, 0xb944b5c2,
|
||||
0x01624128, 0xa2385ab7, 0xd3e10d35, 0xbe570ec3, 0xc951656f, 0xbe8944a0,
|
||||
0x7be41062, 0x5709f919, 0xd745feda, 0x9870b9ae, 0xb44b8168, 0x19e7683b,
|
||||
0xded8017f, 0xc6e4d544, 0x91ae4225, 0xd6745fba, 0xb992f284, 0x65b12b33,
|
||||
0xa9d5fdb4, 0xf105ce1a, 0x35ca1a6e, 0x2ff70dd0, 0xd8335e49, 0xfb71ddf2,
|
||||
0xcaeabb89, 0x5c6f5f84, 0x9a811a7d, 0xbcecbbd1, 0x0f661ba0, 0x9ad93b9d,
|
||||
0xedd23e0b, 0x42062f48, 0xd38dd7e4, 0x6cd63c9c, 0x640b98ae, 0x4bff5653,
|
||||
0x12626371, 0x13266017, 0xe7a698d8, 0x39c74667, 0xe8fdf2e3, 0x52803bf8,
|
||||
0x2af6895b, 0x91335b7b, 0x699e4961, 0x00a40fff, 0x253ff2b6, 0x4a6cf672,
|
||||
0x9584e85f, 0xf2a5000c, 0x4d58aba8, 0xb8513e6a, 0x767fad65, 0x8e326f9e,
|
||||
0x182f15a1, 0x163dab52, 0xdf99c780, 0x047282a1, 0xee4f90dd, 0xd50394ae,
|
||||
0x6c9fd5f0, 0xb06a9194, 0x387e3840, 0x04a9487b, 0xf678a4c2, 0xd0a78810,
|
||||
0xd502c97e, 0xd6a9b12a, 0x4accc5dc, 0x416ed53e, 0x50411536, 0xeeb89c24,
|
||||
0x813a7902, 0x034ebca6, 0xffa52e7c, 0x7ecd3d0e, 0xfa37a0d2, 0xb1fbe2c1,
|
||||
0xb7efc6d1, 0xefa4ccee, 0xf6f80424, 0x2283f3d9, 0x68732284, 0x94f3b5c8,
|
||||
0xbbdeceb9,
|
||||
};
|
||||
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
pcg32_2018_engine engine(seed_sequence);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%08x, ", engine());
|
||||
if (i % 6 == 5) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed(seed_sequence);
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PCG322018EngineTest, VerifyGoldenFromDeserializedEngine) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0x780f7042, 0xba137215, 0x43ab6f22, 0x0cb55f46, 0x44b2627d, 0x835597af,
|
||||
0xea973ea1, 0x0d2abd35, 0x4fdd601c, 0xac4342fe, 0x7db7e93c, 0xe56ebcaf,
|
||||
0x3596470a, 0x7770a9ad, 0x9b893320, 0x57db3415, 0xb432de54, 0xa02baf71,
|
||||
0xa256aadb, 0x88921fc7, 0xa35fa6b3, 0xde3eca46, 0x605739a7, 0xa890b82b,
|
||||
0xe457b7ad, 0x335fb903, 0xeb06790c, 0xb3c54bf6, 0x6141e442, 0xa599a482,
|
||||
0xb78987cc, 0xc61dfe9d, 0x0f1d6ace, 0x17460594, 0x8f6a5061, 0x083dc354,
|
||||
0xe9c337fb, 0xcfd105f7, 0x926764b6, 0x638d24dc, 0xeaac650a, 0x67d2cb9c,
|
||||
0xd807733c, 0x205fc52e, 0xf5399e2e, 0x6c46ddcc, 0xb603e875, 0xce113a25,
|
||||
0x3c8d4813, 0xfb584db8, 0xf6d255ff, 0xea80954f, 0x42e8be85, 0xb2feee72,
|
||||
0x62bd8d16, 0x1be4a142, 0x97dca1a4, 0xdd6e7333, 0xb2caa20e, 0xa12b1588,
|
||||
0xeb3a5a1a, 0x6fa5ba89, 0x077ea931, 0x8ddb1713, 0x0dd03079, 0x2c2ba965,
|
||||
0xa77fac17, 0xc8325742, 0x8bb893bf, 0xc2315741, 0xeaceee92, 0x81dd2ee2,
|
||||
0xe5214216, 0x1b9b8fb2, 0x01646d03, 0x24facc25, 0xd8c0e0bb, 0xa33fe106,
|
||||
0xf34fe976, 0xb3b4b44e, 0x65618fed, 0x032c6192, 0xa9dd72ce, 0xf391887b,
|
||||
0xf41c6a6e, 0x05c4bd6d, 0x37fa260e, 0x46b05659, 0xb5f6348a, 0x62d26d89,
|
||||
0x39f6452d, 0xb17b30a2, 0xbdd82743, 0x38ecae3b, 0xfe90f0a2, 0xcb2d226d,
|
||||
0xcf8a0b1c, 0x0eed3d4d, 0xa1f69cfc, 0xd7ac3ba5, 0xce9d9a6b, 0x121deb4c,
|
||||
0x4a0d03f3, 0xc1821ed1, 0x59c249ac, 0xc0abb474, 0x28149985, 0xfd9a82ba,
|
||||
0x5960c3b2, 0xeff00cba, 0x6073aa17, 0x25dc0919, 0x9976626e, 0xdd2ccc33,
|
||||
0x39ecb6ec, 0xc6e15d13, 0xfac94cfd, 0x28cfd34f, 0xf2d2c32d, 0x51c23d08,
|
||||
0x4fdb2f48, 0x97baa807, 0xf2c1004c, 0xc4ae8136, 0x71f31c94, 0x8c92d601,
|
||||
0x36caf5cd,
|
||||
};
|
||||
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
std::seed_seq seed_sequence{1, 2, 3};
|
||||
pcg32_2018_engine engine(seed_sequence);
|
||||
std::ostringstream stream;
|
||||
stream << engine;
|
||||
auto str = stream.str();
|
||||
printf("%s\n\n", str.c_str());
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%08x, ", engine());
|
||||
if (i % 6 == 5) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
|
||||
EXPECT_FALSE(true);
|
||||
#else
|
||||
pcg32_2018_engine engine;
|
||||
std::istringstream stream(
|
||||
"6364136223846793005 1442695040888963407 6537028157270659894");
|
||||
stream >> engine;
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
171
TMessagesProj/jni/voip/webrtc/absl/random/internal/platform.h
Normal file
171
TMessagesProj/jni/voip/webrtc/absl/random/internal/platform.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// SKIP_ABSL_INLINE_NAMESPACE_CHECK
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_PLATFORM_H_
|
||||
#define ABSL_RANDOM_INTERNAL_PLATFORM_H_
|
||||
|
||||
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
|
||||
// symbols from arbitrary system and other headers, since it may be built
|
||||
// with different flags from other targets, using different levels of
|
||||
// optimization, potentially introducing ODR violations.
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Platform Feature Checks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Currently supported operating systems and associated preprocessor
|
||||
// symbols:
|
||||
//
|
||||
// Linux and Linux-derived __linux__
|
||||
// Android __ANDROID__ (implies __linux__)
|
||||
// Linux (non-Android) __linux__ && !__ANDROID__
|
||||
// Darwin (macOS and iOS) __APPLE__
|
||||
// Akaros (http://akaros.org) __ros__
|
||||
// Windows _WIN32
|
||||
// NaCL __native_client__
|
||||
// AsmJS __asmjs__
|
||||
// WebAssembly __wasm__
|
||||
// Fuchsia __Fuchsia__
|
||||
//
|
||||
// Note that since Android defines both __ANDROID__ and __linux__, one
|
||||
// may probe for either Linux or Android by simply testing for __linux__.
|
||||
//
|
||||
// NOTE: For __APPLE__ platforms, we use #include <TargetConditionals.h>
|
||||
// to distinguish os variants.
|
||||
//
|
||||
// http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Architecture Checks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// These preprocessor directives are trying to determine CPU architecture,
|
||||
// including necessary headers to support hardware AES.
|
||||
//
|
||||
// ABSL_ARCH_{X86/PPC/ARM} macros determine the platform.
|
||||
#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || \
|
||||
defined(_M_X64)
|
||||
#define ABSL_ARCH_X86_64
|
||||
#elif defined(__i386) || defined(_M_IX86)
|
||||
#define ABSL_ARCH_X86_32
|
||||
#elif defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64)
|
||||
#define ABSL_ARCH_AARCH64
|
||||
#elif defined(__arm__) || defined(__ARMEL__) || defined(_M_ARM)
|
||||
#define ABSL_ARCH_ARM
|
||||
#elif defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
|
||||
defined(__ppc__) || defined(__PPC__)
|
||||
#define ABSL_ARCH_PPC
|
||||
#else
|
||||
// Unsupported architecture.
|
||||
// * https://sourceforge.net/p/predef/wiki/Architectures/
|
||||
// * https://msdn.microsoft.com/en-us/library/b0084kay.aspx
|
||||
// * for gcc, clang: "echo | gcc -E -dM -"
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Attribute Checks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// ABSL_RANDOM_INTERNAL_RESTRICT annotates whether pointers may be considered
|
||||
// to be unaliased.
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict__
|
||||
#elif defined(_MSC_VER)
|
||||
#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict
|
||||
#else
|
||||
#define ABSL_RANDOM_INTERNAL_RESTRICT
|
||||
#endif
|
||||
|
||||
// ABSL_HAVE_ACCELERATED_AES indicates whether the currently active compiler
|
||||
// flags (e.g. -maes) allow using hardware accelerated AES instructions, which
|
||||
// implies us assuming that the target platform supports them.
|
||||
#define ABSL_HAVE_ACCELERATED_AES 0
|
||||
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
|
||||
#if defined(__AES__) || defined(__AVX__)
|
||||
#undef ABSL_HAVE_ACCELERATED_AES
|
||||
#define ABSL_HAVE_ACCELERATED_AES 1
|
||||
#endif
|
||||
|
||||
#elif defined(ABSL_ARCH_PPC)
|
||||
|
||||
// Rely on VSX and CRYPTO extensions for vcipher on PowerPC.
|
||||
#if (defined(__VEC__) || defined(__ALTIVEC__)) && defined(__VSX__) && \
|
||||
defined(__CRYPTO__)
|
||||
#undef ABSL_HAVE_ACCELERATED_AES
|
||||
#define ABSL_HAVE_ACCELERATED_AES 1
|
||||
#endif
|
||||
|
||||
#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
|
||||
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053c/IHI0053C_acle_2_0.pdf
|
||||
// Rely on NEON+CRYPTO extensions for ARM.
|
||||
#if defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO)
|
||||
#undef ABSL_HAVE_ACCELERATED_AES
|
||||
#define ABSL_HAVE_ACCELERATED_AES 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// NaCl does not allow AES.
|
||||
#if defined(__native_client__)
|
||||
#undef ABSL_HAVE_ACCELERATED_AES
|
||||
#define ABSL_HAVE_ACCELERATED_AES 0
|
||||
#endif
|
||||
|
||||
// ABSL_RANDOM_INTERNAL_AES_DISPATCH indicates whether the currently active
|
||||
// platform has, or should use run-time dispatch for selecting the
|
||||
// accelerated Randen implementation.
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
|
||||
|
||||
// iOS does not support dispatch, even on x86, since applications
|
||||
// should be bundled as fat binaries, with a different build tailored for
|
||||
// each specific supported platform/architecture.
|
||||
#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
|
||||
(defined(TARGET_OS_IPHONE_SIMULATOR) && TARGET_OS_IPHONE_SIMULATOR)
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
|
||||
#elif defined(ABSL_ARCH_X86_64)
|
||||
// Dispatch is available on x86_64
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
|
||||
#elif defined(__linux__) && defined(ABSL_ARCH_PPC)
|
||||
// Or when running linux PPC
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
|
||||
#elif (defined(__linux__) || defined(__APPLE__)) && defined(ABSL_ARCH_AARCH64)
|
||||
// Or when running linux or macOS AArch64
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
|
||||
#elif defined(__linux__) && defined(ABSL_ARCH_ARM) && (__ARM_ARCH >= 8)
|
||||
// Or when running linux ARM v8 or higher.
|
||||
// (This captures a lot of Android configurations.)
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1
|
||||
#endif
|
||||
|
||||
// NaCl does not allow dispatch.
|
||||
#if defined(__native_client__)
|
||||
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
|
||||
#endif
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_PLATFORM_H_
|
||||
253
TMessagesProj/jni/voip/webrtc/absl/random/internal/pool_urbg.cc
Normal file
253
TMessagesProj/jni/voip/webrtc/absl/random/internal/pool_urbg.cc
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/pool_urbg.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/call_once.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/endian.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/base/internal/spinlock.h"
|
||||
#include "absl/base/internal/sysinfo.h"
|
||||
#include "absl/base/internal/unaligned_access.h"
|
||||
#include "absl/base/optimization.h"
|
||||
#include "absl/random/internal/randen.h"
|
||||
#include "absl/random/internal/seed_material.h"
|
||||
#include "absl/random/seed_gen_exception.h"
|
||||
|
||||
using absl::base_internal::SpinLock;
|
||||
using absl::base_internal::SpinLockHolder;
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
// RandenPoolEntry is a thread-safe pseudorandom bit generator, implementing a
|
||||
// single generator within a RandenPool<T>. It is an internal implementation
|
||||
// detail, and does not aim to conform to [rand.req.urng].
|
||||
//
|
||||
// NOTE: There are alignment issues when used on ARM, for instance.
|
||||
// See the allocation code in PoolAlignedAlloc().
|
||||
class RandenPoolEntry {
|
||||
public:
|
||||
static constexpr size_t kState = RandenTraits::kStateBytes / sizeof(uint32_t);
|
||||
static constexpr size_t kCapacity =
|
||||
RandenTraits::kCapacityBytes / sizeof(uint32_t);
|
||||
|
||||
void Init(absl::Span<const uint32_t> data) {
|
||||
SpinLockHolder l(&mu_); // Always uncontested.
|
||||
std::copy(data.begin(), data.end(), std::begin(state_));
|
||||
next_ = kState;
|
||||
}
|
||||
|
||||
// Copy bytes into out.
|
||||
void Fill(uint8_t* out, size_t bytes) ABSL_LOCKS_EXCLUDED(mu_);
|
||||
|
||||
// Returns random bits from the buffer in units of T.
|
||||
template <typename T>
|
||||
inline T Generate() ABSL_LOCKS_EXCLUDED(mu_);
|
||||
|
||||
inline void MaybeRefill() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
|
||||
if (next_ >= kState) {
|
||||
next_ = kCapacity;
|
||||
impl_.Generate(state_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Randen URBG state.
|
||||
uint32_t state_[kState] ABSL_GUARDED_BY(mu_); // First to satisfy alignment.
|
||||
SpinLock mu_;
|
||||
const Randen impl_;
|
||||
size_t next_ ABSL_GUARDED_BY(mu_);
|
||||
};
|
||||
|
||||
template <>
|
||||
inline uint8_t RandenPoolEntry::Generate<uint8_t>() {
|
||||
SpinLockHolder l(&mu_);
|
||||
MaybeRefill();
|
||||
return static_cast<uint8_t>(state_[next_++]);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint16_t RandenPoolEntry::Generate<uint16_t>() {
|
||||
SpinLockHolder l(&mu_);
|
||||
MaybeRefill();
|
||||
return static_cast<uint16_t>(state_[next_++]);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t RandenPoolEntry::Generate<uint32_t>() {
|
||||
SpinLockHolder l(&mu_);
|
||||
MaybeRefill();
|
||||
return state_[next_++];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t RandenPoolEntry::Generate<uint64_t>() {
|
||||
SpinLockHolder l(&mu_);
|
||||
if (next_ >= kState - 1) {
|
||||
next_ = kCapacity;
|
||||
impl_.Generate(state_);
|
||||
}
|
||||
auto p = state_ + next_;
|
||||
next_ += 2;
|
||||
|
||||
uint64_t result;
|
||||
std::memcpy(&result, p, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void RandenPoolEntry::Fill(uint8_t* out, size_t bytes) {
|
||||
SpinLockHolder l(&mu_);
|
||||
while (bytes > 0) {
|
||||
MaybeRefill();
|
||||
size_t remaining = (kState - next_) * sizeof(state_[0]);
|
||||
size_t to_copy = std::min(bytes, remaining);
|
||||
std::memcpy(out, &state_[next_], to_copy);
|
||||
out += to_copy;
|
||||
bytes -= to_copy;
|
||||
next_ += (to_copy + sizeof(state_[0]) - 1) / sizeof(state_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Number of pooled urbg entries.
|
||||
static constexpr size_t kPoolSize = 8;
|
||||
|
||||
// Shared pool entries.
|
||||
static absl::once_flag pool_once;
|
||||
ABSL_CACHELINE_ALIGNED static RandenPoolEntry* shared_pools[kPoolSize];
|
||||
|
||||
// Returns an id in the range [0 ... kPoolSize), which indexes into the
|
||||
// pool of random engines.
|
||||
//
|
||||
// Each thread to access the pool is assigned a sequential ID (without reuse)
|
||||
// from the pool-id space; the id is cached in a thread_local variable.
|
||||
// This id is assigned based on the arrival-order of the thread to the
|
||||
// GetPoolID call; this has no binary, CL, or runtime stability because
|
||||
// on subsequent runs the order within the same program may be significantly
|
||||
// different. However, as other thread IDs are not assigned sequentially,
|
||||
// this is not expected to matter.
|
||||
size_t GetPoolID() {
|
||||
static_assert(kPoolSize >= 1,
|
||||
"At least one urbg instance is required for PoolURBG");
|
||||
|
||||
ABSL_CONST_INIT static std::atomic<uint64_t> sequence{0};
|
||||
|
||||
#ifdef ABSL_HAVE_THREAD_LOCAL
|
||||
static thread_local size_t my_pool_id = kPoolSize;
|
||||
if (ABSL_PREDICT_FALSE(my_pool_id == kPoolSize)) {
|
||||
my_pool_id = (sequence++ % kPoolSize);
|
||||
}
|
||||
return my_pool_id;
|
||||
#else
|
||||
static pthread_key_t tid_key = [] {
|
||||
pthread_key_t tmp_key;
|
||||
int err = pthread_key_create(&tmp_key, nullptr);
|
||||
if (err) {
|
||||
ABSL_RAW_LOG(FATAL, "pthread_key_create failed with %d", err);
|
||||
}
|
||||
return tmp_key;
|
||||
}();
|
||||
|
||||
// Store the value in the pthread_{get/set}specific. However an uninitialized
|
||||
// value is 0, so add +1 to distinguish from the null value.
|
||||
uintptr_t my_pool_id =
|
||||
reinterpret_cast<uintptr_t>(pthread_getspecific(tid_key));
|
||||
if (ABSL_PREDICT_FALSE(my_pool_id == 0)) {
|
||||
// No allocated ID, allocate the next value, cache it, and return.
|
||||
my_pool_id = (sequence++ % kPoolSize) + 1;
|
||||
int err = pthread_setspecific(tid_key, reinterpret_cast<void*>(my_pool_id));
|
||||
if (err) {
|
||||
ABSL_RAW_LOG(FATAL, "pthread_setspecific failed with %d", err);
|
||||
}
|
||||
}
|
||||
return my_pool_id - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Allocate a RandenPoolEntry with at least 32-byte alignment, which is required
|
||||
// by ARM platform code.
|
||||
RandenPoolEntry* PoolAlignedAlloc() {
|
||||
constexpr size_t kAlignment =
|
||||
ABSL_CACHELINE_SIZE > 32 ? ABSL_CACHELINE_SIZE : 32;
|
||||
|
||||
// Not all the platforms that we build for have std::aligned_alloc, however
|
||||
// since we never free these objects, we can over allocate and munge the
|
||||
// pointers to the correct alignment.
|
||||
uintptr_t x = reinterpret_cast<uintptr_t>(
|
||||
new char[sizeof(RandenPoolEntry) + kAlignment]);
|
||||
auto y = x % kAlignment;
|
||||
void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y));
|
||||
return new (aligned) RandenPoolEntry();
|
||||
}
|
||||
|
||||
// Allocate and initialize kPoolSize objects of type RandenPoolEntry.
|
||||
//
|
||||
// The initialization strategy is to initialize one object directly from
|
||||
// OS entropy, then to use that object to seed all of the individual
|
||||
// pool instances.
|
||||
void InitPoolURBG() {
|
||||
static constexpr size_t kSeedSize =
|
||||
RandenTraits::kStateBytes / sizeof(uint32_t);
|
||||
// Read the seed data from OS entropy once.
|
||||
uint32_t seed_material[kPoolSize * kSeedSize];
|
||||
if (!random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::MakeSpan(seed_material))) {
|
||||
random_internal::ThrowSeedGenException();
|
||||
}
|
||||
for (size_t i = 0; i < kPoolSize; i++) {
|
||||
shared_pools[i] = PoolAlignedAlloc();
|
||||
shared_pools[i]->Init(
|
||||
absl::MakeSpan(&seed_material[i * kSeedSize], kSeedSize));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the pool entry for the current thread.
|
||||
RandenPoolEntry* GetPoolForCurrentThread() {
|
||||
absl::call_once(pool_once, InitPoolURBG);
|
||||
return shared_pools[GetPoolID()];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
typename RandenPool<T>::result_type RandenPool<T>::Generate() {
|
||||
auto* pool = GetPoolForCurrentThread();
|
||||
return pool->Generate<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RandenPool<T>::Fill(absl::Span<result_type> data) {
|
||||
auto* pool = GetPoolForCurrentThread();
|
||||
pool->Fill(reinterpret_cast<uint8_t*>(data.data()),
|
||||
data.size() * sizeof(result_type));
|
||||
}
|
||||
|
||||
template class RandenPool<uint8_t>;
|
||||
template class RandenPool<uint16_t>;
|
||||
template class RandenPool<uint32_t>;
|
||||
template class RandenPool<uint64_t>;
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
131
TMessagesProj/jni/voip/webrtc/absl/random/internal/pool_urbg.h
Normal file
131
TMessagesProj/jni/voip/webrtc/absl/random/internal/pool_urbg.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_POOL_URBG_H_
|
||||
#define ABSL_RANDOM_INTERNAL_POOL_URBG_H_
|
||||
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/random/internal/traits.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RandenPool is a thread-safe random number generator [random.req.urbg] that
|
||||
// uses an underlying pool of Randen generators to generate values. Each thread
|
||||
// has affinity to one instance of the underlying pool generators. Concurrent
|
||||
// access is guarded by a spin-lock.
|
||||
template <typename T>
|
||||
class RandenPool {
|
||||
public:
|
||||
using result_type = T;
|
||||
static_assert(std::is_unsigned<result_type>::value,
|
||||
"RandenPool template argument must be a built-in unsigned "
|
||||
"integer type");
|
||||
|
||||
static constexpr result_type(min)() {
|
||||
return (std::numeric_limits<result_type>::min)();
|
||||
}
|
||||
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
RandenPool() {}
|
||||
|
||||
// Returns a single value.
|
||||
inline result_type operator()() { return Generate(); }
|
||||
|
||||
// Fill data with random values.
|
||||
static void Fill(absl::Span<result_type> data);
|
||||
|
||||
protected:
|
||||
// Generate returns a single value.
|
||||
static result_type Generate();
|
||||
};
|
||||
|
||||
extern template class RandenPool<uint8_t>;
|
||||
extern template class RandenPool<uint16_t>;
|
||||
extern template class RandenPool<uint32_t>;
|
||||
extern template class RandenPool<uint64_t>;
|
||||
|
||||
// PoolURBG uses an underlying pool of random generators to implement a
|
||||
// thread-compatible [random.req.urbg] interface with an internal cache of
|
||||
// values.
|
||||
template <typename T, size_t kBufferSize>
|
||||
class PoolURBG {
|
||||
// Inheritance to access the protected static members of RandenPool.
|
||||
using unsigned_type = typename make_unsigned_bits<T>::type;
|
||||
using PoolType = RandenPool<unsigned_type>;
|
||||
using SpanType = absl::Span<unsigned_type>;
|
||||
|
||||
static constexpr size_t kInitialBuffer = kBufferSize + 1;
|
||||
static constexpr size_t kHalfBuffer = kBufferSize / 2;
|
||||
|
||||
public:
|
||||
using result_type = T;
|
||||
|
||||
static_assert(std::is_unsigned<result_type>::value,
|
||||
"PoolURBG must be parameterized by an unsigned integer type");
|
||||
|
||||
static_assert(kBufferSize > 1,
|
||||
"PoolURBG must be parameterized by a buffer-size > 1");
|
||||
|
||||
static_assert(kBufferSize <= 256,
|
||||
"PoolURBG must be parameterized by a buffer-size <= 256");
|
||||
|
||||
static constexpr result_type(min)() {
|
||||
return (std::numeric_limits<result_type>::min)();
|
||||
}
|
||||
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
PoolURBG() : next_(kInitialBuffer) {}
|
||||
|
||||
// copy-constructor does not copy cache.
|
||||
PoolURBG(const PoolURBG&) : next_(kInitialBuffer) {}
|
||||
const PoolURBG& operator=(const PoolURBG&) {
|
||||
next_ = kInitialBuffer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// move-constructor does move cache.
|
||||
PoolURBG(PoolURBG&&) = default;
|
||||
PoolURBG& operator=(PoolURBG&&) = default;
|
||||
|
||||
inline result_type operator()() {
|
||||
if (next_ >= kBufferSize) {
|
||||
next_ = (kBufferSize > 2 && next_ > kBufferSize) ? kHalfBuffer : 0;
|
||||
PoolType::Fill(SpanType(reinterpret_cast<unsigned_type*>(state_ + next_),
|
||||
kBufferSize - next_));
|
||||
}
|
||||
return state_[next_++];
|
||||
}
|
||||
|
||||
private:
|
||||
// Buffer size.
|
||||
size_t next_; // index within state_
|
||||
result_type state_[kBufferSize];
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_POOL_URBG_H_
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/pool_urbg.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
using absl::random_internal::PoolURBG;
|
||||
using absl::random_internal::RandenPool;
|
||||
|
||||
namespace {
|
||||
|
||||
// is_randen_pool trait is true when parameterized by an RandenPool
|
||||
template <typename T>
|
||||
using is_randen_pool = typename absl::disjunction< //
|
||||
std::is_same<T, RandenPool<uint8_t>>, //
|
||||
std::is_same<T, RandenPool<uint16_t>>, //
|
||||
std::is_same<T, RandenPool<uint32_t>>, //
|
||||
std::is_same<T, RandenPool<uint64_t>>>; //
|
||||
|
||||
// MyFill either calls RandenPool::Fill() or std::generate(..., rng)
|
||||
template <typename T, typename V>
|
||||
typename absl::enable_if_t<absl::negation<is_randen_pool<T>>::value, void> //
|
||||
MyFill(T& rng, absl::Span<V> data) { // NOLINT(runtime/references)
|
||||
std::generate(std::begin(data), std::end(data), rng);
|
||||
}
|
||||
|
||||
template <typename T, typename V>
|
||||
typename absl::enable_if_t<is_randen_pool<T>::value, void> //
|
||||
MyFill(T& rng, absl::Span<V> data) { // NOLINT(runtime/references)
|
||||
rng.Fill(data);
|
||||
}
|
||||
|
||||
template <typename EngineType>
|
||||
class PoolURBGTypedTest : public ::testing::Test {};
|
||||
|
||||
using EngineTypes = ::testing::Types< //
|
||||
RandenPool<uint8_t>, //
|
||||
RandenPool<uint16_t>, //
|
||||
RandenPool<uint32_t>, //
|
||||
RandenPool<uint64_t>, //
|
||||
PoolURBG<uint8_t, 2>, //
|
||||
PoolURBG<uint16_t, 2>, //
|
||||
PoolURBG<uint32_t, 2>, //
|
||||
PoolURBG<uint64_t, 2>, //
|
||||
PoolURBG<unsigned int, 8>, // NOLINT(runtime/int)
|
||||
PoolURBG<unsigned long, 8>, // NOLINT(runtime/int)
|
||||
PoolURBG<unsigned long int, 4>, // NOLINT(runtime/int)
|
||||
PoolURBG<unsigned long long, 4>>; // NOLINT(runtime/int)
|
||||
|
||||
TYPED_TEST_SUITE(PoolURBGTypedTest, EngineTypes);
|
||||
|
||||
// This test is checks that the engines meet the URBG interface requirements
|
||||
// defined in [rand.req.urbg].
|
||||
TYPED_TEST(PoolURBGTypedTest, URBGInterface) {
|
||||
using E = TypeParam;
|
||||
using T = typename E::result_type;
|
||||
|
||||
static_assert(std::is_copy_constructible<E>::value,
|
||||
"engine must be copy constructible");
|
||||
|
||||
static_assert(absl::is_copy_assignable<E>::value,
|
||||
"engine must be copy assignable");
|
||||
|
||||
E e;
|
||||
const E x;
|
||||
|
||||
e();
|
||||
|
||||
static_assert(std::is_same<decltype(e()), T>::value,
|
||||
"return type of operator() must be result_type");
|
||||
|
||||
E u0(x);
|
||||
u0();
|
||||
|
||||
E u1 = e;
|
||||
u1();
|
||||
}
|
||||
|
||||
// This validates that sequences are independent.
|
||||
TYPED_TEST(PoolURBGTypedTest, VerifySequences) {
|
||||
using E = TypeParam;
|
||||
using result_type = typename E::result_type;
|
||||
|
||||
E rng;
|
||||
(void)rng(); // Discard one value.
|
||||
|
||||
constexpr int kNumOutputs = 64;
|
||||
result_type a[kNumOutputs];
|
||||
result_type b[kNumOutputs];
|
||||
std::fill(std::begin(b), std::end(b), 0);
|
||||
|
||||
// Fill a using Fill or generate, depending on the engine type.
|
||||
{
|
||||
E x = rng;
|
||||
MyFill(x, absl::MakeSpan(a));
|
||||
}
|
||||
|
||||
// Fill b using std::generate().
|
||||
{
|
||||
E x = rng;
|
||||
std::generate(std::begin(b), std::end(b), x);
|
||||
}
|
||||
|
||||
// Test that generated sequence changed as sequence of bits, i.e. if about
|
||||
// half of the bites were flipped between two non-correlated values.
|
||||
size_t changed_bits = 0;
|
||||
size_t unchanged_bits = 0;
|
||||
size_t total_set = 0;
|
||||
size_t total_bits = 0;
|
||||
size_t equal_count = 0;
|
||||
for (size_t i = 0; i < kNumOutputs; ++i) {
|
||||
equal_count += (a[i] == b[i]) ? 1 : 0;
|
||||
std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
|
||||
changed_bits += bitset.count();
|
||||
unchanged_bits += bitset.size() - bitset.count();
|
||||
|
||||
std::bitset<sizeof(result_type) * 8> a_set(a[i]);
|
||||
std::bitset<sizeof(result_type) * 8> b_set(b[i]);
|
||||
total_set += a_set.count() + b_set.count();
|
||||
total_bits += 2 * 8 * sizeof(result_type);
|
||||
}
|
||||
// On average, half the bits are changed between two calls.
|
||||
EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
|
||||
EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
|
||||
|
||||
// verify using a quick normal-approximation to the binomial.
|
||||
EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
|
||||
<< "@" << total_set / static_cast<double>(total_bits);
|
||||
|
||||
// Also, A[i] == B[i] with probability (1/range) * N.
|
||||
// Give this a pretty wide latitude, though.
|
||||
const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
|
||||
EXPECT_LE(equal_count, 1.0 + kExpected);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
$ nanobenchmarks 1 RandenPool construct
|
||||
$ nanobenchmarks 1 PoolURBG construct
|
||||
|
||||
RandenPool<uint32_t> | 1 | 1000 | 48482.00 ticks | 48.48 ticks | 13.9 ns
|
||||
RandenPool<uint32_t> | 10 | 2000 | 1028795.00 ticks | 51.44 ticks | 14.7 ns
|
||||
RandenPool<uint32_t> | 100 | 1000 | 5119968.00 ticks | 51.20 ticks | 14.6 ns
|
||||
RandenPool<uint32_t> | 1000 | 500 | 25867936.00 ticks | 51.74 ticks | 14.8 ns
|
||||
|
||||
RandenPool<uint64_t> | 1 | 1000 | 49921.00 ticks | 49.92 ticks | 14.3 ns
|
||||
RandenPool<uint64_t> | 10 | 2000 | 1208269.00 ticks | 60.41 ticks | 17.3 ns
|
||||
RandenPool<uint64_t> | 100 | 1000 | 5844955.00 ticks | 58.45 ticks | 16.7 ns
|
||||
RandenPool<uint64_t> | 1000 | 500 | 28767404.00 ticks | 57.53 ticks | 16.4 ns
|
||||
|
||||
PoolURBG<uint32_t,8> | 1 | 1000 | 86431.00 ticks | 86.43 ticks | 24.7 ns
|
||||
PoolURBG<uint32_t,8> | 10 | 1000 | 206191.00 ticks | 20.62 ticks | 5.9 ns
|
||||
PoolURBG<uint32_t,8> | 100 | 1000 | 1516049.00 ticks | 15.16 ticks | 4.3 ns
|
||||
PoolURBG<uint32_t,8> | 1000 | 500 | 7613936.00 ticks | 15.23 ticks | 4.4 ns
|
||||
|
||||
PoolURBG<uint64_t,4> | 1 | 1000 | 96668.00 ticks | 96.67 ticks | 27.6 ns
|
||||
PoolURBG<uint64_t,4> | 10 | 1000 | 282423.00 ticks | 28.24 ticks | 8.1 ns
|
||||
PoolURBG<uint64_t,4> | 100 | 1000 | 2609587.00 ticks | 26.10 ticks | 7.5 ns
|
||||
PoolURBG<uint64_t,4> | 1000 | 500 | 12408757.00 ticks | 24.82 ticks | 7.1 ns
|
||||
|
||||
*/
|
||||
91
TMessagesProj/jni/voip/webrtc/absl/random/internal/randen.cc
Normal file
91
TMessagesProj/jni/voip/webrtc/absl/random/internal/randen.cc
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen.h"
|
||||
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/random/internal/randen_detect.h"
|
||||
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
|
||||
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
|
||||
//
|
||||
// High-level summary:
|
||||
// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is
|
||||
// a sponge-like random generator that requires a cryptographic permutation.
|
||||
// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by
|
||||
// achieving backtracking resistance with only one Permute() per buffer.
|
||||
//
|
||||
// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round
|
||||
// Function" constructs up to 1024-bit permutations using an improved
|
||||
// Generalized Feistel network with 2-round AES-128 functions. This Feistel
|
||||
// block shuffle achieves diffusion faster and is less vulnerable to
|
||||
// sliced-biclique attacks than the Type-2 cyclic shuffle.
|
||||
//
|
||||
// 3) "Improving the Generalized Feistel" and "New criterion for diffusion
|
||||
// property" extends the same kind of improved Feistel block shuffle to 16
|
||||
// branches, which enables a 2048-bit permutation.
|
||||
//
|
||||
// We combine these three ideas and also change Simpira's subround keys from
|
||||
// structured/low-entropy counters to digits of Pi.
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
struct RandenState {
|
||||
const void* keys;
|
||||
bool has_crypto;
|
||||
};
|
||||
|
||||
RandenState GetRandenState() {
|
||||
static const RandenState state = []() {
|
||||
RandenState tmp;
|
||||
#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
// HW AES Dispatch.
|
||||
if (HasRandenHwAesImplementation() && CPUSupportsRandenHwAes()) {
|
||||
tmp.has_crypto = true;
|
||||
tmp.keys = RandenHwAes::GetKeys();
|
||||
} else {
|
||||
tmp.has_crypto = false;
|
||||
tmp.keys = RandenSlow::GetKeys();
|
||||
}
|
||||
#elif ABSL_HAVE_ACCELERATED_AES
|
||||
// HW AES is enabled.
|
||||
tmp.has_crypto = true;
|
||||
tmp.keys = RandenHwAes::GetKeys();
|
||||
#else
|
||||
// HW AES is disabled.
|
||||
tmp.has_crypto = false;
|
||||
tmp.keys = RandenSlow::GetKeys();
|
||||
#endif
|
||||
return tmp;
|
||||
}();
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Randen::Randen() {
|
||||
auto tmp = GetRandenState();
|
||||
keys_ = tmp.keys;
|
||||
#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
has_crypto_ = tmp.has_crypto;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
96
TMessagesProj/jni/voip/webrtc/absl/random/internal/randen.h
Normal file
96
TMessagesProj/jni/voip/webrtc/absl/random/internal/randen.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen_hwaes.h"
|
||||
#include "absl/random/internal/randen_slow.h"
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
|
||||
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
|
||||
//
|
||||
// Randen implements the basic state manipulation methods.
|
||||
class Randen {
|
||||
public:
|
||||
static constexpr size_t kStateBytes = RandenTraits::kStateBytes;
|
||||
static constexpr size_t kCapacityBytes = RandenTraits::kCapacityBytes;
|
||||
static constexpr size_t kSeedBytes = RandenTraits::kSeedBytes;
|
||||
|
||||
~Randen() = default;
|
||||
|
||||
Randen();
|
||||
|
||||
// Generate updates the randen sponge. The outer portion of the sponge
|
||||
// (kCapacityBytes .. kStateBytes) may be consumed as PRNG state.
|
||||
// REQUIRES: state points to kStateBytes of state.
|
||||
inline void Generate(void* state) const {
|
||||
#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
// HW AES Dispatch.
|
||||
if (has_crypto_) {
|
||||
RandenHwAes::Generate(keys_, state);
|
||||
} else {
|
||||
RandenSlow::Generate(keys_, state);
|
||||
}
|
||||
#elif ABSL_HAVE_ACCELERATED_AES
|
||||
// HW AES is enabled.
|
||||
RandenHwAes::Generate(keys_, state);
|
||||
#else
|
||||
// HW AES is disabled.
|
||||
RandenSlow::Generate(keys_, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Absorb incorporates additional seed material into the randen sponge. After
|
||||
// absorb returns, Generate must be called before the state may be consumed.
|
||||
// REQUIRES: seed points to kSeedBytes of seed.
|
||||
// REQUIRES: state points to kStateBytes of state.
|
||||
inline void Absorb(const void* seed, void* state) const {
|
||||
#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
// HW AES Dispatch.
|
||||
if (has_crypto_) {
|
||||
RandenHwAes::Absorb(seed, state);
|
||||
} else {
|
||||
RandenSlow::Absorb(seed, state);
|
||||
}
|
||||
#elif ABSL_HAVE_ACCELERATED_AES
|
||||
// HW AES is enabled.
|
||||
RandenHwAes::Absorb(seed, state);
|
||||
#else
|
||||
// HW AES is disabled.
|
||||
RandenSlow::Absorb(seed, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
const void* keys_;
|
||||
#if ABSL_RANDOM_INTERNAL_AES_DISPATCH
|
||||
bool has_crypto_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_H_
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/random/internal/nanobenchmark.h"
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen.h"
|
||||
#include "absl/random/internal/randen_engine.h"
|
||||
#include "absl/random/internal/randen_hwaes.h"
|
||||
#include "absl/random/internal/randen_slow.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::Randen;
|
||||
using absl::random_internal::RandenHwAes;
|
||||
using absl::random_internal::RandenSlow;
|
||||
|
||||
using absl::random_internal_nanobenchmark::FuncInput;
|
||||
using absl::random_internal_nanobenchmark::FuncOutput;
|
||||
using absl::random_internal_nanobenchmark::InvariantTicksPerSecond;
|
||||
using absl::random_internal_nanobenchmark::MeasureClosure;
|
||||
using absl::random_internal_nanobenchmark::Params;
|
||||
using absl::random_internal_nanobenchmark::PinThreadToCPU;
|
||||
using absl::random_internal_nanobenchmark::Result;
|
||||
|
||||
// Local state parameters.
|
||||
static constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
|
||||
static constexpr size_t kSeedSizeT = Randen::kSeedBytes / sizeof(uint32_t);
|
||||
|
||||
// Randen implementation benchmarks.
|
||||
template <typename T>
|
||||
struct AbsorbFn : public T {
|
||||
// These are both cast to uint128* in the RandenHwAes implementation, so
|
||||
// ensure they are 16 byte aligned.
|
||||
alignas(16) mutable uint64_t state[kStateSizeT] = {};
|
||||
alignas(16) mutable uint32_t seed[kSeedSizeT] = {};
|
||||
|
||||
static constexpr size_t bytes() { return sizeof(seed); }
|
||||
|
||||
FuncOutput operator()(const FuncInput num_iters) const {
|
||||
for (size_t i = 0; i < num_iters; ++i) {
|
||||
this->Absorb(seed, state);
|
||||
}
|
||||
return state[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct GenerateFn : public T {
|
||||
mutable uint64_t state[kStateSizeT];
|
||||
GenerateFn() { std::memset(state, 0, sizeof(state)); }
|
||||
|
||||
static constexpr size_t bytes() { return sizeof(state); }
|
||||
|
||||
FuncOutput operator()(const FuncInput num_iters) const {
|
||||
const auto* keys = this->GetKeys();
|
||||
for (size_t i = 0; i < num_iters; ++i) {
|
||||
this->Generate(keys, state);
|
||||
}
|
||||
return state[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename UInt>
|
||||
struct Engine {
|
||||
mutable absl::random_internal::randen_engine<UInt> rng;
|
||||
|
||||
static constexpr size_t bytes() { return sizeof(UInt); }
|
||||
|
||||
FuncOutput operator()(const FuncInput num_iters) const {
|
||||
for (size_t i = 0; i < num_iters - 1; ++i) {
|
||||
rng();
|
||||
}
|
||||
return rng();
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
void Print(const char* name, const size_t n, const Result (&results)[N],
|
||||
const size_t bytes) {
|
||||
if (n == 0) {
|
||||
ABSL_RAW_LOG(
|
||||
WARNING,
|
||||
"WARNING: Measurement failed, should not happen when using "
|
||||
"PinThreadToCPU unless the region to measure takes > 1 second.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static const double ns_per_tick = 1e9 / InvariantTicksPerSecond();
|
||||
static constexpr const double kNsPerS = 1e9; // ns/s
|
||||
static constexpr const double kMBPerByte = 1.0 / 1048576.0; // Mb / b
|
||||
static auto header = [] {
|
||||
return printf("%20s %8s: %12s ticks; %9s (%9s) %8s\n", "Name", "Count",
|
||||
"Total", "Variance", "Time", "bytes/s");
|
||||
}();
|
||||
(void)header;
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const double ticks_per_call = results[i].ticks / results[i].input;
|
||||
const double ns_per_call = ns_per_tick * ticks_per_call;
|
||||
const double bytes_per_ns = bytes / ns_per_call;
|
||||
const double mb_per_s = bytes_per_ns * kNsPerS * kMBPerByte;
|
||||
// Output
|
||||
printf("%20s %8zu: %12.2f ticks; MAD=%4.2f%% (%6.1f ns) %8.1f Mb/s\n",
|
||||
name, results[i].input, results[i].ticks,
|
||||
results[i].variability * 100.0, ns_per_call, mb_per_s);
|
||||
}
|
||||
}
|
||||
|
||||
// Fails here
|
||||
template <typename Op, size_t N>
|
||||
void Measure(const char* name, const FuncInput (&inputs)[N]) {
|
||||
Op op;
|
||||
|
||||
Result results[N];
|
||||
Params params;
|
||||
params.verbose = false;
|
||||
params.max_evals = 6; // avoid test timeout
|
||||
const size_t num_results = MeasureClosure(op, inputs, N, results, params);
|
||||
Print(name, num_results, results, op.bytes());
|
||||
}
|
||||
|
||||
// unpredictable == 1 but the compiler does not know that.
|
||||
void RunAll(const int argc, char* argv[]) {
|
||||
if (argc == 2) {
|
||||
int cpu = -1;
|
||||
if (!absl::SimpleAtoi(argv[1], &cpu)) {
|
||||
ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n");
|
||||
}
|
||||
PinThreadToCPU(cpu);
|
||||
}
|
||||
|
||||
// The compiler cannot reduce this to a constant.
|
||||
const FuncInput unpredictable = (argc != 999);
|
||||
static const FuncInput inputs[] = {unpredictable * 100, unpredictable * 1000};
|
||||
|
||||
#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES
|
||||
Measure<AbsorbFn<RandenHwAes>>("Absorb (HwAes)", inputs);
|
||||
#endif
|
||||
Measure<AbsorbFn<RandenSlow>>("Absorb (Slow)", inputs);
|
||||
|
||||
#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES
|
||||
Measure<GenerateFn<RandenHwAes>>("Generate (HwAes)", inputs);
|
||||
#endif
|
||||
Measure<GenerateFn<RandenSlow>>("Generate (Slow)", inputs);
|
||||
|
||||
// Measure the production engine.
|
||||
static const FuncInput inputs1[] = {unpredictable * 1000,
|
||||
unpredictable * 10000};
|
||||
Measure<Engine<uint64_t>>("randen_engine<uint64_t>", inputs1);
|
||||
Measure<Engine<uint32_t>>("randen_engine<uint32_t>", inputs1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
RunAll(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
|
||||
// symbols from arbitrary system and other headers, since it may be built
|
||||
// with different flags from other targets, using different levels of
|
||||
// optimization, potentially introducing ODR violations.
|
||||
|
||||
#include "absl/random/internal/randen_detect.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<arm/cpu_capabilities_public.h>)
|
||||
#include <arm/cpu_capabilities_public.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/types/optional.h" // IWYU pragma: keep
|
||||
|
||||
#if !defined(__UCLIBC__) && defined(__GLIBC__) && \
|
||||
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
|
||||
#define ABSL_HAVE_GETAUXVAL
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
#define ABSL_INTERNAL_USE_X86_CPUID
|
||||
#elif defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \
|
||||
defined(ABSL_ARCH_AARCH64)
|
||||
#if defined(__ANDROID__)
|
||||
#define ABSL_INTERNAL_USE_ANDROID_GETAUXVAL
|
||||
#define ABSL_INTERNAL_USE_GETAUXVAL
|
||||
#elif defined(__linux__) && defined(ABSL_HAVE_GETAUXVAL)
|
||||
#define ABSL_INTERNAL_USE_LINUX_GETAUXVAL
|
||||
#define ABSL_INTERNAL_USE_GETAUXVAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_INTERNAL_USE_X86_CPUID)
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <intrin.h> // NOLINT(build/include_order)
|
||||
#elif ABSL_HAVE_BUILTIN(__cpuid)
|
||||
// MSVC-equivalent __cpuid intrinsic declaration for clang-like compilers
|
||||
// for non-Windows build environments.
|
||||
extern void __cpuid(int[4], int);
|
||||
#else
|
||||
// MSVC-equivalent __cpuid intrinsic function.
|
||||
static void __cpuid(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile("cpuid \n\t"
|
||||
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
|
||||
"=d"(cpu_info[3])
|
||||
: "a"(info_type), "c"(0));
|
||||
}
|
||||
#endif
|
||||
#endif // ABSL_INTERNAL_USE_X86_CPUID
|
||||
|
||||
// On linux, just use the c-library getauxval call.
|
||||
#if defined(ABSL_INTERNAL_USE_LINUX_GETAUXVAL)
|
||||
|
||||
#include <sys/auxv.h>
|
||||
|
||||
static uint32_t GetAuxval(uint32_t hwcap_type) {
|
||||
return static_cast<uint32_t>(getauxval(hwcap_type));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// On android, probe the system's C library for getauxval().
|
||||
// This is the same technique used by the android NDK cpu features library
|
||||
// as well as the google open-source cpu_features library.
|
||||
//
|
||||
// TODO(absl-team): Consider implementing a fallback of directly reading
|
||||
// /proc/self/auxval.
|
||||
#if defined(ABSL_INTERNAL_USE_ANDROID_GETAUXVAL)
|
||||
#include <dlfcn.h>
|
||||
|
||||
static uint32_t GetAuxval(uint32_t hwcap_type) {
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
typedef unsigned long (*getauxval_func_t)(unsigned long);
|
||||
|
||||
dlerror(); // Cleaning error state before calling dlopen.
|
||||
void* libc_handle = dlopen("libc.so", RTLD_NOW);
|
||||
if (!libc_handle) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t result = 0;
|
||||
void* sym = dlsym(libc_handle, "getauxval");
|
||||
if (sym) {
|
||||
getauxval_func_t func;
|
||||
memcpy(&func, &sym, sizeof(func));
|
||||
result = static_cast<uint32_t>((*func)(hwcap_type));
|
||||
}
|
||||
dlclose(libc_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(ABSL_ARCH_AARCH64)
|
||||
template <typename T>
|
||||
static absl::optional<T> ReadSysctlByName(const char* name) {
|
||||
T val;
|
||||
size_t val_size = sizeof(T);
|
||||
int ret = sysctlbyname(name, &val, &val_size, nullptr, 0);
|
||||
if (ret == -1) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// The default return at the end of the function might be unreachable depending
|
||||
// on the configuration. Ignore that warning.
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunreachable-code-return"
|
||||
#endif
|
||||
|
||||
// CPUSupportsRandenHwAes returns whether the CPU is a microarchitecture
|
||||
// which supports the crpyto/aes instructions or extensions necessary to use the
|
||||
// accelerated RandenHwAes implementation.
|
||||
//
|
||||
// 1. For x86 it is sufficient to use the CPUID instruction to detect whether
|
||||
// the cpu supports AES instructions. Done.
|
||||
//
|
||||
// Fon non-x86 it is much more complicated.
|
||||
//
|
||||
// 2. When ABSL_INTERNAL_USE_GETAUXVAL is defined, use getauxval() (either
|
||||
// the direct c-library version, or the android probing version which loads
|
||||
// libc), and read the hardware capability bits.
|
||||
// This is based on the technique used by boringssl uses to detect
|
||||
// cpu capabilities, and should allow us to enable crypto in the android
|
||||
// builds where it is supported.
|
||||
//
|
||||
// 3. When __APPLE__ is defined on AARCH64, use sysctlbyname().
|
||||
//
|
||||
// 4. Use the default for the compiler architecture.
|
||||
//
|
||||
|
||||
bool CPUSupportsRandenHwAes() {
|
||||
#if defined(ABSL_INTERNAL_USE_X86_CPUID)
|
||||
// 1. For x86: Use CPUID to detect the required AES instruction set.
|
||||
int regs[4];
|
||||
__cpuid(reinterpret_cast<int*>(regs), 1);
|
||||
return regs[2] & (1 << 25); // AES
|
||||
|
||||
#elif defined(ABSL_INTERNAL_USE_GETAUXVAL)
|
||||
// 2. Use getauxval() to read the hardware bits and determine
|
||||
// cpu capabilities.
|
||||
|
||||
#define AT_HWCAP 16
|
||||
#define AT_HWCAP2 26
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
// For Power / PPC: Expect that the cpu supports VCRYPTO
|
||||
// See https://members.openpowerfoundation.org/document/dl/576
|
||||
// VCRYPTO should be present in POWER8 >= 2.07.
|
||||
// Uses Linux kernel constants from arch/powerpc/include/uapi/asm/cputable.h
|
||||
static const uint32_t kVCRYPTO = 0x02000000;
|
||||
const uint32_t hwcap = GetAuxval(AT_HWCAP2);
|
||||
return (hwcap & kVCRYPTO) != 0;
|
||||
|
||||
#elif defined(ABSL_ARCH_ARM)
|
||||
// For ARM: Require crypto+neon
|
||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html
|
||||
// Uses Linux kernel constants from arch/arm64/include/asm/hwcap.h
|
||||
static const uint32_t kNEON = 1 << 12;
|
||||
uint32_t hwcap = GetAuxval(AT_HWCAP);
|
||||
if ((hwcap & kNEON) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// And use it again to detect AES.
|
||||
static const uint32_t kAES = 1 << 0;
|
||||
const uint32_t hwcap2 = GetAuxval(AT_HWCAP2);
|
||||
return (hwcap2 & kAES) != 0;
|
||||
|
||||
#elif defined(ABSL_ARCH_AARCH64)
|
||||
// For AARCH64: Require crypto+neon
|
||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html
|
||||
static const uint32_t kNEON = 1 << 1;
|
||||
static const uint32_t kAES = 1 << 3;
|
||||
const uint32_t hwcap = GetAuxval(AT_HWCAP);
|
||||
return ((hwcap & kNEON) != 0) && ((hwcap & kAES) != 0);
|
||||
#endif
|
||||
|
||||
#elif defined(__APPLE__) && defined(ABSL_ARCH_AARCH64)
|
||||
// 3. Use sysctlbyname.
|
||||
|
||||
// Newer XNU kernels support querying all capabilities in a single
|
||||
// sysctlbyname.
|
||||
#if defined(CAP_BIT_AdvSIMD) && defined(CAP_BIT_FEAT_AES)
|
||||
static const absl::optional<uint64_t> caps =
|
||||
ReadSysctlByName<uint64_t>("hw.optional.arm.caps");
|
||||
if (caps.has_value()) {
|
||||
constexpr uint64_t kNeonAndAesCaps =
|
||||
(uint64_t{1} << CAP_BIT_AdvSIMD) | (uint64_t{1} << CAP_BIT_FEAT_AES);
|
||||
return (*caps & kNeonAndAesCaps) == kNeonAndAesCaps;
|
||||
}
|
||||
#endif
|
||||
|
||||
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics#overview
|
||||
static const absl::optional<int> adv_simd =
|
||||
ReadSysctlByName<int>("hw.optional.AdvSIMD");
|
||||
if (adv_simd.value_or(0) == 0) {
|
||||
return false;
|
||||
}
|
||||
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics#3918855
|
||||
static const absl::optional<int> feat_aes =
|
||||
ReadSysctlByName<int>("hw.optional.arm.FEAT_AES");
|
||||
if (feat_aes.value_or(0) == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else // ABSL_INTERNAL_USE_GETAUXVAL
|
||||
// 4. By default, assume that the compiler default.
|
||||
return ABSL_HAVE_ACCELERATED_AES ? true : false;
|
||||
|
||||
#endif
|
||||
// NOTE: There are some other techniques that may be worth trying:
|
||||
//
|
||||
// * Use an environment variable: ABSL_RANDOM_USE_HWAES
|
||||
//
|
||||
// * Rely on compiler-generated target-based dispatch.
|
||||
// Using x86/gcc it might look something like this:
|
||||
//
|
||||
// int __attribute__((target("aes"))) HasAes() { return 1; }
|
||||
// int __attribute__((target("default"))) HasAes() { return 0; }
|
||||
//
|
||||
// This does not work on all architecture/compiler combinations.
|
||||
//
|
||||
// * On Linux consider reading /proc/cpuinfo and/or /proc/self/auxv.
|
||||
// These files have lines which are easy to parse; for ARM/AARCH64 it is quite
|
||||
// easy to find the Features: line and extract aes / neon. Likewise for
|
||||
// PPC.
|
||||
//
|
||||
// * Fork a process and test for SIGILL:
|
||||
//
|
||||
// * Many architectures have instructions to read the ISA. Unfortunately
|
||||
// most of those require that the code is running in ring 0 /
|
||||
// protected-mode.
|
||||
//
|
||||
// There are several examples. e.g. Valgrind detects PPC ISA 2.07:
|
||||
// https://github.com/lu-zero/valgrind/blob/master/none/tests/ppc64/test_isa_2_07_part1.c
|
||||
//
|
||||
// MRS <Xt>, ID_AA64ISAR0_EL1 ; Read ID_AA64ISAR0_EL1 into Xt
|
||||
//
|
||||
// uint64_t val;
|
||||
// __asm __volatile("mrs %0, id_aa64isar0_el1" :"=&r" (val));
|
||||
//
|
||||
// * Use a CPUID-style heuristic database.
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// Returns whether the current CPU supports RandenHwAes implementation.
|
||||
// This typically involves supporting cryptographic extensions on whichever
|
||||
// platform is currently running.
|
||||
bool CPUSupportsRandenHwAes();
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/internal/endian.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/internal/iostream_state_saver.h"
|
||||
#include "absl/random/internal/randen.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// Deterministic pseudorandom byte generator with backtracking resistance
|
||||
// (leaking the state does not compromise prior outputs). Based on Reverie
|
||||
// (see "A Robust and Sponge-Like PRNG with Improved Efficiency") instantiated
|
||||
// with an improved Simpira-like permutation.
|
||||
// Returns values of type "T" (must be a built-in unsigned integer type).
|
||||
//
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
|
||||
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
|
||||
template <typename T>
|
||||
class alignas(8) randen_engine {
|
||||
public:
|
||||
// C++11 URBG interface:
|
||||
using result_type = T;
|
||||
static_assert(std::is_unsigned<result_type>::value,
|
||||
"randen_engine template argument must be a built-in unsigned "
|
||||
"integer type");
|
||||
|
||||
static constexpr result_type(min)() {
|
||||
return (std::numeric_limits<result_type>::min)();
|
||||
}
|
||||
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
randen_engine() : randen_engine(0) {}
|
||||
explicit randen_engine(result_type seed_value) { seed(seed_value); }
|
||||
|
||||
template <class SeedSequence,
|
||||
typename = typename absl::enable_if_t<
|
||||
!std::is_same<SeedSequence, randen_engine>::value>>
|
||||
explicit randen_engine(SeedSequence&& seq) {
|
||||
seed(seq);
|
||||
}
|
||||
|
||||
// alignment requirements dictate custom copy and move constructors.
|
||||
randen_engine(const randen_engine& other)
|
||||
: next_(other.next_), impl_(other.impl_) {
|
||||
std::memcpy(state(), other.state(), kStateSizeT * sizeof(result_type));
|
||||
}
|
||||
randen_engine& operator=(const randen_engine& other) {
|
||||
next_ = other.next_;
|
||||
impl_ = other.impl_;
|
||||
std::memcpy(state(), other.state(), kStateSizeT * sizeof(result_type));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns random bits from the buffer in units of result_type.
|
||||
result_type operator()() {
|
||||
// Refill the buffer if needed (unlikely).
|
||||
auto* begin = state();
|
||||
if (next_ >= kStateSizeT) {
|
||||
next_ = kCapacityT;
|
||||
impl_.Generate(begin);
|
||||
}
|
||||
return little_endian::ToHost(begin[next_++]);
|
||||
}
|
||||
|
||||
template <class SeedSequence>
|
||||
typename absl::enable_if_t<
|
||||
!std::is_convertible<SeedSequence, result_type>::value>
|
||||
seed(SeedSequence&& seq) {
|
||||
// Zeroes the state.
|
||||
seed();
|
||||
reseed(seq);
|
||||
}
|
||||
|
||||
void seed(result_type seed_value = 0) {
|
||||
next_ = kStateSizeT;
|
||||
// Zeroes the inner state and fills the outer state with seed_value to
|
||||
// mimic the behaviour of reseed
|
||||
auto* begin = state();
|
||||
std::fill(begin, begin + kCapacityT, 0);
|
||||
std::fill(begin + kCapacityT, begin + kStateSizeT, seed_value);
|
||||
}
|
||||
|
||||
// Inserts entropy into (part of) the state. Calling this periodically with
|
||||
// sufficient entropy ensures prediction resistance (attackers cannot predict
|
||||
// future outputs even if state is compromised).
|
||||
template <class SeedSequence>
|
||||
void reseed(SeedSequence& seq) {
|
||||
using sequence_result_type = typename SeedSequence::result_type;
|
||||
static_assert(sizeof(sequence_result_type) == 4,
|
||||
"SeedSequence::result_type must be 32-bit");
|
||||
constexpr size_t kBufferSize =
|
||||
Randen::kSeedBytes / sizeof(sequence_result_type);
|
||||
alignas(16) sequence_result_type buffer[kBufferSize];
|
||||
|
||||
// Randen::Absorb XORs the seed into state, which is then mixed by a call
|
||||
// to Randen::Generate. Seeding with only the provided entropy is preferred
|
||||
// to using an arbitrary generate() call, so use [rand.req.seed_seq]
|
||||
// size as a proxy for the number of entropy units that can be generated
|
||||
// without relying on seed sequence mixing...
|
||||
const size_t entropy_size = seq.size();
|
||||
if (entropy_size < kBufferSize) {
|
||||
// ... and only request that many values, or 256-bits, when unspecified.
|
||||
const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size;
|
||||
std::fill(buffer + requested_entropy, buffer + kBufferSize, 0);
|
||||
seq.generate(buffer, buffer + requested_entropy);
|
||||
#ifdef ABSL_IS_BIG_ENDIAN
|
||||
// Randen expects the seed buffer to be in Little Endian; reverse it on
|
||||
// Big Endian platforms.
|
||||
for (sequence_result_type& e : buffer) {
|
||||
e = absl::little_endian::FromHost(e);
|
||||
}
|
||||
#endif
|
||||
// The Randen paper suggests preferentially initializing even-numbered
|
||||
// 128-bit vectors of the randen state (there are 16 such vectors).
|
||||
// The seed data is merged into the state offset by 128-bits, which
|
||||
// implies preferring seed bytes [16..31, ..., 208..223]. Since the
|
||||
// buffer is 32-bit values, we swap the corresponding buffer positions in
|
||||
// 128-bit chunks.
|
||||
size_t dst = kBufferSize;
|
||||
while (dst > 7) {
|
||||
// leave the odd bucket as-is.
|
||||
dst -= 4;
|
||||
size_t src = dst >> 1;
|
||||
// swap 128-bits into the even bucket
|
||||
std::swap(buffer[--dst], buffer[--src]);
|
||||
std::swap(buffer[--dst], buffer[--src]);
|
||||
std::swap(buffer[--dst], buffer[--src]);
|
||||
std::swap(buffer[--dst], buffer[--src]);
|
||||
}
|
||||
} else {
|
||||
seq.generate(buffer, buffer + kBufferSize);
|
||||
}
|
||||
impl_.Absorb(buffer, state());
|
||||
|
||||
// Generate will be called when operator() is called
|
||||
next_ = kStateSizeT;
|
||||
}
|
||||
|
||||
void discard(uint64_t count) {
|
||||
uint64_t step = std::min<uint64_t>(kStateSizeT - next_, count);
|
||||
count -= step;
|
||||
|
||||
constexpr uint64_t kRateT = kStateSizeT - kCapacityT;
|
||||
auto* begin = state();
|
||||
while (count > 0) {
|
||||
next_ = kCapacityT;
|
||||
impl_.Generate(*reinterpret_cast<result_type(*)[kStateSizeT]>(begin));
|
||||
step = std::min<uint64_t>(kRateT, count);
|
||||
count -= step;
|
||||
}
|
||||
next_ += step;
|
||||
}
|
||||
|
||||
bool operator==(const randen_engine& other) const {
|
||||
const auto* begin = state();
|
||||
return next_ == other.next_ &&
|
||||
std::equal(begin, begin + kStateSizeT, other.state());
|
||||
}
|
||||
|
||||
bool operator!=(const randen_engine& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
|
||||
const randen_engine<T>& engine) { // NOLINT(runtime/references)
|
||||
using numeric_type =
|
||||
typename random_internal::stream_format_type<result_type>::type;
|
||||
auto saver = random_internal::make_ostream_state_saver(os);
|
||||
auto* it = engine.state();
|
||||
for (auto* end = it + kStateSizeT; it < end; ++it) {
|
||||
// In the case that `elem` is `uint8_t`, it must be cast to something
|
||||
// larger so that it prints as an integer rather than a character. For
|
||||
// simplicity, apply the cast all circumstances.
|
||||
os << static_cast<numeric_type>(little_endian::FromHost(*it))
|
||||
<< os.fill();
|
||||
}
|
||||
os << engine.next_;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_istream<CharT, Traits>& operator>>(
|
||||
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
|
||||
randen_engine<T>& engine) { // NOLINT(runtime/references)
|
||||
using numeric_type =
|
||||
typename random_internal::stream_format_type<result_type>::type;
|
||||
result_type state[kStateSizeT];
|
||||
size_t next;
|
||||
for (auto& elem : state) {
|
||||
// It is not possible to read uint8_t from wide streams, so it is
|
||||
// necessary to read a wider type and then cast it to uint8_t.
|
||||
numeric_type value;
|
||||
is >> value;
|
||||
elem = little_endian::ToHost(static_cast<result_type>(value));
|
||||
}
|
||||
is >> next;
|
||||
if (is.fail()) {
|
||||
return is;
|
||||
}
|
||||
std::memcpy(engine.state(), state, sizeof(state));
|
||||
engine.next_ = next;
|
||||
return is;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t kStateSizeT =
|
||||
Randen::kStateBytes / sizeof(result_type);
|
||||
static constexpr size_t kCapacityT =
|
||||
Randen::kCapacityBytes / sizeof(result_type);
|
||||
|
||||
// Returns the state array pointer, which is aligned to 16 bytes.
|
||||
// The first kCapacityT are the `inner' sponge; the remainder are available.
|
||||
result_type* state() {
|
||||
return reinterpret_cast<result_type*>(
|
||||
(reinterpret_cast<uintptr_t>(&raw_state_) & 0xf) ? (raw_state_ + 8)
|
||||
: raw_state_);
|
||||
}
|
||||
const result_type* state() const {
|
||||
return const_cast<randen_engine*>(this)->state();
|
||||
}
|
||||
|
||||
// raw state array, manually aligned in state(). This overallocates
|
||||
// by 8 bytes since C++ does not guarantee extended heap alignment.
|
||||
alignas(8) char raw_state_[Randen::kStateBytes + 8];
|
||||
size_t next_; // index within state()
|
||||
Randen impl_;
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
|
||||
|
|
@ -0,0 +1,655 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen_engine.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/random/internal/explicit_seed_seq.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#define UPDATE_GOLDEN 0
|
||||
|
||||
using randen_u64 = absl::random_internal::randen_engine<uint64_t>;
|
||||
using randen_u32 = absl::random_internal::randen_engine<uint32_t>;
|
||||
using absl::random_internal::ExplicitSeedSeq;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename UIntType>
|
||||
class RandenEngineTypedTest : public ::testing::Test {};
|
||||
|
||||
using UIntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
|
||||
|
||||
TYPED_TEST_SUITE(RandenEngineTypedTest, UIntTypes);
|
||||
|
||||
TYPED_TEST(RandenEngineTypedTest, VerifyReseedChangesAllValues) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
using result_type = typename randen::result_type;
|
||||
|
||||
const size_t kNumOutputs = (sizeof(randen) * 2 / sizeof(TypeParam)) + 1;
|
||||
randen engine;
|
||||
|
||||
// MSVC emits error 2719 without the use of std::ref below.
|
||||
// * formal parameter with __declspec(align('#')) won't be aligned
|
||||
|
||||
{
|
||||
std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7};
|
||||
engine.seed(seq1);
|
||||
}
|
||||
result_type a[kNumOutputs];
|
||||
std::generate(std::begin(a), std::end(a), std::ref(engine));
|
||||
|
||||
{
|
||||
std::random_device rd;
|
||||
std::seed_seq seq2{rd(), rd(), rd()};
|
||||
engine.seed(seq2);
|
||||
}
|
||||
result_type b[kNumOutputs];
|
||||
std::generate(std::begin(b), std::end(b), std::ref(engine));
|
||||
|
||||
// Test that generated sequence changed as sequence of bits, i.e. if about
|
||||
// half of the bites were flipped between two non-correlated values.
|
||||
size_t changed_bits = 0;
|
||||
size_t unchanged_bits = 0;
|
||||
size_t total_set = 0;
|
||||
size_t total_bits = 0;
|
||||
size_t equal_count = 0;
|
||||
for (size_t i = 0; i < kNumOutputs; ++i) {
|
||||
equal_count += (a[i] == b[i]) ? 1 : 0;
|
||||
std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]);
|
||||
changed_bits += bitset.count();
|
||||
unchanged_bits += bitset.size() - bitset.count();
|
||||
|
||||
std::bitset<sizeof(result_type) * 8> a_set(a[i]);
|
||||
std::bitset<sizeof(result_type) * 8> b_set(b[i]);
|
||||
total_set += a_set.count() + b_set.count();
|
||||
total_bits += 2 * 8 * sizeof(result_type);
|
||||
}
|
||||
// On average, half the bits are changed between two calls.
|
||||
EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits));
|
||||
EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits));
|
||||
|
||||
// Verify using a quick normal-approximation to the binomial.
|
||||
EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits))
|
||||
<< "@" << total_set / static_cast<double>(total_bits);
|
||||
|
||||
// Also, A[i] == B[i] with probability (1/range) * N.
|
||||
// Give this a pretty wide latitude, though.
|
||||
const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8);
|
||||
EXPECT_LE(equal_count, 1.0 + kExpected);
|
||||
}
|
||||
|
||||
// Number of values that needs to be consumed to clean two sizes of buffer
|
||||
// and trigger third refresh. (slightly overestimates the actual state size).
|
||||
constexpr size_t kTwoBufferValues = sizeof(randen_u64) / sizeof(uint16_t) + 1;
|
||||
|
||||
TYPED_TEST(RandenEngineTypedTest, VerifyDiscard) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
|
||||
for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) {
|
||||
randen engine_used;
|
||||
for (size_t i = 0; i < num_used; ++i) {
|
||||
engine_used();
|
||||
}
|
||||
|
||||
for (size_t num_discard = 0; num_discard < kTwoBufferValues;
|
||||
++num_discard) {
|
||||
randen engine1 = engine_used;
|
||||
randen engine2 = engine_used;
|
||||
for (size_t i = 0; i < num_discard; ++i) {
|
||||
engine1();
|
||||
}
|
||||
engine2.discard(num_discard);
|
||||
for (size_t i = 0; i < kTwoBufferValues; ++i) {
|
||||
const auto r1 = engine1();
|
||||
const auto r2 = engine2();
|
||||
ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(RandenEngineTypedTest, StreamOperatorsResult) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
std::wostringstream os;
|
||||
std::wistringstream is;
|
||||
randen engine;
|
||||
|
||||
EXPECT_EQ(&(os << engine), &os);
|
||||
EXPECT_EQ(&(is >> engine), &is);
|
||||
}
|
||||
|
||||
TYPED_TEST(RandenEngineTypedTest, StreamSerialization) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
|
||||
for (size_t discard = 0; discard < kTwoBufferValues; ++discard) {
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
randen engine(seed_sequence);
|
||||
engine.discard(discard);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << engine;
|
||||
|
||||
randen new_engine;
|
||||
stream >> new_engine;
|
||||
for (size_t i = 0; i < 64; ++i) {
|
||||
EXPECT_EQ(engine(), new_engine()) << " " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t kNumGoldenOutputs = 127;
|
||||
|
||||
// This test is checking if randen_engine is meets interface requirements
|
||||
// defined in [rand.req.urbg].
|
||||
TYPED_TEST(RandenEngineTypedTest, RandomNumberEngineInterface) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
|
||||
using E = randen;
|
||||
using T = typename E::result_type;
|
||||
|
||||
static_assert(std::is_copy_constructible<E>::value,
|
||||
"randen_engine must be copy constructible");
|
||||
|
||||
static_assert(absl::is_copy_assignable<E>::value,
|
||||
"randen_engine must be copy assignable");
|
||||
|
||||
static_assert(std::is_move_constructible<E>::value,
|
||||
"randen_engine must be move constructible");
|
||||
|
||||
static_assert(absl::is_move_assignable<E>::value,
|
||||
"randen_engine must be move assignable");
|
||||
|
||||
static_assert(std::is_same<decltype(std::declval<E>()()), T>::value,
|
||||
"return type of operator() must be result_type");
|
||||
|
||||
// Names after definition of [rand.req.urbg] in C++ standard.
|
||||
// e us a value of E
|
||||
// v is a lvalue of E
|
||||
// x, y are possibly const values of E
|
||||
// s is a value of T
|
||||
// q is a value satisfying requirements of seed_sequence
|
||||
// z is a value of type unsigned long long
|
||||
// os is a some specialization of basic_ostream
|
||||
// is is a some specialization of basic_istream
|
||||
|
||||
E e, v;
|
||||
const E x, y;
|
||||
T s = 1;
|
||||
std::seed_seq q{1, 2, 3};
|
||||
unsigned long long z = 1; // NOLINT(runtime/int)
|
||||
std::wostringstream os;
|
||||
std::wistringstream is;
|
||||
|
||||
E{};
|
||||
E{x};
|
||||
E{s};
|
||||
E{q};
|
||||
|
||||
e.seed();
|
||||
|
||||
// MSVC emits error 2718 when using EXPECT_EQ(e, x)
|
||||
// * actual parameter with __declspec(align('#')) won't be aligned
|
||||
EXPECT_TRUE(e == x);
|
||||
|
||||
e.seed(q);
|
||||
{
|
||||
E tmp(q);
|
||||
EXPECT_TRUE(e == tmp);
|
||||
}
|
||||
|
||||
e();
|
||||
{
|
||||
E tmp(q);
|
||||
EXPECT_TRUE(e != tmp);
|
||||
}
|
||||
|
||||
e.discard(z);
|
||||
|
||||
static_assert(std::is_same<decltype(x == y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
|
||||
static_assert(std::is_same<decltype(x != y), bool>::value,
|
||||
"return type of operator== must be bool");
|
||||
}
|
||||
|
||||
TYPED_TEST(RandenEngineTypedTest, RandenEngineSFINAETest) {
|
||||
using randen = typename absl::random_internal::randen_engine<TypeParam>;
|
||||
using result_type = typename randen::result_type;
|
||||
|
||||
{
|
||||
randen engine(result_type(1));
|
||||
engine.seed(result_type(1));
|
||||
}
|
||||
|
||||
{
|
||||
result_type n = 1;
|
||||
randen engine(n);
|
||||
engine.seed(n);
|
||||
}
|
||||
|
||||
{
|
||||
randen engine(1);
|
||||
engine.seed(1);
|
||||
}
|
||||
|
||||
{
|
||||
int n = 1;
|
||||
randen engine(n);
|
||||
engine.seed(n);
|
||||
}
|
||||
|
||||
{
|
||||
std::seed_seq seed_seq;
|
||||
randen engine(seed_seq);
|
||||
engine.seed(seed_seq);
|
||||
}
|
||||
|
||||
{
|
||||
randen engine{std::seed_seq()};
|
||||
engine.seed(std::seed_seq());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RandenTest, VerifyGoldenRanden64Default) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0xc3c14f134e433977, 0xdda9f47cd90410ee, 0x887bf3087fd8ca10,
|
||||
0xf0b780f545c72912, 0x15dbb1d37696599f, 0x30ec63baff3c6d59,
|
||||
0xb29f73606f7f20a6, 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e,
|
||||
0x9cbf605e3fd9de8a, 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5,
|
||||
0xf4b327fe0fc73c37, 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d,
|
||||
0xd5af05dd3eff9556, 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c,
|
||||
0x58d3575834956d42, 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3,
|
||||
0x497cabf3431154fc, 0x4e24370570029a8b, 0xd88b5749f090e5ea,
|
||||
0xc651a582a970692f, 0x78fcec2cbb6342f5, 0x463cb745612f55db,
|
||||
0x352ee4ad1816afe3, 0x026ff374c101da7e, 0x811ef0821c3de851,
|
||||
0x6f7e616704c4fa59, 0xa0660379992d58fc, 0x04b0a374a3b795c7,
|
||||
0x915f3445685da798, 0x26802a8ac76571ce, 0x4663352533ce1882,
|
||||
0xb9fdefb4a24dc738, 0x5588ba3a4d6e6c51, 0xa2101a42d35f1956,
|
||||
0x607195a5e200f5fd, 0x7e100308f3290764, 0xe1e5e03c759c0709,
|
||||
0x082572cc5da6606f, 0xcbcf585399e432f1, 0xe8a2be4f8335d8f1,
|
||||
0x0904469acbfee8f2, 0xf08bd31b6daecd51, 0x08e8a1f1a69da69a,
|
||||
0x6542a20aad57bff5, 0x2e9705bb053d6b46, 0xda2fc9db0713c391,
|
||||
0x78e3a810213b6ffb, 0xdc16a59cdd85f8a6, 0xc0932718cd55781f,
|
||||
0xb9bfb29c2b20bfe5, 0xb97289c1be0f2f9c, 0xc0a2a0e403a892d4,
|
||||
0x5524bb834771435b, 0x8265da3d39d1a750, 0xff4af3ab8d1b78c5,
|
||||
0xf0ec5f424bcad77f, 0x66e455f627495189, 0xc82d3120b57e3270,
|
||||
0x3424e47dc22596e3, 0xbc0c95129ccedcdd, 0xc191c595afc4dcbf,
|
||||
0x120392bd2bb70939, 0x7f90650ea6cd6ab4, 0x7287491832695ad3,
|
||||
0xa7c8fac5a7917eb0, 0xd088cb9418be0361, 0x7c1bf9839c7c1ce5,
|
||||
0xe2e991fa58e1e79e, 0x78565cdefd28c4ad, 0x7351b9fef98bafad,
|
||||
0x2a9eac28b08c96bf, 0x6c4f179696cb2225, 0x13a685861bab87e0,
|
||||
0x64c6de5aa0501971, 0x30537425cac70991, 0x01590d9dc6c532b7,
|
||||
0x7e05e3aa8ec720dc, 0x74a07d9c54e3e63f, 0x738184388f3bc1d2,
|
||||
0x26ffdc5067be3acb, 0x6bcdf185561f255f, 0xa0eaf2e1cf99b1c6,
|
||||
0x171df81934f68604, 0x7ea5a21665683e5a, 0x5d1cb02075ba1cea,
|
||||
0x957f38cbd2123fdf, 0xba6364eff80de02f, 0x606e0a0e41d452ee,
|
||||
0x892d8317de82f7a2, 0xe707b1db50f7b43e, 0x4eb28826766fcf5b,
|
||||
0x5a362d56e80a0951, 0x6ee217df16527d78, 0xf6737962ba6b23dd,
|
||||
0x443e63857d4076ca, 0x790d9a5f048adfeb, 0xd796b052151ee94d,
|
||||
0x033ed95c12b04a03, 0x8b833ff84893da5d, 0x3d6724b1bb15eab9,
|
||||
0x9877c4225061ca76, 0xd68d6810adf74fb3, 0x42e5352fe30ce989,
|
||||
0x265b565a7431fde7, 0x3cdbf7e358df4b8b, 0x2922a47f6d3e8779,
|
||||
0x52d2242f65b37f88, 0x5d836d6e2958d6b5, 0x29d40f00566d5e26,
|
||||
0x288db0e1124b14a0, 0x6c056608b7d9c1b6, 0x0b9471bdb8f19d32,
|
||||
0x8fb946504faa6c9d, 0x8943a9464540251c, 0xfd1fe27d144a09e0,
|
||||
0xea6ac458da141bda, 0x8048f217633fce36, 0xfeda1384ade74d31,
|
||||
0x4334b8b02ff7612f, 0xdbc8441f5227e216, 0x096d119a3605c85b,
|
||||
0x2b72b31c21b7d7d0};
|
||||
|
||||
randen_u64 engine;
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed();
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RandenTest, VerifyGoldenRanden64Seeded) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0x83a9e58f94d3dcd5, 0x70bbdff3d97949fb, 0x0438481f7471c1b4,
|
||||
0x34fdc58ee5fb5930, 0xceee4f2d2a937d17, 0xb5a26a68e432aea9,
|
||||
0x8b64774a3fb51740, 0xd89ac1fc74249c74, 0x03910d1d23fc3fdf,
|
||||
0xd38f630878aa897f, 0x0ee8f0f5615f7e44, 0x98f5a53df8279d52,
|
||||
0xb403f52c25938d0e, 0x240072996ea6e838, 0xd3a791246190fa61,
|
||||
0xaaedd3df7a7b4f80, 0xc6eacabe05deaf6e, 0xb7967dd8790edf4d,
|
||||
0x9a0a8e67e049d279, 0x0494f606aebc23e7, 0x598dcd687bc3e0ee,
|
||||
0x010ac81802d452a1, 0x6407c87160aa2842, 0x5a56e276486f93a0,
|
||||
0xc887a399d46a8f02, 0x9e1e6100fe93b740, 0x12d02e330f8901f6,
|
||||
0xc39ca52b47e790b7, 0xb0b0a2fa11e82e61, 0x1542d841a303806a,
|
||||
0x1fe659fd7d6e9d86, 0xb8c90d80746541ac, 0x239d56a5669ddc94,
|
||||
0xd40db57c8123d13c, 0x3abc2414153a0db0, 0x9bad665630cb8d61,
|
||||
0x0bd1fb90ee3f4bbc, 0x8f0b4d7e079b4e42, 0xfa0fb0e0ee59e793,
|
||||
0x51080b283e071100, 0x2c4b9e715081cc15, 0xbe10ed49de4941df,
|
||||
0xf8eaac9d4b1b0d37, 0x4bcce4b54605e139, 0xa64722b76765dda6,
|
||||
0xb9377d738ca28ab5, 0x779fad81a8ccc1af, 0x65cb3ee61ffd3ba7,
|
||||
0xd74e79087862836f, 0xd05b9c584c3f25bf, 0x2ba93a4693579827,
|
||||
0xd81530aff05420ce, 0xec06cea215478621, 0x4b1798a6796d65ad,
|
||||
0xf142f3fb3a6f6fa6, 0x002b7bf7e237b560, 0xf47f2605ef65b4f8,
|
||||
0x9804ec5517effc18, 0xaed3d7f8b7d481cd, 0x5651c24c1ce338d1,
|
||||
0x3e7a38208bf0a3c6, 0x6796a7b614534aed, 0x0d0f3b848358460f,
|
||||
0x0fa5fe7600b19524, 0x2b0cf38253faaedc, 0x10df9188233a9fd6,
|
||||
0x3a10033880138b59, 0x5fb0b0d23948e80f, 0x9e76f7b02fbf5350,
|
||||
0x0816052304b1a985, 0x30c9880db41fd218, 0x14aa399b65e20f28,
|
||||
0xe1454a8cace787b4, 0x325ac971b6c6f0f5, 0x716b1aa2784f3d36,
|
||||
0x3d5ce14accfd144f, 0x6c0c97710f651792, 0xbc5b0f59fb333532,
|
||||
0x2a90a7d2140470bc, 0x8da269f55c1e1c8d, 0xcfc37143895792ca,
|
||||
0xbe21eab1f30b238f, 0x8c47229dee4d65fd, 0x5743614ed1ed7d54,
|
||||
0x351372a99e9c476e, 0x2bd5ea15e5db085f, 0x6925fde46e0af4ca,
|
||||
0xed3eda2bdc1f45bd, 0xdef68c68d460fa6e, 0xe42a0de76253e2b5,
|
||||
0x4e5176dcbc29c305, 0xbfd85fba9f810f6e, 0x76a5a2a9beb815c6,
|
||||
0x01edc4ddceaf414c, 0xa4e98904b4bb3b4b, 0x00bd63ac7d2f1ddd,
|
||||
0xb8491fe6e998ddbb, 0xb386a3463dda6800, 0x0081887688871619,
|
||||
0x33d394b3344e9a38, 0x815dba65a3a8baf9, 0x4232f6ec02c2fd1a,
|
||||
0xb5cff603edd20834, 0x580189243f687663, 0xa8d5a2cbdc27fe99,
|
||||
0x725d881693fa0131, 0xa2be2c13db2c7ac5, 0x7b6a9614b509fd78,
|
||||
0xb6b136d71e717636, 0x660f1a71aff046ea, 0x0ba10ae346c8ec9e,
|
||||
0xe66dde53e3145b41, 0x3b18288c88c26be6, 0x4d9d9d2ff02db933,
|
||||
0x4167da8c70f46e8a, 0xf183beef8c6318b4, 0x4d889e1e71eeeef1,
|
||||
0x7175c71ad6689b6b, 0xfb9e42beacd1b7dd, 0xc33d0e91b29b5e0d,
|
||||
0xd39b83291ce47922, 0xc4d570fb8493d12e, 0x23d5a5724f424ae6,
|
||||
0x5245f161876b6616, 0x38d77dbd21ab578d, 0x9c3423311f4ecbfe,
|
||||
0x76fe31389bacd9d5,
|
||||
};
|
||||
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
randen_u64 engine(seed_sequence);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed(seed_sequence);
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RandenTest, VerifyGoldenRanden32Default) {
|
||||
constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = {
|
||||
0x4e433977, 0xc3c14f13, 0xd90410ee, 0xdda9f47c, 0x7fd8ca10, 0x887bf308,
|
||||
0x45c72912, 0xf0b780f5, 0x7696599f, 0x15dbb1d3, 0xff3c6d59, 0x30ec63ba,
|
||||
0x6f7f20a6, 0xb29f7360, 0x6f49a54c, 0x02808a31, 0xd5c8e50e, 0x3b8feaf9,
|
||||
0x3fd9de8a, 0x9cbf605e, 0x78183bbb, 0xc970ae1a, 0x56301ed5, 0xd8b2ffd3,
|
||||
0x0fc73c37, 0xf4b327fe, 0xeb8f9a19, 0xcdfd8d76, 0x91420c9d, 0xc3a506eb,
|
||||
0x3eff9556, 0xd5af05dd, 0x8f83c4a1, 0x48db1bb7, 0x0d6bfe8c, 0x7023920e,
|
||||
0x34956d42, 0x58d35758, 0x6b87b840, 0xed1ef4c2, 0x3e0b2df3, 0x8eef32a2,
|
||||
0x431154fc, 0x497cabf3, 0x70029a8b, 0x4e243705, 0xf090e5ea, 0xd88b5749,
|
||||
0xa970692f, 0xc651a582, 0xbb6342f5, 0x78fcec2c, 0x612f55db, 0x463cb745,
|
||||
0x1816afe3, 0x352ee4ad, 0xc101da7e, 0x026ff374, 0x1c3de851, 0x811ef082,
|
||||
0x04c4fa59, 0x6f7e6167, 0x992d58fc, 0xa0660379, 0xa3b795c7, 0x04b0a374,
|
||||
0x685da798, 0x915f3445, 0xc76571ce, 0x26802a8a, 0x33ce1882, 0x46633525,
|
||||
0xa24dc738, 0xb9fdefb4, 0x4d6e6c51, 0x5588ba3a, 0xd35f1956, 0xa2101a42,
|
||||
0xe200f5fd, 0x607195a5, 0xf3290764, 0x7e100308, 0x759c0709, 0xe1e5e03c,
|
||||
0x5da6606f, 0x082572cc, 0x99e432f1, 0xcbcf5853, 0x8335d8f1, 0xe8a2be4f,
|
||||
0xcbfee8f2, 0x0904469a, 0x6daecd51, 0xf08bd31b, 0xa69da69a, 0x08e8a1f1,
|
||||
0xad57bff5, 0x6542a20a, 0x053d6b46, 0x2e9705bb, 0x0713c391, 0xda2fc9db,
|
||||
0x213b6ffb, 0x78e3a810, 0xdd85f8a6, 0xdc16a59c, 0xcd55781f, 0xc0932718,
|
||||
0x2b20bfe5, 0xb9bfb29c, 0xbe0f2f9c, 0xb97289c1, 0x03a892d4, 0xc0a2a0e4,
|
||||
0x4771435b, 0x5524bb83, 0x39d1a750, 0x8265da3d, 0x8d1b78c5, 0xff4af3ab,
|
||||
0x4bcad77f, 0xf0ec5f42, 0x27495189, 0x66e455f6, 0xb57e3270, 0xc82d3120,
|
||||
0xc22596e3, 0x3424e47d, 0x9ccedcdd, 0xbc0c9512, 0xafc4dcbf, 0xc191c595,
|
||||
0x2bb70939, 0x120392bd, 0xa6cd6ab4, 0x7f90650e, 0x32695ad3, 0x72874918,
|
||||
0xa7917eb0, 0xa7c8fac5, 0x18be0361, 0xd088cb94, 0x9c7c1ce5, 0x7c1bf983,
|
||||
0x58e1e79e, 0xe2e991fa, 0xfd28c4ad, 0x78565cde, 0xf98bafad, 0x7351b9fe,
|
||||
0xb08c96bf, 0x2a9eac28, 0x96cb2225, 0x6c4f1796, 0x1bab87e0, 0x13a68586,
|
||||
0xa0501971, 0x64c6de5a, 0xcac70991, 0x30537425, 0xc6c532b7, 0x01590d9d,
|
||||
0x8ec720dc, 0x7e05e3aa, 0x54e3e63f, 0x74a07d9c, 0x8f3bc1d2, 0x73818438,
|
||||
0x67be3acb, 0x26ffdc50, 0x561f255f, 0x6bcdf185, 0xcf99b1c6, 0xa0eaf2e1,
|
||||
0x34f68604, 0x171df819, 0x65683e5a, 0x7ea5a216, 0x75ba1cea, 0x5d1cb020,
|
||||
0xd2123fdf, 0x957f38cb, 0xf80de02f, 0xba6364ef, 0x41d452ee, 0x606e0a0e,
|
||||
0xde82f7a2, 0x892d8317, 0x50f7b43e, 0xe707b1db, 0x766fcf5b, 0x4eb28826,
|
||||
0xe80a0951, 0x5a362d56, 0x16527d78, 0x6ee217df, 0xba6b23dd, 0xf6737962,
|
||||
0x7d4076ca, 0x443e6385, 0x048adfeb, 0x790d9a5f, 0x151ee94d, 0xd796b052,
|
||||
0x12b04a03, 0x033ed95c, 0x4893da5d, 0x8b833ff8, 0xbb15eab9, 0x3d6724b1,
|
||||
0x5061ca76, 0x9877c422, 0xadf74fb3, 0xd68d6810, 0xe30ce989, 0x42e5352f,
|
||||
0x7431fde7, 0x265b565a, 0x58df4b8b, 0x3cdbf7e3, 0x6d3e8779, 0x2922a47f,
|
||||
0x65b37f88, 0x52d2242f, 0x2958d6b5, 0x5d836d6e, 0x566d5e26, 0x29d40f00,
|
||||
0x124b14a0, 0x288db0e1, 0xb7d9c1b6, 0x6c056608, 0xb8f19d32, 0x0b9471bd,
|
||||
0x4faa6c9d, 0x8fb94650, 0x4540251c, 0x8943a946, 0x144a09e0, 0xfd1fe27d,
|
||||
0xda141bda, 0xea6ac458, 0x633fce36, 0x8048f217, 0xade74d31, 0xfeda1384,
|
||||
0x2ff7612f, 0x4334b8b0, 0x5227e216, 0xdbc8441f, 0x3605c85b, 0x096d119a,
|
||||
0x21b7d7d0, 0x2b72b31c};
|
||||
|
||||
randen_u32 engine;
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) {
|
||||
printf("0x%08x, ", engine());
|
||||
if (i % 6 == 5) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed();
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RandenTest, VerifyGoldenRanden32Seeded) {
|
||||
constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = {
|
||||
0x94d3dcd5, 0x83a9e58f, 0xd97949fb, 0x70bbdff3, 0x7471c1b4, 0x0438481f,
|
||||
0xe5fb5930, 0x34fdc58e, 0x2a937d17, 0xceee4f2d, 0xe432aea9, 0xb5a26a68,
|
||||
0x3fb51740, 0x8b64774a, 0x74249c74, 0xd89ac1fc, 0x23fc3fdf, 0x03910d1d,
|
||||
0x78aa897f, 0xd38f6308, 0x615f7e44, 0x0ee8f0f5, 0xf8279d52, 0x98f5a53d,
|
||||
0x25938d0e, 0xb403f52c, 0x6ea6e838, 0x24007299, 0x6190fa61, 0xd3a79124,
|
||||
0x7a7b4f80, 0xaaedd3df, 0x05deaf6e, 0xc6eacabe, 0x790edf4d, 0xb7967dd8,
|
||||
0xe049d279, 0x9a0a8e67, 0xaebc23e7, 0x0494f606, 0x7bc3e0ee, 0x598dcd68,
|
||||
0x02d452a1, 0x010ac818, 0x60aa2842, 0x6407c871, 0x486f93a0, 0x5a56e276,
|
||||
0xd46a8f02, 0xc887a399, 0xfe93b740, 0x9e1e6100, 0x0f8901f6, 0x12d02e33,
|
||||
0x47e790b7, 0xc39ca52b, 0x11e82e61, 0xb0b0a2fa, 0xa303806a, 0x1542d841,
|
||||
0x7d6e9d86, 0x1fe659fd, 0x746541ac, 0xb8c90d80, 0x669ddc94, 0x239d56a5,
|
||||
0x8123d13c, 0xd40db57c, 0x153a0db0, 0x3abc2414, 0x30cb8d61, 0x9bad6656,
|
||||
0xee3f4bbc, 0x0bd1fb90, 0x079b4e42, 0x8f0b4d7e, 0xee59e793, 0xfa0fb0e0,
|
||||
0x3e071100, 0x51080b28, 0x5081cc15, 0x2c4b9e71, 0xde4941df, 0xbe10ed49,
|
||||
0x4b1b0d37, 0xf8eaac9d, 0x4605e139, 0x4bcce4b5, 0x6765dda6, 0xa64722b7,
|
||||
0x8ca28ab5, 0xb9377d73, 0xa8ccc1af, 0x779fad81, 0x1ffd3ba7, 0x65cb3ee6,
|
||||
0x7862836f, 0xd74e7908, 0x4c3f25bf, 0xd05b9c58, 0x93579827, 0x2ba93a46,
|
||||
0xf05420ce, 0xd81530af, 0x15478621, 0xec06cea2, 0x796d65ad, 0x4b1798a6,
|
||||
0x3a6f6fa6, 0xf142f3fb, 0xe237b560, 0x002b7bf7, 0xef65b4f8, 0xf47f2605,
|
||||
0x17effc18, 0x9804ec55, 0xb7d481cd, 0xaed3d7f8, 0x1ce338d1, 0x5651c24c,
|
||||
0x8bf0a3c6, 0x3e7a3820, 0x14534aed, 0x6796a7b6, 0x8358460f, 0x0d0f3b84,
|
||||
0x00b19524, 0x0fa5fe76, 0x53faaedc, 0x2b0cf382, 0x233a9fd6, 0x10df9188,
|
||||
0x80138b59, 0x3a100338, 0x3948e80f, 0x5fb0b0d2, 0x2fbf5350, 0x9e76f7b0,
|
||||
0x04b1a985, 0x08160523, 0xb41fd218, 0x30c9880d, 0x65e20f28, 0x14aa399b,
|
||||
0xace787b4, 0xe1454a8c, 0xb6c6f0f5, 0x325ac971, 0x784f3d36, 0x716b1aa2,
|
||||
0xccfd144f, 0x3d5ce14a, 0x0f651792, 0x6c0c9771, 0xfb333532, 0xbc5b0f59,
|
||||
0x140470bc, 0x2a90a7d2, 0x5c1e1c8d, 0x8da269f5, 0x895792ca, 0xcfc37143,
|
||||
0xf30b238f, 0xbe21eab1, 0xee4d65fd, 0x8c47229d, 0xd1ed7d54, 0x5743614e,
|
||||
0x9e9c476e, 0x351372a9, 0xe5db085f, 0x2bd5ea15, 0x6e0af4ca, 0x6925fde4,
|
||||
0xdc1f45bd, 0xed3eda2b, 0xd460fa6e, 0xdef68c68, 0x6253e2b5, 0xe42a0de7,
|
||||
0xbc29c305, 0x4e5176dc, 0x9f810f6e, 0xbfd85fba, 0xbeb815c6, 0x76a5a2a9,
|
||||
0xceaf414c, 0x01edc4dd, 0xb4bb3b4b, 0xa4e98904, 0x7d2f1ddd, 0x00bd63ac,
|
||||
0xe998ddbb, 0xb8491fe6, 0x3dda6800, 0xb386a346, 0x88871619, 0x00818876,
|
||||
0x344e9a38, 0x33d394b3, 0xa3a8baf9, 0x815dba65, 0x02c2fd1a, 0x4232f6ec,
|
||||
0xedd20834, 0xb5cff603, 0x3f687663, 0x58018924, 0xdc27fe99, 0xa8d5a2cb,
|
||||
0x93fa0131, 0x725d8816, 0xdb2c7ac5, 0xa2be2c13, 0xb509fd78, 0x7b6a9614,
|
||||
0x1e717636, 0xb6b136d7, 0xaff046ea, 0x660f1a71, 0x46c8ec9e, 0x0ba10ae3,
|
||||
0xe3145b41, 0xe66dde53, 0x88c26be6, 0x3b18288c, 0xf02db933, 0x4d9d9d2f,
|
||||
0x70f46e8a, 0x4167da8c, 0x8c6318b4, 0xf183beef, 0x71eeeef1, 0x4d889e1e,
|
||||
0xd6689b6b, 0x7175c71a, 0xacd1b7dd, 0xfb9e42be, 0xb29b5e0d, 0xc33d0e91,
|
||||
0x1ce47922, 0xd39b8329, 0x8493d12e, 0xc4d570fb, 0x4f424ae6, 0x23d5a572,
|
||||
0x876b6616, 0x5245f161, 0x21ab578d, 0x38d77dbd, 0x1f4ecbfe, 0x9c342331,
|
||||
0x9bacd9d5, 0x76fe3138,
|
||||
};
|
||||
|
||||
ExplicitSeedSeq seed_sequence{12, 34, 56};
|
||||
randen_u32 engine(seed_sequence);
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) {
|
||||
printf("0x%08x, ", engine());
|
||||
if (i % 6 == 5) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
engine.seed(seed_sequence);
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RandenTest, VerifyGoldenFromDeserializedEngine) {
|
||||
constexpr uint64_t kGolden[kNumGoldenOutputs] = {
|
||||
0x067f9f9ab919657a, 0x0534605912988583, 0x8a303f72feaa673f,
|
||||
0x77b7fd747909185c, 0xd9af90403c56d891, 0xd939c6cb204d14b5,
|
||||
0x7fbe6b954a47b483, 0x8b31a47cc34c768d, 0x3a9e546da2701a9c,
|
||||
0x5246539046253e71, 0x417191ffb2a848a1, 0x7b1c7bf5a5001d09,
|
||||
0x9489b15d194f2361, 0xfcebdeea3bcd2461, 0xd643027c854cec97,
|
||||
0x5885397f91e0d21c, 0x53173b0efae30d58, 0x1c9c71168449fac1,
|
||||
0xe358202b711ed8aa, 0x94e3918ed1d8227c, 0x5bb4e251450144cf,
|
||||
0xb5c7a519b489af3b, 0x6f8b560b1f7b3469, 0xfde11dd4a1c74eef,
|
||||
0x33383d2f76457dcf, 0x3060c0ec6db9fce1, 0x18f451fcddeec766,
|
||||
0xe73c5d6b9f26da2a, 0x8d4cc566671b32a4, 0xb8189b73776bc9ff,
|
||||
0x497a70f9caf0bc23, 0x23afcc509791dcea, 0x18af70dc4b27d306,
|
||||
0xd3853f955a0ce5b9, 0x441db6c01a0afb17, 0xd0136c3fb8e1f13f,
|
||||
0x5e4fd6fc2f33783c, 0xe0d24548adb5da51, 0x0f4d8362a7d3485a,
|
||||
0x9f572d68270fa563, 0x6351fbc823024393, 0xa66dbfc61810e9ab,
|
||||
0x0ff17fc14b651af8, 0xd74c55dafb99e623, 0x36303bc1ad85c6c2,
|
||||
0x4920cd6a2af7e897, 0x0b8848addc30fecd, 0x9e1562eda6488e93,
|
||||
0x197553807d607828, 0xbef5eaeda5e21235, 0x18d91d2616aca527,
|
||||
0xb7821937f5c873cd, 0x2cd4ae5650dbeefc, 0xb35a64376f75ffdf,
|
||||
0x9226d414d647fe07, 0x663f3db455bbb35e, 0xa829eead6ae93247,
|
||||
0x7fd69c204dd0d25f, 0xbe1411f891c9acb1, 0xd476f34a506d5f11,
|
||||
0xf423d2831649c5ca, 0x1e503962951abd75, 0xeccc9e8b1e34b537,
|
||||
0xb11a147294044854, 0xc4cf27f0abf4929d, 0xe9193abf6fa24c8c,
|
||||
0xa94a259e3aba8808, 0x21dc414197deffa3, 0xa2ae211d1ff622ae,
|
||||
0xfe3995c46be5a4f4, 0xe9984c284bf11128, 0xcb1ce9d2f0851a80,
|
||||
0x42fee17971d87cd8, 0xac76a98d177adc88, 0xa0973b3dedc4af6f,
|
||||
0xdf56d6bbcb1b8e86, 0xf1e6485f407b11c9, 0x2c63de4deccb15c0,
|
||||
0x6fe69db32ed4fad7, 0xaa51a65f84bca1f1, 0x242f2ee81d608afc,
|
||||
0x8eb88b2b69fc153b, 0x22c20098baf73fd1, 0x57759466f576488c,
|
||||
0x075ca562cea1be9d, 0x9a74814d73d28891, 0x73d1555fc02f4d3d,
|
||||
0xc17f8f210ee89337, 0x46cca7999eaeafd4, 0x5db8d6a327a0d8ac,
|
||||
0xb79b4f93c738d7a1, 0x9994512f0036ded1, 0xd3883026f38747f4,
|
||||
0xf31f7458078d097c, 0x736ce4d480680669, 0x7a496f4c7e1033e3,
|
||||
0xecf85bf297fbc68c, 0x9e37e1d0f24f3c4e, 0x15b6e067ca0746fc,
|
||||
0xdd4a39905c5db81c, 0xb5dfafa7bcfdf7da, 0xca6646fb6f92a276,
|
||||
0x1c6b35f363ef0efd, 0x6a33d06037ad9f76, 0x45544241afd8f80f,
|
||||
0x83f8d83f859c90c5, 0x22aea9c5365e8c19, 0xfac35b11f20b6a6a,
|
||||
0xd1acf49d1a27dd2f, 0xf281cd09c4fed405, 0x076000a42cd38e4f,
|
||||
0x6ace300565070445, 0x463a62781bddc4db, 0x1477126b46b569ac,
|
||||
0x127f2bb15035fbb8, 0xdfa30946049c04a8, 0x89072a586ba8dd3e,
|
||||
0x62c809582bb7e74d, 0x22c0c3641406c28b, 0x9b66e36c47ff004d,
|
||||
0xb9cd2c7519653330, 0x18608d79cd7a598d, 0x92c0bd1323e53e32,
|
||||
0x887ff00de8524aa5, 0xa074410b787abd10, 0x18ab41b8057a2063,
|
||||
0x1560abf26bc5f987};
|
||||
|
||||
#if UPDATE_GOLDEN
|
||||
(void)kGolden; // Silence warning.
|
||||
std::seed_seq seed_sequence{1, 2, 3, 4, 5};
|
||||
randen_u64 engine(seed_sequence);
|
||||
std::ostringstream stream;
|
||||
stream << engine;
|
||||
auto str = stream.str();
|
||||
printf("%s\n\n", str.c_str());
|
||||
for (size_t i = 0; i < kNumGoldenOutputs; ++i) {
|
||||
printf("0x%016lx, ", engine());
|
||||
if (i % 3 == 2) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n\n\n");
|
||||
#else
|
||||
randen_u64 engine;
|
||||
std::istringstream stream(
|
||||
"0 0 9824501439887287479 3242284395352394785 243836530774933777 "
|
||||
"4047941804708365596 17165468127298385802 949276103645889255 "
|
||||
"10659970394998657921 1657570836810929787 11697746266668051452 "
|
||||
"9967209969299905230 14140390331161524430 7383014124183271684 "
|
||||
"13146719127702337852 13983155220295807171 11121125587542359264 "
|
||||
"195757810993252695 17138580243103178492 11326030747260920501 "
|
||||
"8585097322474965590 18342582839328350995 15052982824209724634 "
|
||||
"7321861343874683609 1806786911778767826 10100850842665572955 "
|
||||
"9249328950653985078 13600624835326909759 11137960060943860251 "
|
||||
"10208781341792329629 9282723971471525577 16373271619486811032 32");
|
||||
stream >> engine;
|
||||
for (const auto& elem : kGolden) {
|
||||
EXPECT_EQ(elem, engine());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RandenTest, IsFastOrSlow) {
|
||||
// randen_engine typically costs ~5ns per value for the optimized code paths,
|
||||
// and the ~1000ns per value for slow code paths. However when running under
|
||||
// msan, asan, etc. it can take much longer.
|
||||
//
|
||||
// The estimated operation time is something like:
|
||||
//
|
||||
// linux, optimized ~5ns
|
||||
// ppc, optimized ~7ns
|
||||
// nacl (slow), ~1100ns
|
||||
//
|
||||
// `kCount` is chosen below so that, in debug builds and without hardware
|
||||
// acceleration, the test (assuming ~1us per call) should finish in ~0.1s
|
||||
static constexpr size_t kCount = 100000;
|
||||
randen_u64 engine;
|
||||
randen_u64::result_type sum = 0;
|
||||
auto start = absl::GetCurrentTimeNanos();
|
||||
for (int i = 0; i < kCount; i++) {
|
||||
sum += engine();
|
||||
}
|
||||
auto duration = absl::GetCurrentTimeNanos() - start;
|
||||
|
||||
LOG(INFO) << static_cast<double>(duration) / static_cast<double>(kCount)
|
||||
<< "ns";
|
||||
|
||||
EXPECT_GT(sum, 0);
|
||||
EXPECT_GE(duration, kCount); // Should be slower than 1ns per call.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,526 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
|
||||
// symbols from arbitrary system and other headers, since it may be built
|
||||
// with different flags from other targets, using different levels of
|
||||
// optimization, potentially introducing ODR violations.
|
||||
|
||||
#include "absl/random/internal/randen_hwaes.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
|
||||
// ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain
|
||||
// a hardware accelerated implementation of randen, or whether it
|
||||
// will contain stubs that exit the process.
|
||||
#if ABSL_HAVE_ACCELERATED_AES
|
||||
// The following platforms have implemented RandenHwAes.
|
||||
#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \
|
||||
defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \
|
||||
defined(ABSL_ARCH_AARCH64)
|
||||
#define ABSL_RANDEN_HWAES_IMPL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(ABSL_RANDEN_HWAES_IMPL)
|
||||
// No accelerated implementation is supported.
|
||||
// The RandenHwAes functions are stubs that print an error and exit.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// No accelerated implementation.
|
||||
bool HasRandenHwAesImplementation() { return false; }
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
const void* RandenHwAes::GetKeys() {
|
||||
// Attempted to dispatch to an unsupported dispatch target.
|
||||
const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
|
||||
fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
|
||||
exit(1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void RandenHwAes::Absorb(const void*, void*) {
|
||||
// Attempted to dispatch to an unsupported dispatch target.
|
||||
const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
|
||||
fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void RandenHwAes::Generate(const void*, void*) {
|
||||
// Attempted to dispatch to an unsupported dispatch target.
|
||||
const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
|
||||
fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#else // defined(ABSL_RANDEN_HWAES_IMPL)
|
||||
//
|
||||
// Accelerated implementations are supported.
|
||||
// We need the per-architecture includes and defines.
|
||||
//
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::RandenTraits;
|
||||
|
||||
} // namespace
|
||||
|
||||
// TARGET_CRYPTO defines a crypto attribute for each architecture.
|
||||
//
|
||||
// NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO.
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
|
||||
#define ABSL_TARGET_CRYPTO __attribute__((target("aes")))
|
||||
#elif defined(ABSL_ARCH_PPC)
|
||||
#define ABSL_TARGET_CRYPTO __attribute__((target("crypto")))
|
||||
#else
|
||||
#define ABSL_TARGET_CRYPTO
|
||||
#endif
|
||||
#else
|
||||
#define ABSL_TARGET_CRYPTO
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
// NOTE: Keep in mind that PPC can operate in little-endian or big-endian mode,
|
||||
// however the PPC altivec vector registers (and thus the AES instructions)
|
||||
// always operate in big-endian mode.
|
||||
|
||||
#include <altivec.h>
|
||||
// <altivec.h> #defines vector __vector; in C++, this is bad form.
|
||||
#undef vector
|
||||
#undef bool
|
||||
|
||||
// Rely on the PowerPC AltiVec vector operations for accelerated AES
|
||||
// instructions. GCC support of the PPC vector types is described in:
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/PowerPC-AltiVec_002fVSX-Built-in-Functions.html
|
||||
//
|
||||
// Already provides operator^=.
|
||||
using Vector128 = __vector unsigned long long; // NOLINT(runtime/int)
|
||||
|
||||
namespace {
|
||||
inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) {
|
||||
// Reverses the bytes of the vector.
|
||||
const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8,
|
||||
7, 6, 5, 4, 3, 2, 1, 0};
|
||||
return vec_perm(v, v, perm);
|
||||
}
|
||||
|
||||
// WARNING: these load/store in native byte order. It is OK to load and then
|
||||
// store an unchanged vector, but interpreting the bits as a number or input
|
||||
// to AES will have undefined results.
|
||||
inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
|
||||
return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from));
|
||||
}
|
||||
|
||||
inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
|
||||
vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to));
|
||||
}
|
||||
|
||||
// One round of AES. "round_key" is a public constant for breaking the
|
||||
// symmetry of AES (ensures previously equal columns differ afterwards).
|
||||
inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
|
||||
const Vector128& round_key) {
|
||||
return Vector128(__builtin_crypto_vcipher(state, round_key));
|
||||
}
|
||||
|
||||
// Enables native loads in the round loop by pre-swapping.
|
||||
inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) {
|
||||
for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
|
||||
Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
|
||||
|
||||
// Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>.
|
||||
// uint8x16_t is the user alias for underlying __simd128_uint8_t type.
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf
|
||||
//
|
||||
// <arm_neon> defines the following
|
||||
//
|
||||
// typedef __attribute__((neon_vector_type(16))) uint8_t uint8x16_t;
|
||||
// typedef __attribute__((neon_vector_type(16))) int8_t int8x16_t;
|
||||
// typedef __attribute__((neon_polyvector_type(16))) int8_t poly8x16_t;
|
||||
//
|
||||
// vld1q_v
|
||||
// vst1q_v
|
||||
// vaeseq_v
|
||||
// vaesmcq_v
|
||||
#include <arm_neon.h>
|
||||
|
||||
// Already provides operator^=.
|
||||
using Vector128 = uint8x16_t;
|
||||
|
||||
namespace {
|
||||
|
||||
inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
|
||||
return vld1q_u8(reinterpret_cast<const uint8_t*>(from));
|
||||
}
|
||||
|
||||
inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
|
||||
vst1q_u8(reinterpret_cast<uint8_t*>(to), v);
|
||||
}
|
||||
|
||||
// One round of AES. "round_key" is a public constant for breaking the
|
||||
// symmetry of AES (ensures previously equal columns differ afterwards).
|
||||
inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
|
||||
const Vector128& round_key) {
|
||||
// It is important to always use the full round function - omitting the
|
||||
// final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
|
||||
// and does not help because we never decrypt.
|
||||
//
|
||||
// Note that ARM divides AES instructions differently than x86 / PPC,
|
||||
// And we need to skip the first AddRoundKey step and add an extra
|
||||
// AddRoundKey step to the end. Lucky for us this is just XOR.
|
||||
return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key;
|
||||
}
|
||||
|
||||
inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
|
||||
|
||||
} // namespace
|
||||
|
||||
#elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
|
||||
// On x86 we rely on the aesni instructions
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// Vector128 class is only wrapper for __m128i, benchmark indicates that it's
|
||||
// faster than using __m128i directly.
|
||||
class Vector128 {
|
||||
public:
|
||||
// Convert from/to intrinsics.
|
||||
inline explicit Vector128(const __m128i& v) : data_(v) {}
|
||||
|
||||
inline __m128i data() const { return data_; }
|
||||
|
||||
inline Vector128& operator^=(const Vector128& other) {
|
||||
data_ = _mm_xor_si128(data_, other.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
__m128i data_;
|
||||
};
|
||||
|
||||
inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
|
||||
return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from)));
|
||||
}
|
||||
|
||||
inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(to), v.data());
|
||||
}
|
||||
|
||||
// One round of AES. "round_key" is a public constant for breaking the
|
||||
// symmetry of AES (ensures previously equal columns differ afterwards).
|
||||
inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
|
||||
const Vector128& round_key) {
|
||||
// It is important to always use the full round function - omitting the
|
||||
// final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
|
||||
// and does not help because we never decrypt.
|
||||
return Vector128(_mm_aesenc_si128(state.data(), round_key.data()));
|
||||
}
|
||||
|
||||
inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
#endif
|
||||
|
||||
// At this point, all of the platform-specific features have been defined /
|
||||
// implemented.
|
||||
//
|
||||
// REQUIRES: using Vector128 = ...
|
||||
// REQUIRES: Vector128 Vector128Load(void*) {...}
|
||||
// REQUIRES: void Vector128Store(Vector128, void*) {...}
|
||||
// REQUIRES: Vector128 AesRound(Vector128, Vector128) {...}
|
||||
// REQUIRES: void SwapEndian(uint64_t*) {...}
|
||||
//
|
||||
// PROVIDES: absl::random_internal::RandenHwAes::Absorb
|
||||
// PROVIDES: absl::random_internal::RandenHwAes::Generate
|
||||
namespace {
|
||||
|
||||
// Block shuffles applies a shuffle to the entire state between AES rounds.
|
||||
// Improved odd-even shuffle from "New criterion for diffusion property".
|
||||
inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) {
|
||||
static_assert(RandenTraits::kFeistelBlocks == 16,
|
||||
"Expecting 16 FeistelBlocks.");
|
||||
|
||||
constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = {
|
||||
7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12};
|
||||
|
||||
const Vector128 v0 = Vector128Load(state + shuffle[0]);
|
||||
const Vector128 v1 = Vector128Load(state + shuffle[1]);
|
||||
const Vector128 v2 = Vector128Load(state + shuffle[2]);
|
||||
const Vector128 v3 = Vector128Load(state + shuffle[3]);
|
||||
const Vector128 v4 = Vector128Load(state + shuffle[4]);
|
||||
const Vector128 v5 = Vector128Load(state + shuffle[5]);
|
||||
const Vector128 v6 = Vector128Load(state + shuffle[6]);
|
||||
const Vector128 v7 = Vector128Load(state + shuffle[7]);
|
||||
const Vector128 w0 = Vector128Load(state + shuffle[8]);
|
||||
const Vector128 w1 = Vector128Load(state + shuffle[9]);
|
||||
const Vector128 w2 = Vector128Load(state + shuffle[10]);
|
||||
const Vector128 w3 = Vector128Load(state + shuffle[11]);
|
||||
const Vector128 w4 = Vector128Load(state + shuffle[12]);
|
||||
const Vector128 w5 = Vector128Load(state + shuffle[13]);
|
||||
const Vector128 w6 = Vector128Load(state + shuffle[14]);
|
||||
const Vector128 w7 = Vector128Load(state + shuffle[15]);
|
||||
|
||||
Vector128Store(v0, state + 0);
|
||||
Vector128Store(v1, state + 1);
|
||||
Vector128Store(v2, state + 2);
|
||||
Vector128Store(v3, state + 3);
|
||||
Vector128Store(v4, state + 4);
|
||||
Vector128Store(v5, state + 5);
|
||||
Vector128Store(v6, state + 6);
|
||||
Vector128Store(v7, state + 7);
|
||||
Vector128Store(w0, state + 8);
|
||||
Vector128Store(w1, state + 9);
|
||||
Vector128Store(w2, state + 10);
|
||||
Vector128Store(w3, state + 11);
|
||||
Vector128Store(w4, state + 12);
|
||||
Vector128Store(w5, state + 13);
|
||||
Vector128Store(w6, state + 14);
|
||||
Vector128Store(w7, state + 15);
|
||||
}
|
||||
|
||||
// Feistel round function using two AES subrounds. Very similar to F()
|
||||
// from Simpira v2, but with independent subround keys. Uses 17 AES rounds
|
||||
// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
|
||||
// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
|
||||
// XORs are 'free' (included in the second AES instruction).
|
||||
inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound(
|
||||
absl::uint128* state,
|
||||
const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
|
||||
static_assert(RandenTraits::kFeistelBlocks == 16,
|
||||
"Expecting 16 FeistelBlocks.");
|
||||
|
||||
// MSVC does a horrible job at unrolling loops.
|
||||
// So we unroll the loop by hand to improve the performance.
|
||||
const Vector128 s0 = Vector128Load(state + 0);
|
||||
const Vector128 s1 = Vector128Load(state + 1);
|
||||
const Vector128 s2 = Vector128Load(state + 2);
|
||||
const Vector128 s3 = Vector128Load(state + 3);
|
||||
const Vector128 s4 = Vector128Load(state + 4);
|
||||
const Vector128 s5 = Vector128Load(state + 5);
|
||||
const Vector128 s6 = Vector128Load(state + 6);
|
||||
const Vector128 s7 = Vector128Load(state + 7);
|
||||
const Vector128 s8 = Vector128Load(state + 8);
|
||||
const Vector128 s9 = Vector128Load(state + 9);
|
||||
const Vector128 s10 = Vector128Load(state + 10);
|
||||
const Vector128 s11 = Vector128Load(state + 11);
|
||||
const Vector128 s12 = Vector128Load(state + 12);
|
||||
const Vector128 s13 = Vector128Load(state + 13);
|
||||
const Vector128 s14 = Vector128Load(state + 14);
|
||||
const Vector128 s15 = Vector128Load(state + 15);
|
||||
|
||||
// Encode even blocks with keys.
|
||||
const Vector128 e0 = AesRound(s0, Vector128Load(keys + 0));
|
||||
const Vector128 e2 = AesRound(s2, Vector128Load(keys + 1));
|
||||
const Vector128 e4 = AesRound(s4, Vector128Load(keys + 2));
|
||||
const Vector128 e6 = AesRound(s6, Vector128Load(keys + 3));
|
||||
const Vector128 e8 = AesRound(s8, Vector128Load(keys + 4));
|
||||
const Vector128 e10 = AesRound(s10, Vector128Load(keys + 5));
|
||||
const Vector128 e12 = AesRound(s12, Vector128Load(keys + 6));
|
||||
const Vector128 e14 = AesRound(s14, Vector128Load(keys + 7));
|
||||
|
||||
// Encode odd blocks with even output from above.
|
||||
const Vector128 o1 = AesRound(e0, s1);
|
||||
const Vector128 o3 = AesRound(e2, s3);
|
||||
const Vector128 o5 = AesRound(e4, s5);
|
||||
const Vector128 o7 = AesRound(e6, s7);
|
||||
const Vector128 o9 = AesRound(e8, s9);
|
||||
const Vector128 o11 = AesRound(e10, s11);
|
||||
const Vector128 o13 = AesRound(e12, s13);
|
||||
const Vector128 o15 = AesRound(e14, s15);
|
||||
|
||||
// Store odd blocks. (These will be shuffled later).
|
||||
Vector128Store(o1, state + 1);
|
||||
Vector128Store(o3, state + 3);
|
||||
Vector128Store(o5, state + 5);
|
||||
Vector128Store(o7, state + 7);
|
||||
Vector128Store(o9, state + 9);
|
||||
Vector128Store(o11, state + 11);
|
||||
Vector128Store(o13, state + 13);
|
||||
Vector128Store(o15, state + 15);
|
||||
|
||||
return keys + 8;
|
||||
}
|
||||
|
||||
// Cryptographic permutation based via type-2 Generalized Feistel Network.
|
||||
// Indistinguishable from ideal by chosen-ciphertext adversaries using less than
|
||||
// 2^64 queries if the round function is a PRF. This is similar to the b=8 case
|
||||
// of Simpira v2, but more efficient than its generic construction for b=16.
|
||||
inline ABSL_TARGET_CRYPTO void Permute(
|
||||
absl::uint128* state,
|
||||
const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
|
||||
// (Successfully unrolled; the first iteration jumps into the second half)
|
||||
#ifdef __clang__
|
||||
#pragma clang loop unroll_count(2)
|
||||
#endif
|
||||
for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
|
||||
keys = FeistelRound(state, keys);
|
||||
BlockShuffle(state);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
bool HasRandenHwAesImplementation() { return true; }
|
||||
|
||||
const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() {
|
||||
// Round keys for one AES per Feistel round and branch.
|
||||
// The canonical implementation uses first digits of Pi.
|
||||
#if defined(ABSL_ARCH_PPC)
|
||||
return kRandenRoundKeysBE;
|
||||
#else
|
||||
return kRandenRoundKeys;
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void,
|
||||
void* state_void) {
|
||||
static_assert(RandenTraits::kCapacityBytes / sizeof(Vector128) == 1,
|
||||
"Unexpected Randen kCapacityBlocks");
|
||||
static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16,
|
||||
"Unexpected Randen kStateBlocks");
|
||||
|
||||
auto* state = reinterpret_cast<absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
|
||||
state_void);
|
||||
const auto* seed =
|
||||
reinterpret_cast<const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
|
||||
seed_void);
|
||||
|
||||
Vector128 b1 = Vector128Load(state + 1);
|
||||
b1 ^= Vector128Load(seed + 0);
|
||||
Vector128Store(b1, state + 1);
|
||||
|
||||
Vector128 b2 = Vector128Load(state + 2);
|
||||
b2 ^= Vector128Load(seed + 1);
|
||||
Vector128Store(b2, state + 2);
|
||||
|
||||
Vector128 b3 = Vector128Load(state + 3);
|
||||
b3 ^= Vector128Load(seed + 2);
|
||||
Vector128Store(b3, state + 3);
|
||||
|
||||
Vector128 b4 = Vector128Load(state + 4);
|
||||
b4 ^= Vector128Load(seed + 3);
|
||||
Vector128Store(b4, state + 4);
|
||||
|
||||
Vector128 b5 = Vector128Load(state + 5);
|
||||
b5 ^= Vector128Load(seed + 4);
|
||||
Vector128Store(b5, state + 5);
|
||||
|
||||
Vector128 b6 = Vector128Load(state + 6);
|
||||
b6 ^= Vector128Load(seed + 5);
|
||||
Vector128Store(b6, state + 6);
|
||||
|
||||
Vector128 b7 = Vector128Load(state + 7);
|
||||
b7 ^= Vector128Load(seed + 6);
|
||||
Vector128Store(b7, state + 7);
|
||||
|
||||
Vector128 b8 = Vector128Load(state + 8);
|
||||
b8 ^= Vector128Load(seed + 7);
|
||||
Vector128Store(b8, state + 8);
|
||||
|
||||
Vector128 b9 = Vector128Load(state + 9);
|
||||
b9 ^= Vector128Load(seed + 8);
|
||||
Vector128Store(b9, state + 9);
|
||||
|
||||
Vector128 b10 = Vector128Load(state + 10);
|
||||
b10 ^= Vector128Load(seed + 9);
|
||||
Vector128Store(b10, state + 10);
|
||||
|
||||
Vector128 b11 = Vector128Load(state + 11);
|
||||
b11 ^= Vector128Load(seed + 10);
|
||||
Vector128Store(b11, state + 11);
|
||||
|
||||
Vector128 b12 = Vector128Load(state + 12);
|
||||
b12 ^= Vector128Load(seed + 11);
|
||||
Vector128Store(b12, state + 12);
|
||||
|
||||
Vector128 b13 = Vector128Load(state + 13);
|
||||
b13 ^= Vector128Load(seed + 12);
|
||||
Vector128Store(b13, state + 13);
|
||||
|
||||
Vector128 b14 = Vector128Load(state + 14);
|
||||
b14 ^= Vector128Load(seed + 13);
|
||||
Vector128Store(b14, state + 14);
|
||||
|
||||
Vector128 b15 = Vector128Load(state + 15);
|
||||
b15 ^= Vector128Load(seed + 14);
|
||||
Vector128Store(b15, state + 15);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void,
|
||||
void* state_void) {
|
||||
static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128),
|
||||
"Capacity mismatch");
|
||||
|
||||
auto* state = reinterpret_cast<absl::uint128*>(state_void);
|
||||
const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void);
|
||||
|
||||
const Vector128 prev_inner = Vector128Load(state);
|
||||
|
||||
SwapEndian(state);
|
||||
|
||||
Permute(state, keys);
|
||||
|
||||
SwapEndian(state);
|
||||
|
||||
// Ensure backtracking resistance.
|
||||
Vector128 inner = Vector128Load(state);
|
||||
inner ^= prev_inner;
|
||||
Vector128Store(inner, state);
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // (ABSL_RANDEN_HWAES_IMPL)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
|
||||
// symbols from arbitrary system and other headers, since it may be built
|
||||
// with different flags from other targets, using different levels of
|
||||
// optimization, potentially introducing ODR violations.
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
|
||||
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
|
||||
//
|
||||
// RandenHwAes implements the basic state manipulation methods.
|
||||
class RandenHwAes {
|
||||
public:
|
||||
static void Generate(const void* keys, void* state_void);
|
||||
static void Absorb(const void* seed_void, void* state_void);
|
||||
static const void* GetKeys();
|
||||
};
|
||||
|
||||
// HasRandenHwAesImplementation returns true when there is an accelerated
|
||||
// implementation, and false otherwise. If there is no implementation,
|
||||
// then attempting to use it will abort the program.
|
||||
bool HasRandenHwAesImplementation();
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen_hwaes.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen_detect.h"
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::RandenHwAes;
|
||||
using absl::random_internal::RandenTraits;
|
||||
|
||||
TEST(RandenHwAesTest, Default) {
|
||||
EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes());
|
||||
|
||||
constexpr uint8_t kGolden[] = {
|
||||
0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
|
||||
0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
|
||||
0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
|
||||
0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
|
||||
0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
|
||||
0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
|
||||
0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
|
||||
0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
|
||||
0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
|
||||
0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
|
||||
0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
|
||||
0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
|
||||
0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
|
||||
0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
|
||||
0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
|
||||
0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
|
||||
0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
|
||||
0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
|
||||
0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
|
||||
0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
|
||||
0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
|
||||
0x82, 0xf0, 0x1e, 0x81,
|
||||
};
|
||||
|
||||
alignas(16) uint8_t state[RandenTraits::kStateBytes];
|
||||
std::memset(state, 0, sizeof(state));
|
||||
|
||||
RandenHwAes::Generate(RandenHwAes::GetKeys(), state);
|
||||
EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
LOG(INFO) << "ABSL_HAVE_ACCELERATED_AES=" << ABSL_HAVE_ACCELERATED_AES;
|
||||
LOG(INFO) << "ABSL_RANDOM_INTERNAL_AES_DISPATCH="
|
||||
<< ABSL_RANDOM_INTERNAL_AES_DISPATCH;
|
||||
|
||||
#if defined(ABSL_ARCH_X86_64)
|
||||
LOG(INFO) << "ABSL_ARCH_X86_64";
|
||||
#elif defined(ABSL_ARCH_X86_32)
|
||||
LOG(INFO) << "ABSL_ARCH_X86_32";
|
||||
#elif defined(ABSL_ARCH_AARCH64)
|
||||
LOG(INFO) << "ABSL_ARCH_AARCH64";
|
||||
#elif defined(ABSL_ARCH_ARM)
|
||||
LOG(INFO) << "ABSL_ARCH_ARM";
|
||||
#elif defined(ABSL_ARCH_PPC)
|
||||
LOG(INFO) << "ABSL_ARCH_PPC";
|
||||
#else
|
||||
LOG(INFO) << "ARCH Unknown";
|
||||
#endif
|
||||
|
||||
int x = absl::random_internal::HasRandenHwAesImplementation();
|
||||
LOG(INFO) << "HasRandenHwAesImplementation = " << x;
|
||||
|
||||
int y = absl::random_internal::CPUSupportsRandenHwAes();
|
||||
LOG(INFO) << "CPUSupportsRandenHwAes = " << y;
|
||||
|
||||
if (!x || !y) {
|
||||
LOG(INFO) << "Skipping Randen HWAES tests.";
|
||||
return 0;
|
||||
}
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
|
@ -0,0 +1,462 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
|
||||
// This file contains only the round keys for randen.
|
||||
//
|
||||
// "Nothing up my sleeve" numbers from the first hex digits of Pi, obtained
|
||||
// from http://hexpi.sourceforge.net/. The array was generated by following
|
||||
// Python script:
|
||||
|
||||
/*
|
||||
python >tmp.cc << EOF
|
||||
"""Generates Randen round keys array from pi-hex.62500.txt file."""
|
||||
import binascii
|
||||
|
||||
KEYS = 17 * 8
|
||||
|
||||
def chunks(l, n):
|
||||
"""Yield successive n-sized chunks from l."""
|
||||
for i in range(0, len(l), n):
|
||||
yield l[i:i + n]
|
||||
|
||||
def pairwise(t):
|
||||
"""Transforms sequence into sequence of pairs."""
|
||||
it = iter(t)
|
||||
return zip(it,it)
|
||||
|
||||
def digits_from_pi():
|
||||
"""Reads digits from hexpi.sourceforge.net file."""
|
||||
with open("pi-hex.62500.txt") as file:
|
||||
return file.read()
|
||||
|
||||
def digits_from_urandom():
|
||||
"""Reads digits from /dev/urandom."""
|
||||
with open("/dev/urandom") as file:
|
||||
return binascii.hexlify(file.read(KEYS * 16))
|
||||
|
||||
def print_row(b)
|
||||
print(" 0x{0}, 0x{1}, 0x{2}, 0x{3}, 0x{4}, 0x{5}, 0x{6}, 0x{7}, 0x{8}, 0x{9},
|
||||
0x{10}, 0x{11}, 0x{12}, 0x{13}, 0x{14}, 0x{15},".format(*b))
|
||||
|
||||
|
||||
digits = digits_from_pi()
|
||||
#digits = digits_from_urandom()
|
||||
|
||||
print("namespace {")
|
||||
print("static constexpr size_t kKeyBytes = {0};\n".format(KEYS * 16))
|
||||
print("}")
|
||||
|
||||
print("alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = {")
|
||||
|
||||
for i, u16 in zip(range(KEYS), chunks(digits, 32)):
|
||||
b = list(chunks(u16, 2))
|
||||
print_row(b)
|
||||
|
||||
print("};")
|
||||
|
||||
print("alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = {")
|
||||
|
||||
for i, u16 in zip(range(KEYS), chunks(digits, 32)):
|
||||
b = list(chunks(u16, 2))
|
||||
b.reverse()
|
||||
print_row(b)
|
||||
|
||||
print("};")
|
||||
|
||||
EOF
|
||||
|
||||
*/
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
static constexpr size_t kKeyBytes = 2176;
|
||||
}
|
||||
|
||||
alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, 0x13, 0x19, 0x8A, 0x2E,
|
||||
0x03, 0x70, 0x73, 0x44, 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89, 0x45, 0x28, 0x21, 0xE6,
|
||||
0x38, 0xD0, 0x13, 0x77, 0xBE, 0x54, 0x66, 0xCF, 0x34, 0xE9, 0x0C, 0x6C,
|
||||
0xC0, 0xAC, 0x29, 0xB7, 0xC9, 0x7C, 0x50, 0xDD, 0x3F, 0x84, 0xD5, 0xB5,
|
||||
0xB5, 0x47, 0x09, 0x17, 0x92, 0x16, 0xD5, 0xD9, 0x89, 0x79, 0xFB, 0x1B,
|
||||
0xD1, 0x31, 0x0B, 0xA6, 0x98, 0xDF, 0xB5, 0xAC, 0x2F, 0xFD, 0x72, 0xDB,
|
||||
0xD0, 0x1A, 0xDF, 0xB7, 0xB8, 0xE1, 0xAF, 0xED, 0x6A, 0x26, 0x7E, 0x96,
|
||||
0xBA, 0x7C, 0x90, 0x45, 0xF1, 0x2C, 0x7F, 0x99, 0x24, 0xA1, 0x99, 0x47,
|
||||
0xB3, 0x91, 0x6C, 0xF7, 0x08, 0x01, 0xF2, 0xE2, 0x85, 0x8E, 0xFC, 0x16,
|
||||
0x63, 0x69, 0x20, 0xD8, 0x71, 0x57, 0x4E, 0x69, 0xA4, 0x58, 0xFE, 0xA3,
|
||||
0xF4, 0x93, 0x3D, 0x7E, 0x0D, 0x95, 0x74, 0x8F, 0x72, 0x8E, 0xB6, 0x58,
|
||||
0x71, 0x8B, 0xCD, 0x58, 0x82, 0x15, 0x4A, 0xEE, 0x7B, 0x54, 0xA4, 0x1D,
|
||||
0xC2, 0x5A, 0x59, 0xB5, 0x9C, 0x30, 0xD5, 0x39, 0x2A, 0xF2, 0x60, 0x13,
|
||||
0xC5, 0xD1, 0xB0, 0x23, 0x28, 0x60, 0x85, 0xF0, 0xCA, 0x41, 0x79, 0x18,
|
||||
0xB8, 0xDB, 0x38, 0xEF, 0x8E, 0x79, 0xDC, 0xB0, 0x60, 0x3A, 0x18, 0x0E,
|
||||
0x6C, 0x9E, 0x0E, 0x8B, 0xB0, 0x1E, 0x8A, 0x3E, 0xD7, 0x15, 0x77, 0xC1,
|
||||
0xBD, 0x31, 0x4B, 0x27, 0x78, 0xAF, 0x2F, 0xDA, 0x55, 0x60, 0x5C, 0x60,
|
||||
0xE6, 0x55, 0x25, 0xF3, 0xAA, 0x55, 0xAB, 0x94, 0x57, 0x48, 0x98, 0x62,
|
||||
0x63, 0xE8, 0x14, 0x40, 0x55, 0xCA, 0x39, 0x6A, 0x2A, 0xAB, 0x10, 0xB6,
|
||||
0xB4, 0xCC, 0x5C, 0x34, 0x11, 0x41, 0xE8, 0xCE, 0xA1, 0x54, 0x86, 0xAF,
|
||||
0x7C, 0x72, 0xE9, 0x93, 0xB3, 0xEE, 0x14, 0x11, 0x63, 0x6F, 0xBC, 0x2A,
|
||||
0x2B, 0xA9, 0xC5, 0x5D, 0x74, 0x18, 0x31, 0xF6, 0xCE, 0x5C, 0x3E, 0x16,
|
||||
0x9B, 0x87, 0x93, 0x1E, 0xAF, 0xD6, 0xBA, 0x33, 0x6C, 0x24, 0xCF, 0x5C,
|
||||
0x7A, 0x32, 0x53, 0x81, 0x28, 0x95, 0x86, 0x77, 0x3B, 0x8F, 0x48, 0x98,
|
||||
0x6B, 0x4B, 0xB9, 0xAF, 0xC4, 0xBF, 0xE8, 0x1B, 0x66, 0x28, 0x21, 0x93,
|
||||
0x61, 0xD8, 0x09, 0xCC, 0xFB, 0x21, 0xA9, 0x91, 0x48, 0x7C, 0xAC, 0x60,
|
||||
0x5D, 0xEC, 0x80, 0x32, 0xEF, 0x84, 0x5D, 0x5D, 0xE9, 0x85, 0x75, 0xB1,
|
||||
0xDC, 0x26, 0x23, 0x02, 0xEB, 0x65, 0x1B, 0x88, 0x23, 0x89, 0x3E, 0x81,
|
||||
0xD3, 0x96, 0xAC, 0xC5, 0x0F, 0x6D, 0x6F, 0xF3, 0x83, 0xF4, 0x42, 0x39,
|
||||
0x2E, 0x0B, 0x44, 0x82, 0xA4, 0x84, 0x20, 0x04, 0x69, 0xC8, 0xF0, 0x4A,
|
||||
0x9E, 0x1F, 0x9B, 0x5E, 0x21, 0xC6, 0x68, 0x42, 0xF6, 0xE9, 0x6C, 0x9A,
|
||||
0x67, 0x0C, 0x9C, 0x61, 0xAB, 0xD3, 0x88, 0xF0, 0x6A, 0x51, 0xA0, 0xD2,
|
||||
0xD8, 0x54, 0x2F, 0x68, 0x96, 0x0F, 0xA7, 0x28, 0xAB, 0x51, 0x33, 0xA3,
|
||||
0x6E, 0xEF, 0x0B, 0x6C, 0x13, 0x7A, 0x3B, 0xE4, 0xBA, 0x3B, 0xF0, 0x50,
|
||||
0x7E, 0xFB, 0x2A, 0x98, 0xA1, 0xF1, 0x65, 0x1D, 0x39, 0xAF, 0x01, 0x76,
|
||||
0x66, 0xCA, 0x59, 0x3E, 0x82, 0x43, 0x0E, 0x88, 0x8C, 0xEE, 0x86, 0x19,
|
||||
0x45, 0x6F, 0x9F, 0xB4, 0x7D, 0x84, 0xA5, 0xC3, 0x3B, 0x8B, 0x5E, 0xBE,
|
||||
0xE0, 0x6F, 0x75, 0xD8, 0x85, 0xC1, 0x20, 0x73, 0x40, 0x1A, 0x44, 0x9F,
|
||||
0x56, 0xC1, 0x6A, 0xA6, 0x4E, 0xD3, 0xAA, 0x62, 0x36, 0x3F, 0x77, 0x06,
|
||||
0x1B, 0xFE, 0xDF, 0x72, 0x42, 0x9B, 0x02, 0x3D, 0x37, 0xD0, 0xD7, 0x24,
|
||||
0xD0, 0x0A, 0x12, 0x48, 0xDB, 0x0F, 0xEA, 0xD3, 0x49, 0xF1, 0xC0, 0x9B,
|
||||
0x07, 0x53, 0x72, 0xC9, 0x80, 0x99, 0x1B, 0x7B, 0x25, 0xD4, 0x79, 0xD8,
|
||||
0xF6, 0xE8, 0xDE, 0xF7, 0xE3, 0xFE, 0x50, 0x1A, 0xB6, 0x79, 0x4C, 0x3B,
|
||||
0x97, 0x6C, 0xE0, 0xBD, 0x04, 0xC0, 0x06, 0xBA, 0xC1, 0xA9, 0x4F, 0xB6,
|
||||
0x40, 0x9F, 0x60, 0xC4, 0x5E, 0x5C, 0x9E, 0xC2, 0x19, 0x6A, 0x24, 0x63,
|
||||
0x68, 0xFB, 0x6F, 0xAF, 0x3E, 0x6C, 0x53, 0xB5, 0x13, 0x39, 0xB2, 0xEB,
|
||||
0x3B, 0x52, 0xEC, 0x6F, 0x6D, 0xFC, 0x51, 0x1F, 0x9B, 0x30, 0x95, 0x2C,
|
||||
0xCC, 0x81, 0x45, 0x44, 0xAF, 0x5E, 0xBD, 0x09, 0xBE, 0xE3, 0xD0, 0x04,
|
||||
0xDE, 0x33, 0x4A, 0xFD, 0x66, 0x0F, 0x28, 0x07, 0x19, 0x2E, 0x4B, 0xB3,
|
||||
0xC0, 0xCB, 0xA8, 0x57, 0x45, 0xC8, 0x74, 0x0F, 0xD2, 0x0B, 0x5F, 0x39,
|
||||
0xB9, 0xD3, 0xFB, 0xDB, 0x55, 0x79, 0xC0, 0xBD, 0x1A, 0x60, 0x32, 0x0A,
|
||||
0xD6, 0xA1, 0x00, 0xC6, 0x40, 0x2C, 0x72, 0x79, 0x67, 0x9F, 0x25, 0xFE,
|
||||
0xFB, 0x1F, 0xA3, 0xCC, 0x8E, 0xA5, 0xE9, 0xF8, 0xDB, 0x32, 0x22, 0xF8,
|
||||
0x3C, 0x75, 0x16, 0xDF, 0xFD, 0x61, 0x6B, 0x15, 0x2F, 0x50, 0x1E, 0xC8,
|
||||
0xAD, 0x05, 0x52, 0xAB, 0x32, 0x3D, 0xB5, 0xFA, 0xFD, 0x23, 0x87, 0x60,
|
||||
0x53, 0x31, 0x7B, 0x48, 0x3E, 0x00, 0xDF, 0x82, 0x9E, 0x5C, 0x57, 0xBB,
|
||||
0xCA, 0x6F, 0x8C, 0xA0, 0x1A, 0x87, 0x56, 0x2E, 0xDF, 0x17, 0x69, 0xDB,
|
||||
0xD5, 0x42, 0xA8, 0xF6, 0x28, 0x7E, 0xFF, 0xC3, 0xAC, 0x67, 0x32, 0xC6,
|
||||
0x8C, 0x4F, 0x55, 0x73, 0x69, 0x5B, 0x27, 0xB0, 0xBB, 0xCA, 0x58, 0xC8,
|
||||
0xE1, 0xFF, 0xA3, 0x5D, 0xB8, 0xF0, 0x11, 0xA0, 0x10, 0xFA, 0x3D, 0x98,
|
||||
0xFD, 0x21, 0x83, 0xB8, 0x4A, 0xFC, 0xB5, 0x6C, 0x2D, 0xD1, 0xD3, 0x5B,
|
||||
0x9A, 0x53, 0xE4, 0x79, 0xB6, 0xF8, 0x45, 0x65, 0xD2, 0x8E, 0x49, 0xBC,
|
||||
0x4B, 0xFB, 0x97, 0x90, 0xE1, 0xDD, 0xF2, 0xDA, 0xA4, 0xCB, 0x7E, 0x33,
|
||||
0x62, 0xFB, 0x13, 0x41, 0xCE, 0xE4, 0xC6, 0xE8, 0xEF, 0x20, 0xCA, 0xDA,
|
||||
0x36, 0x77, 0x4C, 0x01, 0xD0, 0x7E, 0x9E, 0xFE, 0x2B, 0xF1, 0x1F, 0xB4,
|
||||
0x95, 0xDB, 0xDA, 0x4D, 0xAE, 0x90, 0x91, 0x98, 0xEA, 0xAD, 0x8E, 0x71,
|
||||
0x6B, 0x93, 0xD5, 0xA0, 0xD0, 0x8E, 0xD1, 0xD0, 0xAF, 0xC7, 0x25, 0xE0,
|
||||
0x8E, 0x3C, 0x5B, 0x2F, 0x8E, 0x75, 0x94, 0xB7, 0x8F, 0xF6, 0xE2, 0xFB,
|
||||
0xF2, 0x12, 0x2B, 0x64, 0x88, 0x88, 0xB8, 0x12, 0x90, 0x0D, 0xF0, 0x1C,
|
||||
0x4F, 0xAD, 0x5E, 0xA0, 0x68, 0x8F, 0xC3, 0x1C, 0xD1, 0xCF, 0xF1, 0x91,
|
||||
0xB3, 0xA8, 0xC1, 0xAD, 0x2F, 0x2F, 0x22, 0x18, 0xBE, 0x0E, 0x17, 0x77,
|
||||
0xEA, 0x75, 0x2D, 0xFE, 0x8B, 0x02, 0x1F, 0xA1, 0xE5, 0xA0, 0xCC, 0x0F,
|
||||
0xB5, 0x6F, 0x74, 0xE8, 0x18, 0xAC, 0xF3, 0xD6, 0xCE, 0x89, 0xE2, 0x99,
|
||||
0xB4, 0xA8, 0x4F, 0xE0, 0xFD, 0x13, 0xE0, 0xB7, 0x7C, 0xC4, 0x3B, 0x81,
|
||||
0xD2, 0xAD, 0xA8, 0xD9, 0x16, 0x5F, 0xA2, 0x66, 0x80, 0x95, 0x77, 0x05,
|
||||
0x93, 0xCC, 0x73, 0x14, 0x21, 0x1A, 0x14, 0x77, 0xE6, 0xAD, 0x20, 0x65,
|
||||
0x77, 0xB5, 0xFA, 0x86, 0xC7, 0x54, 0x42, 0xF5, 0xFB, 0x9D, 0x35, 0xCF,
|
||||
0xEB, 0xCD, 0xAF, 0x0C, 0x7B, 0x3E, 0x89, 0xA0, 0xD6, 0x41, 0x1B, 0xD3,
|
||||
0xAE, 0x1E, 0x7E, 0x49, 0x00, 0x25, 0x0E, 0x2D, 0x20, 0x71, 0xB3, 0x5E,
|
||||
0x22, 0x68, 0x00, 0xBB, 0x57, 0xB8, 0xE0, 0xAF, 0x24, 0x64, 0x36, 0x9B,
|
||||
0xF0, 0x09, 0xB9, 0x1E, 0x55, 0x63, 0x91, 0x1D, 0x59, 0xDF, 0xA6, 0xAA,
|
||||
0x78, 0xC1, 0x43, 0x89, 0xD9, 0x5A, 0x53, 0x7F, 0x20, 0x7D, 0x5B, 0xA2,
|
||||
0x02, 0xE5, 0xB9, 0xC5, 0x83, 0x26, 0x03, 0x76, 0x62, 0x95, 0xCF, 0xA9,
|
||||
0x11, 0xC8, 0x19, 0x68, 0x4E, 0x73, 0x4A, 0x41, 0xB3, 0x47, 0x2D, 0xCA,
|
||||
0x7B, 0x14, 0xA9, 0x4A, 0x1B, 0x51, 0x00, 0x52, 0x9A, 0x53, 0x29, 0x15,
|
||||
0xD6, 0x0F, 0x57, 0x3F, 0xBC, 0x9B, 0xC6, 0xE4, 0x2B, 0x60, 0xA4, 0x76,
|
||||
0x81, 0xE6, 0x74, 0x00, 0x08, 0xBA, 0x6F, 0xB5, 0x57, 0x1B, 0xE9, 0x1F,
|
||||
0xF2, 0x96, 0xEC, 0x6B, 0x2A, 0x0D, 0xD9, 0x15, 0xB6, 0x63, 0x65, 0x21,
|
||||
0xE7, 0xB9, 0xF9, 0xB6, 0xFF, 0x34, 0x05, 0x2E, 0xC5, 0x85, 0x56, 0x64,
|
||||
0x53, 0xB0, 0x2D, 0x5D, 0xA9, 0x9F, 0x8F, 0xA1, 0x08, 0xBA, 0x47, 0x99,
|
||||
0x6E, 0x85, 0x07, 0x6A, 0x4B, 0x7A, 0x70, 0xE9, 0xB5, 0xB3, 0x29, 0x44,
|
||||
0xDB, 0x75, 0x09, 0x2E, 0xC4, 0x19, 0x26, 0x23, 0xAD, 0x6E, 0xA6, 0xB0,
|
||||
0x49, 0xA7, 0xDF, 0x7D, 0x9C, 0xEE, 0x60, 0xB8, 0x8F, 0xED, 0xB2, 0x66,
|
||||
0xEC, 0xAA, 0x8C, 0x71, 0x69, 0x9A, 0x18, 0xFF, 0x56, 0x64, 0x52, 0x6C,
|
||||
0xC2, 0xB1, 0x9E, 0xE1, 0x19, 0x36, 0x02, 0xA5, 0x75, 0x09, 0x4C, 0x29,
|
||||
0xA0, 0x59, 0x13, 0x40, 0xE4, 0x18, 0x3A, 0x3E, 0x3F, 0x54, 0x98, 0x9A,
|
||||
0x5B, 0x42, 0x9D, 0x65, 0x6B, 0x8F, 0xE4, 0xD6, 0x99, 0xF7, 0x3F, 0xD6,
|
||||
0xA1, 0xD2, 0x9C, 0x07, 0xEF, 0xE8, 0x30, 0xF5, 0x4D, 0x2D, 0x38, 0xE6,
|
||||
0xF0, 0x25, 0x5D, 0xC1, 0x4C, 0xDD, 0x20, 0x86, 0x84, 0x70, 0xEB, 0x26,
|
||||
0x63, 0x82, 0xE9, 0xC6, 0x02, 0x1E, 0xCC, 0x5E, 0x09, 0x68, 0x6B, 0x3F,
|
||||
0x3E, 0xBA, 0xEF, 0xC9, 0x3C, 0x97, 0x18, 0x14, 0x6B, 0x6A, 0x70, 0xA1,
|
||||
0x68, 0x7F, 0x35, 0x84, 0x52, 0xA0, 0xE2, 0x86, 0xB7, 0x9C, 0x53, 0x05,
|
||||
0xAA, 0x50, 0x07, 0x37, 0x3E, 0x07, 0x84, 0x1C, 0x7F, 0xDE, 0xAE, 0x5C,
|
||||
0x8E, 0x7D, 0x44, 0xEC, 0x57, 0x16, 0xF2, 0xB8, 0xB0, 0x3A, 0xDA, 0x37,
|
||||
0xF0, 0x50, 0x0C, 0x0D, 0xF0, 0x1C, 0x1F, 0x04, 0x02, 0x00, 0xB3, 0xFF,
|
||||
0xAE, 0x0C, 0xF5, 0x1A, 0x3C, 0xB5, 0x74, 0xB2, 0x25, 0x83, 0x7A, 0x58,
|
||||
0xDC, 0x09, 0x21, 0xBD, 0xD1, 0x91, 0x13, 0xF9, 0x7C, 0xA9, 0x2F, 0xF6,
|
||||
0x94, 0x32, 0x47, 0x73, 0x22, 0xF5, 0x47, 0x01, 0x3A, 0xE5, 0xE5, 0x81,
|
||||
0x37, 0xC2, 0xDA, 0xDC, 0xC8, 0xB5, 0x76, 0x34, 0x9A, 0xF3, 0xDD, 0xA7,
|
||||
0xA9, 0x44, 0x61, 0x46, 0x0F, 0xD0, 0x03, 0x0E, 0xEC, 0xC8, 0xC7, 0x3E,
|
||||
0xA4, 0x75, 0x1E, 0x41, 0xE2, 0x38, 0xCD, 0x99, 0x3B, 0xEA, 0x0E, 0x2F,
|
||||
0x32, 0x80, 0xBB, 0xA1, 0x18, 0x3E, 0xB3, 0x31, 0x4E, 0x54, 0x8B, 0x38,
|
||||
0x4F, 0x6D, 0xB9, 0x08, 0x6F, 0x42, 0x0D, 0x03, 0xF6, 0x0A, 0x04, 0xBF,
|
||||
0x2C, 0xB8, 0x12, 0x90, 0x24, 0x97, 0x7C, 0x79, 0x56, 0x79, 0xB0, 0x72,
|
||||
0xBC, 0xAF, 0x89, 0xAF, 0xDE, 0x9A, 0x77, 0x1F, 0xD9, 0x93, 0x08, 0x10,
|
||||
0xB3, 0x8B, 0xAE, 0x12, 0xDC, 0xCF, 0x3F, 0x2E, 0x55, 0x12, 0x72, 0x1F,
|
||||
0x2E, 0x6B, 0x71, 0x24, 0x50, 0x1A, 0xDD, 0xE6, 0x9F, 0x84, 0xCD, 0x87,
|
||||
0x7A, 0x58, 0x47, 0x18, 0x74, 0x08, 0xDA, 0x17, 0xBC, 0x9F, 0x9A, 0xBC,
|
||||
0xE9, 0x4B, 0x7D, 0x8C, 0xEC, 0x7A, 0xEC, 0x3A, 0xDB, 0x85, 0x1D, 0xFA,
|
||||
0x63, 0x09, 0x43, 0x66, 0xC4, 0x64, 0xC3, 0xD2, 0xEF, 0x1C, 0x18, 0x47,
|
||||
0x32, 0x15, 0xD8, 0x08, 0xDD, 0x43, 0x3B, 0x37, 0x24, 0xC2, 0xBA, 0x16,
|
||||
0x12, 0xA1, 0x4D, 0x43, 0x2A, 0x65, 0xC4, 0x51, 0x50, 0x94, 0x00, 0x02,
|
||||
0x13, 0x3A, 0xE4, 0xDD, 0x71, 0xDF, 0xF8, 0x9E, 0x10, 0x31, 0x4E, 0x55,
|
||||
0x81, 0xAC, 0x77, 0xD6, 0x5F, 0x11, 0x19, 0x9B, 0x04, 0x35, 0x56, 0xF1,
|
||||
0xD7, 0xA3, 0xC7, 0x6B, 0x3C, 0x11, 0x18, 0x3B, 0x59, 0x24, 0xA5, 0x09,
|
||||
0xF2, 0x8F, 0xE6, 0xED, 0x97, 0xF1, 0xFB, 0xFA, 0x9E, 0xBA, 0xBF, 0x2C,
|
||||
0x1E, 0x15, 0x3C, 0x6E, 0x86, 0xE3, 0x45, 0x70, 0xEA, 0xE9, 0x6F, 0xB1,
|
||||
0x86, 0x0E, 0x5E, 0x0A, 0x5A, 0x3E, 0x2A, 0xB3, 0x77, 0x1F, 0xE7, 0x1C,
|
||||
0x4E, 0x3D, 0x06, 0xFA, 0x29, 0x65, 0xDC, 0xB9, 0x99, 0xE7, 0x1D, 0x0F,
|
||||
0x80, 0x3E, 0x89, 0xD6, 0x52, 0x66, 0xC8, 0x25, 0x2E, 0x4C, 0xC9, 0x78,
|
||||
0x9C, 0x10, 0xB3, 0x6A, 0xC6, 0x15, 0x0E, 0xBA, 0x94, 0xE2, 0xEA, 0x78,
|
||||
0xA6, 0xFC, 0x3C, 0x53, 0x1E, 0x0A, 0x2D, 0xF4, 0xF2, 0xF7, 0x4E, 0xA7,
|
||||
0x36, 0x1D, 0x2B, 0x3D, 0x19, 0x39, 0x26, 0x0F, 0x19, 0xC2, 0x79, 0x60,
|
||||
0x52, 0x23, 0xA7, 0x08, 0xF7, 0x13, 0x12, 0xB6, 0xEB, 0xAD, 0xFE, 0x6E,
|
||||
0xEA, 0xC3, 0x1F, 0x66, 0xE3, 0xBC, 0x45, 0x95, 0xA6, 0x7B, 0xC8, 0x83,
|
||||
0xB1, 0x7F, 0x37, 0xD1, 0x01, 0x8C, 0xFF, 0x28, 0xC3, 0x32, 0xDD, 0xEF,
|
||||
0xBE, 0x6C, 0x5A, 0xA5, 0x65, 0x58, 0x21, 0x85, 0x68, 0xAB, 0x97, 0x02,
|
||||
0xEE, 0xCE, 0xA5, 0x0F, 0xDB, 0x2F, 0x95, 0x3B, 0x2A, 0xEF, 0x7D, 0xAD,
|
||||
0x5B, 0x6E, 0x2F, 0x84, 0x15, 0x21, 0xB6, 0x28, 0x29, 0x07, 0x61, 0x70,
|
||||
0xEC, 0xDD, 0x47, 0x75, 0x61, 0x9F, 0x15, 0x10, 0x13, 0xCC, 0xA8, 0x30,
|
||||
0xEB, 0x61, 0xBD, 0x96, 0x03, 0x34, 0xFE, 0x1E, 0xAA, 0x03, 0x63, 0xCF,
|
||||
0xB5, 0x73, 0x5C, 0x90, 0x4C, 0x70, 0xA2, 0x39, 0xD5, 0x9E, 0x9E, 0x0B,
|
||||
0xCB, 0xAA, 0xDE, 0x14, 0xEE, 0xCC, 0x86, 0xBC, 0x60, 0x62, 0x2C, 0xA7,
|
||||
0x9C, 0xAB, 0x5C, 0xAB, 0xB2, 0xF3, 0x84, 0x6E, 0x64, 0x8B, 0x1E, 0xAF,
|
||||
0x19, 0xBD, 0xF0, 0xCA, 0xA0, 0x23, 0x69, 0xB9, 0x65, 0x5A, 0xBB, 0x50,
|
||||
0x40, 0x68, 0x5A, 0x32, 0x3C, 0x2A, 0xB4, 0xB3, 0x31, 0x9E, 0xE9, 0xD5,
|
||||
0xC0, 0x21, 0xB8, 0xF7, 0x9B, 0x54, 0x0B, 0x19, 0x87, 0x5F, 0xA0, 0x99,
|
||||
0x95, 0xF7, 0x99, 0x7E, 0x62, 0x3D, 0x7D, 0xA8, 0xF8, 0x37, 0x88, 0x9A,
|
||||
0x97, 0xE3, 0x2D, 0x77, 0x11, 0xED, 0x93, 0x5F, 0x16, 0x68, 0x12, 0x81,
|
||||
0x0E, 0x35, 0x88, 0x29, 0xC7, 0xE6, 0x1F, 0xD6, 0x96, 0xDE, 0xDF, 0xA1,
|
||||
0x78, 0x58, 0xBA, 0x99, 0x57, 0xF5, 0x84, 0xA5, 0x1B, 0x22, 0x72, 0x63,
|
||||
0x9B, 0x83, 0xC3, 0xFF, 0x1A, 0xC2, 0x46, 0x96, 0xCD, 0xB3, 0x0A, 0xEB,
|
||||
0x53, 0x2E, 0x30, 0x54, 0x8F, 0xD9, 0x48, 0xE4, 0x6D, 0xBC, 0x31, 0x28,
|
||||
0x58, 0xEB, 0xF2, 0xEF, 0x34, 0xC6, 0xFF, 0xEA, 0xFE, 0x28, 0xED, 0x61,
|
||||
0xEE, 0x7C, 0x3C, 0x73, 0x5D, 0x4A, 0x14, 0xD9, 0xE8, 0x64, 0xB7, 0xE3,
|
||||
0x42, 0x10, 0x5D, 0x14, 0x20, 0x3E, 0x13, 0xE0, 0x45, 0xEE, 0xE2, 0xB6,
|
||||
0xA3, 0xAA, 0xAB, 0xEA, 0xDB, 0x6C, 0x4F, 0x15, 0xFA, 0xCB, 0x4F, 0xD0,
|
||||
0xC7, 0x42, 0xF4, 0x42, 0xEF, 0x6A, 0xBB, 0xB5, 0x65, 0x4F, 0x3B, 0x1D,
|
||||
0x41, 0xCD, 0x21, 0x05, 0xD8, 0x1E, 0x79, 0x9E, 0x86, 0x85, 0x4D, 0xC7,
|
||||
0xE4, 0x4B, 0x47, 0x6A, 0x3D, 0x81, 0x62, 0x50, 0xCF, 0x62, 0xA1, 0xF2,
|
||||
0x5B, 0x8D, 0x26, 0x46, 0xFC, 0x88, 0x83, 0xA0, 0xC1, 0xC7, 0xB6, 0xA3,
|
||||
0x7F, 0x15, 0x24, 0xC3, 0x69, 0xCB, 0x74, 0x92, 0x47, 0x84, 0x8A, 0x0B,
|
||||
0x56, 0x92, 0xB2, 0x85, 0x09, 0x5B, 0xBF, 0x00, 0xAD, 0x19, 0x48, 0x9D,
|
||||
0x14, 0x62, 0xB1, 0x74, 0x23, 0x82, 0x0D, 0x00, 0x58, 0x42, 0x8D, 0x2A,
|
||||
0x0C, 0x55, 0xF5, 0xEA, 0x1D, 0xAD, 0xF4, 0x3E, 0x23, 0x3F, 0x70, 0x61,
|
||||
0x33, 0x72, 0xF0, 0x92, 0x8D, 0x93, 0x7E, 0x41, 0xD6, 0x5F, 0xEC, 0xF1,
|
||||
0x6C, 0x22, 0x3B, 0xDB, 0x7C, 0xDE, 0x37, 0x59, 0xCB, 0xEE, 0x74, 0x60,
|
||||
0x40, 0x85, 0xF2, 0xA7, 0xCE, 0x77, 0x32, 0x6E, 0xA6, 0x07, 0x80, 0x84,
|
||||
0x19, 0xF8, 0x50, 0x9E, 0xE8, 0xEF, 0xD8, 0x55, 0x61, 0xD9, 0x97, 0x35,
|
||||
0xA9, 0x69, 0xA7, 0xAA, 0xC5, 0x0C, 0x06, 0xC2, 0x5A, 0x04, 0xAB, 0xFC,
|
||||
0x80, 0x0B, 0xCA, 0xDC, 0x9E, 0x44, 0x7A, 0x2E, 0xC3, 0x45, 0x34, 0x84,
|
||||
0xFD, 0xD5, 0x67, 0x05, 0x0E, 0x1E, 0x9E, 0xC9, 0xDB, 0x73, 0xDB, 0xD3,
|
||||
0x10, 0x55, 0x88, 0xCD, 0x67, 0x5F, 0xDA, 0x79, 0xE3, 0x67, 0x43, 0x40,
|
||||
0xC5, 0xC4, 0x34, 0x65, 0x71, 0x3E, 0x38, 0xD8, 0x3D, 0x28, 0xF8, 0x9E,
|
||||
0xF1, 0x6D, 0xFF, 0x20, 0x15, 0x3E, 0x21, 0xE7, 0x8F, 0xB0, 0x3D, 0x4A,
|
||||
0xE6, 0xE3, 0x9F, 0x2B, 0xDB, 0x83, 0xAD, 0xF7, 0xE9, 0x3D, 0x5A, 0x68,
|
||||
0x94, 0x81, 0x40, 0xF7, 0xF6, 0x4C, 0x26, 0x1C, 0x94, 0x69, 0x29, 0x34,
|
||||
0x41, 0x15, 0x20, 0xF7, 0x76, 0x02, 0xD4, 0xF7, 0xBC, 0xF4, 0x6B, 0x2E,
|
||||
0xD4, 0xA1, 0x00, 0x68, 0xD4, 0x08, 0x24, 0x71, 0x33, 0x20, 0xF4, 0x6A,
|
||||
0x43, 0xB7, 0xD4, 0xB7, 0x50, 0x00, 0x61, 0xAF, 0x1E, 0x39, 0xF6, 0x2E,
|
||||
0x97, 0x24, 0x45, 0x46,
|
||||
};
|
||||
|
||||
alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = {
|
||||
0x44, 0x73, 0x70, 0x03, 0x2E, 0x8A, 0x19, 0x13, 0xD3, 0x08, 0xA3, 0x85,
|
||||
0x88, 0x6A, 0x3F, 0x24, 0x89, 0x6C, 0x4E, 0xEC, 0x98, 0xFA, 0x2E, 0x08,
|
||||
0xD0, 0x31, 0x9F, 0x29, 0x22, 0x38, 0x09, 0xA4, 0x6C, 0x0C, 0xE9, 0x34,
|
||||
0xCF, 0x66, 0x54, 0xBE, 0x77, 0x13, 0xD0, 0x38, 0xE6, 0x21, 0x28, 0x45,
|
||||
0x17, 0x09, 0x47, 0xB5, 0xB5, 0xD5, 0x84, 0x3F, 0xDD, 0x50, 0x7C, 0xC9,
|
||||
0xB7, 0x29, 0xAC, 0xC0, 0xAC, 0xB5, 0xDF, 0x98, 0xA6, 0x0B, 0x31, 0xD1,
|
||||
0x1B, 0xFB, 0x79, 0x89, 0xD9, 0xD5, 0x16, 0x92, 0x96, 0x7E, 0x26, 0x6A,
|
||||
0xED, 0xAF, 0xE1, 0xB8, 0xB7, 0xDF, 0x1A, 0xD0, 0xDB, 0x72, 0xFD, 0x2F,
|
||||
0xF7, 0x6C, 0x91, 0xB3, 0x47, 0x99, 0xA1, 0x24, 0x99, 0x7F, 0x2C, 0xF1,
|
||||
0x45, 0x90, 0x7C, 0xBA, 0x69, 0x4E, 0x57, 0x71, 0xD8, 0x20, 0x69, 0x63,
|
||||
0x16, 0xFC, 0x8E, 0x85, 0xE2, 0xF2, 0x01, 0x08, 0x58, 0xB6, 0x8E, 0x72,
|
||||
0x8F, 0x74, 0x95, 0x0D, 0x7E, 0x3D, 0x93, 0xF4, 0xA3, 0xFE, 0x58, 0xA4,
|
||||
0xB5, 0x59, 0x5A, 0xC2, 0x1D, 0xA4, 0x54, 0x7B, 0xEE, 0x4A, 0x15, 0x82,
|
||||
0x58, 0xCD, 0x8B, 0x71, 0xF0, 0x85, 0x60, 0x28, 0x23, 0xB0, 0xD1, 0xC5,
|
||||
0x13, 0x60, 0xF2, 0x2A, 0x39, 0xD5, 0x30, 0x9C, 0x0E, 0x18, 0x3A, 0x60,
|
||||
0xB0, 0xDC, 0x79, 0x8E, 0xEF, 0x38, 0xDB, 0xB8, 0x18, 0x79, 0x41, 0xCA,
|
||||
0x27, 0x4B, 0x31, 0xBD, 0xC1, 0x77, 0x15, 0xD7, 0x3E, 0x8A, 0x1E, 0xB0,
|
||||
0x8B, 0x0E, 0x9E, 0x6C, 0x94, 0xAB, 0x55, 0xAA, 0xF3, 0x25, 0x55, 0xE6,
|
||||
0x60, 0x5C, 0x60, 0x55, 0xDA, 0x2F, 0xAF, 0x78, 0xB6, 0x10, 0xAB, 0x2A,
|
||||
0x6A, 0x39, 0xCA, 0x55, 0x40, 0x14, 0xE8, 0x63, 0x62, 0x98, 0x48, 0x57,
|
||||
0x93, 0xE9, 0x72, 0x7C, 0xAF, 0x86, 0x54, 0xA1, 0xCE, 0xE8, 0x41, 0x11,
|
||||
0x34, 0x5C, 0xCC, 0xB4, 0xF6, 0x31, 0x18, 0x74, 0x5D, 0xC5, 0xA9, 0x2B,
|
||||
0x2A, 0xBC, 0x6F, 0x63, 0x11, 0x14, 0xEE, 0xB3, 0x5C, 0xCF, 0x24, 0x6C,
|
||||
0x33, 0xBA, 0xD6, 0xAF, 0x1E, 0x93, 0x87, 0x9B, 0x16, 0x3E, 0x5C, 0xCE,
|
||||
0xAF, 0xB9, 0x4B, 0x6B, 0x98, 0x48, 0x8F, 0x3B, 0x77, 0x86, 0x95, 0x28,
|
||||
0x81, 0x53, 0x32, 0x7A, 0x91, 0xA9, 0x21, 0xFB, 0xCC, 0x09, 0xD8, 0x61,
|
||||
0x93, 0x21, 0x28, 0x66, 0x1B, 0xE8, 0xBF, 0xC4, 0xB1, 0x75, 0x85, 0xE9,
|
||||
0x5D, 0x5D, 0x84, 0xEF, 0x32, 0x80, 0xEC, 0x5D, 0x60, 0xAC, 0x7C, 0x48,
|
||||
0xC5, 0xAC, 0x96, 0xD3, 0x81, 0x3E, 0x89, 0x23, 0x88, 0x1B, 0x65, 0xEB,
|
||||
0x02, 0x23, 0x26, 0xDC, 0x04, 0x20, 0x84, 0xA4, 0x82, 0x44, 0x0B, 0x2E,
|
||||
0x39, 0x42, 0xF4, 0x83, 0xF3, 0x6F, 0x6D, 0x0F, 0x9A, 0x6C, 0xE9, 0xF6,
|
||||
0x42, 0x68, 0xC6, 0x21, 0x5E, 0x9B, 0x1F, 0x9E, 0x4A, 0xF0, 0xC8, 0x69,
|
||||
0x68, 0x2F, 0x54, 0xD8, 0xD2, 0xA0, 0x51, 0x6A, 0xF0, 0x88, 0xD3, 0xAB,
|
||||
0x61, 0x9C, 0x0C, 0x67, 0xE4, 0x3B, 0x7A, 0x13, 0x6C, 0x0B, 0xEF, 0x6E,
|
||||
0xA3, 0x33, 0x51, 0xAB, 0x28, 0xA7, 0x0F, 0x96, 0x76, 0x01, 0xAF, 0x39,
|
||||
0x1D, 0x65, 0xF1, 0xA1, 0x98, 0x2A, 0xFB, 0x7E, 0x50, 0xF0, 0x3B, 0xBA,
|
||||
0xB4, 0x9F, 0x6F, 0x45, 0x19, 0x86, 0xEE, 0x8C, 0x88, 0x0E, 0x43, 0x82,
|
||||
0x3E, 0x59, 0xCA, 0x66, 0x73, 0x20, 0xC1, 0x85, 0xD8, 0x75, 0x6F, 0xE0,
|
||||
0xBE, 0x5E, 0x8B, 0x3B, 0xC3, 0xA5, 0x84, 0x7D, 0x06, 0x77, 0x3F, 0x36,
|
||||
0x62, 0xAA, 0xD3, 0x4E, 0xA6, 0x6A, 0xC1, 0x56, 0x9F, 0x44, 0x1A, 0x40,
|
||||
0x48, 0x12, 0x0A, 0xD0, 0x24, 0xD7, 0xD0, 0x37, 0x3D, 0x02, 0x9B, 0x42,
|
||||
0x72, 0xDF, 0xFE, 0x1B, 0x7B, 0x1B, 0x99, 0x80, 0xC9, 0x72, 0x53, 0x07,
|
||||
0x9B, 0xC0, 0xF1, 0x49, 0xD3, 0xEA, 0x0F, 0xDB, 0x3B, 0x4C, 0x79, 0xB6,
|
||||
0x1A, 0x50, 0xFE, 0xE3, 0xF7, 0xDE, 0xE8, 0xF6, 0xD8, 0x79, 0xD4, 0x25,
|
||||
0xC4, 0x60, 0x9F, 0x40, 0xB6, 0x4F, 0xA9, 0xC1, 0xBA, 0x06, 0xC0, 0x04,
|
||||
0xBD, 0xE0, 0x6C, 0x97, 0xB5, 0x53, 0x6C, 0x3E, 0xAF, 0x6F, 0xFB, 0x68,
|
||||
0x63, 0x24, 0x6A, 0x19, 0xC2, 0x9E, 0x5C, 0x5E, 0x2C, 0x95, 0x30, 0x9B,
|
||||
0x1F, 0x51, 0xFC, 0x6D, 0x6F, 0xEC, 0x52, 0x3B, 0xEB, 0xB2, 0x39, 0x13,
|
||||
0xFD, 0x4A, 0x33, 0xDE, 0x04, 0xD0, 0xE3, 0xBE, 0x09, 0xBD, 0x5E, 0xAF,
|
||||
0x44, 0x45, 0x81, 0xCC, 0x0F, 0x74, 0xC8, 0x45, 0x57, 0xA8, 0xCB, 0xC0,
|
||||
0xB3, 0x4B, 0x2E, 0x19, 0x07, 0x28, 0x0F, 0x66, 0x0A, 0x32, 0x60, 0x1A,
|
||||
0xBD, 0xC0, 0x79, 0x55, 0xDB, 0xFB, 0xD3, 0xB9, 0x39, 0x5F, 0x0B, 0xD2,
|
||||
0xCC, 0xA3, 0x1F, 0xFB, 0xFE, 0x25, 0x9F, 0x67, 0x79, 0x72, 0x2C, 0x40,
|
||||
0xC6, 0x00, 0xA1, 0xD6, 0x15, 0x6B, 0x61, 0xFD, 0xDF, 0x16, 0x75, 0x3C,
|
||||
0xF8, 0x22, 0x32, 0xDB, 0xF8, 0xE9, 0xA5, 0x8E, 0x60, 0x87, 0x23, 0xFD,
|
||||
0xFA, 0xB5, 0x3D, 0x32, 0xAB, 0x52, 0x05, 0xAD, 0xC8, 0x1E, 0x50, 0x2F,
|
||||
0xA0, 0x8C, 0x6F, 0xCA, 0xBB, 0x57, 0x5C, 0x9E, 0x82, 0xDF, 0x00, 0x3E,
|
||||
0x48, 0x7B, 0x31, 0x53, 0xC3, 0xFF, 0x7E, 0x28, 0xF6, 0xA8, 0x42, 0xD5,
|
||||
0xDB, 0x69, 0x17, 0xDF, 0x2E, 0x56, 0x87, 0x1A, 0xC8, 0x58, 0xCA, 0xBB,
|
||||
0xB0, 0x27, 0x5B, 0x69, 0x73, 0x55, 0x4F, 0x8C, 0xC6, 0x32, 0x67, 0xAC,
|
||||
0xB8, 0x83, 0x21, 0xFD, 0x98, 0x3D, 0xFA, 0x10, 0xA0, 0x11, 0xF0, 0xB8,
|
||||
0x5D, 0xA3, 0xFF, 0xE1, 0x65, 0x45, 0xF8, 0xB6, 0x79, 0xE4, 0x53, 0x9A,
|
||||
0x5B, 0xD3, 0xD1, 0x2D, 0x6C, 0xB5, 0xFC, 0x4A, 0x33, 0x7E, 0xCB, 0xA4,
|
||||
0xDA, 0xF2, 0xDD, 0xE1, 0x90, 0x97, 0xFB, 0x4B, 0xBC, 0x49, 0x8E, 0xD2,
|
||||
0x01, 0x4C, 0x77, 0x36, 0xDA, 0xCA, 0x20, 0xEF, 0xE8, 0xC6, 0xE4, 0xCE,
|
||||
0x41, 0x13, 0xFB, 0x62, 0x98, 0x91, 0x90, 0xAE, 0x4D, 0xDA, 0xDB, 0x95,
|
||||
0xB4, 0x1F, 0xF1, 0x2B, 0xFE, 0x9E, 0x7E, 0xD0, 0xE0, 0x25, 0xC7, 0xAF,
|
||||
0xD0, 0xD1, 0x8E, 0xD0, 0xA0, 0xD5, 0x93, 0x6B, 0x71, 0x8E, 0xAD, 0xEA,
|
||||
0x64, 0x2B, 0x12, 0xF2, 0xFB, 0xE2, 0xF6, 0x8F, 0xB7, 0x94, 0x75, 0x8E,
|
||||
0x2F, 0x5B, 0x3C, 0x8E, 0x1C, 0xC3, 0x8F, 0x68, 0xA0, 0x5E, 0xAD, 0x4F,
|
||||
0x1C, 0xF0, 0x0D, 0x90, 0x12, 0xB8, 0x88, 0x88, 0x77, 0x17, 0x0E, 0xBE,
|
||||
0x18, 0x22, 0x2F, 0x2F, 0xAD, 0xC1, 0xA8, 0xB3, 0x91, 0xF1, 0xCF, 0xD1,
|
||||
0xE8, 0x74, 0x6F, 0xB5, 0x0F, 0xCC, 0xA0, 0xE5, 0xA1, 0x1F, 0x02, 0x8B,
|
||||
0xFE, 0x2D, 0x75, 0xEA, 0xB7, 0xE0, 0x13, 0xFD, 0xE0, 0x4F, 0xA8, 0xB4,
|
||||
0x99, 0xE2, 0x89, 0xCE, 0xD6, 0xF3, 0xAC, 0x18, 0x05, 0x77, 0x95, 0x80,
|
||||
0x66, 0xA2, 0x5F, 0x16, 0xD9, 0xA8, 0xAD, 0xD2, 0x81, 0x3B, 0xC4, 0x7C,
|
||||
0x86, 0xFA, 0xB5, 0x77, 0x65, 0x20, 0xAD, 0xE6, 0x77, 0x14, 0x1A, 0x21,
|
||||
0x14, 0x73, 0xCC, 0x93, 0xA0, 0x89, 0x3E, 0x7B, 0x0C, 0xAF, 0xCD, 0xEB,
|
||||
0xCF, 0x35, 0x9D, 0xFB, 0xF5, 0x42, 0x54, 0xC7, 0x5E, 0xB3, 0x71, 0x20,
|
||||
0x2D, 0x0E, 0x25, 0x00, 0x49, 0x7E, 0x1E, 0xAE, 0xD3, 0x1B, 0x41, 0xD6,
|
||||
0x1E, 0xB9, 0x09, 0xF0, 0x9B, 0x36, 0x64, 0x24, 0xAF, 0xE0, 0xB8, 0x57,
|
||||
0xBB, 0x00, 0x68, 0x22, 0x7F, 0x53, 0x5A, 0xD9, 0x89, 0x43, 0xC1, 0x78,
|
||||
0xAA, 0xA6, 0xDF, 0x59, 0x1D, 0x91, 0x63, 0x55, 0xA9, 0xCF, 0x95, 0x62,
|
||||
0x76, 0x03, 0x26, 0x83, 0xC5, 0xB9, 0xE5, 0x02, 0xA2, 0x5B, 0x7D, 0x20,
|
||||
0x4A, 0xA9, 0x14, 0x7B, 0xCA, 0x2D, 0x47, 0xB3, 0x41, 0x4A, 0x73, 0x4E,
|
||||
0x68, 0x19, 0xC8, 0x11, 0xE4, 0xC6, 0x9B, 0xBC, 0x3F, 0x57, 0x0F, 0xD6,
|
||||
0x15, 0x29, 0x53, 0x9A, 0x52, 0x00, 0x51, 0x1B, 0x1F, 0xE9, 0x1B, 0x57,
|
||||
0xB5, 0x6F, 0xBA, 0x08, 0x00, 0x74, 0xE6, 0x81, 0x76, 0xA4, 0x60, 0x2B,
|
||||
0xB6, 0xF9, 0xB9, 0xE7, 0x21, 0x65, 0x63, 0xB6, 0x15, 0xD9, 0x0D, 0x2A,
|
||||
0x6B, 0xEC, 0x96, 0xF2, 0xA1, 0x8F, 0x9F, 0xA9, 0x5D, 0x2D, 0xB0, 0x53,
|
||||
0x64, 0x56, 0x85, 0xC5, 0x2E, 0x05, 0x34, 0xFF, 0x44, 0x29, 0xB3, 0xB5,
|
||||
0xE9, 0x70, 0x7A, 0x4B, 0x6A, 0x07, 0x85, 0x6E, 0x99, 0x47, 0xBA, 0x08,
|
||||
0x7D, 0xDF, 0xA7, 0x49, 0xB0, 0xA6, 0x6E, 0xAD, 0x23, 0x26, 0x19, 0xC4,
|
||||
0x2E, 0x09, 0x75, 0xDB, 0xFF, 0x18, 0x9A, 0x69, 0x71, 0x8C, 0xAA, 0xEC,
|
||||
0x66, 0xB2, 0xED, 0x8F, 0xB8, 0x60, 0xEE, 0x9C, 0x29, 0x4C, 0x09, 0x75,
|
||||
0xA5, 0x02, 0x36, 0x19, 0xE1, 0x9E, 0xB1, 0xC2, 0x6C, 0x52, 0x64, 0x56,
|
||||
0x65, 0x9D, 0x42, 0x5B, 0x9A, 0x98, 0x54, 0x3F, 0x3E, 0x3A, 0x18, 0xE4,
|
||||
0x40, 0x13, 0x59, 0xA0, 0xF5, 0x30, 0xE8, 0xEF, 0x07, 0x9C, 0xD2, 0xA1,
|
||||
0xD6, 0x3F, 0xF7, 0x99, 0xD6, 0xE4, 0x8F, 0x6B, 0x26, 0xEB, 0x70, 0x84,
|
||||
0x86, 0x20, 0xDD, 0x4C, 0xC1, 0x5D, 0x25, 0xF0, 0xE6, 0x38, 0x2D, 0x4D,
|
||||
0xC9, 0xEF, 0xBA, 0x3E, 0x3F, 0x6B, 0x68, 0x09, 0x5E, 0xCC, 0x1E, 0x02,
|
||||
0xC6, 0xE9, 0x82, 0x63, 0x86, 0xE2, 0xA0, 0x52, 0x84, 0x35, 0x7F, 0x68,
|
||||
0xA1, 0x70, 0x6A, 0x6B, 0x14, 0x18, 0x97, 0x3C, 0x5C, 0xAE, 0xDE, 0x7F,
|
||||
0x1C, 0x84, 0x07, 0x3E, 0x37, 0x07, 0x50, 0xAA, 0x05, 0x53, 0x9C, 0xB7,
|
||||
0x0D, 0x0C, 0x50, 0xF0, 0x37, 0xDA, 0x3A, 0xB0, 0xB8, 0xF2, 0x16, 0x57,
|
||||
0xEC, 0x44, 0x7D, 0x8E, 0xB2, 0x74, 0xB5, 0x3C, 0x1A, 0xF5, 0x0C, 0xAE,
|
||||
0xFF, 0xB3, 0x00, 0x02, 0x04, 0x1F, 0x1C, 0xF0, 0xF6, 0x2F, 0xA9, 0x7C,
|
||||
0xF9, 0x13, 0x91, 0xD1, 0xBD, 0x21, 0x09, 0xDC, 0x58, 0x7A, 0x83, 0x25,
|
||||
0xDC, 0xDA, 0xC2, 0x37, 0x81, 0xE5, 0xE5, 0x3A, 0x01, 0x47, 0xF5, 0x22,
|
||||
0x73, 0x47, 0x32, 0x94, 0x0E, 0x03, 0xD0, 0x0F, 0x46, 0x61, 0x44, 0xA9,
|
||||
0xA7, 0xDD, 0xF3, 0x9A, 0x34, 0x76, 0xB5, 0xC8, 0x2F, 0x0E, 0xEA, 0x3B,
|
||||
0x99, 0xCD, 0x38, 0xE2, 0x41, 0x1E, 0x75, 0xA4, 0x3E, 0xC7, 0xC8, 0xEC,
|
||||
0x08, 0xB9, 0x6D, 0x4F, 0x38, 0x8B, 0x54, 0x4E, 0x31, 0xB3, 0x3E, 0x18,
|
||||
0xA1, 0xBB, 0x80, 0x32, 0x79, 0x7C, 0x97, 0x24, 0x90, 0x12, 0xB8, 0x2C,
|
||||
0xBF, 0x04, 0x0A, 0xF6, 0x03, 0x0D, 0x42, 0x6F, 0x10, 0x08, 0x93, 0xD9,
|
||||
0x1F, 0x77, 0x9A, 0xDE, 0xAF, 0x89, 0xAF, 0xBC, 0x72, 0xB0, 0x79, 0x56,
|
||||
0x24, 0x71, 0x6B, 0x2E, 0x1F, 0x72, 0x12, 0x55, 0x2E, 0x3F, 0xCF, 0xDC,
|
||||
0x12, 0xAE, 0x8B, 0xB3, 0x17, 0xDA, 0x08, 0x74, 0x18, 0x47, 0x58, 0x7A,
|
||||
0x87, 0xCD, 0x84, 0x9F, 0xE6, 0xDD, 0x1A, 0x50, 0xFA, 0x1D, 0x85, 0xDB,
|
||||
0x3A, 0xEC, 0x7A, 0xEC, 0x8C, 0x7D, 0x4B, 0xE9, 0xBC, 0x9A, 0x9F, 0xBC,
|
||||
0x08, 0xD8, 0x15, 0x32, 0x47, 0x18, 0x1C, 0xEF, 0xD2, 0xC3, 0x64, 0xC4,
|
||||
0x66, 0x43, 0x09, 0x63, 0x51, 0xC4, 0x65, 0x2A, 0x43, 0x4D, 0xA1, 0x12,
|
||||
0x16, 0xBA, 0xC2, 0x24, 0x37, 0x3B, 0x43, 0xDD, 0x55, 0x4E, 0x31, 0x10,
|
||||
0x9E, 0xF8, 0xDF, 0x71, 0xDD, 0xE4, 0x3A, 0x13, 0x02, 0x00, 0x94, 0x50,
|
||||
0x6B, 0xC7, 0xA3, 0xD7, 0xF1, 0x56, 0x35, 0x04, 0x9B, 0x19, 0x11, 0x5F,
|
||||
0xD6, 0x77, 0xAC, 0x81, 0xFA, 0xFB, 0xF1, 0x97, 0xED, 0xE6, 0x8F, 0xF2,
|
||||
0x09, 0xA5, 0x24, 0x59, 0x3B, 0x18, 0x11, 0x3C, 0xB1, 0x6F, 0xE9, 0xEA,
|
||||
0x70, 0x45, 0xE3, 0x86, 0x6E, 0x3C, 0x15, 0x1E, 0x2C, 0xBF, 0xBA, 0x9E,
|
||||
0xFA, 0x06, 0x3D, 0x4E, 0x1C, 0xE7, 0x1F, 0x77, 0xB3, 0x2A, 0x3E, 0x5A,
|
||||
0x0A, 0x5E, 0x0E, 0x86, 0x25, 0xC8, 0x66, 0x52, 0xD6, 0x89, 0x3E, 0x80,
|
||||
0x0F, 0x1D, 0xE7, 0x99, 0xB9, 0xDC, 0x65, 0x29, 0x78, 0xEA, 0xE2, 0x94,
|
||||
0xBA, 0x0E, 0x15, 0xC6, 0x6A, 0xB3, 0x10, 0x9C, 0x78, 0xC9, 0x4C, 0x2E,
|
||||
0x3D, 0x2B, 0x1D, 0x36, 0xA7, 0x4E, 0xF7, 0xF2, 0xF4, 0x2D, 0x0A, 0x1E,
|
||||
0x53, 0x3C, 0xFC, 0xA6, 0xB6, 0x12, 0x13, 0xF7, 0x08, 0xA7, 0x23, 0x52,
|
||||
0x60, 0x79, 0xC2, 0x19, 0x0F, 0x26, 0x39, 0x19, 0x83, 0xC8, 0x7B, 0xA6,
|
||||
0x95, 0x45, 0xBC, 0xE3, 0x66, 0x1F, 0xC3, 0xEA, 0x6E, 0xFE, 0xAD, 0xEB,
|
||||
0xA5, 0x5A, 0x6C, 0xBE, 0xEF, 0xDD, 0x32, 0xC3, 0x28, 0xFF, 0x8C, 0x01,
|
||||
0xD1, 0x37, 0x7F, 0xB1, 0x3B, 0x95, 0x2F, 0xDB, 0x0F, 0xA5, 0xCE, 0xEE,
|
||||
0x02, 0x97, 0xAB, 0x68, 0x85, 0x21, 0x58, 0x65, 0x70, 0x61, 0x07, 0x29,
|
||||
0x28, 0xB6, 0x21, 0x15, 0x84, 0x2F, 0x6E, 0x5B, 0xAD, 0x7D, 0xEF, 0x2A,
|
||||
0x96, 0xBD, 0x61, 0xEB, 0x30, 0xA8, 0xCC, 0x13, 0x10, 0x15, 0x9F, 0x61,
|
||||
0x75, 0x47, 0xDD, 0xEC, 0x39, 0xA2, 0x70, 0x4C, 0x90, 0x5C, 0x73, 0xB5,
|
||||
0xCF, 0x63, 0x03, 0xAA, 0x1E, 0xFE, 0x34, 0x03, 0xA7, 0x2C, 0x62, 0x60,
|
||||
0xBC, 0x86, 0xCC, 0xEE, 0x14, 0xDE, 0xAA, 0xCB, 0x0B, 0x9E, 0x9E, 0xD5,
|
||||
0xCA, 0xF0, 0xBD, 0x19, 0xAF, 0x1E, 0x8B, 0x64, 0x6E, 0x84, 0xF3, 0xB2,
|
||||
0xAB, 0x5C, 0xAB, 0x9C, 0xB3, 0xB4, 0x2A, 0x3C, 0x32, 0x5A, 0x68, 0x40,
|
||||
0x50, 0xBB, 0x5A, 0x65, 0xB9, 0x69, 0x23, 0xA0, 0x99, 0xA0, 0x5F, 0x87,
|
||||
0x19, 0x0B, 0x54, 0x9B, 0xF7, 0xB8, 0x21, 0xC0, 0xD5, 0xE9, 0x9E, 0x31,
|
||||
0x77, 0x2D, 0xE3, 0x97, 0x9A, 0x88, 0x37, 0xF8, 0xA8, 0x7D, 0x3D, 0x62,
|
||||
0x7E, 0x99, 0xF7, 0x95, 0xD6, 0x1F, 0xE6, 0xC7, 0x29, 0x88, 0x35, 0x0E,
|
||||
0x81, 0x12, 0x68, 0x16, 0x5F, 0x93, 0xED, 0x11, 0x63, 0x72, 0x22, 0x1B,
|
||||
0xA5, 0x84, 0xF5, 0x57, 0x99, 0xBA, 0x58, 0x78, 0xA1, 0xDF, 0xDE, 0x96,
|
||||
0x54, 0x30, 0x2E, 0x53, 0xEB, 0x0A, 0xB3, 0xCD, 0x96, 0x46, 0xC2, 0x1A,
|
||||
0xFF, 0xC3, 0x83, 0x9B, 0xEA, 0xFF, 0xC6, 0x34, 0xEF, 0xF2, 0xEB, 0x58,
|
||||
0x28, 0x31, 0xBC, 0x6D, 0xE4, 0x48, 0xD9, 0x8F, 0xE3, 0xB7, 0x64, 0xE8,
|
||||
0xD9, 0x14, 0x4A, 0x5D, 0x73, 0x3C, 0x7C, 0xEE, 0x61, 0xED, 0x28, 0xFE,
|
||||
0xEA, 0xAB, 0xAA, 0xA3, 0xB6, 0xE2, 0xEE, 0x45, 0xE0, 0x13, 0x3E, 0x20,
|
||||
0x14, 0x5D, 0x10, 0x42, 0xB5, 0xBB, 0x6A, 0xEF, 0x42, 0xF4, 0x42, 0xC7,
|
||||
0xD0, 0x4F, 0xCB, 0xFA, 0x15, 0x4F, 0x6C, 0xDB, 0xC7, 0x4D, 0x85, 0x86,
|
||||
0x9E, 0x79, 0x1E, 0xD8, 0x05, 0x21, 0xCD, 0x41, 0x1D, 0x3B, 0x4F, 0x65,
|
||||
0x46, 0x26, 0x8D, 0x5B, 0xF2, 0xA1, 0x62, 0xCF, 0x50, 0x62, 0x81, 0x3D,
|
||||
0x6A, 0x47, 0x4B, 0xE4, 0x92, 0x74, 0xCB, 0x69, 0xC3, 0x24, 0x15, 0x7F,
|
||||
0xA3, 0xB6, 0xC7, 0xC1, 0xA0, 0x83, 0x88, 0xFC, 0x9D, 0x48, 0x19, 0xAD,
|
||||
0x00, 0xBF, 0x5B, 0x09, 0x85, 0xB2, 0x92, 0x56, 0x0B, 0x8A, 0x84, 0x47,
|
||||
0xEA, 0xF5, 0x55, 0x0C, 0x2A, 0x8D, 0x42, 0x58, 0x00, 0x0D, 0x82, 0x23,
|
||||
0x74, 0xB1, 0x62, 0x14, 0x41, 0x7E, 0x93, 0x8D, 0x92, 0xF0, 0x72, 0x33,
|
||||
0x61, 0x70, 0x3F, 0x23, 0x3E, 0xF4, 0xAD, 0x1D, 0x60, 0x74, 0xEE, 0xCB,
|
||||
0x59, 0x37, 0xDE, 0x7C, 0xDB, 0x3B, 0x22, 0x6C, 0xF1, 0xEC, 0x5F, 0xD6,
|
||||
0x9E, 0x50, 0xF8, 0x19, 0x84, 0x80, 0x07, 0xA6, 0x6E, 0x32, 0x77, 0xCE,
|
||||
0xA7, 0xF2, 0x85, 0x40, 0xC2, 0x06, 0x0C, 0xC5, 0xAA, 0xA7, 0x69, 0xA9,
|
||||
0x35, 0x97, 0xD9, 0x61, 0x55, 0xD8, 0xEF, 0xE8, 0x84, 0x34, 0x45, 0xC3,
|
||||
0x2E, 0x7A, 0x44, 0x9E, 0xDC, 0xCA, 0x0B, 0x80, 0xFC, 0xAB, 0x04, 0x5A,
|
||||
0xCD, 0x88, 0x55, 0x10, 0xD3, 0xDB, 0x73, 0xDB, 0xC9, 0x9E, 0x1E, 0x0E,
|
||||
0x05, 0x67, 0xD5, 0xFD, 0xD8, 0x38, 0x3E, 0x71, 0x65, 0x34, 0xC4, 0xC5,
|
||||
0x40, 0x43, 0x67, 0xE3, 0x79, 0xDA, 0x5F, 0x67, 0x4A, 0x3D, 0xB0, 0x8F,
|
||||
0xE7, 0x21, 0x3E, 0x15, 0x20, 0xFF, 0x6D, 0xF1, 0x9E, 0xF8, 0x28, 0x3D,
|
||||
0xF7, 0x40, 0x81, 0x94, 0x68, 0x5A, 0x3D, 0xE9, 0xF7, 0xAD, 0x83, 0xDB,
|
||||
0x2B, 0x9F, 0xE3, 0xE6, 0xF7, 0xD4, 0x02, 0x76, 0xF7, 0x20, 0x15, 0x41,
|
||||
0x34, 0x29, 0x69, 0x94, 0x1C, 0x26, 0x4C, 0xF6, 0x6A, 0xF4, 0x20, 0x33,
|
||||
0x71, 0x24, 0x08, 0xD4, 0x68, 0x00, 0xA1, 0xD4, 0x2E, 0x6B, 0xF4, 0xBC,
|
||||
0x46, 0x45, 0x24, 0x97, 0x2E, 0xF6, 0x39, 0x1E, 0xAF, 0x61, 0x00, 0x50,
|
||||
0xB7, 0xD4, 0xB7, 0x43,
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen_slow.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/internal/endian.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/random/internal/platform.h"
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
|
||||
#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \
|
||||
__attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
// We can achieve something similar to attribute((always_inline)) with MSVC by
|
||||
// using the __forceinline keyword, however this is not perfect. MSVC is
|
||||
// much less aggressive about inlining, and even with the __forceinline keyword.
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline
|
||||
#else
|
||||
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// AES portions based on rijndael-alg-fst.c,
|
||||
// https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for
|
||||
// platform-endianness.
|
||||
//
|
||||
// Implementation of
|
||||
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||
constexpr uint32_t te0[256] = {
|
||||
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
|
||||
0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56,
|
||||
0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f,
|
||||
0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
|
||||
0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
|
||||
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
|
||||
0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
|
||||
0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
|
||||
0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637,
|
||||
0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
|
||||
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d,
|
||||
0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
|
||||
0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
|
||||
0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1,
|
||||
0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
|
||||
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
|
||||
0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a,
|
||||
0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe,
|
||||
0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
|
||||
0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
|
||||
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5,
|
||||
0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
|
||||
0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755,
|
||||
0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
|
||||
0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
|
||||
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
|
||||
0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264,
|
||||
0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
|
||||
0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531,
|
||||
0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
|
||||
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
|
||||
0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
|
||||
0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657,
|
||||
0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
|
||||
0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
|
||||
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
|
||||
0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
|
||||
0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
|
||||
0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c,
|
||||
0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
|
||||
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7,
|
||||
0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
|
||||
0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
|
||||
};
|
||||
|
||||
constexpr uint32_t te1[256] = {
|
||||
0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
|
||||
0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d,
|
||||
0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d,
|
||||
0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
|
||||
0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
|
||||
0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a,
|
||||
0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
|
||||
0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
|
||||
0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1,
|
||||
0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
|
||||
0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e,
|
||||
0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
|
||||
0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
|
||||
0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c,
|
||||
0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
|
||||
0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
|
||||
0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7,
|
||||
0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81,
|
||||
0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
|
||||
0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
|
||||
0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a,
|
||||
0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
|
||||
0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2,
|
||||
0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
|
||||
0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
|
||||
0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c,
|
||||
0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456,
|
||||
0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
|
||||
0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4,
|
||||
0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
|
||||
0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
|
||||
0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
|
||||
0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1,
|
||||
0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
|
||||
0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
|
||||
0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
|
||||
0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
|
||||
0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233,
|
||||
0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22,
|
||||
0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
|
||||
0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731,
|
||||
0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11,
|
||||
0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
|
||||
};
|
||||
|
||||
constexpr uint32_t te2[256] = {
|
||||
0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
|
||||
0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b,
|
||||
0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82,
|
||||
0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
|
||||
0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
|
||||
0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26,
|
||||
0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
|
||||
0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
|
||||
0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196,
|
||||
0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
|
||||
0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83,
|
||||
0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
|
||||
0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
|
||||
0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced,
|
||||
0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
|
||||
0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
|
||||
0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d,
|
||||
0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f,
|
||||
0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
|
||||
0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
|
||||
0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff,
|
||||
0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
|
||||
0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7,
|
||||
0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
|
||||
0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
|
||||
0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14,
|
||||
0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632,
|
||||
0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
|
||||
0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495,
|
||||
0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
|
||||
0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
|
||||
0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
|
||||
0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6,
|
||||
0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
|
||||
0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
|
||||
0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
|
||||
0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
|
||||
0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311,
|
||||
0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e,
|
||||
0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
|
||||
0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6,
|
||||
0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f,
|
||||
0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
|
||||
};
|
||||
|
||||
constexpr uint32_t te3[256] = {
|
||||
0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
|
||||
0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b,
|
||||
0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282,
|
||||
0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
|
||||
0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
|
||||
0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626,
|
||||
0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
|
||||
0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
|
||||
0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696,
|
||||
0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
|
||||
0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383,
|
||||
0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
|
||||
0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
|
||||
0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded,
|
||||
0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
|
||||
0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
|
||||
0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d,
|
||||
0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f,
|
||||
0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
|
||||
0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
|
||||
0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff,
|
||||
0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
|
||||
0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7,
|
||||
0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
|
||||
0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
|
||||
0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414,
|
||||
0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232,
|
||||
0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
|
||||
0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595,
|
||||
0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
|
||||
0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
|
||||
0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
|
||||
0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6,
|
||||
0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
|
||||
0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
|
||||
0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
|
||||
0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
|
||||
0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111,
|
||||
0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e,
|
||||
0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
|
||||
0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6,
|
||||
0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f,
|
||||
0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
|
||||
};
|
||||
|
||||
// Software implementation of the Vector128 class, using uint32_t
|
||||
// as an underlying vector register.
|
||||
struct alignas(16) Vector128 {
|
||||
uint32_t s[4];
|
||||
};
|
||||
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
|
||||
Vector128Load(const void* from) {
|
||||
Vector128 result;
|
||||
std::memcpy(result.s, from, sizeof(Vector128));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store(
|
||||
const Vector128& v, void* to) {
|
||||
std::memcpy(to, v.s, sizeof(Vector128));
|
||||
}
|
||||
|
||||
// One round of AES. "round_key" is a public constant for breaking the
|
||||
// symmetry of AES (ensures previously equal columns differ afterwards).
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
|
||||
AesRound(const Vector128& state, const Vector128& round_key) {
|
||||
Vector128 result;
|
||||
#ifdef ABSL_IS_LITTLE_ENDIAN
|
||||
result.s[0] = round_key.s[0] ^ //
|
||||
te0[uint8_t(state.s[0])] ^ //
|
||||
te1[uint8_t(state.s[1] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[2] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[3] >> 24)];
|
||||
result.s[1] = round_key.s[1] ^ //
|
||||
te0[uint8_t(state.s[1])] ^ //
|
||||
te1[uint8_t(state.s[2] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[3] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[0] >> 24)];
|
||||
result.s[2] = round_key.s[2] ^ //
|
||||
te0[uint8_t(state.s[2])] ^ //
|
||||
te1[uint8_t(state.s[3] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[0] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[1] >> 24)];
|
||||
result.s[3] = round_key.s[3] ^ //
|
||||
te0[uint8_t(state.s[3])] ^ //
|
||||
te1[uint8_t(state.s[0] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[1] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[2] >> 24)];
|
||||
#else
|
||||
result.s[0] = round_key.s[0] ^ //
|
||||
te0[uint8_t(state.s[0])] ^ //
|
||||
te1[uint8_t(state.s[3] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[2] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[1] >> 24)];
|
||||
result.s[1] = round_key.s[1] ^ //
|
||||
te0[uint8_t(state.s[1])] ^ //
|
||||
te1[uint8_t(state.s[0] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[3] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[2] >> 24)];
|
||||
result.s[2] = round_key.s[2] ^ //
|
||||
te0[uint8_t(state.s[2])] ^ //
|
||||
te1[uint8_t(state.s[1] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[0] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[3] >> 24)];
|
||||
result.s[3] = round_key.s[3] ^ //
|
||||
te0[uint8_t(state.s[3])] ^ //
|
||||
te1[uint8_t(state.s[2] >> 8)] ^ //
|
||||
te2[uint8_t(state.s[1] >> 16)] ^ //
|
||||
te3[uint8_t(state.s[0] >> 24)];
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
using ::absl::random_internal::RandenTraits;
|
||||
|
||||
// The improved Feistel block shuffle function for 16 blocks.
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle(
|
||||
absl::uint128* state) {
|
||||
static_assert(RandenTraits::kFeistelBlocks == 16,
|
||||
"Feistel block shuffle only works for 16 blocks.");
|
||||
|
||||
constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = {
|
||||
7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12};
|
||||
|
||||
// The fully unrolled loop without the memcpy improves the speed by about
|
||||
// 30% over the equivalent:
|
||||
#if 0
|
||||
absl::uint128 source[RandenTraits::kFeistelBlocks];
|
||||
std::memcpy(source, state, sizeof(source));
|
||||
for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) {
|
||||
const absl::uint128 v0 = source[shuffle[i]];
|
||||
state[i] = v0;
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
|
||||
const absl::uint128 v0 = state[shuffle[0]];
|
||||
const absl::uint128 v1 = state[shuffle[1]];
|
||||
const absl::uint128 v2 = state[shuffle[2]];
|
||||
const absl::uint128 v3 = state[shuffle[3]];
|
||||
const absl::uint128 v4 = state[shuffle[4]];
|
||||
const absl::uint128 v5 = state[shuffle[5]];
|
||||
const absl::uint128 v6 = state[shuffle[6]];
|
||||
const absl::uint128 v7 = state[shuffle[7]];
|
||||
const absl::uint128 w0 = state[shuffle[8]];
|
||||
const absl::uint128 w1 = state[shuffle[9]];
|
||||
const absl::uint128 w2 = state[shuffle[10]];
|
||||
const absl::uint128 w3 = state[shuffle[11]];
|
||||
const absl::uint128 w4 = state[shuffle[12]];
|
||||
const absl::uint128 w5 = state[shuffle[13]];
|
||||
const absl::uint128 w6 = state[shuffle[14]];
|
||||
const absl::uint128 w7 = state[shuffle[15]];
|
||||
state[0] = v0;
|
||||
state[1] = v1;
|
||||
state[2] = v2;
|
||||
state[3] = v3;
|
||||
state[4] = v4;
|
||||
state[5] = v5;
|
||||
state[6] = v6;
|
||||
state[7] = v7;
|
||||
state[8] = w0;
|
||||
state[9] = w1;
|
||||
state[10] = w2;
|
||||
state[11] = w3;
|
||||
state[12] = w4;
|
||||
state[13] = w5;
|
||||
state[14] = w6;
|
||||
state[15] = w7;
|
||||
}
|
||||
|
||||
// Feistel round function using two AES subrounds. Very similar to F()
|
||||
// from Simpira v2, but with independent subround keys. Uses 17 AES rounds
|
||||
// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
|
||||
// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
|
||||
// XORs are 'free' (included in the second AES instruction).
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128*
|
||||
FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state,
|
||||
const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
|
||||
for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) {
|
||||
const Vector128 s0 = Vector128Load(state + branch);
|
||||
const Vector128 s1 = Vector128Load(state + branch + 1);
|
||||
const Vector128 f0 = AesRound(s0, Vector128Load(keys));
|
||||
keys++;
|
||||
const Vector128 o1 = AesRound(f0, s1);
|
||||
Vector128Store(o1, state + branch + 1);
|
||||
|
||||
// Manually unroll this loop once. about 10% better than not unrolled.
|
||||
const Vector128 s2 = Vector128Load(state + branch + 2);
|
||||
const Vector128 s3 = Vector128Load(state + branch + 3);
|
||||
const Vector128 f2 = AesRound(s2, Vector128Load(keys));
|
||||
keys++;
|
||||
const Vector128 o3 = AesRound(f2, s3);
|
||||
Vector128Store(o3, state + branch + 3);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Cryptographic permutation based via type-2 Generalized Feistel Network.
|
||||
// Indistinguishable from ideal by chosen-ciphertext adversaries using less than
|
||||
// 2^64 queries if the round function is a PRF. This is similar to the b=8 case
|
||||
// of Simpira v2, but more efficient than its generic construction for b=16.
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute(
|
||||
absl::uint128* state,
|
||||
const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
|
||||
for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
|
||||
keys = FeistelRound(state, keys);
|
||||
BlockShuffle(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Enables native loads in the round loop by pre-swapping.
|
||||
inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian(
|
||||
absl::uint128* state) {
|
||||
#ifdef ABSL_IS_BIG_ENDIAN
|
||||
for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
|
||||
uint64_t new_lo = absl::little_endian::ToHost64(
|
||||
static_cast<uint64_t>(state[block] >> 64));
|
||||
uint64_t new_hi = absl::little_endian::ToHost64(
|
||||
static_cast<uint64_t>((state[block] << 64) >> 64));
|
||||
state[block] = (static_cast<absl::uint128>(new_hi) << 64) | new_lo;
|
||||
}
|
||||
#else
|
||||
// Avoid warning about unused variable.
|
||||
(void)state;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
const void* RandenSlow::GetKeys() {
|
||||
// Round keys for one AES per Feistel round and branch.
|
||||
// The canonical implementation uses first digits of Pi.
|
||||
#ifdef ABSL_IS_LITTLE_ENDIAN
|
||||
return kRandenRoundKeys;
|
||||
#else
|
||||
return kRandenRoundKeysBE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RandenSlow::Absorb(const void* seed_void, void* state_void) {
|
||||
auto* state =
|
||||
reinterpret_cast<uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>(state_void);
|
||||
const auto* seed =
|
||||
reinterpret_cast<const uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>(
|
||||
seed_void);
|
||||
|
||||
constexpr size_t kCapacityBlocks =
|
||||
RandenTraits::kCapacityBytes / sizeof(uint64_t);
|
||||
static_assert(
|
||||
kCapacityBlocks * sizeof(uint64_t) == RandenTraits::kCapacityBytes,
|
||||
"Not i*V");
|
||||
|
||||
for (size_t i = kCapacityBlocks;
|
||||
i < RandenTraits::kStateBytes / sizeof(uint64_t); ++i) {
|
||||
state[i] ^= seed[i - kCapacityBlocks];
|
||||
}
|
||||
}
|
||||
|
||||
void RandenSlow::Generate(const void* keys_void, void* state_void) {
|
||||
static_assert(RandenTraits::kCapacityBytes == sizeof(absl::uint128),
|
||||
"Capacity mismatch");
|
||||
|
||||
auto* state = reinterpret_cast<absl::uint128*>(state_void);
|
||||
const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void);
|
||||
|
||||
const absl::uint128 prev_inner = state[0];
|
||||
|
||||
SwapEndian(state);
|
||||
|
||||
Permute(state, keys);
|
||||
|
||||
SwapEndian(state);
|
||||
|
||||
// Ensure backtracking resistance.
|
||||
*state ^= prev_inner;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// RandenSlow implements the basic state manipulation methods for
|
||||
// architectures lacking AES hardware acceleration intrinsics.
|
||||
class RandenSlow {
|
||||
public:
|
||||
static void Generate(const void* keys, void* state_void);
|
||||
static void Absorb(const void* seed_void, void* state_void);
|
||||
static const void* GetKeys();
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen_slow.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/base/internal/endian.h"
|
||||
#include "absl/random/internal/randen_traits.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::RandenSlow;
|
||||
using absl::random_internal::RandenTraits;
|
||||
|
||||
TEST(RandenSlowTest, Default) {
|
||||
constexpr uint8_t kGolden[] = {
|
||||
0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
|
||||
0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
|
||||
0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
|
||||
0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
|
||||
0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
|
||||
0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
|
||||
0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
|
||||
0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
|
||||
0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
|
||||
0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
|
||||
0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
|
||||
0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
|
||||
0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
|
||||
0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
|
||||
0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
|
||||
0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
|
||||
0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
|
||||
0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
|
||||
0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
|
||||
0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
|
||||
0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
|
||||
0x82, 0xf0, 0x1e, 0x81,
|
||||
};
|
||||
|
||||
alignas(16) uint8_t state[RandenTraits::kStateBytes];
|
||||
std::memset(state, 0, sizeof(state));
|
||||
|
||||
RandenSlow::Generate(RandenSlow::GetKeys(), state);
|
||||
EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/randen.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::Randen;
|
||||
|
||||
TEST(RandenTest, CopyAndMove) {
|
||||
static_assert(std::is_copy_constructible<Randen>::value,
|
||||
"Randen must be copy constructible");
|
||||
|
||||
static_assert(absl::is_copy_assignable<Randen>::value,
|
||||
"Randen must be copy assignable");
|
||||
|
||||
static_assert(std::is_move_constructible<Randen>::value,
|
||||
"Randen must be move constructible");
|
||||
|
||||
static_assert(absl::is_move_assignable<Randen>::value,
|
||||
"Randen must be move assignable");
|
||||
}
|
||||
|
||||
TEST(RandenTest, Default) {
|
||||
constexpr uint8_t kGolden[] = {
|
||||
0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
|
||||
0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
|
||||
0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
|
||||
0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
|
||||
0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
|
||||
0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
|
||||
0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
|
||||
0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
|
||||
0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
|
||||
0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
|
||||
0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
|
||||
0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
|
||||
0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
|
||||
0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
|
||||
0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
|
||||
0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
|
||||
0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
|
||||
0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
|
||||
0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
|
||||
0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
|
||||
0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
|
||||
0x82, 0xf0, 0x1e, 0x81,
|
||||
};
|
||||
|
||||
alignas(16) uint8_t state[Randen::kStateBytes];
|
||||
std::memset(state, 0, sizeof(state));
|
||||
|
||||
Randen r;
|
||||
r.Generate(state);
|
||||
|
||||
EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
|
||||
#define ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
|
||||
|
||||
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
|
||||
// symbols from arbitrary system and other headers, since it may be built
|
||||
// with different flags from other targets, using different levels of
|
||||
// optimization, potentially introducing ODR violations.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// RANDen = RANDom generator or beetroots in Swiss High German.
|
||||
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
|
||||
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
|
||||
//
|
||||
// High-level summary:
|
||||
// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is
|
||||
// a sponge-like random generator that requires a cryptographic permutation.
|
||||
// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by
|
||||
// achieving backtracking resistance with only one Permute() per buffer.
|
||||
//
|
||||
// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round
|
||||
// Function" constructs up to 1024-bit permutations using an improved
|
||||
// Generalized Feistel network with 2-round AES-128 functions. This Feistel
|
||||
// block shuffle achieves diffusion faster and is less vulnerable to
|
||||
// sliced-biclique attacks than the Type-2 cyclic shuffle.
|
||||
//
|
||||
// 3) "Improving the Generalized Feistel" and "New criterion for diffusion
|
||||
// property" extends the same kind of improved Feistel block shuffle to 16
|
||||
// branches, which enables a 2048-bit permutation.
|
||||
//
|
||||
// Combine these three ideas and also change Simpira's subround keys from
|
||||
// structured/low-entropy counters to digits of Pi (or other random source).
|
||||
|
||||
// RandenTraits contains the basic algorithm traits, such as the size of the
|
||||
// state, seed, sponge, etc.
|
||||
struct RandenTraits {
|
||||
// Size of the entire sponge / state for the randen PRNG.
|
||||
static constexpr size_t kStateBytes = 256; // 2048-bit
|
||||
|
||||
// Size of the 'inner' (inaccessible) part of the sponge. Larger values would
|
||||
// require more frequent calls to RandenGenerate.
|
||||
static constexpr size_t kCapacityBytes = 16; // 128-bit
|
||||
|
||||
// Size of the default seed consumed by the sponge.
|
||||
static constexpr size_t kSeedBytes = kStateBytes - kCapacityBytes;
|
||||
|
||||
// Assuming 128-bit blocks, the number of blocks in the state.
|
||||
// Largest size for which security proofs are known.
|
||||
static constexpr size_t kFeistelBlocks = 16;
|
||||
|
||||
// Ensures SPRP security and two full subblock diffusions.
|
||||
// Must be > 4 * log2(kFeistelBlocks).
|
||||
static constexpr size_t kFeistelRounds = 16 + 1;
|
||||
|
||||
// Size of the key. A 128-bit key block is used for every-other
|
||||
// feistel block (Type-2 generalized Feistel network) in each round.
|
||||
static constexpr size_t kKeyBytes = 16 * kFeistelRounds * kFeistelBlocks / 2;
|
||||
};
|
||||
|
||||
// Randen key arrays. In randen_round_keys.cc
|
||||
extern const unsigned char kRandenRoundKeys[RandenTraits::kKeyBytes];
|
||||
extern const unsigned char kRandenRoundKeysBE[RandenTraits::kKeyBytes];
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
|
||||
#define ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/internal/seed_material.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// This class conforms to the C++ Standard "Seed Sequence" concept
|
||||
// [rand.req.seedseq].
|
||||
//
|
||||
// A `SaltedSeedSeq` is meant to wrap an existing seed sequence and modify
|
||||
// generated sequence by mixing with extra entropy. This entropy may be
|
||||
// build-dependent or process-dependent. The implementation may change to be
|
||||
// have either or both kinds of entropy. If salt is not available sequence is
|
||||
// not modified.
|
||||
template <typename SSeq>
|
||||
class SaltedSeedSeq {
|
||||
public:
|
||||
using inner_sequence_type = SSeq;
|
||||
using result_type = typename SSeq::result_type;
|
||||
|
||||
SaltedSeedSeq() : seq_(absl::make_unique<SSeq>()) {}
|
||||
|
||||
template <typename Iterator>
|
||||
SaltedSeedSeq(Iterator begin, Iterator end)
|
||||
: seq_(absl::make_unique<SSeq>(begin, end)) {}
|
||||
|
||||
template <typename T>
|
||||
SaltedSeedSeq(std::initializer_list<T> il)
|
||||
: SaltedSeedSeq(il.begin(), il.end()) {}
|
||||
|
||||
SaltedSeedSeq(const SaltedSeedSeq&) = delete;
|
||||
SaltedSeedSeq& operator=(const SaltedSeedSeq&) = delete;
|
||||
|
||||
SaltedSeedSeq(SaltedSeedSeq&&) = default;
|
||||
SaltedSeedSeq& operator=(SaltedSeedSeq&&) = default;
|
||||
|
||||
template <typename RandomAccessIterator>
|
||||
void generate(RandomAccessIterator begin, RandomAccessIterator end) {
|
||||
using U = typename std::iterator_traits<RandomAccessIterator>::value_type;
|
||||
|
||||
// The common case is that generate is called with ContiguousIterators
|
||||
// to uint arrays. Such contiguous memory regions may be optimized,
|
||||
// which we detect here.
|
||||
using TagType = absl::conditional_t<
|
||||
(std::is_same<U, uint32_t>::value &&
|
||||
(std::is_pointer<RandomAccessIterator>::value ||
|
||||
std::is_same<RandomAccessIterator,
|
||||
typename std::vector<U>::iterator>::value)),
|
||||
ContiguousAndUint32Tag, DefaultTag>;
|
||||
if (begin != end) {
|
||||
generate_impl(TagType{}, begin, end, std::distance(begin, end));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutIterator>
|
||||
void param(OutIterator out) const {
|
||||
seq_->param(out);
|
||||
}
|
||||
|
||||
size_t size() const { return seq_->size(); }
|
||||
|
||||
private:
|
||||
struct ContiguousAndUint32Tag {};
|
||||
struct DefaultTag {};
|
||||
|
||||
// Generate which requires the iterators are contiguous pointers to uint32_t.
|
||||
// Fills the initial seed buffer the underlying SSeq::generate() call,
|
||||
// then mixes in the salt material.
|
||||
template <typename Contiguous>
|
||||
void generate_impl(ContiguousAndUint32Tag, Contiguous begin, Contiguous end,
|
||||
size_t n) {
|
||||
seq_->generate(begin, end);
|
||||
const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0);
|
||||
auto span = absl::Span<uint32_t>(&*begin, n);
|
||||
MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), span);
|
||||
}
|
||||
|
||||
// The uncommon case for generate is that it is called with iterators over
|
||||
// some other buffer type which is assignable from a 32-bit value. In this
|
||||
// case we allocate a temporary 32-bit buffer and then copy-assign back
|
||||
// to the initial inputs.
|
||||
template <typename RandomAccessIterator>
|
||||
void generate_impl(DefaultTag, RandomAccessIterator begin,
|
||||
RandomAccessIterator, size_t n) {
|
||||
// Allocates a seed buffer of `n` elements, generates the seed, then
|
||||
// copies the result into the `out` iterator.
|
||||
absl::InlinedVector<uint32_t, 8> data(n, 0);
|
||||
generate_impl(ContiguousAndUint32Tag{}, data.begin(), data.end(), n);
|
||||
std::copy(data.begin(), data.end(), begin);
|
||||
}
|
||||
|
||||
// Because [rand.req.seedseq] is not required to be copy-constructible,
|
||||
// copy-assignable nor movable, we wrap it with unique pointer to be able
|
||||
// to move SaltedSeedSeq.
|
||||
std::unique_ptr<SSeq> seq_;
|
||||
};
|
||||
|
||||
// is_salted_seed_seq indicates whether the type is a SaltedSeedSeq.
|
||||
template <typename T, typename = void>
|
||||
struct is_salted_seed_seq : public std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_salted_seed_seq<
|
||||
T, typename std::enable_if<std::is_same<
|
||||
T, SaltedSeedSeq<typename T::inner_sequence_type>>::value>::type>
|
||||
: public std::true_type {};
|
||||
|
||||
// MakeSaltedSeedSeq returns a salted variant of the seed sequence.
|
||||
// When provided with an existing SaltedSeedSeq, returns the input parameter,
|
||||
// otherwise constructs a new SaltedSeedSeq which embodies the original
|
||||
// non-salted seed parameters.
|
||||
template <
|
||||
typename SSeq, //
|
||||
typename EnableIf = absl::enable_if_t<is_salted_seed_seq<SSeq>::value>>
|
||||
SSeq MakeSaltedSeedSeq(SSeq&& seq) {
|
||||
return SSeq(std::forward<SSeq>(seq));
|
||||
}
|
||||
|
||||
template <
|
||||
typename SSeq, //
|
||||
typename EnableIf = absl::enable_if_t<!is_salted_seed_seq<SSeq>::value>>
|
||||
SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) {
|
||||
using sseq_type = typename std::decay<SSeq>::type;
|
||||
using result_type = typename sseq_type::result_type;
|
||||
|
||||
absl::InlinedVector<result_type, 8> data;
|
||||
seq.param(std::back_inserter(data));
|
||||
return SaltedSeedSeq<sseq_type>(data.begin(), data.end());
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/salted_seed_seq.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using absl::random_internal::GetSaltMaterial;
|
||||
using absl::random_internal::MakeSaltedSeedSeq;
|
||||
using absl::random_internal::SaltedSeedSeq;
|
||||
using testing::Eq;
|
||||
using testing::Pointwise;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Sseq>
|
||||
void ConformsToInterface() {
|
||||
// Check that the SeedSequence can be default-constructed.
|
||||
{
|
||||
Sseq default_constructed_seq;
|
||||
}
|
||||
// Check that the SeedSequence can be constructed with two iterators.
|
||||
{
|
||||
uint32_t init_array[] = {1, 3, 5, 7, 9};
|
||||
Sseq iterator_constructed_seq(std::begin(init_array), std::end(init_array));
|
||||
}
|
||||
// Check that the SeedSequence can be std::initializer_list-constructed.
|
||||
{
|
||||
Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13};
|
||||
}
|
||||
// Check that param() and size() return state provided to constructor.
|
||||
{
|
||||
uint32_t init_array[] = {1, 2, 3, 4, 5};
|
||||
Sseq seq(std::begin(init_array), std::end(init_array));
|
||||
EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));
|
||||
|
||||
std::vector<uint32_t> state_vector;
|
||||
seq.param(std::back_inserter(state_vector));
|
||||
|
||||
EXPECT_EQ(state_vector.size(), ABSL_ARRAYSIZE(init_array));
|
||||
for (int i = 0; i < state_vector.size(); i++) {
|
||||
EXPECT_EQ(state_vector[i], i + 1);
|
||||
}
|
||||
}
|
||||
// Check for presence of generate() method.
|
||||
{
|
||||
Sseq seq;
|
||||
uint32_t seeds[5];
|
||||
|
||||
seq.generate(std::begin(seeds), std::end(seeds));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SaltedSeedSeq, CheckInterfaces) {
|
||||
// Control case
|
||||
ConformsToInterface<std::seed_seq>();
|
||||
|
||||
// Abseil classes
|
||||
ConformsToInterface<SaltedSeedSeq<std::seed_seq>>();
|
||||
}
|
||||
|
||||
TEST(SaltedSeedSeq, CheckConstructingFromOtherSequence) {
|
||||
std::vector<uint32_t> seed_values(10, 1);
|
||||
std::seed_seq seq(seed_values.begin(), seed_values.end());
|
||||
auto salted_seq = MakeSaltedSeedSeq(std::move(seq));
|
||||
|
||||
EXPECT_EQ(seq.size(), salted_seq.size());
|
||||
|
||||
std::vector<uint32_t> param_result;
|
||||
seq.param(std::back_inserter(param_result));
|
||||
|
||||
EXPECT_EQ(seed_values, param_result);
|
||||
}
|
||||
|
||||
TEST(SaltedSeedSeq, SaltedSaltedSeedSeqIsNotDoubleSalted) {
|
||||
uint32_t init[] = {1, 3, 5, 7, 9};
|
||||
|
||||
std::seed_seq seq(std::begin(init), std::end(init));
|
||||
|
||||
// The first salting.
|
||||
SaltedSeedSeq<std::seed_seq> salted_seq = MakeSaltedSeedSeq(std::move(seq));
|
||||
uint32_t a[16];
|
||||
salted_seq.generate(std::begin(a), std::end(a));
|
||||
|
||||
// The second salting.
|
||||
SaltedSeedSeq<std::seed_seq> salted_salted_seq =
|
||||
MakeSaltedSeedSeq(std::move(salted_seq));
|
||||
uint32_t b[16];
|
||||
salted_salted_seq.generate(std::begin(b), std::end(b));
|
||||
|
||||
// ... both should be equal.
|
||||
EXPECT_THAT(b, Pointwise(Eq(), a)) << "a[0] " << a[0];
|
||||
}
|
||||
|
||||
TEST(SaltedSeedSeq, SeedMaterialIsSalted) {
|
||||
const size_t kNumBlocks = 16;
|
||||
|
||||
uint32_t seed_material[kNumBlocks];
|
||||
std::random_device urandom{"/dev/urandom"};
|
||||
for (uint32_t& seed : seed_material) {
|
||||
seed = urandom();
|
||||
}
|
||||
|
||||
std::seed_seq seq(std::begin(seed_material), std::end(seed_material));
|
||||
SaltedSeedSeq<std::seed_seq> salted_seq(std::begin(seed_material),
|
||||
std::end(seed_material));
|
||||
|
||||
bool salt_is_available = GetSaltMaterial().has_value();
|
||||
|
||||
// If salt is available generated sequence should be different.
|
||||
if (salt_is_available) {
|
||||
uint32_t outputs[kNumBlocks];
|
||||
uint32_t salted_outputs[kNumBlocks];
|
||||
|
||||
seq.generate(std::begin(outputs), std::end(outputs));
|
||||
salted_seq.generate(std::begin(salted_outputs), std::end(salted_outputs));
|
||||
|
||||
EXPECT_THAT(outputs, Pointwise(testing::Ne(), salted_outputs));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SaltedSeedSeq, GenerateAcceptsDifferentTypes) {
|
||||
const size_t kNumBlocks = 4;
|
||||
|
||||
SaltedSeedSeq<std::seed_seq> seq({1, 2, 3});
|
||||
|
||||
uint32_t expected[kNumBlocks];
|
||||
seq.generate(std::begin(expected), std::end(expected));
|
||||
|
||||
// 32-bit outputs
|
||||
{
|
||||
unsigned long seed_material[kNumBlocks]; // NOLINT(runtime/int)
|
||||
seq.generate(std::begin(seed_material), std::end(seed_material));
|
||||
EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
|
||||
}
|
||||
{
|
||||
unsigned int seed_material[kNumBlocks]; // NOLINT(runtime/int)
|
||||
seq.generate(std::begin(seed_material), std::end(seed_material));
|
||||
EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
|
||||
}
|
||||
|
||||
// 64-bit outputs.
|
||||
{
|
||||
uint64_t seed_material[kNumBlocks];
|
||||
seq.generate(std::begin(seed_material), std::end(seed_material));
|
||||
EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
|
||||
}
|
||||
{
|
||||
int64_t seed_material[kNumBlocks];
|
||||
seq.generate(std::begin(seed_material), std::end(seed_material));
|
||||
EXPECT_THAT(seed_material, Pointwise(Eq(), expected));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/seed_material.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "absl/base/dynamic_annotations.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
|
||||
#if defined(__native_client__)
|
||||
|
||||
#include <nacl/nacl_random.h>
|
||||
#define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#define ABSL_RANDOM_USE_BCRYPT 1
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
|
||||
#elif defined(__Fuchsia__)
|
||||
#include <zircon/syscalls.h>
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBC__) && \
|
||||
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
|
||||
// glibc >= 2.25 has getentropy()
|
||||
#define ABSL_RANDOM_USE_GET_ENTROPY 1
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <sys/random.h>
|
||||
// Emscripten has getentropy, but it resides in a different header.
|
||||
#define ABSL_RANDOM_USE_GET_ENTROPY 1
|
||||
#endif
|
||||
|
||||
#if defined(ABSL_RANDOM_USE_BCRYPT)
|
||||
#include <bcrypt.h>
|
||||
|
||||
#ifndef BCRYPT_SUCCESS
|
||||
#define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
// Also link bcrypt; this can be done via linker options or:
|
||||
// #pragma comment(lib, "bcrypt.lib")
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
namespace {
|
||||
|
||||
// Read OS Entropy for random number seeds.
|
||||
// TODO(absl-team): Possibly place a cap on how much entropy may be read at a
|
||||
// time.
|
||||
|
||||
#if defined(ABSL_RANDOM_USE_BCRYPT)
|
||||
|
||||
// On Windows potentially use the BCRYPT CNG API to read available entropy.
|
||||
bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
|
||||
BCRYPT_ALG_HANDLE hProvider;
|
||||
NTSTATUS ret;
|
||||
ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM,
|
||||
MS_PRIMITIVE_PROVIDER, 0);
|
||||
if (!(BCRYPT_SUCCESS(ret))) {
|
||||
ABSL_RAW_LOG(ERROR, "Failed to open crypto provider.");
|
||||
return false;
|
||||
}
|
||||
ret = BCryptGenRandom(
|
||||
hProvider, // provider
|
||||
reinterpret_cast<UCHAR*>(values.data()), // buffer
|
||||
static_cast<ULONG>(sizeof(uint32_t) * values.size()), // bytes
|
||||
0); // flags
|
||||
BCryptCloseAlgorithmProvider(hProvider, 0);
|
||||
return BCRYPT_SUCCESS(ret);
|
||||
}
|
||||
|
||||
#elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM)
|
||||
|
||||
// On NaCL use nacl_secure_random to acquire bytes.
|
||||
bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
|
||||
auto buffer = reinterpret_cast<uint8_t*>(values.data());
|
||||
size_t buffer_size = sizeof(uint32_t) * values.size();
|
||||
|
||||
uint8_t* output_ptr = buffer;
|
||||
while (buffer_size > 0) {
|
||||
size_t nread = 0;
|
||||
const int error = nacl_secure_random(output_ptr, buffer_size, &nread);
|
||||
if (error != 0 || nread > buffer_size) {
|
||||
ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error);
|
||||
return false;
|
||||
}
|
||||
output_ptr += nread;
|
||||
buffer_size -= nread;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(__Fuchsia__)
|
||||
|
||||
bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
|
||||
auto buffer = reinterpret_cast<uint8_t*>(values.data());
|
||||
size_t buffer_size = sizeof(uint32_t) * values.size();
|
||||
zx_cprng_draw(buffer, buffer_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if defined(ABSL_RANDOM_USE_GET_ENTROPY)
|
||||
// On *nix, use getentropy() if supported. Note that libc may support
|
||||
// getentropy(), but the kernel may not, in which case this function will return
|
||||
// false.
|
||||
bool ReadSeedMaterialFromGetEntropy(absl::Span<uint32_t> values) {
|
||||
auto buffer = reinterpret_cast<uint8_t*>(values.data());
|
||||
size_t buffer_size = sizeof(uint32_t) * values.size();
|
||||
while (buffer_size > 0) {
|
||||
// getentropy() has a maximum permitted length of 256.
|
||||
size_t to_read = std::min<size_t>(buffer_size, 256);
|
||||
int result = getentropy(buffer, to_read);
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
// https://github.com/google/sanitizers/issues/1173
|
||||
// MemorySanitizer can't see through getentropy().
|
||||
ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, to_read);
|
||||
buffer += to_read;
|
||||
buffer_size -= to_read;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // defined(ABSL_RANDOM_GETENTROPY)
|
||||
|
||||
// On *nix, read entropy from /dev/urandom.
|
||||
bool ReadSeedMaterialFromDevURandom(absl::Span<uint32_t> values) {
|
||||
const char kEntropyFile[] = "/dev/urandom";
|
||||
|
||||
auto buffer = reinterpret_cast<uint8_t*>(values.data());
|
||||
size_t buffer_size = sizeof(uint32_t) * values.size();
|
||||
|
||||
int dev_urandom = open(kEntropyFile, O_RDONLY);
|
||||
bool success = (-1 != dev_urandom);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (success && buffer_size > 0) {
|
||||
ssize_t bytes_read = read(dev_urandom, buffer, buffer_size);
|
||||
int read_error = errno;
|
||||
success = (bytes_read > 0);
|
||||
if (success) {
|
||||
buffer += bytes_read;
|
||||
buffer_size -= static_cast<size_t>(bytes_read);
|
||||
} else if (bytes_read == -1 && read_error == EINTR) {
|
||||
success = true; // Need to try again.
|
||||
}
|
||||
}
|
||||
close(dev_urandom);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
|
||||
#if defined(ABSL_RANDOM_USE_GET_ENTROPY)
|
||||
if (ReadSeedMaterialFromGetEntropy(values)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
// Libc may support getentropy, but the kernel may not, so we still have
|
||||
// to fallback to ReadSeedMaterialFromDevURandom().
|
||||
return ReadSeedMaterialFromDevURandom(values);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) {
|
||||
assert(values.data() != nullptr);
|
||||
if (values.data() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (values.empty()) {
|
||||
return true;
|
||||
}
|
||||
return ReadSeedMaterialFromOSEntropyImpl(values);
|
||||
}
|
||||
|
||||
void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
|
||||
absl::Span<uint32_t> seed_material) {
|
||||
// Algorithm is based on code available at
|
||||
// https://gist.github.com/imneme/540829265469e673d045
|
||||
constexpr uint32_t kInitVal = 0x43b0d7e5;
|
||||
constexpr uint32_t kHashMul = 0x931e8875;
|
||||
constexpr uint32_t kMixMulL = 0xca01f9dd;
|
||||
constexpr uint32_t kMixMulR = 0x4973f715;
|
||||
constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2;
|
||||
|
||||
uint32_t hash_const = kInitVal;
|
||||
auto hash = [&](uint32_t value) {
|
||||
value ^= hash_const;
|
||||
hash_const *= kHashMul;
|
||||
value *= hash_const;
|
||||
value ^= value >> kShiftSize;
|
||||
return value;
|
||||
};
|
||||
|
||||
auto mix = [&](uint32_t x, uint32_t y) {
|
||||
uint32_t result = kMixMulL * x - kMixMulR * y;
|
||||
result ^= result >> kShiftSize;
|
||||
return result;
|
||||
};
|
||||
|
||||
for (const auto& seq_val : sequence) {
|
||||
for (auto& elem : seed_material) {
|
||||
elem = mix(elem, hash(seq_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> GetSaltMaterial() {
|
||||
// Salt must be common for all generators within the same process so read it
|
||||
// only once and store in static variable.
|
||||
static const auto salt_material = []() -> absl::optional<uint32_t> {
|
||||
uint32_t salt_value = 0;
|
||||
|
||||
if (random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
MakeSpan(&salt_value, 1))) {
|
||||
return salt_value;
|
||||
}
|
||||
|
||||
return absl::nullopt;
|
||||
}();
|
||||
|
||||
return salt_material;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
|
||||
#define ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/random/internal/fast_uniform_bits.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// Returns the number of 32-bit blocks needed to contain the given number of
|
||||
// bits.
|
||||
constexpr size_t SeedBitsToBlocks(size_t seed_size) {
|
||||
return (seed_size + 31) / 32;
|
||||
}
|
||||
|
||||
// Amount of entropy (measured in bits) used to instantiate a Seed Sequence,
|
||||
// with which to create a URBG.
|
||||
constexpr size_t kEntropyBitsNeeded = 256;
|
||||
|
||||
// Amount of entropy (measured in 32-bit blocks) used to instantiate a Seed
|
||||
// Sequence, with which to create a URBG.
|
||||
constexpr size_t kEntropyBlocksNeeded =
|
||||
random_internal::SeedBitsToBlocks(kEntropyBitsNeeded);
|
||||
|
||||
static_assert(kEntropyBlocksNeeded > 0,
|
||||
"Entropy used to seed URBGs must be nonzero.");
|
||||
|
||||
// Attempts to fill a span of uint32_t-values using an OS-provided source of
|
||||
// true entropy (eg. /dev/urandom) into an array of uint32_t blocks of data. The
|
||||
// resulting array may be used to initialize an instance of a class conforming
|
||||
// to the C++ Standard "Seed Sequence" concept [rand.req.seedseq].
|
||||
//
|
||||
// If values.data() == nullptr, the behavior is undefined.
|
||||
ABSL_MUST_USE_RESULT
|
||||
bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values);
|
||||
|
||||
// Attempts to fill a span of uint32_t-values using variates generated by an
|
||||
// existing instance of a class conforming to the C++ Standard "Uniform Random
|
||||
// Bit Generator" concept [rand.req.urng]. The resulting data may be used to
|
||||
// initialize an instance of a class conforming to the C++ Standard
|
||||
// "Seed Sequence" concept [rand.req.seedseq].
|
||||
//
|
||||
// If urbg == nullptr or values.data() == nullptr, the behavior is undefined.
|
||||
template <typename URBG>
|
||||
ABSL_MUST_USE_RESULT bool ReadSeedMaterialFromURBG(
|
||||
URBG* urbg, absl::Span<uint32_t> values) {
|
||||
random_internal::FastUniformBits<uint32_t> distr;
|
||||
|
||||
assert(urbg != nullptr && values.data() != nullptr);
|
||||
if (urbg == nullptr || values.data() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t& seed_value : values) {
|
||||
seed_value = distr(*urbg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mixes given sequence of values with into given sequence of seed material.
|
||||
// Time complexity of this function is O(sequence.size() *
|
||||
// seed_material.size()).
|
||||
//
|
||||
// Algorithm is based on code available at
|
||||
// https://gist.github.com/imneme/540829265469e673d045
|
||||
// by Melissa O'Neill.
|
||||
void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
|
||||
absl::Span<uint32_t> seed_material);
|
||||
|
||||
// Returns salt value.
|
||||
//
|
||||
// Salt is obtained only once and stored in static variable.
|
||||
//
|
||||
// May return empty value if optaining the salt was not possible.
|
||||
absl::optional<uint32_t> GetSaltMaterial();
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/seed_material.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Android assert messages only go to system log, so death tests cannot inspect
|
||||
// the message for matching.
|
||||
#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
EXPECT_DEATH_IF_SUPPORTED(statement, ".*")
|
||||
#else
|
||||
#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||
EXPECT_DEATH_IF_SUPPORTED(statement, regex)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using testing::Each;
|
||||
using testing::ElementsAre;
|
||||
using testing::Eq;
|
||||
using testing::Ne;
|
||||
using testing::Pointwise;
|
||||
|
||||
TEST(SeedBitsToBlocks, VerifyCases) {
|
||||
EXPECT_EQ(0, absl::random_internal::SeedBitsToBlocks(0));
|
||||
EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(1));
|
||||
EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(31));
|
||||
EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(32));
|
||||
EXPECT_EQ(2, absl::random_internal::SeedBitsToBlocks(33));
|
||||
EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(127));
|
||||
EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(128));
|
||||
EXPECT_EQ(5, absl::random_internal::SeedBitsToBlocks(129));
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromOSEntropy, SuccessiveReadsAreDistinct) {
|
||||
constexpr size_t kSeedMaterialSize = 64;
|
||||
uint32_t seed_material_1[kSeedMaterialSize] = {};
|
||||
uint32_t seed_material_2[kSeedMaterialSize] = {};
|
||||
|
||||
EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::Span<uint32_t>(seed_material_1, kSeedMaterialSize)));
|
||||
EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::Span<uint32_t>(seed_material_2, kSeedMaterialSize)));
|
||||
|
||||
EXPECT_THAT(seed_material_1, Pointwise(Ne(), seed_material_2));
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromOSEntropy, ReadZeroBytesIsNoOp) {
|
||||
uint32_t seed_material[32] = {};
|
||||
std::memset(seed_material, 0xAA, sizeof(seed_material));
|
||||
EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::Span<uint32_t>(seed_material, 0)));
|
||||
|
||||
EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA)));
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromOSEntropy, NullPtrVectorArgument) {
|
||||
#ifdef NDEBUG
|
||||
EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::Span<uint32_t>(nullptr, 32)));
|
||||
#else
|
||||
bool result;
|
||||
ABSL_EXPECT_DEATH_IF_SUPPORTED(
|
||||
result = absl::random_internal::ReadSeedMaterialFromOSEntropy(
|
||||
absl::Span<uint32_t>(nullptr, 32)),
|
||||
"!= nullptr");
|
||||
(void)result; // suppress unused-variable warning
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromURBG, SeedMaterialEqualsVariateSequence) {
|
||||
// Two default-constructed instances of std::mt19937_64 are guaranteed to
|
||||
// produce equal variate-sequences.
|
||||
std::mt19937 urbg_1;
|
||||
std::mt19937 urbg_2;
|
||||
constexpr size_t kSeedMaterialSize = 1024;
|
||||
uint32_t seed_material[kSeedMaterialSize] = {};
|
||||
|
||||
EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG(
|
||||
&urbg_1, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)));
|
||||
for (uint32_t seed : seed_material) {
|
||||
EXPECT_EQ(seed, urbg_2());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromURBG, ReadZeroBytesIsNoOp) {
|
||||
std::mt19937_64 urbg;
|
||||
uint32_t seed_material[32];
|
||||
std::memset(seed_material, 0xAA, sizeof(seed_material));
|
||||
EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG(
|
||||
&urbg, absl::Span<uint32_t>(seed_material, 0)));
|
||||
|
||||
EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA)));
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromURBG, NullUrbgArgument) {
|
||||
constexpr size_t kSeedMaterialSize = 32;
|
||||
uint32_t seed_material[kSeedMaterialSize];
|
||||
#ifdef NDEBUG
|
||||
EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>(
|
||||
nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)));
|
||||
#else
|
||||
bool result;
|
||||
ABSL_EXPECT_DEATH_IF_SUPPORTED(
|
||||
result = absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>(
|
||||
nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)),
|
||||
"!= nullptr");
|
||||
(void)result; // suppress unused-variable warning
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ReadSeedMaterialFromURBG, NullPtrVectorArgument) {
|
||||
std::mt19937_64 urbg;
|
||||
#ifdef NDEBUG
|
||||
EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG(
|
||||
&urbg, absl::Span<uint32_t>(nullptr, 32)));
|
||||
#else
|
||||
bool result;
|
||||
ABSL_EXPECT_DEATH_IF_SUPPORTED(
|
||||
result = absl::random_internal::ReadSeedMaterialFromURBG(
|
||||
&urbg, absl::Span<uint32_t>(nullptr, 32)),
|
||||
"!= nullptr");
|
||||
(void)result; // suppress unused-variable warning
|
||||
#endif
|
||||
}
|
||||
|
||||
// The avalanche effect is a desirable cryptographic property of hashes in which
|
||||
// changing a single bit in the input causes each bit of the output to be
|
||||
// changed with probability near 50%.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Avalanche_effect
|
||||
|
||||
TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitLong) {
|
||||
std::vector<uint32_t> seed_material = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
// For every 32-bit number with exactly one bit set, verify the avalanche
|
||||
// effect holds. In order to reduce flakiness of tests, accept values
|
||||
// anywhere in the range of 30%-70%.
|
||||
for (uint32_t v = 1; v != 0; v <<= 1) {
|
||||
std::vector<uint32_t> seed_material_copy = seed_material;
|
||||
absl::random_internal::MixIntoSeedMaterial(
|
||||
absl::Span<uint32_t>(&v, 1),
|
||||
absl::Span<uint32_t>(seed_material_copy.data(),
|
||||
seed_material_copy.size()));
|
||||
|
||||
uint32_t changed_bits = 0;
|
||||
for (size_t i = 0; i < seed_material.size(); i++) {
|
||||
std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^
|
||||
seed_material_copy[i]);
|
||||
changed_bits += bitset.count();
|
||||
}
|
||||
|
||||
EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size());
|
||||
EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitShort) {
|
||||
std::vector<uint32_t> seed_material = {1};
|
||||
|
||||
// For every 32-bit number with exactly one bit set, verify the avalanche
|
||||
// effect holds. In order to reduce flakiness of tests, accept values
|
||||
// anywhere in the range of 30%-70%.
|
||||
for (uint32_t v = 1; v != 0; v <<= 1) {
|
||||
std::vector<uint32_t> seed_material_copy = seed_material;
|
||||
absl::random_internal::MixIntoSeedMaterial(
|
||||
absl::Span<uint32_t>(&v, 1),
|
||||
absl::Span<uint32_t>(seed_material_copy.data(),
|
||||
seed_material_copy.size()));
|
||||
|
||||
uint32_t changed_bits = 0;
|
||||
for (size_t i = 0; i < seed_material.size(); i++) {
|
||||
std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^
|
||||
seed_material_copy[i]);
|
||||
changed_bits += bitset.count();
|
||||
}
|
||||
|
||||
EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size());
|
||||
EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
|
||||
#define ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// `sequence_urbg` is a simple random number generator which meets the
|
||||
// requirements of [rand.req.urbg], and is solely for testing absl
|
||||
// distributions.
|
||||
class sequence_urbg {
|
||||
public:
|
||||
using result_type = uint64_t;
|
||||
|
||||
static constexpr result_type(min)() {
|
||||
return (std::numeric_limits<result_type>::min)();
|
||||
}
|
||||
static constexpr result_type(max)() {
|
||||
return (std::numeric_limits<result_type>::max)();
|
||||
}
|
||||
|
||||
sequence_urbg(std::initializer_list<result_type> data) : i_(0), data_(data) {}
|
||||
void reset() { i_ = 0; }
|
||||
|
||||
result_type operator()() { return data_[i_++ % data_.size()]; }
|
||||
|
||||
size_t invocations() const { return i_; }
|
||||
|
||||
private:
|
||||
size_t i_;
|
||||
std::vector<result_type> data_;
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
|
||||
149
TMessagesProj/jni/voip/webrtc/absl/random/internal/traits.h
Normal file
149
TMessagesProj/jni/voip/webrtc/absl/random/internal/traits.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_TRAITS_H_
|
||||
#define ABSL_RANDOM_INTERNAL_TRAITS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// random_internal::is_widening_convertible<A, B>
|
||||
//
|
||||
// Returns whether a type A is widening-convertible to a type B.
|
||||
//
|
||||
// A is widening-convertible to B means:
|
||||
// A a = <any number>;
|
||||
// B b = a;
|
||||
// A c = b;
|
||||
// EXPECT_EQ(a, c);
|
||||
template <typename A, typename B>
|
||||
class is_widening_convertible {
|
||||
// As long as there are enough bits in the exact part of a number:
|
||||
// - unsigned can fit in float, signed, unsigned
|
||||
// - signed can fit in float, signed
|
||||
// - float can fit in float
|
||||
// So we define rank to be:
|
||||
// - rank(float) -> 2
|
||||
// - rank(signed) -> 1
|
||||
// - rank(unsigned) -> 0
|
||||
template <class T>
|
||||
static constexpr int rank() {
|
||||
return !std::numeric_limits<T>::is_integer +
|
||||
std::numeric_limits<T>::is_signed;
|
||||
}
|
||||
|
||||
public:
|
||||
// If an arithmetic-type B can represent at least as many digits as a type A,
|
||||
// and B belongs to a rank no lower than A, then A can be safely represented
|
||||
// by B through a widening-conversion.
|
||||
static constexpr bool value =
|
||||
std::numeric_limits<A>::digits <= std::numeric_limits<B>::digits &&
|
||||
rank<A>() <= rank<B>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsIntegral : std::is_integral<T> {};
|
||||
template <>
|
||||
struct IsIntegral<absl::int128> : std::true_type {};
|
||||
template <>
|
||||
struct IsIntegral<absl::uint128> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct MakeUnsigned : std::make_unsigned<T> {};
|
||||
template <>
|
||||
struct MakeUnsigned<absl::int128> {
|
||||
using type = absl::uint128;
|
||||
};
|
||||
template <>
|
||||
struct MakeUnsigned<absl::uint128> {
|
||||
using type = absl::uint128;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsUnsigned : std::is_unsigned<T> {};
|
||||
template <>
|
||||
struct IsUnsigned<absl::int128> : std::false_type {};
|
||||
template <>
|
||||
struct IsUnsigned<absl::uint128> : std::true_type {};
|
||||
|
||||
// unsigned_bits<N>::type returns the unsigned int type with the indicated
|
||||
// number of bits.
|
||||
template <size_t N>
|
||||
struct unsigned_bits;
|
||||
|
||||
template <>
|
||||
struct unsigned_bits<8> {
|
||||
using type = uint8_t;
|
||||
};
|
||||
template <>
|
||||
struct unsigned_bits<16> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
template <>
|
||||
struct unsigned_bits<32> {
|
||||
using type = uint32_t;
|
||||
};
|
||||
template <>
|
||||
struct unsigned_bits<64> {
|
||||
using type = uint64_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unsigned_bits<128> {
|
||||
using type = absl::uint128;
|
||||
};
|
||||
|
||||
// 256-bit wrapper for wide multiplications.
|
||||
struct U256 {
|
||||
uint128 hi;
|
||||
uint128 lo;
|
||||
};
|
||||
template <>
|
||||
struct unsigned_bits<256> {
|
||||
using type = U256;
|
||||
};
|
||||
|
||||
template <typename IntType>
|
||||
struct make_unsigned_bits {
|
||||
using type = typename unsigned_bits<
|
||||
std::numeric_limits<typename MakeUnsigned<IntType>::type>::digits>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
int BitWidth(T v) {
|
||||
// Workaround for bit_width not supporting int128.
|
||||
// Don't hardcode `64` to make sure this code does not trigger compiler
|
||||
// warnings in smaller types.
|
||||
constexpr int half_bits = sizeof(T) * 8 / 2;
|
||||
if (sizeof(T) == 16 && (v >> half_bits) != 0) {
|
||||
return bit_width(static_cast<uint64_t>(v >> half_bits)) + half_bits;
|
||||
} else {
|
||||
return bit_width(static_cast<uint64_t>(v));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_TRAITS_H_
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/traits.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::random_internal::is_widening_convertible;
|
||||
|
||||
// CheckWideningConvertsToSelf<T1, T2, ...>()
|
||||
//
|
||||
// For each type T, checks:
|
||||
// - T IS widening-convertible to itself.
|
||||
//
|
||||
template <typename T>
|
||||
void CheckWideningConvertsToSelf() {
|
||||
static_assert(is_widening_convertible<T, T>::value,
|
||||
"Type is not convertible to self!");
|
||||
}
|
||||
|
||||
template <typename T, typename Next, typename... Args>
|
||||
void CheckWideningConvertsToSelf() {
|
||||
CheckWideningConvertsToSelf<T>();
|
||||
CheckWideningConvertsToSelf<Next, Args...>();
|
||||
}
|
||||
|
||||
// CheckNotWideningConvertibleWithSigned<T1, T2, ...>()
|
||||
//
|
||||
// For each unsigned-type T, checks that:
|
||||
// - T is NOT widening-convertible to Signed(T)
|
||||
// - Signed(T) is NOT widening-convertible to T
|
||||
//
|
||||
template <typename T>
|
||||
void CheckNotWideningConvertibleWithSigned() {
|
||||
using signed_t = typename std::make_signed<T>::type;
|
||||
|
||||
static_assert(!is_widening_convertible<T, signed_t>::value,
|
||||
"Unsigned type is convertible to same-sized signed-type!");
|
||||
static_assert(!is_widening_convertible<signed_t, T>::value,
|
||||
"Signed type is convertible to same-sized unsigned-type!");
|
||||
}
|
||||
|
||||
template <typename T, typename Next, typename... Args>
|
||||
void CheckNotWideningConvertibleWithSigned() {
|
||||
CheckNotWideningConvertibleWithSigned<T>();
|
||||
CheckWideningConvertsToSelf<Next, Args...>();
|
||||
}
|
||||
|
||||
// CheckWideningConvertsToLargerType<T1, T2, ...>()
|
||||
//
|
||||
// For each successive unsigned-types {Ti, Ti+1}, checks that:
|
||||
// - Ti IS widening-convertible to Ti+1
|
||||
// - Ti IS widening-convertible to Signed(Ti+1)
|
||||
// - Signed(Ti) is NOT widening-convertible to Ti
|
||||
// - Signed(Ti) IS widening-convertible to Ti+1
|
||||
template <typename T, typename Higher>
|
||||
void CheckWideningConvertsToLargerTypes() {
|
||||
using signed_t = typename std::make_signed<T>::type;
|
||||
using higher_t = Higher;
|
||||
using signed_higher_t = typename std::make_signed<Higher>::type;
|
||||
|
||||
static_assert(is_widening_convertible<T, higher_t>::value,
|
||||
"Type not embeddable into larger type!");
|
||||
static_assert(is_widening_convertible<T, signed_higher_t>::value,
|
||||
"Type not embeddable into larger signed type!");
|
||||
static_assert(!is_widening_convertible<signed_t, higher_t>::value,
|
||||
"Signed type is embeddable into larger unsigned type!");
|
||||
static_assert(is_widening_convertible<signed_t, signed_higher_t>::value,
|
||||
"Signed type not embeddable into larger signed type!");
|
||||
}
|
||||
|
||||
template <typename T, typename Higher, typename Next, typename... Args>
|
||||
void CheckWideningConvertsToLargerTypes() {
|
||||
CheckWideningConvertsToLargerTypes<T, Higher>();
|
||||
CheckWideningConvertsToLargerTypes<Higher, Next, Args...>();
|
||||
}
|
||||
|
||||
// CheckWideningConvertsTo<T, U, [expect]>
|
||||
//
|
||||
// Checks that T DOES widening-convert to U.
|
||||
// If "expect" is false, then asserts that T does NOT widening-convert to U.
|
||||
template <typename T, typename U, bool expect = true>
|
||||
void CheckWideningConvertsTo() {
|
||||
static_assert(is_widening_convertible<T, U>::value == expect,
|
||||
"Unexpected result for is_widening_convertible<T, U>!");
|
||||
}
|
||||
|
||||
TEST(TraitsTest, IsWideningConvertibleTest) {
|
||||
constexpr bool kInvalid = false;
|
||||
|
||||
CheckWideningConvertsToSelf<uint8_t, uint16_t, uint32_t, uint64_t, int8_t,
|
||||
int16_t, int32_t, int64_t, float, double>();
|
||||
CheckNotWideningConvertibleWithSigned<uint8_t, uint16_t, uint32_t,
|
||||
uint64_t>();
|
||||
CheckWideningConvertsToLargerTypes<uint8_t, uint16_t, uint32_t, uint64_t>();
|
||||
|
||||
CheckWideningConvertsTo<float, double>();
|
||||
CheckWideningConvertsTo<uint16_t, float>();
|
||||
CheckWideningConvertsTo<uint32_t, double>();
|
||||
CheckWideningConvertsTo<uint64_t, double, kInvalid>();
|
||||
CheckWideningConvertsTo<double, float, kInvalid>();
|
||||
|
||||
CheckWideningConvertsTo<bool, int>();
|
||||
CheckWideningConvertsTo<bool, float>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
#ifndef ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
|
||||
#define ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/internal/traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
template <typename IntType>
|
||||
class uniform_int_distribution;
|
||||
|
||||
template <typename RealType>
|
||||
class uniform_real_distribution;
|
||||
|
||||
// Interval tag types which specify whether the interval is open or closed
|
||||
// on either boundary.
|
||||
|
||||
namespace random_internal {
|
||||
template <typename T>
|
||||
struct TagTypeCompare {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) {
|
||||
// Tags are mono-states. They always compare equal.
|
||||
return true;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace random_internal
|
||||
|
||||
struct IntervalClosedClosedTag
|
||||
: public random_internal::TagTypeCompare<IntervalClosedClosedTag> {};
|
||||
struct IntervalClosedOpenTag
|
||||
: public random_internal::TagTypeCompare<IntervalClosedOpenTag> {};
|
||||
struct IntervalOpenClosedTag
|
||||
: public random_internal::TagTypeCompare<IntervalOpenClosedTag> {};
|
||||
struct IntervalOpenOpenTag
|
||||
: public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
|
||||
|
||||
namespace random_internal {
|
||||
|
||||
// In the absence of an explicitly provided return-type, the template
|
||||
// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
|
||||
// the data-types of the endpoint-arguments {A lo, B hi}.
|
||||
//
|
||||
// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the
|
||||
// return-type, if one type can be implicitly converted into the other, in a
|
||||
// lossless way. The template "is_widening_convertible" implements the
|
||||
// compile-time logic for deciding if such a conversion is possible.
|
||||
//
|
||||
// If no such conversion between {A, B} exists, then the overload for
|
||||
// absl::Uniform() will be discarded, and the call will be ill-formed.
|
||||
// Return-type for absl::Uniform() when the return-type is inferred.
|
||||
template <typename A, typename B>
|
||||
using uniform_inferred_return_t =
|
||||
absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>,
|
||||
is_widening_convertible<B, A>>::value,
|
||||
typename std::conditional<
|
||||
is_widening_convertible<A, B>::value, B, A>::type>;
|
||||
|
||||
// The functions
|
||||
// uniform_lower_bound(tag, a, b)
|
||||
// and
|
||||
// uniform_upper_bound(tag, a, b)
|
||||
// are used as implementation-details for absl::Uniform().
|
||||
//
|
||||
// Conceptually,
|
||||
// [a, b] == [uniform_lower_bound(IntervalClosedClosed, a, b),
|
||||
// uniform_upper_bound(IntervalClosedClosed, a, b)]
|
||||
// (a, b) == [uniform_lower_bound(IntervalOpenOpen, a, b),
|
||||
// uniform_upper_bound(IntervalOpenOpen, a, b)]
|
||||
// [a, b) == [uniform_lower_bound(IntervalClosedOpen, a, b),
|
||||
// uniform_upper_bound(IntervalClosedOpen, a, b)]
|
||||
// (a, b] == [uniform_lower_bound(IntervalOpenClosed, a, b),
|
||||
// uniform_upper_bound(IntervalOpenClosed, a, b)]
|
||||
//
|
||||
template <typename IntType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
IsIntegral<IntType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
|
||||
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
|
||||
IntType>
|
||||
uniform_lower_bound(Tag, IntType a, IntType) {
|
||||
return a < (std::numeric_limits<IntType>::max)() ? (a + 1) : a;
|
||||
}
|
||||
|
||||
template <typename FloatType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
std::is_floating_point<FloatType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
|
||||
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
|
||||
FloatType>
|
||||
uniform_lower_bound(Tag, FloatType a, FloatType b) {
|
||||
return std::nextafter(a, b);
|
||||
}
|
||||
|
||||
template <typename NumType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
|
||||
std::is_same<Tag, IntervalClosedOpenTag>>::value,
|
||||
NumType>
|
||||
uniform_lower_bound(Tag, NumType a, NumType) {
|
||||
return a;
|
||||
}
|
||||
|
||||
template <typename IntType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
IsIntegral<IntType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
|
||||
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
|
||||
IntType>
|
||||
uniform_upper_bound(Tag, IntType, IntType b) {
|
||||
return b > (std::numeric_limits<IntType>::min)() ? (b - 1) : b;
|
||||
}
|
||||
|
||||
template <typename FloatType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
std::is_floating_point<FloatType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
|
||||
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
|
||||
FloatType>
|
||||
uniform_upper_bound(Tag, FloatType, FloatType b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename IntType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
IsIntegral<IntType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
|
||||
std::is_same<Tag, IntervalOpenClosedTag>>>::value,
|
||||
IntType>
|
||||
uniform_upper_bound(Tag, IntType, IntType b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename FloatType, typename Tag>
|
||||
typename absl::enable_if_t<
|
||||
absl::conjunction<
|
||||
std::is_floating_point<FloatType>,
|
||||
absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
|
||||
std::is_same<Tag, IntervalOpenClosedTag>>>::value,
|
||||
FloatType>
|
||||
uniform_upper_bound(Tag, FloatType, FloatType b) {
|
||||
return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
|
||||
}
|
||||
|
||||
// Returns whether the bounds are valid for the underlying distribution.
|
||||
// Inputs must have already been resolved via uniform_*_bound calls.
|
||||
//
|
||||
// The c++ standard constraints in [rand.dist.uni.int] are listed as:
|
||||
// requires: lo <= hi.
|
||||
//
|
||||
// In the uniform_int_distrubtion, {lo, hi} are closed, closed. Thus:
|
||||
// [0, 0] is legal.
|
||||
// [0, 0) is not legal, but [0, 1) is, which translates to [0, 0].
|
||||
// (0, 1) is not legal, but (0, 2) is, which translates to [1, 1].
|
||||
// (0, 0] is not legal, but (0, 1] is, which translates to [1, 1].
|
||||
//
|
||||
// The c++ standard constraints in [rand.dist.uni.real] are listed as:
|
||||
// requires: lo <= hi.
|
||||
// requires: (hi - lo) <= numeric_limits<T>::max()
|
||||
//
|
||||
// In the uniform_real_distribution, {lo, hi} are closed, open, Thus:
|
||||
// [0, 0] is legal, which is [0, 0+epsilon).
|
||||
// [0, 0) is legal.
|
||||
// (0, 0) is not legal, but (0-epsilon, 0+epsilon) is.
|
||||
// (0, 0] is not legal, but (0, 0+epsilon] is.
|
||||
//
|
||||
template <typename FloatType>
|
||||
absl::enable_if_t<std::is_floating_point<FloatType>::value, bool>
|
||||
is_uniform_range_valid(FloatType a, FloatType b) {
|
||||
return a <= b && std::isfinite(b - a);
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
absl::enable_if_t<IsIntegral<IntType>::value, bool> is_uniform_range_valid(
|
||||
IntType a, IntType b) {
|
||||
return a <= b;
|
||||
}
|
||||
|
||||
// UniformDistribution selects either absl::uniform_int_distribution
|
||||
// or absl::uniform_real_distribution depending on the NumType parameter.
|
||||
template <typename NumType>
|
||||
using UniformDistribution =
|
||||
typename std::conditional<IsIntegral<NumType>::value,
|
||||
absl::uniform_int_distribution<NumType>,
|
||||
absl::uniform_real_distribution<NumType>>::type;
|
||||
|
||||
// UniformDistributionWrapper is used as the underlying distribution type
|
||||
// by the absl::Uniform template function. It selects the proper Abseil
|
||||
// uniform distribution and provides constructor overloads that match the
|
||||
// expected parameter order as well as adjusting distribution bounds based
|
||||
// on the tag.
|
||||
template <typename NumType>
|
||||
struct UniformDistributionWrapper : public UniformDistribution<NumType> {
|
||||
template <typename TagType>
|
||||
explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi)
|
||||
: UniformDistribution<NumType>(
|
||||
uniform_lower_bound<NumType>(TagType{}, lo, hi),
|
||||
uniform_upper_bound<NumType>(TagType{}, lo, hi)) {}
|
||||
|
||||
explicit UniformDistributionWrapper(NumType lo, NumType hi)
|
||||
: UniformDistribution<NumType>(
|
||||
uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi),
|
||||
uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {}
|
||||
|
||||
explicit UniformDistributionWrapper()
|
||||
: UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(),
|
||||
(std::numeric_limits<NumType>::max)()) {}
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/uniform_helper.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using absl::IntervalClosedClosedTag;
|
||||
using absl::IntervalClosedOpenTag;
|
||||
using absl::IntervalOpenClosedTag;
|
||||
using absl::IntervalOpenOpenTag;
|
||||
using absl::random_internal::uniform_inferred_return_t;
|
||||
using absl::random_internal::uniform_lower_bound;
|
||||
using absl::random_internal::uniform_upper_bound;
|
||||
|
||||
class UniformHelperTest : public testing::Test {};
|
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsGeneral) {
|
||||
constexpr IntervalClosedClosedTag IntervalClosedClosed;
|
||||
constexpr IntervalClosedOpenTag IntervalClosedOpen;
|
||||
constexpr IntervalOpenClosedTag IntervalOpenClosed;
|
||||
constexpr IntervalOpenOpenTag IntervalOpenOpen;
|
||||
|
||||
// absl::uniform_int_distribution natively assumes IntervalClosedClosed
|
||||
// absl::uniform_real_distribution natively assumes IntervalClosedOpen
|
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1);
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1);
|
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0);
|
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0);
|
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0);
|
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0);
|
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0);
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0);
|
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0);
|
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0);
|
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0);
|
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0);
|
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99);
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99);
|
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0);
|
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100);
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100);
|
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0);
|
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0);
|
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0);
|
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0);
|
||||
|
||||
// Negative value tests
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99);
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99);
|
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0);
|
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0);
|
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0);
|
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0);
|
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100);
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100);
|
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0);
|
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0);
|
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0),
|
||||
-2.0);
|
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0);
|
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2);
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2);
|
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0);
|
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0);
|
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1);
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1);
|
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0);
|
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0);
|
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0);
|
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0),
|
||||
-1.0);
|
||||
|
||||
EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0);
|
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0);
|
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0);
|
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0);
|
||||
}
|
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsIntBounds) {
|
||||
// Verifies the saturating nature of uniform_lower_bound and
|
||||
// uniform_upper_bound
|
||||
constexpr IntervalOpenOpenTag IntervalOpenOpen;
|
||||
|
||||
// uint max.
|
||||
constexpr auto m = (std::numeric_limits<uint64_t>::max)();
|
||||
|
||||
EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0u, 0u));
|
||||
EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m, m));
|
||||
EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m - 1, m - 1));
|
||||
EXPECT_EQ(0, uniform_upper_bound(IntervalOpenOpen, 0u, 0u));
|
||||
EXPECT_EQ(m - 1, uniform_upper_bound(IntervalOpenOpen, m, m));
|
||||
|
||||
// int min/max
|
||||
constexpr auto l = (std::numeric_limits<int64_t>::min)();
|
||||
constexpr auto r = (std::numeric_limits<int64_t>::max)();
|
||||
EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0, 0));
|
||||
EXPECT_EQ(l + 1, uniform_lower_bound(IntervalOpenOpen, l, l));
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r - 1, r - 1));
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r, r));
|
||||
EXPECT_EQ(-1, uniform_upper_bound(IntervalOpenOpen, 0, 0));
|
||||
EXPECT_EQ(l, uniform_upper_bound(IntervalOpenOpen, l, l));
|
||||
EXPECT_EQ(r - 1, uniform_upper_bound(IntervalOpenOpen, r, r));
|
||||
}
|
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsRealBounds) {
|
||||
// absl::uniform_real_distribution natively assumes IntervalClosedOpen;
|
||||
// use the inverse here so each bound has to change.
|
||||
constexpr IntervalOpenClosedTag IntervalOpenClosed;
|
||||
|
||||
// Edge cases: the next value toward itself is itself.
|
||||
EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, 1.0, 1.0));
|
||||
EXPECT_EQ(1.0f, uniform_lower_bound(IntervalOpenClosed, 1.0f, 1.0f));
|
||||
|
||||
// rightmost and leftmost finite values.
|
||||
constexpr auto r = (std::numeric_limits<double>::max)();
|
||||
const auto re = std::nexttoward(r, 0.0);
|
||||
constexpr auto l = -r;
|
||||
const auto le = std::nexttoward(l, 0.0);
|
||||
|
||||
EXPECT_EQ(l, uniform_lower_bound(IntervalOpenClosed, l, l)); // (l,l)
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, r, r)); // (r,r)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, r)); // (l,r)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, 0.0)); // (l, 0)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, le)); // (l, le)
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, re, r)); // (re, r)
|
||||
|
||||
EXPECT_EQ(le, uniform_upper_bound(IntervalOpenClosed, l, l)); // (l,l)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, r, r)); // (r,r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, r)); // (l,r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, re)); // (l,re)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, 0.0, r)); // (0, r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, re, r)); // (re, r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, le, re)); // (le, re)
|
||||
|
||||
const double e = std::nextafter(1.0, 2.0); // 1 + epsilon
|
||||
const double f = std::nextafter(1.0, 0.0); // 1 - epsilon
|
||||
|
||||
// (1.0, 1.0 + epsilon)
|
||||
EXPECT_EQ(e, uniform_lower_bound(IntervalOpenClosed, 1.0, e));
|
||||
EXPECT_EQ(std::nextafter(e, 2.0),
|
||||
uniform_upper_bound(IntervalOpenClosed, 1.0, e));
|
||||
|
||||
// (1.0-epsilon, 1.0)
|
||||
EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, f, 1.0));
|
||||
EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, f, 1.0));
|
||||
|
||||
// denorm cases.
|
||||
const double g = std::numeric_limits<double>::denorm_min();
|
||||
const double h = std::nextafter(g, 1.0);
|
||||
|
||||
// (0, denorm_min)
|
||||
EXPECT_EQ(g, uniform_lower_bound(IntervalOpenClosed, 0.0, g));
|
||||
EXPECT_EQ(h, uniform_upper_bound(IntervalOpenClosed, 0.0, g));
|
||||
|
||||
// (denorm_min, 1.0)
|
||||
EXPECT_EQ(h, uniform_lower_bound(IntervalOpenClosed, g, 1.0));
|
||||
EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, g, 1.0));
|
||||
|
||||
// Edge cases: invalid bounds.
|
||||
EXPECT_EQ(f, uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0));
|
||||
}
|
||||
|
||||
struct Invalid {};
|
||||
|
||||
template <typename A, typename B>
|
||||
auto InferredUniformReturnT(int) -> uniform_inferred_return_t<A, B>;
|
||||
|
||||
template <typename, typename>
|
||||
Invalid InferredUniformReturnT(...);
|
||||
|
||||
// Given types <A, B, Expect>, CheckArgsInferType() verifies that
|
||||
//
|
||||
// uniform_inferred_return_t<A, B> and
|
||||
// uniform_inferred_return_t<B, A>
|
||||
//
|
||||
// returns the type "Expect".
|
||||
//
|
||||
// This interface can also be used to assert that a given inferred return types
|
||||
// are invalid. Writing:
|
||||
//
|
||||
// CheckArgsInferType<float, int, Invalid>()
|
||||
//
|
||||
// will assert that this overload does not exist.
|
||||
template <typename A, typename B, typename Expect>
|
||||
void CheckArgsInferType() {
|
||||
static_assert(
|
||||
absl::conjunction<
|
||||
std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>,
|
||||
std::is_same<Expect,
|
||||
decltype(InferredUniformReturnT<B, A>(0))>>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST_F(UniformHelperTest, UniformTypeInference) {
|
||||
// Infers common types.
|
||||
CheckArgsInferType<uint16_t, uint16_t, uint16_t>();
|
||||
CheckArgsInferType<uint32_t, uint32_t, uint32_t>();
|
||||
CheckArgsInferType<uint64_t, uint64_t, uint64_t>();
|
||||
CheckArgsInferType<int16_t, int16_t, int16_t>();
|
||||
CheckArgsInferType<int32_t, int32_t, int32_t>();
|
||||
CheckArgsInferType<int64_t, int64_t, int64_t>();
|
||||
CheckArgsInferType<float, float, float>();
|
||||
CheckArgsInferType<double, double, double>();
|
||||
|
||||
// Properly promotes uint16_t.
|
||||
CheckArgsInferType<uint16_t, uint32_t, uint32_t>();
|
||||
CheckArgsInferType<uint16_t, uint64_t, uint64_t>();
|
||||
CheckArgsInferType<uint16_t, int32_t, int32_t>();
|
||||
CheckArgsInferType<uint16_t, int64_t, int64_t>();
|
||||
CheckArgsInferType<uint16_t, float, float>();
|
||||
CheckArgsInferType<uint16_t, double, double>();
|
||||
|
||||
// Properly promotes int16_t.
|
||||
CheckArgsInferType<int16_t, int32_t, int32_t>();
|
||||
CheckArgsInferType<int16_t, int64_t, int64_t>();
|
||||
CheckArgsInferType<int16_t, float, float>();
|
||||
CheckArgsInferType<int16_t, double, double>();
|
||||
|
||||
// Invalid (u)int16_t-pairings do not compile.
|
||||
// See "CheckArgsInferType" comments above, for how this is achieved.
|
||||
CheckArgsInferType<uint16_t, int16_t, Invalid>();
|
||||
CheckArgsInferType<int16_t, uint32_t, Invalid>();
|
||||
CheckArgsInferType<int16_t, uint64_t, Invalid>();
|
||||
|
||||
// Properly promotes uint32_t.
|
||||
CheckArgsInferType<uint32_t, uint64_t, uint64_t>();
|
||||
CheckArgsInferType<uint32_t, int64_t, int64_t>();
|
||||
CheckArgsInferType<uint32_t, double, double>();
|
||||
|
||||
// Properly promotes int32_t.
|
||||
CheckArgsInferType<int32_t, int64_t, int64_t>();
|
||||
CheckArgsInferType<int32_t, double, double>();
|
||||
|
||||
// Invalid (u)int32_t-pairings do not compile.
|
||||
CheckArgsInferType<uint32_t, int32_t, Invalid>();
|
||||
CheckArgsInferType<int32_t, uint64_t, Invalid>();
|
||||
CheckArgsInferType<int32_t, float, Invalid>();
|
||||
CheckArgsInferType<uint32_t, float, Invalid>();
|
||||
|
||||
// Invalid (u)int64_t-pairings do not compile.
|
||||
CheckArgsInferType<uint64_t, int64_t, Invalid>();
|
||||
CheckArgsInferType<int64_t, float, Invalid>();
|
||||
CheckArgsInferType<int64_t, double, Invalid>();
|
||||
|
||||
// Properly promotes float.
|
||||
CheckArgsInferType<float, double, double>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
|
||||
#define ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64)
|
||||
#include <intrin.h> // NOLINT(build/include_order)
|
||||
#pragma intrinsic(_umul128)
|
||||
#define ABSL_INTERNAL_USE_UMUL128 1
|
||||
#endif
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/numeric/bits.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/random/internal/traits.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace random_internal {
|
||||
|
||||
// wide_multiply<T> multiplies two N-bit values to a 2N-bit result.
|
||||
template <typename UIntType>
|
||||
struct wide_multiply {
|
||||
static constexpr size_t kN = std::numeric_limits<UIntType>::digits;
|
||||
using input_type = UIntType;
|
||||
using result_type = typename random_internal::unsigned_bits<kN * 2>::type;
|
||||
|
||||
static result_type multiply(input_type a, input_type b) {
|
||||
return static_cast<result_type>(a) * b;
|
||||
}
|
||||
|
||||
static input_type hi(result_type r) {
|
||||
return static_cast<input_type>(r >> kN);
|
||||
}
|
||||
static input_type lo(result_type r) { return static_cast<input_type>(r); }
|
||||
|
||||
static_assert(std::is_unsigned<UIntType>::value,
|
||||
"Class-template wide_multiply<> argument must be unsigned.");
|
||||
};
|
||||
|
||||
// MultiplyU128ToU256 multiplies two 128-bit values to a 256-bit value.
|
||||
inline U256 MultiplyU128ToU256(uint128 a, uint128 b) {
|
||||
const uint128 a00 = static_cast<uint64_t>(a);
|
||||
const uint128 a64 = a >> 64;
|
||||
const uint128 b00 = static_cast<uint64_t>(b);
|
||||
const uint128 b64 = b >> 64;
|
||||
|
||||
const uint128 c00 = a00 * b00;
|
||||
const uint128 c64a = a00 * b64;
|
||||
const uint128 c64b = a64 * b00;
|
||||
const uint128 c128 = a64 * b64;
|
||||
|
||||
const uint64_t carry =
|
||||
static_cast<uint64_t>(((c00 >> 64) + static_cast<uint64_t>(c64a) +
|
||||
static_cast<uint64_t>(c64b)) >>
|
||||
64);
|
||||
|
||||
return {c128 + (c64a >> 64) + (c64b >> 64) + carry,
|
||||
c00 + (c64a << 64) + (c64b << 64)};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct wide_multiply<uint128> {
|
||||
using input_type = uint128;
|
||||
using result_type = U256;
|
||||
|
||||
static result_type multiply(input_type a, input_type b) {
|
||||
return MultiplyU128ToU256(a, b);
|
||||
}
|
||||
|
||||
static input_type hi(result_type r) { return r.hi; }
|
||||
static input_type lo(result_type r) { return r.lo; }
|
||||
};
|
||||
|
||||
} // namespace random_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/wide_multiply.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
|
||||
using absl::random_internal::MultiplyU128ToU256;
|
||||
using absl::random_internal::U256;
|
||||
|
||||
namespace {
|
||||
|
||||
U256 LeftShift(U256 v, int s) {
|
||||
if (s == 0) {
|
||||
return v;
|
||||
} else if (s < 128) {
|
||||
return {(v.hi << s) | (v.lo >> (128 - s)), v.lo << s};
|
||||
} else {
|
||||
return {v.lo << (s - 128), 0};
|
||||
}
|
||||
}
|
||||
|
||||
MATCHER_P2(Eq256, hi, lo, "") { return arg.hi == hi && arg.lo == lo; }
|
||||
MATCHER_P(Eq256, v, "") { return arg.hi == v.hi && arg.lo == v.lo; }
|
||||
|
||||
TEST(WideMultiplyTest, MultiplyU128ToU256Test) {
|
||||
using absl::uint128;
|
||||
constexpr uint128 k1 = 1;
|
||||
constexpr uint128 kMax = ~static_cast<uint128>(0);
|
||||
|
||||
EXPECT_THAT(MultiplyU128ToU256(0, 0), Eq256(0, 0));
|
||||
|
||||
// Max uin128_t
|
||||
EXPECT_THAT(MultiplyU128ToU256(kMax, kMax), Eq256(kMax << 1, 1));
|
||||
EXPECT_THAT(MultiplyU128ToU256(kMax, 1), Eq256(0, kMax));
|
||||
EXPECT_THAT(MultiplyU128ToU256(1, kMax), Eq256(0, kMax));
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
SCOPED_TRACE(i);
|
||||
EXPECT_THAT(MultiplyU128ToU256(kMax, k1 << i),
|
||||
Eq256(LeftShift({0, kMax}, i)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(k1 << i, kMax),
|
||||
Eq256(LeftShift({0, kMax}, i)));
|
||||
}
|
||||
|
||||
// 1-bit x 1-bit.
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
EXPECT_THAT(MultiplyU128ToU256(k1 << i, k1 << j),
|
||||
Eq256(LeftShift({0, 1}, i + j)));
|
||||
}
|
||||
}
|
||||
|
||||
// Verified multiplies
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0xc502da0d6ea99fe8, 0xfa3c9141a1f50912),
|
||||
absl::MakeUint128(0x96bcf1ac37f97bd6, 0x27e2cdeb5fb2299e)),
|
||||
Eq256(absl::MakeUint128(0x740113d838f96a64, 0x22e8cfa4d71f89ea),
|
||||
absl::MakeUint128(0x19184a345c62e993, 0x237871b630337b1c)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0x6f29e670cee07230, 0xc3d8e6c3e4d86759),
|
||||
absl::MakeUint128(0x3227d29fa6386db1, 0x231682bb1e4b764f)),
|
||||
Eq256(absl::MakeUint128(0x15c779d9d5d3b07c, 0xd7e6c827f0c81cbe),
|
||||
absl::MakeUint128(0xf88e3914f7fa287a, 0x15b79975137dea77)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0xafb77107215646e1, 0x3b844cb1ac5769e7),
|
||||
absl::MakeUint128(0x1ff7b2d888b62479, 0x92f758ae96fcba0b)),
|
||||
Eq256(absl::MakeUint128(0x15f13b70181f6985, 0x2adb36bbabce7d02),
|
||||
absl::MakeUint128(0x6c470d72e13aad04, 0x63fba3f5841762ed)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0xd85d5558d67ac905, 0xf88c70654dae19b1),
|
||||
absl::MakeUint128(0x17252c6727db3738, 0x399ff658c511eedc)),
|
||||
Eq256(absl::MakeUint128(0x138fcdaf8b0421ee, 0x1b465ddf2a0d03f6),
|
||||
absl::MakeUint128(0x8f573ba68296860f, 0xf327d2738741a21c)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0x46f0421a37ff6bee, 0xa61df89f09d140b1),
|
||||
absl::MakeUint128(0x3d712ec9f37ca2e1, 0x9658a2cba47ef4b1)),
|
||||
Eq256(absl::MakeUint128(0x11069cc48ee7c95d, 0xd35fb1c7aa91c978),
|
||||
absl::MakeUint128(0xbe2f4a6de874b015, 0xd2f7ac1b76746e61)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0x730d27c72d58fa49, 0x3ebeda7498f8827c),
|
||||
absl::MakeUint128(0xa2c959eca9f503af, 0x189c687eb842bbd8)),
|
||||
Eq256(absl::MakeUint128(0x4928d0ea356ba022, 0x1546d34a2963393),
|
||||
absl::MakeUint128(0x7481531e1e0a16d1, 0xdd8025015cf6aca0)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0x6ca41020f856d2f1, 0xb9b0838c04a7f4aa),
|
||||
absl::MakeUint128(0x9cf41d28a8396f54, 0x1d681695e377ffe6)),
|
||||
Eq256(absl::MakeUint128(0x429b92934d9be6f1, 0xea182877157c1e7),
|
||||
absl::MakeUint128(0x7135c23f0a4a475, 0xc1adc366f4a126bc)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0x57472833797c332, 0x6c79272fdec4687a),
|
||||
absl::MakeUint128(0xb5f022ea3838e46b, 0x16face2f003e27a6)),
|
||||
Eq256(absl::MakeUint128(0x3e072e0962b3400, 0x5d9fe8fdc3d0e1f4),
|
||||
absl::MakeUint128(0x7dc0df47cedafd62, 0xbe6501f1acd2551c)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0xf0fb4198322eb1c2, 0xfe7f5f31f3885938),
|
||||
absl::MakeUint128(0xd99012b71bb7aa31, 0xac7a6f9eb190789)),
|
||||
Eq256(absl::MakeUint128(0xcccc998cf075ca01, 0x642d144322fb873a),
|
||||
absl::MakeUint128(0xc79dc12b69d91ed4, 0xa83459132ce046f8)));
|
||||
EXPECT_THAT(MultiplyU128ToU256(
|
||||
absl::MakeUint128(0xb5c04120848cdb47, 0x8aa62a827bf52635),
|
||||
absl::MakeUint128(0x8d07a359be2f1380, 0x467bb90d59da0dea)),
|
||||
Eq256(absl::MakeUint128(0x64205019d139a9ce, 0x99425c5fb6e7a977),
|
||||
absl::MakeUint128(0xd3e99628a9e5fca7, 0x9c7824cb7279d72)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Loading…
Add table
Add a link
Reference in a new issue