Files
mercury/library/random.sfc16.m
Julien Fischer 0b92543c5e Fix more library documentation errors.
library/*.m:
   As above.
2026-01-23 19:53:58 +11:00

144 lines
4.4 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2019, 2022, 2025 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: random.sfc16.m.
% Main author: Mark Brown.
% Stability: high.
%
% 16-bit Small Fast Counting generator, by Chris Doty-Humphrey.
%
% https://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 random.sfc16.
:- interface.
%---------------------------------------------------------------------------%
% A fast, 16-bit SFC generator.
%
:- type random.
:- instance random(random).
% Initialise a 16-bit SFC generator with the default seed.
% The resulting generator produces the same sequence every time.
%
:- func init = random.
% Initialise a 16-bit SFC generator with the given seed.
%
:- func seed(uint64) = random.
% Generate a uniformly distributed pseudo-random unsigned integer
% of 8, 16, 32 or 64 bits, respectively.
%
:- pred generate_uint8(uint8::out, random::in, random::out) is det.
:- pred generate_uint16(uint16::out, random::in, random::out) is det.
:- pred generate_uint32(uint32::out, random::in, random::out) is det.
:- pred generate_uint64(uint64::out, random::in, random::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module int.
:- import_module uint.
:- import_module uint8.
:- import_module uint16.
:- import_module uint32.
:- import_module uint64.
%---------------------------------------------------------------------------%
:- type random
---> random(uint64).
:- instance random(random) where [
pred(generate_uint8/3) is sfc16.generate_uint8,
pred(generate_uint16/3) is sfc16.generate_uint16,
pred(generate_uint32/3) is sfc16.generate_uint32,
pred(generate_uint64/3) is sfc16.generate_uint64
].
init = seed(0x6048_5623_5e79_371e_u64).
seed(Seed) = R :-
skip(10, random(Seed), R).
:- pred skip(int::in, random::in, random::out) is det.
skip(N, !R) :-
( if N > 0 then
sfc16.generate_uint16(_, !R),
skip(N - 1, !R)
else
true
).
%---------------------------------------------------------------------------%
generate_uint8(N, !R) :-
sfc16.generate_uint16(N0, !R),
N1 = uint16.to_int(N0 >> 8),
N = uint8.cast_from_int(N1).
generate_uint16(N, random(S0), random(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).
generate_uint32(N, !R) :-
sfc16.generate_uint16(A0, !R),
sfc16.generate_uint16(B0, !R),
A = uint16.cast_to_uint(A0),
B = uint16.cast_to_uint(B0),
N = uint32.cast_from_uint(A + (B << 16)).
generate_uint64(N, !R) :-
sfc16.generate_uint16(A, !R),
sfc16.generate_uint16(B, !R),
sfc16.generate_uint16(C, !R),
sfc16.generate_uint16(D, !R),
N = pack_uint64(A, B, C, D).
%---------------------------------------------------------------------------%
:- func pack_uint64(uint16, uint16, uint16, uint16) = uint64.
pack_uint64(P1, P2, P3, P4) =
uint16.cast_to_uint64(P1) +
(uint16.cast_to_uint64(P2) << 16) +
(uint16.cast_to_uint64(P3) << 32) +
(uint16.cast_to_uint64(P4) << 48).
:- pred unpack_uint64(uint64::in, uint16::out, uint16::out, uint16::out,
uint16::out) is det.
unpack_uint64(S, P1, P2, P3, P4) :-
Mask = 0xffffu64,
P1 = uint16.cast_from_uint64(S /\ Mask),
P2 = uint16.cast_from_uint64((S >> 16) /\ Mask),
P3 = uint16.cast_from_uint64((S >> 32) /\ Mask),
P4 = uint16.cast_from_uint64(S >> 48).
%---------------------------------------------------------------------------%
:- end_module random.sfc16.
%---------------------------------------------------------------------------%