[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