Skip to content

Commit

Permalink
Address more of Julien's review comments.
Browse files Browse the repository at this point in the history
library/random.m:
    Move the new code to the existing random module. Update names and
    typeclass methods.

    Add adaptors to attach any ground or shared generator to the I/O
    state.

library/random.sfc{16,32,64}.m:
    Move sfc generators to here. Update for changes to interface.

extras/README:
extras/random/*.m:
    Move unused generators to a new directory under extras.

library/uint32.m:
    Add cast_from_uint/1.

library/MODULES_DOC:
library/library.m:
    Update for the module changes.

tests/hard_coded/*:
    Rename test cases to correspond with the library module name.

    Test the I/O adaptor.
  • Loading branch information
markbrown committed Aug 26, 2019
1 parent b3ecdae commit fcdfc5c
Show file tree
Hide file tree
Showing 27 changed files with 1,754 additions and 1,331 deletions.
3 changes: 3 additions & 0 deletions extras/README
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ old_term_parser A library containing versions of the the standard library's
posix A Mercury interface to some of the POSIX
(Portable Operating System Interface) APIs.

random Some additional instances of the random typeclasses from
the standard library.

references A library package containing modules for manipulating
ML-style references (mutable state).

Expand Down
61 changes: 42 additions & 19 deletions library/rng.binfile.m → extras/random/binfile.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: rng.binfile.m
% File: binfile.m
% Main author: Mark Brown
%
% "Random" number generator that reads numbers from a binary file.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module rng.binfile.
:- module binfile.
:- interface.

:- import_module io.
:- import_module random.

%---------------------------------------------------------------------------%

:- type binfile.
:- instance urng(binfile, io).
:- instance urandom(binfile, io).

% Open a binfile generator from a filename. This should be closed
% when no longer needed.
Expand All @@ -36,19 +37,16 @@

%---------------------------------------------------------------------------%

% Generate a number between 0 and max_uint64. This reads 8 bytes
% at a time from the binfile and interprets them as an unsigned,
% big-endian integer.
% Generate an unsigned integer of 8, 16, 32 or 64 bits, reespectively.
% This reads the required number of bytes from the file and interprets
% them as an unsigned, big-endian integer.
%
% Throws an exception if the end-of-file is reached.
%
:- pred rand(binfile, uint64, io, io).
:- mode rand(in, out, di, uo) is det.

% Returns max_uint64, the maximum number that can be returned by this
% generator.
%
:- func rand_max(binfile) = uint64.
:- pred gen_uint8(binfile::in, uint8::out, io::di, io::uo) is det.
:- pred gen_uint16(binfile::in, uint16::out, io::di, io::uo) is det.
:- pred gen_uint32(binfile::in, uint32::out, io::di, io::uo) is det.
:- pred gen_uint64(binfile::in, uint64::out, io::di, io::uo) is det.

%---------------------------------------------------------------------------%

Expand All @@ -62,9 +60,11 @@
:- type binfile
---> binfile(binary_input_stream).

:- instance urng(binfile, io) where [
pred(urandom/4) is rand,
func(urandom_max/1) is rand_max
:- instance urandom(binfile, io) where [
pred(gen_uint8/4) is binfile.gen_uint8,
pred(gen_uint16/4) is binfile.gen_uint16,
pred(gen_uint32/4) is binfile.gen_uint32,
pred(gen_uint64/4) is binfile.gen_uint64
].

%---------------------------------------------------------------------------%
Expand All @@ -84,8 +84,33 @@

%---------------------------------------------------------------------------%

rand(binfile(Stream), N, !IO) :-
gen_uint8(binfile(Stream), N, !IO) :-
io.read_binary_uint8(Stream, Res, !IO),
(
Res = ok(N)
;
Res = eof,
unexpected($pred, "end of file")
;
Res = error(E),
unexpected($pred, io.error_message(E))
).

gen_uint16(binfile(Stream), N, !IO) :-
io.read_binary_uint16_be(Stream, Res, !IO),
handle_res(Res, N).

gen_uint32(binfile(Stream), N, !IO) :-
io.read_binary_uint32_be(Stream, Res, !IO),
handle_res(Res, N).

gen_uint64(binfile(Stream), N, !IO) :-
io.read_binary_uint64_be(Stream, Res, !IO),
handle_res(Res, N).

:- pred handle_res(maybe_incomplete_result(T)::in, T::out) is det.

handle_res(Res, N) :-
(
Res = ok(N)
;
Expand All @@ -98,6 +123,4 @@
unexpected($pred, io.error_message(E))
).

rand_max(_) = uint64.max_uint64.

%---------------------------------------------------------------------------%
85 changes: 50 additions & 35 deletions library/rng.marsaglia.m → extras/random/marsaglia.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: rng.marsaglia.m
% File: marsaglia.m
% Main author: Mark Brown
%
% Very fast concatenation of two 16-bit MWC generators.
Expand All @@ -17,67 +17,84 @@
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module rng.marsaglia.
:- module marsaglia.
:- interface.

:- import_module random.

%---------------------------------------------------------------------------%

:- type marsaglia.
:- type random.

:- instance rng(marsaglia).
:- instance random(random).

% Initialise a marsaglia RNG with the default seed.
% Initialise a marsaglia generator with the default seed.
%
:- func init = marsaglia.
:- func init = random.

% Initialise a marsaglia RNG with the given seed.
% Initialise a marsaglia generator with the given seed.
%
:- func seed(uint32, uint32) = marsaglia.

%---------------------------------------------------------------------------%
:- func seed(uint32, uint32) = random.

% Generate a random number between 0 and max_uint32.
% Generate a uniformly distributed pseudo-random unsigned integer
% of 8, 16, 32 or 64 bytes, respectively.
%
:- pred rand(uint32, marsaglia, marsaglia).
:- mode rand(out, in, out) is det.

% Return max_uint32, the maximum number that can be returned by this
% generator.
%
:- func rand_max(marsaglia) = uint32.
:- pred gen_uint8(uint8::out, random::in, random::out) is det.
:- pred gen_uint16(uint16::out, random::in, random::out) is det.
:- pred gen_uint32(uint32::out, random::in, random::out) is det.
:- pred gen_uint64(uint64::out, random::in, random::out) is det.

%---------------------------------------------------------------------------%

:- implementation.

:- import_module uint8.
:- import_module uint16.
:- import_module uint32.
:- import_module uint64.

%---------------------------------------------------------------------------%

:- type marsaglia
---> marsaglia(uint64).
:- type random
---> random(uint64).

:- instance rng(marsaglia) where [
( random(N, !RNG) :-
rand(N0, !RNG),
N = uint32.cast_to_uint64(N0)
),
( random_max(RNG) = uint32.cast_to_uint64(rand_max(RNG)) )
:- instance random(random) where [
pred(gen_uint8/3) is marsaglia.gen_uint8,
pred(gen_uint16/3) is marsaglia.gen_uint16,
pred(gen_uint32/3) is marsaglia.gen_uint32,
pred(gen_uint64/3) is marsaglia.gen_uint64
].

%---------------------------------------------------------------------------%

init = seed(0u32, 0u32).

seed(SX0, SY0) = RNG :-
seed(SX0, SY0) = R :-
SX = ( if SX0 = 0u32 then 521288629u32 else SX0 ),
SY = ( if SY0 = 0u32 then 362436069u32 else SY0 ),
RNG = marsaglia(pack_uint64(SX, SY)).
R = random(pack_uint64(SX, SY)).

%---------------------------------------------------------------------------%

rand(N, RNG0, RNG) :-
RNG0 = marsaglia(S0),
gen_uint8(N, !R) :-
marsaglia.gen_uint32(N0, !R),
N1 = uint32.cast_to_int(N0 >> 24),
N = uint8.cast_from_int(N1).

gen_uint16(N, !R) :-
marsaglia.gen_uint32(N0, !R),
N1 = uint32.cast_to_int(N0 >> 16),
N = uint16.cast_from_int(N1).

gen_uint64(N, !R) :-
marsaglia.gen_uint32(A0, !R),
marsaglia.gen_uint32(B0, !R),
A = uint32.cast_to_uint64(A0),
B = uint32.cast_to_uint64(B0),
N = A + (B << 32).

%---------------------------------------------------------------------------%

gen_uint32(N, R0, R) :-
R0 = random(S0),
unpack_uint64(S0, SX0, SY0),
A = 18000u32,
B = 30903u32,
Expand All @@ -86,9 +103,7 @@
SY = B * (SY0 /\ M) + (SY0 >> 16),
N = (SX << 16) + (SY /\ M),
S = pack_uint64(SX, SY),
RNG = marsaglia(S).

rand_max(_) = uint32.max_uint32.
R = random(S).

%---------------------------------------------------------------------------%

Expand Down

0 comments on commit fcdfc5c

Please sign in to comment.