tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }