Blame mfbt/tests/TestFastBernoulliTrial.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
Packit f0b94e
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "mozilla/Assertions.h"
Packit f0b94e
#include "mozilla/FastBernoulliTrial.h"
Packit f0b94e
Packit f0b94e
#include <math.h>
Packit f0b94e
Packit f0b94e
// Note that because we always provide FastBernoulliTrial with a fixed
Packit f0b94e
// pseudorandom seed in these tests, the results here are completely
Packit f0b94e
// deterministic.
Packit f0b94e
//
Packit f0b94e
// A non-optimized version of this test runs in .009s on my laptop. Using larger
Packit f0b94e
// sample sizes lets us meet tighter bounds on the counts.
Packit f0b94e
Packit f0b94e
static void
Packit f0b94e
TestProportions()
Packit f0b94e
{
Packit f0b94e
  mozilla::FastBernoulliTrial bernoulli(1.0,
Packit f0b94e
                                        698079309544035222ULL,
Packit f0b94e
                                        6012389156611637584ULL);
Packit f0b94e
Packit f0b94e
  for (size_t i = 0; i < 100; i++)
Packit f0b94e
    MOZ_RELEASE_ASSERT(bernoulli.trial());
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    bernoulli.setProbability(0.5);
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 1000; i++)
Packit f0b94e
      count += bernoulli.trial();
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 496);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    bernoulli.setProbability(0.001);
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 1000; i++)
Packit f0b94e
      count += bernoulli.trial();
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 2);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    bernoulli.setProbability(0.85);
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 1000; i++)
Packit f0b94e
      count += bernoulli.trial();
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 852);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  bernoulli.setProbability(0.0);
Packit f0b94e
  for (size_t i = 0; i < 100; i++)
Packit f0b94e
    MOZ_RELEASE_ASSERT(!bernoulli.trial());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void
Packit f0b94e
TestHarmonics()
Packit f0b94e
{
Packit f0b94e
  mozilla::FastBernoulliTrial bernoulli(0.1,
Packit f0b94e
                                        698079309544035222ULL,
Packit f0b94e
                                        6012389156611637584ULL);
Packit f0b94e
Packit f0b94e
  const size_t n = 100000;
Packit f0b94e
  bool trials[n];
Packit f0b94e
  for (size_t i = 0; i < n; i++)
Packit f0b94e
    trials[i] = bernoulli.trial();
Packit f0b94e
Packit f0b94e
  // For each harmonic and phase, check that the proportion sampled is
Packit f0b94e
  // within acceptable bounds.
Packit f0b94e
  for (size_t harmonic = 1; harmonic < 20; harmonic++) {
Packit f0b94e
    size_t expected = n / harmonic / 10;
Packit f0b94e
    size_t low_expected = expected * 85 / 100;
Packit f0b94e
    size_t high_expected = expected * 115 / 100;
Packit f0b94e
Packit f0b94e
    for (size_t phase = 0; phase < harmonic; phase++) {
Packit f0b94e
      size_t count = 0;
Packit f0b94e
      for (size_t i = phase; i < n; i += harmonic)
Packit f0b94e
        count += trials[i];
Packit f0b94e
Packit f0b94e
      MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void
Packit f0b94e
TestTrialN()
Packit f0b94e
{
Packit f0b94e
  mozilla::FastBernoulliTrial bernoulli(0.01,
Packit f0b94e
                                        0x67ff17e25d855942ULL,
Packit f0b94e
                                        0x74f298193fe1c5b1ULL);
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 10000; i++)
Packit f0b94e
      count += bernoulli.trial(1);
Packit f0b94e
Packit f0b94e
    // Expected value: 0.01 * 10000 == 100
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 97);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 10000; i++)
Packit f0b94e
      count += bernoulli.trial(3);
Packit f0b94e
Packit f0b94e
    // Expected value: (1 - (1 - 0.01) ** 3) == 0.0297,
Packit f0b94e
    // 0.0297 * 10000 == 297
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 304);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 10000; i++)
Packit f0b94e
      count += bernoulli.trial(10);
Packit f0b94e
Packit f0b94e
    // Expected value: (1 - (1 - 0.01) ** 10) == 0.0956,
Packit f0b94e
    // 0.0956 * 10000 == 956
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 936);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 10000; i++)
Packit f0b94e
      count += bernoulli.trial(100);
Packit f0b94e
Packit f0b94e
    // Expected value: (1 - (1 - 0.01) ** 100) == 0.6339
Packit f0b94e
    // 0.6339 * 10000 == 6339
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 6372);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  {
Packit f0b94e
    size_t count = 0;
Packit f0b94e
    for (size_t i = 0; i < 10000; i++)
Packit f0b94e
      count += bernoulli.trial(1000);
Packit f0b94e
Packit f0b94e
    // Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999
Packit f0b94e
    // 0.9999 * 10000 == 9999
Packit f0b94e
    MOZ_RELEASE_ASSERT(count == 9998);
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void
Packit f0b94e
TestChangeProbability()
Packit f0b94e
{
Packit f0b94e
  mozilla::FastBernoulliTrial bernoulli(1.0,
Packit f0b94e
                                        0x67ff17e25d855942ULL,
Packit f0b94e
                                        0x74f298193fe1c5b1ULL);
Packit f0b94e
Packit f0b94e
  // Establish a very high skip count.
Packit f0b94e
  bernoulli.setProbability(0.0);
Packit f0b94e
Packit f0b94e
  // This should re-establish a zero skip count.
Packit f0b94e
  bernoulli.setProbability(1.0);
Packit f0b94e
Packit f0b94e
  // So this should return true.
Packit f0b94e
  MOZ_RELEASE_ASSERT(bernoulli.trial());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void
Packit f0b94e
TestCuspProbabilities()
Packit f0b94e
{
Packit f0b94e
  /*
Packit f0b94e
   * FastBernoulliTrial takes care to avoid screwing up on edge cases. The
Packit f0b94e
   * checks here all look pretty dumb, but they exercise paths in the code that
Packit f0b94e
   * could exhibit undefined behavior if coded naïvely.
Packit f0b94e
   */
Packit f0b94e
Packit f0b94e
  /*
Packit f0b94e
   * This should not be perceptibly different from 1; for 64-bit doubles, this
Packit f0b94e
   * is a one in ten trillion chance of the trial not succeeding. Overflows
Packit f0b94e
   * converting doubles to size_t skip counts may change this, though.
Packit f0b94e
   */
Packit f0b94e
  mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0),
Packit f0b94e
                                        0x67ff17e25d855942ULL,
Packit f0b94e
                                        0x74f298193fe1c5b1ULL);
Packit f0b94e
Packit f0b94e
  for (size_t i = 0; i < 1000; i++)
Packit f0b94e
    MOZ_RELEASE_ASSERT(bernoulli.trial());
Packit f0b94e
Packit f0b94e
  /*
Packit f0b94e
   * This should not be perceptibly different from 0; for 64-bit doubles,
Packit f0b94e
   * the FastBernoulliTrial will actually treat this as exactly zero.
Packit f0b94e
   */
Packit f0b94e
  bernoulli.setProbability(nextafter(0, 1));
Packit f0b94e
  for (size_t i = 0; i < 1000; i++)
Packit f0b94e
    MOZ_RELEASE_ASSERT(!bernoulli.trial());
Packit f0b94e
Packit f0b94e
  /*
Packit f0b94e
   * This should be a vanishingly low probability which FastBernoulliTrial does
Packit f0b94e
   * *not* treat as exactly zero.
Packit f0b94e
   */
Packit f0b94e
  bernoulli.setProbability(1 - nextafter(1, 0));
Packit f0b94e
  for (size_t i = 0; i < 1000; i++)
Packit f0b94e
    MOZ_RELEASE_ASSERT(!bernoulli.trial());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
int
Packit f0b94e
main()
Packit f0b94e
{
Packit f0b94e
  TestProportions();
Packit f0b94e
  TestHarmonics();
Packit f0b94e
  TestTrialN();
Packit f0b94e
  TestChangeProbability();
Packit f0b94e
  TestCuspProbabilities();
Packit f0b94e
Packit f0b94e
  return 0;
Packit f0b94e
}