Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EFIIX64x48 bitgenerator. #31

Merged
merged 2 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Philox4x64.initialize seedseq |> Seq.iterate Philox4x64.jump |> Seq.take 5 |> Li
```
The resulting list of bitgenerators produce non-overlapping streams of random numbers.

Supported bitgenerators include: `PCG64`, `Philox4x64`, `Xoshiro256`, `ChaCha` and `SFC64`. More coming soon!
Supported bitgenerators include: `PCG64`, `Philox4x64`, `Xoshiro256`, `ChaCha`, `SFC64`, `LXM` and `EFIIX64x48`. More coming soon!

## Empirical Randomness Testing
Running the test suite provided by [TestU01][6] on the supported generators is supported.
Expand Down
3 changes: 2 additions & 1 deletion bin/bench.ml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ let pairs = [
"Xoshiro256", (module Xoshiro256: S);
"Philox4x64", (module Philox4x64: S);
"ChaCha", (module ChaCha: S);
"LXM", (module LXM: S)
"LXM", (module LXM: S);
"EFIIX64x48", (module EFIIX64x48: S);
]


Expand Down
1 change: 1 addition & 0 deletions bin/crush.ml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ let to_module = function
| "sfc64" -> (module SFC64 : S)
| "chacha" -> (module ChaCha : S)
| "lxm" -> (module LXM : S)
| "efiix64x48" -> (module EFIIX64x48 : S)
| _ -> failwith "Unknown PRNG"


Expand Down
1 change: 1 addition & 0 deletions lib/bitgen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ module Xoshiro256 = Xoshiro.Xoshiro256StarStar
module Philox4x64 = Philox.Philox
module ChaCha = Chacha.ChaCha128Counter
module LXM = Lxm.LXM
module EFIIX64x48 = Efiix.EFIIX64
102 changes: 102 additions & 0 deletions lib/efiix.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
open Stdint


module EFIIX64 : sig
(** EFIIX64x48 is a 64-bit PRNG that uses a set of tables to generate random values.
This produces a fast PRNG with statistical quality similar to cryptographic
generators but faster.

The EFIIX64x48 state vector consists of a 16-element array of 64-bit
unsigned integers and a 32-element array of 64-bit unsigned integers.
In addition, 3 constant values and a counter are used in the update.

The initial state of the bitgenerator is provided using {!SeedSequence.t}. *)

include Common.BITGEN
end = struct
type t = {s : state; ustore : uint32 option}
and state = {ind_table : uint64 array; iter_table : uint64 array;
i : uint64; a : uint64; b : uint64; c : uint64}
type arbee = {a : uint64; b : uint64; c : uint64; d : uint64; i : uint64}


let rotate x k = let y = 64 - k in Uint64.(logor (shift_left x k) (shift_right x y))


let iter_size, ind_size = Uint64.(of_int 32, of_int 16)

let next (s : state) =
let open Uint64 in
let i = rem s.i iter_size |> to_int in
let j = rem s.c ind_size |> to_int in
(* copying and assigning is much faster than using Array.mapi here *)
let ind_table, iter_table = Array.(copy s.ind_table, copy s.iter_table) in
ind_table.(j) <- s.iter_table.(i) + s.a; iter_table.(i) <- s.ind_table.(j);

let b = s.c + s.ind_table.(j) in
logxor b s.iter_table.(i),
{ind_table; iter_table; b; a = s.b + s.i; c = logxor s.a s.b + rotate s.c 25; i = s.i + one}


let next_uint64 t = let u, s = next t.s in u, {t with s}


let next_uint32 t = match Common.next_uint32 ~next:next t.s t.ustore with
| u, s, ustore -> u, {s; ustore}


let next_double t = Common.next_double ~nextu64:next_uint64 t


let next_bounded_uint64 bound t = Common.next_bounded_uint64 bound ~nextu64:next_uint64 t


let arbee_next (s : arbee) =
let open Uint64 in
let e = s.a + rotate s.b 45 in
let a = logxor s.b (rotate s.c 13) in
let d = e + a in
{a; b = s.c + rotate s.d 37; c = e + s.d + s.i; d; i = s.i + one}, d


let rec mix (s : arbee) = function
| 0 -> s
| i -> mix (arbee_next s |> fst) (i - 1)


let rec discard (s : state) = function
| 0 -> s
| i -> discard (next s |> snd) (i - 1)


let arbee_seed seed =
mix {a = seed.(0); b = seed.(1); c = seed.(2); d = seed.(3); i = Uint64.one} 12


let set_seed seed =
let seeder, ind_table = Array.fold_left_map
(fun acc _ -> arbee_next acc) (arbee_seed seed) (Array.make 16 0) in
let seeder, i = arbee_next seeder in
let iter_table = (Array.make 32 Uint64.zero) in
let seeder, iter_values = Array.fold_left_map
(fun acc _ -> arbee_next acc) seeder iter_table in
Array.iteri
(fun idx v -> iter_table.(Uint64.(rem (of_int idx + i) iter_size |> to_int)) <- v) iter_values;
let seeder, a = arbee_next seeder in
let seeder, b = arbee_next seeder in
let seeder, c = arbee_next seeder in
let s = discard {ind_table; iter_table; i; a; b; c} 64 in

let seeder = arbee_next seeder |> fst in
let seeder, s1 = arbee_next seeder in
let seeder, s2 = arbee_next seeder in
let seeder = arbee_seed Uint64.[|s1 + seed.(0) |> logxor s.a; s2 + seed.(1) |> logxor s.b;
(arbee_next seeder |> snd) + seed.(2) |> logxor s.c; lognot seed.(3)|] in
let ind_table = Array.fold_left_map
(fun acc y -> let acc', x = arbee_next acc in acc', Uint64.logxor x y) seeder s.ind_table |> snd in
discard {s with ind_table} 48


let initialize seed =
{s = Seed.SeedSequence.generate_64bit_state 4 seed |> set_seed; ustore = None}
end
Loading
Loading