1 // Copyright 2019 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/base/internal/exponential_biased.h"
16
17 #include <stddef.h>
18
19 #include <cmath>
20 #include <cstdint>
21 #include <vector>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/strings/str_cat.h"
26
27 using ::testing::Ge;
28
29 namespace absl {
30 ABSL_NAMESPACE_BEGIN
31 namespace base_internal {
32
33 MATCHER_P2(IsBetween, a, b,
34 absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a,
35 " and ", b)) {
36 return a <= arg && arg <= b;
37 }
38
39 // Tests of the quality of the random numbers generated
40 // This uses the Anderson Darling test for uniformity.
41 // See "Evaluating the Anderson-Darling Distribution" by Marsaglia
42 // for details.
43
44 // Short cut version of ADinf(z), z>0 (from Marsaglia)
45 // This returns the p-value for Anderson Darling statistic in
46 // the limit as n-> infinity. For finite n, apply the error fix below.
AndersonDarlingInf(double z)47 double AndersonDarlingInf(double z) {
48 if (z < 2) {
49 return exp(-1.2337141 / z) / sqrt(z) *
50 (2.00012 +
51 (0.247105 -
52 (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) *
53 z) *
54 z);
55 }
56 return exp(
57 -exp(1.0776 -
58 (2.30695 -
59 (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) *
60 z));
61 }
62
63 // Corrects the approximation error in AndersonDarlingInf for small values of n
64 // Add this to AndersonDarlingInf to get a better approximation
65 // (from Marsaglia)
AndersonDarlingErrFix(int n,double x)66 double AndersonDarlingErrFix(int n, double x) {
67 if (x > 0.8) {
68 return (-130.2137 +
69 (745.2337 -
70 (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) *
71 x) /
72 n;
73 }
74 double cutoff = 0.01265 + 0.1757 / n;
75 if (x < cutoff) {
76 double t = x / cutoff;
77 t = sqrt(t) * (1 - t) * (49 * t - 102);
78 return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n;
79 } else {
80 double t = (x - cutoff) / (0.8 - cutoff);
81 t = -0.00022633 +
82 (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) *
83 t;
84 return t * (0.04213 + 0.01365 / n) / n;
85 }
86 }
87
88 // Returns the AndersonDarling p-value given n and the value of the statistic
AndersonDarlingPValue(int n,double z)89 double AndersonDarlingPValue(int n, double z) {
90 double ad = AndersonDarlingInf(z);
91 double errfix = AndersonDarlingErrFix(n, ad);
92 return ad + errfix;
93 }
94
AndersonDarlingStatistic(const std::vector<double> & random_sample)95 double AndersonDarlingStatistic(const std::vector<double>& random_sample) {
96 int n = random_sample.size();
97 double ad_sum = 0;
98 for (int i = 0; i < n; i++) {
99 ad_sum += (2 * i + 1) *
100 std::log(random_sample[i] * (1 - random_sample[n - 1 - i]));
101 }
102 double ad_statistic = -n - 1 / static_cast<double>(n) * ad_sum;
103 return ad_statistic;
104 }
105
106 // Tests if the array of doubles is uniformly distributed.
107 // Returns the p-value of the Anderson Darling Statistic
108 // for the given set of sorted random doubles
109 // See "Evaluating the Anderson-Darling Distribution" by
110 // Marsaglia and Marsaglia for details.
AndersonDarlingTest(const std::vector<double> & random_sample)111 double AndersonDarlingTest(const std::vector<double>& random_sample) {
112 double ad_statistic = AndersonDarlingStatistic(random_sample);
113 double p = AndersonDarlingPValue(random_sample.size(), ad_statistic);
114 return p;
115 }
116
TEST(ExponentialBiasedTest,CoinTossDemoWithGetSkipCount)117 TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) {
118 ExponentialBiased eb;
119 for (int runs = 0; runs < 10; ++runs) {
120 for (int flips = eb.GetSkipCount(1); flips > 0; --flips) {
121 printf("head...");
122 }
123 printf("tail\n");
124 }
125 int heads = 0;
126 for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) {
127 ++heads;
128 }
129 printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000);
130 }
131
TEST(ExponentialBiasedTest,SampleDemoWithStride)132 TEST(ExponentialBiasedTest, SampleDemoWithStride) {
133 ExponentialBiased eb;
134 int stride = eb.GetStride(10);
135 int samples = 0;
136 for (int i = 0; i < 10000000; ++i) {
137 if (--stride == 0) {
138 ++samples;
139 stride = eb.GetStride(10);
140 }
141 }
142 printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000);
143 }
144
145
146 // Testing that NextRandom generates uniform random numbers. Applies the
147 // Anderson-Darling test for uniformity
TEST(ExponentialBiasedTest,TestNextRandom)148 TEST(ExponentialBiasedTest, TestNextRandom) {
149 for (auto n : std::vector<int>({
150 10, // Check short-range correlation
151 100, 1000,
152 10000 // Make sure there's no systemic error
153 })) {
154 uint64_t x = 1;
155 // This assumes that the prng returns 48 bit numbers
156 uint64_t max_prng_value = static_cast<uint64_t>(1) << 48;
157 // Initialize.
158 for (int i = 1; i <= 20; i++) {
159 x = ExponentialBiased::NextRandom(x);
160 }
161 std::vector<uint64_t> int_random_sample(n);
162 // Collect samples
163 for (int i = 0; i < n; i++) {
164 int_random_sample[i] = x;
165 x = ExponentialBiased::NextRandom(x);
166 }
167 // First sort them...
168 std::sort(int_random_sample.begin(), int_random_sample.end());
169 std::vector<double> random_sample(n);
170 // Convert them to uniform randoms (in the range [0,1])
171 for (int i = 0; i < n; i++) {
172 random_sample[i] =
173 static_cast<double>(int_random_sample[i]) / max_prng_value;
174 }
175 // Now compute the Anderson-Darling statistic
176 double ad_pvalue = AndersonDarlingTest(random_sample);
177 EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001)
178 << "prng is not uniform: n = " << n << " p = " << ad_pvalue;
179 }
180 }
181
182 // The generator needs to be available as a thread_local and as a static
183 // variable.
TEST(ExponentialBiasedTest,InitializationModes)184 TEST(ExponentialBiasedTest, InitializationModes) {
185 ABSL_CONST_INIT static ExponentialBiased eb_static;
186 EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0));
187
188 #if ABSL_HAVE_THREAD_LOCAL
189 thread_local ExponentialBiased eb_thread;
190 EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0));
191 #endif
192
193 ExponentialBiased eb_stack;
194 EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0));
195 }
196
197 } // namespace base_internal
198 ABSL_NAMESPACE_END
199 } // namespace absl
200