• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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/random/beta_distribution.h"
16 
17 #include <algorithm>
18 #include <cfloat>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iterator>
22 #include <random>
23 #include <sstream>
24 #include <string>
25 #include <type_traits>
26 #include <unordered_map>
27 #include <vector>
28 
29 #include "gmock/gmock.h"
30 #include "gtest/gtest.h"
31 #include "absl/base/internal/raw_logging.h"
32 #include "absl/numeric/internal/representation.h"
33 #include "absl/random/internal/chi_square.h"
34 #include "absl/random/internal/distribution_test_util.h"
35 #include "absl/random/internal/pcg_engine.h"
36 #include "absl/random/internal/sequence_urbg.h"
37 #include "absl/random/random.h"
38 #include "absl/strings/str_cat.h"
39 #include "absl/strings/str_format.h"
40 #include "absl/strings/str_replace.h"
41 #include "absl/strings/strip.h"
42 
43 namespace {
44 
45 template <typename IntType>
46 class BetaDistributionInterfaceTest : public ::testing::Test {};
47 
48 // double-double arithmetic is not supported well by either GCC or Clang; see
49 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
50 // https://bugs.llvm.org/show_bug.cgi?id=49131, and
51 // https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
52 // with double doubles until compiler support is better.
53 using RealTypes =
54     std::conditional<absl::numeric_internal::IsDoubleDouble(),
55                      ::testing::Types<float, double>,
56                      ::testing::Types<float, double, long double>>::type;
57 TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes);
58 
TYPED_TEST(BetaDistributionInterfaceTest,SerializeTest)59 TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
60   // The threshold for whether std::exp(1/a) is finite.
61   const TypeParam kSmallA =
62       1.0f / std::log((std::numeric_limits<TypeParam>::max)());
63   // The threshold for whether a * std::log(a) is finite.
64   const TypeParam kLargeA =
65       std::exp(std::log((std::numeric_limits<TypeParam>::max)()) -
66                std::log(std::log((std::numeric_limits<TypeParam>::max)())));
67   using param_type = typename absl::beta_distribution<TypeParam>::param_type;
68 
69   constexpr int kCount = 1000;
70   absl::InsecureBitGen gen;
71   const TypeParam kValues[] = {
72       TypeParam(1e-20), TypeParam(1e-12), TypeParam(1e-8), TypeParam(1e-4),
73       TypeParam(1e-3), TypeParam(0.1), TypeParam(0.25),
74       std::nextafter(TypeParam(0.5), TypeParam(0)),  // 0.5 - epsilon
75       std::nextafter(TypeParam(0.5), TypeParam(1)),  // 0.5 + epsilon
76       TypeParam(0.5), TypeParam(1.0),                //
77       std::nextafter(TypeParam(1), TypeParam(0)),    // 1 - epsilon
78       std::nextafter(TypeParam(1), TypeParam(2)),    // 1 + epsilon
79       TypeParam(12.5), TypeParam(1e2), TypeParam(1e8), TypeParam(1e12),
80       TypeParam(1e20),                        //
81       kSmallA,                                //
82       std::nextafter(kSmallA, TypeParam(0)),  //
83       std::nextafter(kSmallA, TypeParam(1)),  //
84       kLargeA,                                //
85       std::nextafter(kLargeA, TypeParam(0)),  //
86       std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()),
87       // Boundary cases.
88       std::numeric_limits<TypeParam>::max(),
89       std::numeric_limits<TypeParam>::epsilon(),
90       std::nextafter(std::numeric_limits<TypeParam>::min(),
91                      TypeParam(1)),                  // min + epsilon
92       std::numeric_limits<TypeParam>::min(),         // smallest normal
93       std::numeric_limits<TypeParam>::denorm_min(),  // smallest denorm
94       std::numeric_limits<TypeParam>::min() / 2,     // denorm
95       std::nextafter(std::numeric_limits<TypeParam>::min(),
96                      TypeParam(0)),  // denorm_max
97   };
98   for (TypeParam alpha : kValues) {
99     for (TypeParam beta : kValues) {
100       ABSL_INTERNAL_LOG(
101           INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta));
102 
103       param_type param(alpha, beta);
104       absl::beta_distribution<TypeParam> before(alpha, beta);
105       EXPECT_EQ(before.alpha(), param.alpha());
106       EXPECT_EQ(before.beta(), param.beta());
107 
108       {
109         absl::beta_distribution<TypeParam> via_param(param);
110         EXPECT_EQ(via_param, before);
111         EXPECT_EQ(via_param.param(), before.param());
112       }
113 
114       // Smoke test.
115       for (int i = 0; i < kCount; ++i) {
116         auto sample = before(gen);
117         EXPECT_TRUE(std::isfinite(sample));
118         EXPECT_GE(sample, before.min());
119         EXPECT_LE(sample, before.max());
120       }
121 
122       // Validate stream serialization.
123       std::stringstream ss;
124       ss << before;
125       absl::beta_distribution<TypeParam> after(3.8f, 1.43f);
126       EXPECT_NE(before.alpha(), after.alpha());
127       EXPECT_NE(before.beta(), after.beta());
128       EXPECT_NE(before.param(), after.param());
129       EXPECT_NE(before, after);
130 
131       ss >> after;
132 
133       EXPECT_EQ(before.alpha(), after.alpha());
134       EXPECT_EQ(before.beta(), after.beta());
135       EXPECT_EQ(before, after)           //
136           << ss.str() << " "             //
137           << (ss.good() ? "good " : "")  //
138           << (ss.bad() ? "bad " : "")    //
139           << (ss.eof() ? "eof " : "")    //
140           << (ss.fail() ? "fail " : "");
141     }
142   }
143 }
144 
TYPED_TEST(BetaDistributionInterfaceTest,DegenerateCases)145 TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) {
146   // We use a fixed bit generator for distribution accuracy tests.  This allows
147   // these tests to be deterministic, while still testing the qualify of the
148   // implementation.
149   absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6);
150 
151   // Extreme cases when the params are abnormal.
152   constexpr int kCount = 1000;
153   const TypeParam kSmallValues[] = {
154       std::numeric_limits<TypeParam>::min(),
155       std::numeric_limits<TypeParam>::denorm_min(),
156       std::nextafter(std::numeric_limits<TypeParam>::min(),
157                      TypeParam(0)),  // denorm_max
158       std::numeric_limits<TypeParam>::epsilon(),
159   };
160   const TypeParam kLargeValues[] = {
161       std::numeric_limits<TypeParam>::max() * static_cast<TypeParam>(0.9999),
162       std::numeric_limits<TypeParam>::max() - 1,
163       std::numeric_limits<TypeParam>::max(),
164   };
165   {
166     // Small alpha and beta.
167     // Useful WolframAlpha plots:
168     //   * plot InverseBetaRegularized[x, 0.0001, 0.0001] from 0.495 to 0.505
169     //   * Beta[1.0, 0.0000001, 0.0000001]
170     //   * Beta[0.9999, 0.0000001, 0.0000001]
171     for (TypeParam alpha : kSmallValues) {
172       for (TypeParam beta : kSmallValues) {
173         int zeros = 0;
174         int ones = 0;
175         absl::beta_distribution<TypeParam> d(alpha, beta);
176         for (int i = 0; i < kCount; ++i) {
177           TypeParam x = d(rng);
178           if (x == 0.0) {
179             zeros++;
180           } else if (x == 1.0) {
181             ones++;
182           }
183         }
184         EXPECT_EQ(ones + zeros, kCount);
185         if (alpha == beta) {
186           EXPECT_NE(ones, 0);
187           EXPECT_NE(zeros, 0);
188         }
189       }
190     }
191   }
192   {
193     // Small alpha, large beta.
194     // Useful WolframAlpha plots:
195     //   * plot InverseBetaRegularized[x, 0.0001, 10000] from 0.995 to 1
196     //   * Beta[0, 0.0000001, 1000000]
197     //   * Beta[0.001, 0.0000001, 1000000]
198     //   * Beta[1, 0.0000001, 1000000]
199     for (TypeParam alpha : kSmallValues) {
200       for (TypeParam beta : kLargeValues) {
201         absl::beta_distribution<TypeParam> d(alpha, beta);
202         for (int i = 0; i < kCount; ++i) {
203           EXPECT_EQ(d(rng), 0.0);
204         }
205       }
206     }
207   }
208   {
209     // Large alpha, small beta.
210     // Useful WolframAlpha plots:
211     //   * plot InverseBetaRegularized[x, 10000, 0.0001] from 0 to 0.001
212     //   * Beta[0.99, 1000000, 0.0000001]
213     //   * Beta[1, 1000000, 0.0000001]
214     for (TypeParam alpha : kLargeValues) {
215       for (TypeParam beta : kSmallValues) {
216         absl::beta_distribution<TypeParam> d(alpha, beta);
217         for (int i = 0; i < kCount; ++i) {
218           EXPECT_EQ(d(rng), 1.0);
219         }
220       }
221     }
222   }
223   {
224     // Large alpha and beta.
225     absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(),
226                                          std::numeric_limits<TypeParam>::max());
227     for (int i = 0; i < kCount; ++i) {
228       EXPECT_EQ(d(rng), 0.5);
229     }
230   }
231   {
232     // Large alpha and beta but unequal.
233     absl::beta_distribution<TypeParam> d(
234         std::numeric_limits<TypeParam>::max(),
235         std::numeric_limits<TypeParam>::max() * 0.9999);
236     for (int i = 0; i < kCount; ++i) {
237       TypeParam x = d(rng);
238       EXPECT_NE(x, 0.5f);
239       EXPECT_FLOAT_EQ(x, 0.500025f);
240     }
241   }
242 }
243 
244 class BetaDistributionModel {
245  public:
BetaDistributionModel(::testing::tuple<double,double> p)246   explicit BetaDistributionModel(::testing::tuple<double, double> p)
247       : alpha_(::testing::get<0>(p)), beta_(::testing::get<1>(p)) {}
248 
Mean() const249   double Mean() const { return alpha_ / (alpha_ + beta_); }
250 
Variance() const251   double Variance() const {
252     return alpha_ * beta_ / (alpha_ + beta_ + 1) / (alpha_ + beta_) /
253            (alpha_ + beta_);
254   }
255 
Kurtosis() const256   double Kurtosis() const {
257     return 3 + 6 *
258                    ((alpha_ - beta_) * (alpha_ - beta_) * (alpha_ + beta_ + 1) -
259                     alpha_ * beta_ * (2 + alpha_ + beta_)) /
260                    alpha_ / beta_ / (alpha_ + beta_ + 2) / (alpha_ + beta_ + 3);
261   }
262 
263  protected:
264   const double alpha_;
265   const double beta_;
266 };
267 
268 class BetaDistributionTest
269     : public ::testing::TestWithParam<::testing::tuple<double, double>>,
270       public BetaDistributionModel {
271  public:
BetaDistributionTest()272   BetaDistributionTest() : BetaDistributionModel(GetParam()) {}
273 
274  protected:
275   template <class D>
276   bool SingleZTestOnMeanAndVariance(double p, size_t samples);
277 
278   template <class D>
279   bool SingleChiSquaredTest(double p, size_t samples, size_t buckets);
280 
281   absl::InsecureBitGen rng_;
282 };
283 
284 template <class D>
SingleZTestOnMeanAndVariance(double p,size_t samples)285 bool BetaDistributionTest::SingleZTestOnMeanAndVariance(double p,
286                                                         size_t samples) {
287   D dis(alpha_, beta_);
288 
289   std::vector<double> data;
290   data.reserve(samples);
291   for (size_t i = 0; i < samples; i++) {
292     const double variate = dis(rng_);
293     EXPECT_FALSE(std::isnan(variate));
294     // Note that equality is allowed on both sides.
295     EXPECT_GE(variate, 0.0);
296     EXPECT_LE(variate, 1.0);
297     data.push_back(variate);
298   }
299 
300   // We validate that the sample mean and sample variance are indeed from a
301   // Beta distribution with the given shape parameters.
302   const auto m = absl::random_internal::ComputeDistributionMoments(data);
303 
304   // The variance of the sample mean is variance / n.
305   const double mean_stddev = std::sqrt(Variance() / static_cast<double>(m.n));
306 
307   // The variance of the sample variance is (approximately):
308   //   (kurtosis - 1) * variance^2 / n
309   const double variance_stddev = std::sqrt(
310       (Kurtosis() - 1) * Variance() * Variance() / static_cast<double>(m.n));
311   // z score for the sample variance.
312   const double z_variance = (m.variance - Variance()) / variance_stddev;
313 
314   const double max_err = absl::random_internal::MaxErrorTolerance(p);
315   const double z_mean = absl::random_internal::ZScore(Mean(), m);
316   const bool pass =
317       absl::random_internal::Near("z", z_mean, 0.0, max_err) &&
318       absl::random_internal::Near("z_variance", z_variance, 0.0, max_err);
319   if (!pass) {
320     ABSL_INTERNAL_LOG(
321         INFO,
322         absl::StrFormat(
323             "Beta(%f, %f), "
324             "mean: sample %f, expect %f, which is %f stddevs away, "
325             "variance: sample %f, expect %f, which is %f stddevs away.",
326             alpha_, beta_, m.mean, Mean(),
327             std::abs(m.mean - Mean()) / mean_stddev, m.variance, Variance(),
328             std::abs(m.variance - Variance()) / variance_stddev));
329   }
330   return pass;
331 }
332 
333 template <class D>
SingleChiSquaredTest(double p,size_t samples,size_t buckets)334 bool BetaDistributionTest::SingleChiSquaredTest(double p, size_t samples,
335                                                 size_t buckets) {
336   constexpr double kErr = 1e-7;
337   std::vector<double> cutoffs, expected;
338   const double bucket_width = 1.0 / static_cast<double>(buckets);
339   int i = 1;
340   int unmerged_buckets = 0;
341   for (; i < buckets; ++i) {
342     const double p = bucket_width * static_cast<double>(i);
343     const double boundary =
344         absl::random_internal::BetaIncompleteInv(alpha_, beta_, p);
345     // The intention is to add `boundary` to the list of `cutoffs`. It becomes
346     // problematic, however, when the boundary values are not monotone, due to
347     // numerical issues when computing the inverse regularized incomplete
348     // Beta function. In these cases, we merge that bucket with its previous
349     // neighbor and merge their expected counts.
350     if ((cutoffs.empty() && boundary < kErr) ||
351         (!cutoffs.empty() && boundary <= cutoffs.back())) {
352       unmerged_buckets++;
353       continue;
354     }
355     if (boundary >= 1.0 - 1e-10) {
356       break;
357     }
358     cutoffs.push_back(boundary);
359     expected.push_back(static_cast<double>(1 + unmerged_buckets) *
360                        bucket_width * static_cast<double>(samples));
361     unmerged_buckets = 0;
362   }
363   cutoffs.push_back(std::numeric_limits<double>::infinity());
364   // Merge all remaining buckets.
365   expected.push_back(static_cast<double>(buckets - i + 1) * bucket_width *
366                      static_cast<double>(samples));
367   // Make sure that we don't merge all the buckets, making this test
368   // meaningless.
369   EXPECT_GE(cutoffs.size(), 3) << alpha_ << ", " << beta_;
370 
371   D dis(alpha_, beta_);
372 
373   std::vector<int32_t> counts(cutoffs.size(), 0);
374   for (int i = 0; i < samples; i++) {
375     const double x = dis(rng_);
376     auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x);
377     counts[std::distance(cutoffs.begin(), it)]++;
378   }
379 
380   // Null-hypothesis is that the distribution is beta distributed with the
381   // provided alpha, beta params (not estimated from the data).
382   const int dof = cutoffs.size() - 1;
383 
384   const double chi_square = absl::random_internal::ChiSquare(
385       counts.begin(), counts.end(), expected.begin(), expected.end());
386   const bool pass =
387       (absl::random_internal::ChiSquarePValue(chi_square, dof) >= p);
388   if (!pass) {
389     for (int i = 0; i < cutoffs.size(); i++) {
390       ABSL_INTERNAL_LOG(
391           INFO, absl::StrFormat("cutoff[%d] = %f, actual count %d, expected %d",
392                                 i, cutoffs[i], counts[i],
393                                 static_cast<int>(expected[i])));
394     }
395 
396     ABSL_INTERNAL_LOG(
397         INFO, absl::StrFormat(
398                   "Beta(%f, %f) %s %f, p = %f", alpha_, beta_,
399                   absl::random_internal::kChiSquared, chi_square,
400                   absl::random_internal::ChiSquarePValue(chi_square, dof)));
401   }
402   return pass;
403 }
404 
TEST_P(BetaDistributionTest,TestSampleStatistics)405 TEST_P(BetaDistributionTest, TestSampleStatistics) {
406   static constexpr int kRuns = 20;
407   static constexpr double kPFail = 0.02;
408   const double p =
409       absl::random_internal::RequiredSuccessProbability(kPFail, kRuns);
410   static constexpr int kSampleCount = 10000;
411   static constexpr int kBucketCount = 100;
412   int failed = 0;
413   for (int i = 0; i < kRuns; ++i) {
414     if (!SingleZTestOnMeanAndVariance<absl::beta_distribution<double>>(
415             p, kSampleCount)) {
416       failed++;
417     }
418     if (!SingleChiSquaredTest<absl::beta_distribution<double>>(
419             0.005, kSampleCount, kBucketCount)) {
420       failed++;
421     }
422   }
423   // Set so that the test is not flaky at --runs_per_test=10000
424   EXPECT_LE(failed, 5);
425 }
426 
ParamName(const::testing::TestParamInfo<::testing::tuple<double,double>> & info)427 std::string ParamName(
428     const ::testing::TestParamInfo<::testing::tuple<double, double>>& info) {
429   std::string name = absl::StrCat("alpha_", ::testing::get<0>(info.param),
430                                   "__beta_", ::testing::get<1>(info.param));
431   return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
432 }
433 
434 INSTANTIATE_TEST_CASE_P(
435     TestSampleStatisticsCombinations, BetaDistributionTest,
436     ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4),
437                        ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)),
438     ParamName);
439 
440 INSTANTIATE_TEST_CASE_P(
441     TestSampleStatistics_SelectedPairs, BetaDistributionTest,
442     ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5),
443                       std::make_pair(900, 1000), std::make_pair(10000, 20000),
444                       std::make_pair(4e5, 2e7), std::make_pair(1e7, 1e5)),
445     ParamName);
446 
447 // NOTE: absl::beta_distribution is not guaranteed to be stable.
TEST(BetaDistributionTest,StabilityTest)448 TEST(BetaDistributionTest, StabilityTest) {
449   // absl::beta_distribution stability relies on the stability of
450   // absl::random_interna::RandU64ToDouble, std::exp, std::log, std::pow,
451   // and std::sqrt.
452   //
453   // This test also depends on the stability of std::frexp.
454   using testing::ElementsAre;
455   absl::random_internal::sequence_urbg urbg({
456       0xffff00000000e6c8ull, 0xffff0000000006c8ull, 0x800003766295CFA9ull,
457       0x11C819684E734A41ull, 0x832603766295CFA9ull, 0x7fbe76c8b4395800ull,
458       0xB3472DCA7B14A94Aull, 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull,
459       0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0x00035C904C70A239ull,
460       0x00009E0BCBAADE14ull, 0x0000000000622CA7ull, 0x4864f22c059bf29eull,
461       0x247856d8b862665cull, 0xe46e86e9a1337e10ull, 0xd8c8541f3519b133ull,
462       0xffe75b52c567b9e4ull, 0xfffff732e5709c5bull, 0xff1f7f0b983532acull,
463       0x1ec2e8986d2362caull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull,
464       0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, 0xECDD4775619F1510ull,
465       0x814c8e35fe9a961aull, 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull,
466       0x1224e62c978bbc7full, 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull,
467       0x1bbc23cfa8fac721ull, 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull,
468       0x836d794457c08849ull, 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull,
469       0xb12d74fdd718c8c5ull, 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull,
470       0x5738341045ba0d85ull, 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull,
471       0xffe6ea4d6edb0c73ull, 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull,
472       0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull,
473       0x8FF6E2FBF2122B64ull, 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull,
474       0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull,
475   });
476 
477   // Convert the real-valued result into a unit64 where we compare
478   // 5 (float) or 10 (double) decimal digits plus the base-2 exponent.
479   auto float_to_u64 = [](float d) {
480     int exp = 0;
481     auto f = std::frexp(d, &exp);
482     return (static_cast<uint64_t>(1e5 * f) * 10000) + std::abs(exp);
483   };
484   auto double_to_u64 = [](double d) {
485     int exp = 0;
486     auto f = std::frexp(d, &exp);
487     return (static_cast<uint64_t>(1e10 * f) * 10000) + std::abs(exp);
488   };
489 
490   std::vector<uint64_t> output(20);
491   {
492     // Algorithm Joehnk (float)
493     absl::beta_distribution<float> dist(0.1f, 0.2f);
494     std::generate(std::begin(output), std::end(output),
495                   [&] { return float_to_u64(dist(urbg)); });
496     EXPECT_EQ(44, urbg.invocations());
497     EXPECT_THAT(output,  //
498                 testing::ElementsAre(
499                     998340000, 619030004, 500000001, 999990000, 996280000,
500                     500000001, 844740004, 847210001, 999970000, 872320000,
501                     585480007, 933280000, 869080042, 647670031, 528240004,
502                     969980004, 626050008, 915930002, 833440033, 878040015));
503   }
504 
505   urbg.reset();
506   {
507     // Algorithm Joehnk (double)
508     absl::beta_distribution<double> dist(0.1, 0.2);
509     std::generate(std::begin(output), std::end(output),
510                   [&] { return double_to_u64(dist(urbg)); });
511     EXPECT_EQ(44, urbg.invocations());
512     EXPECT_THAT(
513         output,  //
514         testing::ElementsAre(
515             99834713000000, 61903356870004, 50000000000001, 99999721170000,
516             99628374770000, 99999999990000, 84474397860004, 84721276240001,
517             99997407490000, 87232528120000, 58548364780007, 93328932910000,
518             86908237770042, 64767917930031, 52824581970004, 96998544140004,
519             62605946270008, 91593604380002, 83345031740033, 87804397230015));
520   }
521 
522   urbg.reset();
523   {
524     // Algorithm Cheng 1
525     absl::beta_distribution<double> dist(0.9, 2.0);
526     std::generate(std::begin(output), std::end(output),
527                   [&] { return double_to_u64(dist(urbg)); });
528     EXPECT_EQ(62, urbg.invocations());
529     EXPECT_THAT(
530         output,  //
531         testing::ElementsAre(
532             62069004780001, 64433204450001, 53607416560000, 89644295430008,
533             61434586310019, 55172615890002, 62187161490000, 56433684810003,
534             80454622050005, 86418558710003, 92920514700001, 64645184680001,
535             58549183380000, 84881283650005, 71078728590002, 69949694970000,
536             73157461710001, 68592191300001, 70747623900000, 78584696930005));
537   }
538 
539   urbg.reset();
540   {
541     // Algorithm Cheng 2
542     absl::beta_distribution<double> dist(1.5, 2.5);
543     std::generate(std::begin(output), std::end(output),
544                   [&] { return double_to_u64(dist(urbg)); });
545     EXPECT_EQ(54, urbg.invocations());
546     EXPECT_THAT(
547         output,  //
548         testing::ElementsAre(
549             75000029250001, 76751482860001, 53264575220000, 69193133650005,
550             78028324470013, 91573587560002, 59167523770000, 60658618560002,
551             80075870540000, 94141320460004, 63196592770003, 78883906300002,
552             96797992590001, 76907587800001, 56645167560000, 65408302280003,
553             53401156320001, 64731238570000, 83065573750001, 79788333820001));
554   }
555 }
556 
557 // This is an implementation-specific test. If any part of the implementation
558 // changes, then it is likely that this test will change as well.  Also, if
559 // dependencies of the distribution change, such as RandU64ToDouble, then this
560 // is also likely to change.
TEST(BetaDistributionTest,AlgorithmBounds)561 TEST(BetaDistributionTest, AlgorithmBounds) {
562 #if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0
563   // We're using an x87-compatible FPU, and intermediate operations are
564   // performed with 80-bit floats. This produces slightly different results from
565   // what we expect below.
566   GTEST_SKIP()
567       << "Skipping the test because we detected x87 floating-point semantics";
568 #endif
569 
570   {
571     absl::random_internal::sequence_urbg urbg(
572         {0x7fbe76c8b4395800ull, 0x8000000000000000ull});
573     // u=0.499, v=0.5
574     absl::beta_distribution<double> dist(1e-4, 1e-4);
575     double a = dist(urbg);
576     EXPECT_EQ(a, 2.0202860861567108529e-09);
577     EXPECT_EQ(2, urbg.invocations());
578   }
579 
580   // Test that both the float & double algorithms appropriately reject the
581   // initial draw.
582   {
583     // 1/alpha = 1/beta = 2.
584     absl::beta_distribution<float> dist(0.5, 0.5);
585 
586     // first two outputs are close to 1.0 - epsilon,
587     // thus:  (u ^ 2 + v ^ 2) > 1.0
588     absl::random_internal::sequence_urbg urbg(
589         {0xffff00000006e6c8ull, 0xffff00000007c7c8ull, 0x800003766295CFA9ull,
590          0x11C819684E734A41ull});
591     {
592       double y = absl::beta_distribution<double>(0.5, 0.5)(urbg);
593       EXPECT_EQ(4, urbg.invocations());
594       EXPECT_EQ(y, 0.9810668952633862) << y;
595     }
596 
597     // ...and:  log(u) * a ~= log(v) * b ~= -0.02
598     // thus z ~= -0.02 + log(1 + e(~0))
599     //        ~= -0.02 + 0.69
600     // thus z > 0
601     urbg.reset();
602     {
603       float x = absl::beta_distribution<float>(0.5, 0.5)(urbg);
604       EXPECT_EQ(4, urbg.invocations());
605       EXPECT_NEAR(0.98106688261032104, x, 0.0000005) << x << "f";
606     }
607   }
608 }
609 
610 }  // namespace
611