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/poisson_distribution.h"
16
17 #include <algorithm>
18 #include <cstddef>
19 #include <cstdint>
20 #include <iterator>
21 #include <random>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include "absl/base/macros.h"
29 #include "absl/container/flat_hash_map.h"
30 #include "absl/log/log.h"
31 #include "absl/random/internal/chi_square.h"
32 #include "absl/random/internal/distribution_test_util.h"
33 #include "absl/random/internal/pcg_engine.h"
34 #include "absl/random/internal/sequence_urbg.h"
35 #include "absl/random/random.h"
36 #include "absl/strings/str_cat.h"
37 #include "absl/strings/str_format.h"
38 #include "absl/strings/str_replace.h"
39 #include "absl/strings/strip.h"
40
41 // Notes about generating poisson variates:
42 //
43 // It is unlikely that any implementation of std::poisson_distribution
44 // will be stable over time and across library implementations. For instance
45 // the three different poisson variate generators listed below all differ:
46 //
47 // https://github.com/ampl/gsl/tree/master/randist/poisson.c
48 // * GSL uses a gamma + binomial + knuth method to compute poisson variates.
49 //
50 // https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/random.tcc
51 // * GCC uses the Devroye rejection algorithm, based on
52 // Devroye, L. Non-Uniform Random Variates Generation. Springer-Verlag,
53 // New York, 1986, Ch. X, Sects. 3.3 & 3.4 (+ Errata!), ~p.511
54 // http://www.nrbook.com/devroye/
55 //
56 // https://github.com/llvm-mirror/libcxx/blob/master/include/random
57 // * CLANG uses a different rejection method, which appears to include a
58 // normal-distribution approximation and an exponential distribution to
59 // compute the threshold, including a similar factorial approximation to this
60 // one, but it is unclear where the algorithm comes from, exactly.
61 //
62
63 namespace {
64
65 using absl::random_internal::kChiSquared;
66
67 // The PoissonDistributionInterfaceTest provides a basic test that
68 // absl::poisson_distribution conforms to the interface and serialization
69 // requirements imposed by [rand.req.dist] for the common integer types.
70
71 template <typename IntType>
72 class PoissonDistributionInterfaceTest : public ::testing::Test {};
73
74 using IntTypes = ::testing::Types<int, int8_t, int16_t, int32_t, int64_t,
75 uint8_t, uint16_t, uint32_t, uint64_t>;
76 TYPED_TEST_SUITE(PoissonDistributionInterfaceTest, IntTypes);
77
TYPED_TEST(PoissonDistributionInterfaceTest,SerializeTest)78 TYPED_TEST(PoissonDistributionInterfaceTest, SerializeTest) {
79 using param_type = typename absl::poisson_distribution<TypeParam>::param_type;
80 const double kMax =
81 std::min(1e10 /* assertion limit */,
82 static_cast<double>(std::numeric_limits<TypeParam>::max()));
83
84 const double kParams[] = {
85 // Cases around 1.
86 1, //
87 std::nextafter(1.0, 0.0), // 1 - epsilon
88 std::nextafter(1.0, 2.0), // 1 + epsilon
89 // Arbitrary values.
90 1e-8, 1e-4,
91 0.0000005, // ~7.2e-7
92 0.2, // ~0.2x
93 0.5, // 0.72
94 2, // ~2.8
95 20, // 3x ~9.6
96 100, 1e4, 1e8, 1.5e9, 1e20,
97 // Boundary cases.
98 std::numeric_limits<double>::max(),
99 std::numeric_limits<double>::epsilon(),
100 std::nextafter(std::numeric_limits<double>::min(),
101 1.0), // min + epsilon
102 std::numeric_limits<double>::min(), // smallest normal
103 std::numeric_limits<double>::denorm_min(), // smallest denorm
104 std::numeric_limits<double>::min() / 2, // denorm
105 std::nextafter(std::numeric_limits<double>::min(),
106 0.0), // denorm_max
107 };
108
109 constexpr int kCount = 1000;
110 absl::InsecureBitGen gen;
111 for (const double m : kParams) {
112 const double mean = std::min(kMax, m);
113 const param_type param(mean);
114
115 // Validate parameters.
116 absl::poisson_distribution<TypeParam> before(mean);
117 EXPECT_EQ(before.mean(), param.mean());
118
119 {
120 absl::poisson_distribution<TypeParam> via_param(param);
121 EXPECT_EQ(via_param, before);
122 EXPECT_EQ(via_param.param(), before.param());
123 }
124
125 // Smoke test.
126 auto sample_min = before.max();
127 auto sample_max = before.min();
128 for (int i = 0; i < kCount; i++) {
129 auto sample = before(gen);
130 EXPECT_GE(sample, before.min());
131 EXPECT_LE(sample, before.max());
132 if (sample > sample_max) sample_max = sample;
133 if (sample < sample_min) sample_min = sample;
134 }
135
136 LOG(INFO) << "Range {" << param.mean() << "}: " << sample_min << ", "
137 << sample_max;
138
139 // Validate stream serialization.
140 std::stringstream ss;
141 ss << before;
142
143 absl::poisson_distribution<TypeParam> after(3.8);
144
145 EXPECT_NE(before.mean(), after.mean());
146 EXPECT_NE(before.param(), after.param());
147 EXPECT_NE(before, after);
148
149 ss >> after;
150
151 EXPECT_EQ(before.mean(), after.mean()) //
152 << ss.str() << " " //
153 << (ss.good() ? "good " : "") //
154 << (ss.bad() ? "bad " : "") //
155 << (ss.eof() ? "eof " : "") //
156 << (ss.fail() ? "fail " : "");
157 }
158 }
159
160 // See http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm
161
162 class PoissonModel {
163 public:
PoissonModel(double mean)164 explicit PoissonModel(double mean) : mean_(mean) {}
165
mean() const166 double mean() const { return mean_; }
variance() const167 double variance() const { return mean_; }
stddev() const168 double stddev() const { return std::sqrt(variance()); }
skew() const169 double skew() const { return 1.0 / mean_; }
kurtosis() const170 double kurtosis() const { return 3.0 + 1.0 / mean_; }
171
172 // InitCDF() initializes the CDF for the distribution parameters.
173 void InitCDF();
174
175 // The InverseCDF, or the Percent-point function returns x, P(x) < v.
176 struct CDF {
177 size_t index;
178 double pmf;
179 double cdf;
180 };
InverseCDF(double p)181 CDF InverseCDF(double p) {
182 CDF target{0, 0, p};
183 auto it = std::upper_bound(
184 std::begin(cdf_), std::end(cdf_), target,
185 [](const CDF& a, const CDF& b) { return a.cdf < b.cdf; });
186 return *it;
187 }
188
LogCDF()189 void LogCDF() {
190 LOG(INFO) << "CDF (mean = " << mean_ << ")";
191 for (const auto c : cdf_) {
192 LOG(INFO) << c.index << ": pmf=" << c.pmf << " cdf=" << c.cdf;
193 }
194 }
195
196 private:
197 const double mean_;
198
199 std::vector<CDF> cdf_;
200 };
201
202 // The goal is to compute an InverseCDF function, or percent point function for
203 // the poisson distribution, and use that to partition our output into equal
204 // range buckets. However there is no closed form solution for the inverse cdf
205 // for poisson distributions (the closest is the incomplete gamma function).
206 // Instead, `InitCDF` iteratively computes the PMF and the CDF. This enables
207 // searching for the bucket points.
InitCDF()208 void PoissonModel::InitCDF() {
209 if (!cdf_.empty()) {
210 // State already initialized.
211 return;
212 }
213 ABSL_ASSERT(mean_ < 201.0);
214
215 const size_t max_i = 50 * stddev() + mean();
216 const double e_neg_mean = std::exp(-mean());
217 ABSL_ASSERT(e_neg_mean > 0);
218
219 double d = 1;
220 double last_result = e_neg_mean;
221 double cumulative = e_neg_mean;
222 if (e_neg_mean > 1e-10) {
223 cdf_.push_back({0, e_neg_mean, cumulative});
224 }
225 for (size_t i = 1; i < max_i; i++) {
226 d *= (mean() / i);
227 double result = e_neg_mean * d;
228 cumulative += result;
229 if (result < 1e-10 && result < last_result && cumulative > 0.999999) {
230 break;
231 }
232 if (result > 1e-7) {
233 cdf_.push_back({i, result, cumulative});
234 }
235 last_result = result;
236 }
237 ABSL_ASSERT(!cdf_.empty());
238 }
239
240 // PoissonDistributionZTest implements a z-test for the poisson distribution.
241
242 struct ZParam {
243 double mean;
244 double p_fail; // Z-Test probability of failure.
245 int trials; // Z-Test trials.
246 size_t samples; // Z-Test samples.
247 };
248
249 class PoissonDistributionZTest : public testing::TestWithParam<ZParam>,
250 public PoissonModel {
251 public:
PoissonDistributionZTest()252 PoissonDistributionZTest() : PoissonModel(GetParam().mean) {}
253
254 // ZTestImpl provides a basic z-squared test of the mean vs. expected
255 // mean for data generated by the poisson distribution.
256 template <typename D>
257 bool SingleZTest(const double p, const size_t samples);
258
259 // We use a fixed bit generator for distribution accuracy tests. This allows
260 // these tests to be deterministic, while still testing the qualify of the
261 // implementation.
262 absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
263 };
264
265 template <typename D>
SingleZTest(const double p,const size_t samples)266 bool PoissonDistributionZTest::SingleZTest(const double p,
267 const size_t samples) {
268 D dis(mean());
269
270 absl::flat_hash_map<int32_t, int> buckets;
271 std::vector<double> data;
272 data.reserve(samples);
273 for (int j = 0; j < samples; j++) {
274 const auto x = dis(rng_);
275 buckets[x]++;
276 data.push_back(x);
277 }
278
279 // The null-hypothesis is that the distribution is a poisson distribution with
280 // the provided mean (not estimated from the data).
281 const auto m = absl::random_internal::ComputeDistributionMoments(data);
282 const double max_err = absl::random_internal::MaxErrorTolerance(p);
283 const double z = absl::random_internal::ZScore(mean(), m);
284 const bool pass = absl::random_internal::Near("z", z, 0.0, max_err);
285
286 if (!pass) {
287 // clang-format off
288 LOG(INFO)
289 << "p=" << p << " max_err=" << max_err << "\n"
290 " mean=" << m.mean << " vs. " << mean() << "\n"
291 " stddev=" << std::sqrt(m.variance) << " vs. " << stddev() << "\n"
292 " skewness=" << m.skewness << " vs. " << skew() << "\n"
293 " kurtosis=" << m.kurtosis << " vs. " << kurtosis() << "\n"
294 " z=" << z;
295 // clang-format on
296 }
297 return pass;
298 }
299
TEST_P(PoissonDistributionZTest,AbslPoissonDistribution)300 TEST_P(PoissonDistributionZTest, AbslPoissonDistribution) {
301 const auto& param = GetParam();
302 const int expected_failures =
303 std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail)));
304 const double p = absl::random_internal::RequiredSuccessProbability(
305 param.p_fail, param.trials);
306
307 int failures = 0;
308 for (int i = 0; i < param.trials; i++) {
309 failures +=
310 SingleZTest<absl::poisson_distribution<int32_t>>(p, param.samples) ? 0
311 : 1;
312 }
313 EXPECT_LE(failures, expected_failures);
314 }
315
GetZParams()316 std::vector<ZParam> GetZParams() {
317 // These values have been adjusted from the "exact" computed values to reduce
318 // failure rates.
319 //
320 // It turns out that the actual values are not as close to the expected values
321 // as would be ideal.
322 return std::vector<ZParam>({
323 // Knuth method.
324 ZParam{0.5, 0.01, 100, 1000},
325 ZParam{1.0, 0.01, 100, 1000},
326 ZParam{10.0, 0.01, 100, 5000},
327 // Split-knuth method.
328 ZParam{20.0, 0.01, 100, 10000},
329 ZParam{50.0, 0.01, 100, 10000},
330 // Ratio of gaussians method.
331 ZParam{51.0, 0.01, 100, 10000},
332 ZParam{200.0, 0.05, 10, 100000},
333 ZParam{100000.0, 0.05, 10, 1000000},
334 });
335 }
336
ZParamName(const::testing::TestParamInfo<ZParam> & info)337 std::string ZParamName(const ::testing::TestParamInfo<ZParam>& info) {
338 const auto& p = info.param;
339 std::string name = absl::StrCat("mean_", absl::SixDigits(p.mean));
340 return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
341 }
342
343 INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionZTest,
344 ::testing::ValuesIn(GetZParams()), ZParamName);
345
346 // The PoissonDistributionChiSquaredTest class provides a basic test framework
347 // for variates generated by a conforming poisson_distribution.
348 class PoissonDistributionChiSquaredTest : public testing::TestWithParam<double>,
349 public PoissonModel {
350 public:
PoissonDistributionChiSquaredTest()351 PoissonDistributionChiSquaredTest() : PoissonModel(GetParam()) {}
352
353 // The ChiSquaredTestImpl provides a chi-squared goodness of fit test for data
354 // generated by the poisson distribution.
355 template <typename D>
356 double ChiSquaredTestImpl();
357
358 private:
359 void InitChiSquaredTest(const double buckets);
360
361 std::vector<size_t> cutoffs_;
362 std::vector<double> expected_;
363
364 // We use a fixed bit generator for distribution accuracy tests. This allows
365 // these tests to be deterministic, while still testing the qualify of the
366 // implementation.
367 absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6};
368 };
369
InitChiSquaredTest(const double buckets)370 void PoissonDistributionChiSquaredTest::InitChiSquaredTest(
371 const double buckets) {
372 if (!cutoffs_.empty() && !expected_.empty()) {
373 return;
374 }
375 InitCDF();
376
377 // The code below finds cuttoffs that yield approximately equally-sized
378 // buckets to the extent that it is possible. However for poisson
379 // distributions this is particularly challenging for small mean parameters.
380 // Track the expected proportion of items in each bucket.
381 double last_cdf = 0;
382 const double inc = 1.0 / buckets;
383 for (double p = inc; p <= 1.0; p += inc) {
384 auto result = InverseCDF(p);
385 if (!cutoffs_.empty() && cutoffs_.back() == result.index) {
386 continue;
387 }
388 double d = result.cdf - last_cdf;
389 cutoffs_.push_back(result.index);
390 expected_.push_back(d);
391 last_cdf = result.cdf;
392 }
393 cutoffs_.push_back(std::numeric_limits<size_t>::max());
394 expected_.push_back(std::max(0.0, 1.0 - last_cdf));
395 }
396
397 template <typename D>
ChiSquaredTestImpl()398 double PoissonDistributionChiSquaredTest::ChiSquaredTestImpl() {
399 const int kSamples = 2000;
400 const int kBuckets = 50;
401
402 // The poisson CDF fails for large mean values, since e^-mean exceeds the
403 // machine precision. For these cases, using a normal approximation would be
404 // appropriate.
405 ABSL_ASSERT(mean() <= 200);
406 InitChiSquaredTest(kBuckets);
407
408 D dis(mean());
409
410 std::vector<int32_t> counts(cutoffs_.size(), 0);
411 for (int j = 0; j < kSamples; j++) {
412 const size_t x = dis(rng_);
413 auto it = std::lower_bound(std::begin(cutoffs_), std::end(cutoffs_), x);
414 counts[std::distance(cutoffs_.begin(), it)]++;
415 }
416
417 // Normalize the counts.
418 std::vector<int32_t> e(expected_.size(), 0);
419 for (int i = 0; i < e.size(); i++) {
420 e[i] = kSamples * expected_[i];
421 }
422
423 // The null-hypothesis is that the distribution is a poisson distribution with
424 // the provided mean (not estimated from the data).
425 const int dof = static_cast<int>(counts.size()) - 1;
426
427 // The threshold for logging is 1-in-50.
428 const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98);
429
430 const double chi_square = absl::random_internal::ChiSquare(
431 std::begin(counts), std::end(counts), std::begin(e), std::end(e));
432
433 const double p = absl::random_internal::ChiSquarePValue(chi_square, dof);
434
435 // Log if the chi_squared value is above the threshold.
436 if (chi_square > threshold) {
437 LogCDF();
438
439 LOG(INFO) << "VALUES buckets=" << counts.size()
440 << " samples=" << kSamples;
441 for (size_t i = 0; i < counts.size(); i++) {
442 LOG(INFO) << cutoffs_[i] << ": " << counts[i] << " vs. E=" << e[i];
443 }
444
445 LOG(INFO) << kChiSquared << "(data, dof=" << dof << ") = " << chi_square
446 << " (" << p << ")\n"
447 << " vs.\n"
448 << kChiSquared << " @ 0.98 = " << threshold;
449 }
450 return p;
451 }
452
TEST_P(PoissonDistributionChiSquaredTest,AbslPoissonDistribution)453 TEST_P(PoissonDistributionChiSquaredTest, AbslPoissonDistribution) {
454 const int kTrials = 20;
455
456 // Large values are not yet supported -- this requires estimating the cdf
457 // using the normal distribution instead of the poisson in this case.
458 ASSERT_LE(mean(), 200.0);
459 if (mean() > 200.0) {
460 return;
461 }
462
463 int failures = 0;
464 for (int i = 0; i < kTrials; i++) {
465 double p_value = ChiSquaredTestImpl<absl::poisson_distribution<int32_t>>();
466 if (p_value < 0.005) {
467 failures++;
468 }
469 }
470 // There is a 0.10% chance of producing at least one failure, so raise the
471 // failure threshold high enough to allow for a flake rate < 10,000.
472 EXPECT_LE(failures, 4);
473 }
474
475 INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionChiSquaredTest,
476 ::testing::Values(0.5, 1.0, 2.0, 10.0, 50.0, 51.0,
477 200.0));
478
479 // NOTE: absl::poisson_distribution is not guaranteed to be stable.
TEST(PoissonDistributionTest,StabilityTest)480 TEST(PoissonDistributionTest, StabilityTest) {
481 using testing::ElementsAre;
482 // absl::poisson_distribution stability relies on stability of
483 // std::exp, std::log, std::sqrt, std::ceil, std::floor, and
484 // absl::FastUniformBits, absl::StirlingLogFactorial, absl::RandU64ToDouble.
485 absl::random_internal::sequence_urbg urbg({
486 0x035b0dc7e0a18acfull, 0x06cebe0d2653682eull, 0x0061e9b23861596bull,
487 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
488 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
489 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
490 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull,
491 0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull,
492 0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull,
493 0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull,
494 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full,
495 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull,
496 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull,
497 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull,
498 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull,
499 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull, 0xffe6ea4d6edb0c73ull,
500 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull, 0xEAAD8E716B93D5A0ull,
501 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull, 0x8FF6E2FBF2122B64ull,
502 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull, 0xD1CFF191B3A8C1ADull,
503 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull, 0xE5A0CC0FB56F74E8ull,
504 0x18ACF3D6CE89E299ull, 0xB4A84FE0FD13E0B7ull, 0x7CC43B81D2ADA8D9ull,
505 0x165FA26680957705ull, 0x93CC7314211A1477ull, 0xE6AD206577B5FA86ull,
506 0xC75442F5FB9D35CFull, 0xEBCDAF0C7B3E89A0ull, 0xD6411BD3AE1E7E49ull,
507 0x00250E2D2071B35Eull, 0x226800BB57B8E0AFull, 0x2464369BF009B91Eull,
508 0x5563911D59DFA6AAull, 0x78C14389D95A537Full, 0x207D5BA202E5B9C5ull,
509 0x832603766295CFA9ull, 0x11C819684E734A41ull, 0xB3472DCA7B14A94Aull,
510 });
511
512 std::vector<int> output(10);
513
514 // Method 1.
515 {
516 absl::poisson_distribution<int> dist(5);
517 std::generate(std::begin(output), std::end(output),
518 [&] { return dist(urbg); });
519 }
520 EXPECT_THAT(output, // mean = 4.2
521 ElementsAre(1, 0, 0, 4, 2, 10, 3, 3, 7, 12));
522
523 // Method 2.
524 {
525 urbg.reset();
526 absl::poisson_distribution<int> dist(25);
527 std::generate(std::begin(output), std::end(output),
528 [&] { return dist(urbg); });
529 }
530 EXPECT_THAT(output, // mean = 19.8
531 ElementsAre(9, 35, 18, 10, 35, 18, 10, 35, 18, 10));
532
533 // Method 3.
534 {
535 urbg.reset();
536 absl::poisson_distribution<int> dist(121);
537 std::generate(std::begin(output), std::end(output),
538 [&] { return dist(urbg); });
539 }
540 EXPECT_THAT(output, // mean = 124.1
541 ElementsAre(161, 122, 129, 124, 112, 112, 117, 120, 130, 114));
542 }
543
TEST(PoissonDistributionTest,AlgorithmExpectedValue_1)544 TEST(PoissonDistributionTest, AlgorithmExpectedValue_1) {
545 // This tests small values of the Knuth method.
546 // The underlying uniform distribution will generate exactly 0.5.
547 absl::random_internal::sequence_urbg urbg({0x8000000000000001ull});
548 absl::poisson_distribution<int> dist(5);
549 EXPECT_EQ(7, dist(urbg));
550 }
551
TEST(PoissonDistributionTest,AlgorithmExpectedValue_2)552 TEST(PoissonDistributionTest, AlgorithmExpectedValue_2) {
553 // This tests larger values of the Knuth method.
554 // The underlying uniform distribution will generate exactly 0.5.
555 absl::random_internal::sequence_urbg urbg({0x8000000000000001ull});
556 absl::poisson_distribution<int> dist(25);
557 EXPECT_EQ(36, dist(urbg));
558 }
559
TEST(PoissonDistributionTest,AlgorithmExpectedValue_3)560 TEST(PoissonDistributionTest, AlgorithmExpectedValue_3) {
561 // This variant uses the ratio of uniforms method.
562 absl::random_internal::sequence_urbg urbg(
563 {0x7fffffffffffffffull, 0x8000000000000000ull});
564
565 absl::poisson_distribution<int> dist(121);
566 EXPECT_EQ(121, dist(urbg));
567 }
568
569 } // namespace
570