TestFastBernoulliTrial.cpp (5436B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Assertions.h" 8 #include "mozilla/FastBernoulliTrial.h" 9 10 #include <math.h> 11 12 // Note that because we always provide FastBernoulliTrial with a fixed 13 // pseudorandom seed in these tests, the results here are completely 14 // deterministic. 15 // 16 // A non-optimized version of this test runs in .009s on my laptop. Using larger 17 // sample sizes lets us meet tighter bounds on the counts. 18 19 static void TestProportions() { 20 mozilla::FastBernoulliTrial bernoulli(1.0, 698079309544035222ULL, 21 6012389156611637584ULL); 22 23 for (size_t i = 0; i < 100; i++) MOZ_RELEASE_ASSERT(bernoulli.trial()); 24 25 { 26 bernoulli.setProbability(0.5); 27 size_t count = 0; 28 for (size_t i = 0; i < 1000; i++) count += bernoulli.trial(); 29 MOZ_RELEASE_ASSERT(count == 496); 30 } 31 32 { 33 bernoulli.setProbability(0.001); 34 size_t count = 0; 35 for (size_t i = 0; i < 1000; i++) count += bernoulli.trial(); 36 MOZ_RELEASE_ASSERT(count == 2); 37 } 38 39 { 40 bernoulli.setProbability(0.85); 41 size_t count = 0; 42 for (size_t i = 0; i < 1000; i++) count += bernoulli.trial(); 43 MOZ_RELEASE_ASSERT(count == 852); 44 } 45 46 bernoulli.setProbability(0.0); 47 for (size_t i = 0; i < 100; i++) MOZ_RELEASE_ASSERT(!bernoulli.trial()); 48 } 49 50 static void TestHarmonics() { 51 mozilla::FastBernoulliTrial bernoulli(0.1, 698079309544035222ULL, 52 6012389156611637584ULL); 53 54 const size_t n = 100000; 55 bool trials[n]; 56 for (size_t i = 0; i < n; i++) trials[i] = bernoulli.trial(); 57 58 // For each harmonic and phase, check that the proportion sampled is 59 // within acceptable bounds. 60 for (size_t harmonic = 1; harmonic < 20; harmonic++) { 61 size_t expected = n / harmonic / 10; 62 size_t low_expected = expected * 85 / 100; 63 size_t high_expected = expected * 115 / 100; 64 65 for (size_t phase = 0; phase < harmonic; phase++) { 66 size_t count = 0; 67 for (size_t i = phase; i < n; i += harmonic) count += trials[i]; 68 69 MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected); 70 } 71 } 72 } 73 74 static void TestTrialN() { 75 mozilla::FastBernoulliTrial bernoulli(0.01, 0x67ff17e25d855942ULL, 76 0x74f298193fe1c5b1ULL); 77 78 { 79 size_t count = 0; 80 for (size_t i = 0; i < 10000; i++) count += bernoulli.trial(1); 81 82 // Expected value: 0.01 * 10000 == 100 83 MOZ_RELEASE_ASSERT(count == 97); 84 } 85 86 { 87 size_t count = 0; 88 for (size_t i = 0; i < 10000; i++) count += bernoulli.trial(3); 89 90 // Expected value: (1 - (1 - 0.01) ** 3) == 0.0297, 91 // 0.0297 * 10000 == 297 92 MOZ_RELEASE_ASSERT(count == 304); 93 } 94 95 { 96 size_t count = 0; 97 for (size_t i = 0; i < 10000; i++) count += bernoulli.trial(10); 98 99 // Expected value: (1 - (1 - 0.01) ** 10) == 0.0956, 100 // 0.0956 * 10000 == 956 101 MOZ_RELEASE_ASSERT(count == 936); 102 } 103 104 { 105 size_t count = 0; 106 for (size_t i = 0; i < 10000; i++) count += bernoulli.trial(100); 107 108 // Expected value: (1 - (1 - 0.01) ** 100) == 0.6339 109 // 0.6339 * 10000 == 6339 110 MOZ_RELEASE_ASSERT(count == 6372); 111 } 112 113 { 114 size_t count = 0; 115 for (size_t i = 0; i < 10000; i++) count += bernoulli.trial(1000); 116 117 // Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999 118 // 0.9999 * 10000 == 9999 119 MOZ_RELEASE_ASSERT(count == 9998); 120 } 121 } 122 123 static void TestChangeProbability() { 124 mozilla::FastBernoulliTrial bernoulli(1.0, 0x67ff17e25d855942ULL, 125 0x74f298193fe1c5b1ULL); 126 127 // Establish a very high skip count. 128 bernoulli.setProbability(0.0); 129 130 // This should re-establish a zero skip count. 131 bernoulli.setProbability(1.0); 132 133 // So this should return true. 134 MOZ_RELEASE_ASSERT(bernoulli.trial()); 135 } 136 137 static void TestCuspProbabilities() { 138 /* 139 * FastBernoulliTrial takes care to avoid screwing up on edge cases. The 140 * checks here all look pretty dumb, but they exercise paths in the code that 141 * could exhibit undefined behavior if coded naïvely. 142 */ 143 144 /* 145 * This should not be perceptibly different from 1; for 64-bit doubles, this 146 * is a one in ten trillion chance of the trial not succeeding. Overflows 147 * converting doubles to size_t skip counts may change this, though. 148 */ 149 mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0), 0x67ff17e25d855942ULL, 150 0x74f298193fe1c5b1ULL); 151 152 for (size_t i = 0; i < 1000; i++) MOZ_RELEASE_ASSERT(bernoulli.trial()); 153 154 /* 155 * This should not be perceptibly different from 0; for 64-bit doubles, 156 * the FastBernoulliTrial will actually treat this as exactly zero. 157 */ 158 bernoulli.setProbability(nextafter(0, 1)); 159 for (size_t i = 0; i < 1000; i++) MOZ_RELEASE_ASSERT(!bernoulli.trial()); 160 161 /* 162 * This should be a vanishingly low probability which FastBernoulliTrial does 163 * *not* treat as exactly zero. 164 */ 165 bernoulli.setProbability(1 - nextafter(1, 0)); 166 for (size_t i = 0; i < 1000; i++) MOZ_RELEASE_ASSERT(!bernoulli.trial()); 167 } 168 169 int main() { 170 TestProportions(); 171 TestHarmonics(); 172 TestTrialN(); 173 TestChangeProbability(); 174 TestCuspProbabilities(); 175 176 return 0; 177 }