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:
- Documentation
- API Documentation: TestU01, Probdist
- GitHub Repository
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
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:
-
The names have changed:
-
The library is all under two main modules,
TestU01
andProbdist
. The documentation of these modules can be found here and here respectively. -
The functions, variables and types are not prefixed by the part of the library they belong to, but are in a corresponding module.
-
The names
are_with_underscore
andNotInCamelcase
.
-
-
The global variables are read and modified using getters and setters.
-
The creation of external generators is done with three functions similar to that of the C library. In the module
TestU01
:-
Unif01.create_extern_gen_bits
takes an OCaml “bits” function, that is a function that returns an integer between0
and2^30
. This is the case ofRandom.bits
, for instance. -
Unif01.create_extern_gen_int32
takes an OCaml function that returns anint32
. Note that such a function is expected to provided all 32 bits. In particular,fun () -> Random.int32 Int32.max_int
is not a good candidate. -
Unif01.create_extern_gen_01
takes an OCaml function that returns a float between0
and1
.
-
-
The destruction of external generators is let to OCaml's garbage collector and cannot be done by hand.
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
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.
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.
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
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.
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/.
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!