OCaml Bindings for TestU01

From TestU01 User’s guide, compact version:

> It is also possible to test one’s own or an external generator \(that is, a > generator that is not predefined in TestU01\) very easily ... as long as this > generator is programmed in C. >

Wrong. Behold: OCaml bindings for TestU01.

About TestU01:

About OCaml Bindings for TestU01:

Installation

The recommended way to install these bindings is via OPAM:

opam install testu01

The package comes with its embedded version of the C library, so there is no external dependency required. This means that it should also be trivial to build and install these bindings the traditional way, with a simple:

make
make install

API

The OCaml API remains as close to that of TestU01 as possible. Some changes are still introduced to provide a more “OCaml-like” interface. The differences are the following:

About the name changes, here are a few examples. The binding for the function:

void bbattery_RepeatBigCrush(unif01_Gen* gen, int* rep);

will be:

val Testu01.Bbattery.repeat_big_crush : Unif01.gen -> int array -> unit

and the binding for the variable:

extern double gofw_Suspectp;

will be:

val Probdist.Gofw.get_suspectp : unit -> float
val Probdist.Gofw.set_suspectp : float -> unit

Example Use

Let us say we want to asses the quality of the PRNG in OCaml's standard library. We can simply write the following file:

open TestU01
open Probdist
let () =
  let gen = Unif01.create_extern_gen_bits "stdlib" Random.bits in
  Gofw.set_suspectp 0.01;
  Bbattery.crush gen

This creates a TestU01 generator of type Unif01.gen out of a function unit -> bits, sets the threshold for suspect p-values to 0.01 \(that is optional, the default is 0.001\) and applies the test battery "crush" to the generator. It can then be compiled to an executable that takes about 40 minutes to run and writes a report on the standard output, similar to the following:

      Test                          p-value
----------------------------------------------
11  BirthdaySpacings, t = 2          eps
31  Gap, r = 0                       eps
33  Gap, r = 0                       eps
47  SampleMean                      0.9940
51  WeightDistrib, r = 0             eps
52  WeightDistrib, r = 8             eps
53  WeightDistrib, r = 16            eps
83  HammingCorr, L = 300            8.6e-3
86  HammingIndep, L = 30            0.9945
----------------------------------------------

Some tests \(with p-value eps\) do not pass. For some tests, the p-value is suspicious \(that is, inferior to suspectp or bigger than 1 - suspectp but not eps nor 1 - eps\), but we cannot really conclude anything from it. Luckily, TestU01 provides Repeat* variants for its test batteries. They allow to provide, via an array, the number of times each test must be run. Let us repeat the 47th test, “SampleMean”, the 83rd test, “HammingCorr with L = 300”, and the 86th test, “HammingIndep with L = 30”. We will repeat them 10 times each, and the others 0 times, to decide whether the p-value is indeed suspicious or if it was an unlucky artefact. For that, we can write the following file:

open TestU01
open Probdist
let () =
  let gen = Unif01.create_extern_gen_bits "stdlib" Random.bits in
  Gofw.set_suspectp 0.01;
  let rep = Array.make (1 + Bbattery.ntests_crush) 0 in
  rep.(47) <- 10;
  rep.(83) <- 10;
  rep.(86) <- 10;
  Bbattery.repeat_crush gen rep

Running it takes about 5 minutes and writes the following report:

All tests were passed

This means that all these tests were indeed unlucky artefacts. \(The other tests, with p-value eps, definitely do not.\)

For more information, we encourage you to refer to TestU01's user's guide.

Examples From TestU01's Documentation

TestU01's User Guide includes several examples. Here are some with their OCaml version. These examples as well as their C version and the expected result can be found in the examples directory.

Figure 4.1

In the User Guide:

#include "ulcg.h"
#include "unif01.h"
#include "bbattery.h"

int main (void)
{
   unif01_Gen *gen;
   gen = ulcg_CreateLCG (2147483647, 16807, 0, 12345);
   bbattery_SmallCrush (gen);
   ulcg_DeleteGen (gen);
   return 0;
}

With these bindings:

open TestU01

let () =
  let gen = Ulcg.create_lcg 2147483647 16807 0 12345 in
  Bbattery.small_crush gen

Figure 4.3

In the User Guide:

#include "gdef.h"
#include "swrite.h"
#include "bbattery.h"

int main (void)
{
   swrite_Basic = FALSE;
   bbattery_RabbitFile ("vax.bin", 1048576);
   return 0;
}

With these bindings:

open TestU01

let () =
  Swrite.set_basic false;
  Bbattery.rabbit_file "vax.bin" 1048576.

You can refer to the examples directory which contains examples from TestU01 as well as their OCaml version.

License

Copyright \(C\) 2020-2021 Martin Pépin, Nicolas “Niols” Jeannerod

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or \(at your option\) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.

FAQ

Missing Bindings

We add bindings on-demand and our use is mainly oriented towards test batteries. So if some bindings do not appear, it is simply that we did not have any use for them. Add them \(PR are welcome\), or ask that we do!