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