[m-rev.] RNGs

Mark Brown mark at mercurylang.org
Sun Aug 18 20:42:31 AEST 2019


Hi,

Attached is a patch for a set of instances that implement the SFC generator (see
http://pracrand.sourceforge.net/). I've added this to the pull request.

On PractRand tests these outperform MT19937, aka Mersenne Twister,
although with a disclaimer that the author of the analysis tool is
also the author of the RNG.

Mark
-------------- next part --------------
commit 46a423ce5b99e2c968163006600ddf088c176f59
Author: Mark Brown <mark at mercurylang.org>
Date:   Sun Aug 18 20:24:14 2019 +1000

    Implement SFC random number generators, in various sizes
    
    library/rng.sfc.m:
    	New module that implements the above.
    
    library/MODULES_DOC:
    library/library.m:
    library/rng.m:
    	Add the new library module.
    
    library/uint16.m:
    	Add functions to cast to/from uint64.
    
    tests/hard_coded/Mmakefile:
    tests/hard_coded/*.*:
    	Update/extend tests.

diff --git a/library/MODULES_DOC b/library/MODULES_DOC
index 822524e..391c51e 100644
--- a/library/MODULES_DOC
+++ b/library/MODULES_DOC
@@ -64,6 +64,7 @@ require.m
 rng.m
 rng.binfile.m
 rng.marsaglia.m
+rng.sfc.m
 rng.tausworthe.m
 rtree.m
 set.m
diff --git a/library/library.m b/library/library.m
index 9b79605..9b438d9 100644
--- a/library/library.m
+++ b/library/library.m
@@ -127,6 +127,7 @@
 :- import_module rng.
 :- import_module rng.binfile.
 :- import_module rng.marsaglia.
+:- import_module rng.sfc.
 :- import_module rng.tausworthe.
 :- import_module robdd.
 :- import_module rtree.
@@ -316,6 +317,7 @@ mercury_std_library_module("require").
 mercury_std_library_module("rng").
 mercury_std_library_module("rng.binfile").
 mercury_std_library_module("rng.marsaglia").
+mercury_std_library_module("rng.sfc").
 mercury_std_library_module("rng.tausworthe").
 mercury_std_library_module("robdd").
 mercury_std_library_module("rtree").
diff --git a/library/rng.m b/library/rng.m
index 24e8086..fbca357 100644
--- a/library/rng.m
+++ b/library/rng.m
@@ -82,6 +82,7 @@
 
 :- include_module binfile.
 :- include_module marsaglia.
+:- include_module sfc.
 :- include_module tausworthe.
 
 %---------------------------------------------------------------------------%
diff --git a/library/rng.sfc.m b/library/rng.sfc.m
new file mode 100644
index 0000000..15d240d
--- /dev/null
+++ b/library/rng.sfc.m
@@ -0,0 +1,333 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sts=4 sw=4 et
+%---------------------------------------------------------------------------%
+% Copyright (C) 2019 The Mercury team.
+% This file is distributed under the terms specified in COPYING.LIB.
+%---------------------------------------------------------------------------%
+%
+% File: rng.sfc.m
+% Main author: Mark Brown
+%
+% Small Fast Counting generators, by Chris Doty-Humphrey.
+%
+% http://pracrand.sourceforge.net/
+%
+% From the above:
+% "[A] good small chaotic RNG driven by a bad smaller linear RNG. The
+% combination gives it the strengths of each - good chaotic behavior,
+% but enough structure to avoid short cycles."
+%
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- module rng.sfc.
+:- interface.
+
+%---------------------------------------------------------------------------%
+
+    % A fast, 16-bit SFC generator.
+    %
+:- type sfc.
+
+:- instance rng(sfc).
+
+    % Initialise a 16-bit SFC RNG with the default seed.
+    %
+:- func init16 = sfc.
+
+    % Initialise a 16-bit SFC RNG with the given seed.
+    %
+:- func seed16(uint64) = sfc.
+
+    % Generate a random number between 0 and max_uint16.
+    %
+:- pred rand16(uint16, sfc, sfc).
+:- mode rand16(out, in, out) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+    % A fast, 64-bit SFC generator with unique state.
+    %
+:- type params.
+:- type state.
+
+:- instance urng(params, state).
+:- instance urng_dup(state).
+
+    % Initialise a 64-bit SFC RNG with the default seed.
+    %
+:- pred init(params, state).
+:- mode init(out, uo) is det.
+
+    % Initialise a 64-bit SFC RNG with the given seed.
+    %
+:- pred seed(uint64, uint64, uint64, params, state).
+:- mode seed(in, in, in, out, uo) is det.
+
+%---------------------------------------------------------------------------%
+
+    % Generate a random number between 0 and max_uint64. Note that the
+    % params are not required for this RNG unless calling via the
+    % typeclass interface.
+    %
+:- pred rand(uint64, state, state).
+:- mode rand(out, di, uo) is det.
+
+    % Duplicate a 64-bit SFC state.
+    %
+:- pred dup(state, state, state).
+:- mode dup(di, uo, uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+    % A fast, 32-bit SFC generator with unique state. This may achieve
+    % better performance on 32-bit architectures, but generally does not
+    % have the quality of the 64-bit generator or the low heap usage of
+    % the 16-bit generator.
+    %
+:- type params32.
+:- type state32.
+
+:- instance urng(params32, state32).
+:- instance urng_dup(state32).
+
+    % Initialise a 32-bit SFC RNG with the default seed.
+    %
+:- pred init32(params32, state32).
+:- mode init32(out, uo) is det.
+
+    % Initialise a 32-bit SFC RNG with the given seed.
+    %
+:- pred seed32(uint32, uint32, uint32, params32, state32).
+:- mode seed32(in, in, in, out, uo) is det.
+
+%---------------------------------------------------------------------------%
+
+    % Generate a random number between 0 and max_uint32. Note that the
+    % params are not required for this RNG unless calling via the
+    % typeclass interface.
+    %
+:- pred rand32(uint32, state32, state32).
+:- mode rand32(out, di, uo) is det.
+
+    % Duplicate a 32-bit SFC state.
+    %
+:- pred dup32(state32, state32, state32).
+:- mode dup32(di, uo, uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module array.
+:- import_module int.
+:- import_module list.
+:- import_module uint16.
+:- import_module uint32.
+:- import_module uint64.
+
+%---------------------------------------------------------------------------%
+
+:- type sfc
+    --->    sfc(uint64).
+
+:- instance rng(sfc) where [
+    ( random(N, !RNG) :-
+        rand16(N0, !RNG),
+        N = uint16.cast_to_uint64(N0)
+    ),
+    ( random_max(_) = uint16.cast_to_uint64(uint16.max_uint16) )
+].
+
+init16 = seed16(0x6048_5623_5e79_371e_u64).
+
+seed16(Seed) = RNG :-
+    skip16(10, sfc(Seed), RNG).
+
+:- pred skip16(int, sfc, sfc).
+:- mode skip16(in, in, out) is det.
+
+skip16(N, !RNG) :-
+    ( if N > 0 then
+        rand16(_, !RNG),
+        skip16(N - 1, !RNG)
+    else
+        true
+    ).
+
+%---------------------------------------------------------------------------%
+
+rand16(N, sfc(S0), sfc(S)) :-
+    unpack_uint64(S0, A0, B0, C0, Counter0),
+    N = A0 + B0 + Counter0,
+    A = B0 `xor` (B0 >> 5),
+    B = C0 + (C0 << 3),
+    C = ((C0 << 6) \/ (C0 >> 10)) + N,
+    Counter = Counter0 + 1u16,
+    S = pack_uint64(A, B, C, Counter).
+
+:- func pack_uint64(uint16, uint16, uint16, uint16) = uint64.
+
+pack_uint64(P1, P2, P3, P4) =
+    (uint16.cast_to_uint64(P1) << 48) +
+    (uint16.cast_to_uint64(P2) << 32) +
+    (uint16.cast_to_uint64(P3) << 16) +
+    uint16.cast_to_uint64(P4).
+
+:- pred unpack_uint64(uint64, uint16, uint16, uint16, uint16).
+:- mode unpack_uint64(in, out, out, out, out) is det.
+
+unpack_uint64(S, P1, P2, P3, P4) :-
+    Mask = 0xffffu64,
+    P1 = uint16.cast_from_uint64(S >> 48),
+    P2 = uint16.cast_from_uint64((S >> 32) /\ Mask),
+    P3 = uint16.cast_from_uint64((S >> 16) /\ Mask),
+    P4 = uint16.cast_from_uint64(S /\ Mask).
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- type params
+    --->    params.
+
+:- type state
+    --->    state(array(uint64)).
+
+:- instance urng(params, state) where [
+    ( urandom(_, N, !RS) :-
+        rand(N, !RS)
+    ),
+    ( urandom_max(_) = uint64.max_uint64 )
+].
+
+:- instance urng_dup(state) where [
+    pred(urandom_dup/3) is dup
+].
+
+dup(S, S1, S2) :-
+    S = state(A),
+    Sc = state(array.copy(A)),
+    S1 = unsafe_promise_unique(S),
+    S2 = unsafe_promise_unique(Sc).
+
+%---------------------------------------------------------------------------%
+
+init(RP, RS) :-
+    sfc.seed(
+        0x9578_32f2_b9e1_43b1_u64,
+        0x9578_32f2_b9e1_43b1_u64,
+        0x9578_32f2_b9e1_43b1_u64,
+        RP, RS).
+
+seed(A, B, C, params, RS) :-
+    Counter = 1u64,
+    S0 = array([A, B, C, Counter]),
+    RS0 = unsafe_promise_unique(state(S0)),
+    skip(18, RS0, RS).
+
+:- pred skip(int, state, state).
+:- mode skip(in, di, uo) is det.
+
+skip(N, !RS) :-
+    ( if N > 0 then
+        rand(_, !RS),
+        skip(N - 1, !RS)
+    else
+        true
+    ).
+
+%---------------------------------------------------------------------------%
+
+rand(N, RS0, RS) :-
+    RS0 = state(S0),
+    array.unsafe_lookup(S0, 0, A0),
+    array.unsafe_lookup(S0, 1, B0),
+    array.unsafe_lookup(S0, 2, C0),
+    array.unsafe_lookup(S0, 3, Counter0),
+    N = A0 + B0 + Counter0,
+    A = B0 `xor` (B0 >> 11),
+    B = C0 + (C0 << 3),
+    C = ((C0 << 24) \/ (C0 >> 40)) + N,
+    Counter = Counter0 + 1u64,
+    array.unsafe_set(0, A, S0, S1),
+    array.unsafe_set(1, B, S1, S2),
+    array.unsafe_set(2, C, S2, S3),
+    array.unsafe_set(3, Counter, S3, S),
+    RS = unsafe_promise_unique(state(S)).
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- type params32
+    --->    params32.
+
+:- type state32
+    --->    state32(array(uint32)).
+
+:- instance urng(params32, state32) where [
+    ( urandom(_, N, !RS) :-
+        rand32(N0, !RS),
+        N = uint32.cast_to_uint64(N0)
+    ),
+    ( urandom_max(_) = uint32.cast_to_uint64(uint32.max_uint32) )
+].
+
+:- instance urng_dup(state32) where [
+    pred(urandom_dup/3) is dup32
+].
+
+dup32(S, S1, S2) :-
+    S = state32(A),
+    Sc = state32(array.copy(A)),
+    S1 = unsafe_promise_unique(S),
+    S2 = unsafe_promise_unique(Sc).
+
+%---------------------------------------------------------------------------%
+
+init32(RP, RS) :-
+    sfc.seed32(
+        0x0_u32,
+        0xf16c_a8bb_u32,
+        0x20a3_6f2d_u32,
+        RP, RS).
+
+seed32(A, B, C, params32, RS) :-
+    Counter = 1u32,
+    S0 = array([A, B, C, Counter]),
+    RS0 = unsafe_promise_unique(state32(S0)),
+    skip32(15, RS0, RS).
+
+:- pred skip32(int, state32, state32).
+:- mode skip32(in, di, uo) is det.
+
+skip32(N, !RS) :-
+    ( if N > 0 then
+        rand32(_, !RS),
+        skip32(N - 1, !RS)
+    else
+        true
+    ).
+
+%---------------------------------------------------------------------------%
+
+rand32(N, RS0, RS) :-
+    RS0 = state32(S0),
+    array.unsafe_lookup(S0, 0, A0),
+    array.unsafe_lookup(S0, 1, B0),
+    array.unsafe_lookup(S0, 2, C0),
+    array.unsafe_lookup(S0, 3, Counter0),
+    N = A0 + B0 + Counter0,
+    A = B0 `xor` (B0 >> 9),
+    B = C0 + (C0 << 3),
+    C = ((C0 << 21) \/ (C0 >> 11)) + N,
+    Counter = Counter0 + 1u32,
+    array.unsafe_set(0, A, S0, S1),
+    array.unsafe_set(1, B, S1, S2),
+    array.unsafe_set(2, C, S2, S3),
+    array.unsafe_set(3, Counter, S3, S),
+    RS = unsafe_promise_unique(state32(S)).
+
+%---------------------------------------------------------------------------%
diff --git a/library/uint16.m b/library/uint16.m
index 1554408..8153896 100644
--- a/library/uint16.m
+++ b/library/uint16.m
@@ -81,6 +81,27 @@
 
 %---------------------------------------------------------------------------%
 %
+% Conversion to/from uint64.
+%
+
+    % cast_to_uint64(U16) = U64:
+    %
+    % Convert a uint16 to a uint64.
+    % Always succeeds, and yields a result that is mathematically equal
+    % to U16.
+    %
+:- func cast_to_uint64(uint16) = uint64.
+
+    % cast_from_uint64(U64) = U16:
+    %
+    % Convert a uint64 to a uint16.
+    % Always succeeds, but will yield a result that is mathematically equal
+    % to I only if I is in [0, 2^16 - 1].
+    %
+:- func cast_from_uint64(uint64) = uint16.
+
+%---------------------------------------------------------------------------%
+%
 % Change of signedness.
 %
 
@@ -473,6 +494,64 @@ cast_to_uint(_) = _ :-
 
 %---------------------------------------------------------------------------%
 
+:- pragma no_determinism_warning(cast_to_uint64/1).
+
+:- pragma foreign_proc("C",
+    cast_to_uint64(U16::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U64 = (uint64_t) U16;
+").
+
+:- pragma foreign_proc("C#",
+    cast_to_uint64(U16::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U64 = (ulong) U16;
+").
+
+:- pragma foreign_proc("Java",
+    cast_to_uint64(U16::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U64 = (long) U16 & 0xffffL;
+").
+
+cast_to_uint64(_) = _ :-
+    sorry($module, "uint16.cast_to_uint64/1 NYI for Erlang").
+
+%---------------------------------------------------------------------------%
+
+:- pragma no_determinism_warning(cast_from_uint64/1).
+
+:- pragma foreign_proc("C",
+    cast_from_uint64(U64::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U16 = (uint16_t) U64;
+").
+
+:- pragma foreign_proc("C#",
+    cast_from_uint64(U64::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U16 = (ushort) U64;
+").
+
+:- pragma foreign_proc("Java",
+    cast_from_uint64(U64::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U16 = (short) U64;
+").
+
+cast_from_uint64(_) = _ :-
+    sorry($module, "uint16.cast_from_uint64/1 NYI for Erlang").
+
+%---------------------------------------------------------------------------%
+
 :- pragma no_determinism_warning(cast_from_int16/1).
 
 :- pragma foreign_proc("C",
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index ccbd972..bb702a9 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -323,6 +323,7 @@ ORDINARY_PROGS = \
 	rnd \
 	rng1 \
 	rng2 \
+	rng3 \
 	rtree_test \
 	rtti_strings \
 	sectag_bits \
diff --git a/tests/hard_coded/rng1.exp b/tests/hard_coded/rng1.exp
index 228e6fd..d23855f 100644
--- a/tests/hard_coded/rng1.exp
+++ b/tests/hard_coded/rng1.exp
@@ -20,6 +20,72 @@ marsaglia:
 1651990379
 2165617597
 
+sfc16:
+43153
+47661
+50096
+11040
+31457
+3072
+18062
+30539
+55957
+45948
+19700
+58569
+33953
+35062
+62409
+59130
+23863
+36035
+47819
+1018
+
+sfc32:
+2534461273
+1740637387
+315143064
+1257908742
+4248582890
+2733657301
+997355610
+2507804315
+4280817565
+2379577006
+1005485750
+3049251309
+1076981879
+1412890451
+3327740060
+2402594395
+1282867031
+2193975016
+573237914
+3480105409
+
+sfc:
+14999697890428624201
+17897111524070149868
+18348328720311300888
+12400857924036243062
+7119980675011474043
+12786611478420272337
+13954982419586076453
+8994463772821127591
+9224140626659912995
+9808064495933469266
+6390380256327158306
+14061562104604753065
+12295065294659453142
+3145200633710418485
+17587157295553805888
+6134487154077740160
+10880205108681602387
+6479776472948376920
+13686761524927771531
+10493207966664694366
+
 tausworthe3:
 364603069
 528378279
diff --git a/tests/hard_coded/rng1.m b/tests/hard_coded/rng1.m
index 0664669..eec604a 100644
--- a/tests/hard_coded/rng1.m
+++ b/tests/hard_coded/rng1.m
@@ -13,20 +13,33 @@
 :- import_module int.
 :- import_module rng.
 :- import_module rng.marsaglia.
+:- import_module rng.sfc.
 :- import_module rng.tausworthe.
 
 main(!IO) :-
     io.write_string("marsaglia:\n", !IO),
-    make_urng(marsaglia.init, RP1, RS1),
-    test(20, RP1, RS1, _, !IO),
+    make_urng(marsaglia.init, RPm, RSm),
+    test(20, RPm, RSm, _, !IO),
+
+    io.write_string("\nsfc16:\n", !IO),
+    make_urng(sfc.init16, RPsfc16, RSsfc16),
+    test(20, RPsfc16, RSsfc16, _, !IO),
+
+    io.write_string("\nsfc32:\n", !IO),
+    sfc.init32(RPsfc32, RSsfc32),
+    test(20, RPsfc32, RSsfc32, _, !IO),
+
+    io.write_string("\nsfc:\n", !IO),
+    sfc.init(RPsfc, RSsfc),
+    test(20, RPsfc, RSsfc, _, !IO),
 
     io.write_string("\ntausworthe3:\n", !IO),
-    tausworthe.init_t3(RP2, RS2),
-    test(20, RP2, RS2, _, !IO),
+    tausworthe.init_t3(RPt3, RSt3),
+    test(20, RPt3, RSt3, _, !IO),
 
     io.write_string("\ntausworthe4:\n", !IO),
-    tausworthe.init_t4(RP3, RS3),
-    test(20, RP3, RS3, _, !IO).
+    tausworthe.init_t4(RPt4, RSt4),
+    test(20, RPt4, RSt4, _, !IO).
 
 :- pred test(int, RP, RS, RS, io, io) <= urng(RP, RS).
 :- mode test(in, in, di, uo, di, uo) is det.
diff --git a/tests/hard_coded/rng2.exp b/tests/hard_coded/rng2.exp
index 228e6fd..d23855f 100644
--- a/tests/hard_coded/rng2.exp
+++ b/tests/hard_coded/rng2.exp
@@ -20,6 +20,72 @@ marsaglia:
 1651990379
 2165617597
 
+sfc16:
+43153
+47661
+50096
+11040
+31457
+3072
+18062
+30539
+55957
+45948
+19700
+58569
+33953
+35062
+62409
+59130
+23863
+36035
+47819
+1018
+
+sfc32:
+2534461273
+1740637387
+315143064
+1257908742
+4248582890
+2733657301
+997355610
+2507804315
+4280817565
+2379577006
+1005485750
+3049251309
+1076981879
+1412890451
+3327740060
+2402594395
+1282867031
+2193975016
+573237914
+3480105409
+
+sfc:
+14999697890428624201
+17897111524070149868
+18348328720311300888
+12400857924036243062
+7119980675011474043
+12786611478420272337
+13954982419586076453
+8994463772821127591
+9224140626659912995
+9808064495933469266
+6390380256327158306
+14061562104604753065
+12295065294659453142
+3145200633710418485
+17587157295553805888
+6134487154077740160
+10880205108681602387
+6479776472948376920
+13686761524927771531
+10493207966664694366
+
 tausworthe3:
 364603069
 528378279
diff --git a/tests/hard_coded/rng2.m b/tests/hard_coded/rng2.m
index c9e36da..fb0c53e 100644
--- a/tests/hard_coded/rng2.m
+++ b/tests/hard_coded/rng2.m
@@ -13,12 +13,27 @@
 :- import_module int.
 :- import_module rng.
 :- import_module rng.marsaglia.
+:- import_module rng.sfc.
 :- import_module rng.tausworthe.
 
 main(!IO) :-
     io.write_string("marsaglia:\n", !IO),
-    RNG1 = marsaglia.init,
-    test(20, RNG1, _, !IO),
+    RNGm = marsaglia.init,
+    test(20, RNGm, _, !IO),
+
+    io.write_string("\nsfc16:\n", !IO),
+    RNGsfc16 = sfc.init16,
+    test(20, RNGsfc16, _, !IO),
+
+    io.write_string("\nsfc32:\n", !IO),
+    sfc.init32(RPsfc32, RSsfc32),
+    RNGsfc32 = make_shared_rng(RPsfc32, RSsfc32),
+    test(20, RNGsfc32, _, !IO),
+
+    io.write_string("\nsfc:\n", !IO),
+    sfc.init(RPsfc, RSsfc),
+    RNGsfc = make_shared_rng(RPsfc, RSsfc),
+    test(20, RNGsfc, _, !IO),
 
     io.write_string("\ntausworthe3:\n", !IO),
     tausworthe.init_t3(RP2, RS2),
diff --git a/tests/hard_coded/rng3.data b/tests/hard_coded/rng3.data
new file mode 100644
index 0000000..32608b4
--- /dev/null
+++ b/tests/hard_coded/rng3.data
@@ -0,0 +1 @@
+)�'曼ネ%sワ何�チu懣メq/xオホ�廰Cラ1<€
V;゚�-�飼�猾@Tキ�。ウ程6最ナAイレАlvA�xイ441PユGフu�碯ォ(コ�)nAvェM2w側-f僖�ユコ蔆ク獷囑ワ┝cモcャ顫Wフrァ]j<=セ*俤sー
\ No newline at end of file
diff --git a/tests/hard_coded/rng3.exp b/tests/hard_coded/rng3.exp
new file mode 100644
index 0000000..d65b256
--- /dev/null
+++ b/tests/hard_coded/rng3.exp
@@ -0,0 +1,20 @@
+2954916798570026696
+1667866628051534109
+13940220771134697775
+322207273700596812
+4888430056450119176
+4314338737435511803
+17802622935837392980
+13252981692086548022
+10215788215724049472
+7783069486986040440
+12840945681691628871
+9208765531625021939
+12333313133034171969
+8574196514119192996
+3249741414590345429
+1298368084489858627
+11139097173025252115
+15212143235644916823
+14732021350738443524
+13699714837961864112
diff --git a/tests/hard_coded/rng3.m b/tests/hard_coded/rng3.m
new file mode 100644
index 0000000..bd65dcb
--- /dev/null
+++ b/tests/hard_coded/rng3.m
@@ -0,0 +1,57 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 sts=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module rng3.
+:- interface.
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+:- implementation.
+
+:- import_module exception.
+:- import_module int.
+:- import_module list.
+:- import_module rng.
+:- import_module rng.binfile.
+:- import_module string.
+
+main(!IO) :-
+    open("rng3.data", Res, !IO),
+    (
+        Res = ok(RPbin),
+        test(20, RPbin, !IO),
+        expect_eof(RPbin, !IO),
+        close(RPbin, !IO)
+    ;
+        Res = error(E),
+        io.progname($module, Name, !IO),
+        io.format("%s: %s\n", [s(Name), s(error_message(E))], !IO)
+    ).
+
+:- pred test(int, binfile, io, io).
+:- mode test(in, in, di, uo) is det.
+
+test(Count, RP, !IO) :-
+    ( if Count > 0 then
+        rand(RP, N, !IO),
+        io.write_uint64(N, !IO),
+        io.nl(!IO),
+        test(Count - 1, RP, !IO)
+    else
+        true
+    ).
+
+:- pred expect_eof(binfile, io, io).
+:- mode expect_eof(in, di, uo) is cc_multi.
+
+expect_eof(RP, !IO) :-
+    ( try [io(!IO)]
+        rand(RP, _, !IO)
+    then
+        io.write_string("EOF not found!\n", !IO)
+    catch _ : software_error ->
+        true
+    ).
+


More information about the reviews mailing list