package bitgenerators
PRNG bitgenerators for OCaml users
Install
Dune Dependency
Authors
Maintainers
Sources
v0.1.0.tar.gz
md5=46965587a11aa08c1e1a891f9fb12221
sha512=49423a091b05d67ee90b19d997da48f41eddabb285acdd04e1ab22f0bd6e8bccd47fe69e5b185813cc3b1c03627020d2dabd703b9c1de92869f0de118847d577
doc/src/bitgenerators/philox.ml.html
Source file philox.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
(* Copyright 2010-2012, D. E. Shaw Research. All rights reserved. Copyright (c) 2024, Zolisa Bleki SPDX-License-Identifier: BSD-3-Clause *) open Stdint module Philox : sig (** Philox4x64 (a mnemonic for Product HI LO Xor) is a 64-bit PRNG that uses a counter-based design based on weaker (and faster) versions of cryptographic functions. Instances using different values of the key produce independent sequences. Philox has a period of {m 2^{256} - 1} and supports arbitrary advancing and jumping the sequence in increments of {m 2^{128}}. These features allow multiple non-overlapping sequences to be generated. Philox's round function is applied 10 times each time the PRNG is advanced forward. The Philox state vector consists of a 256-bit value encoded as a 4-element unsigned 64-bit tuple and a 128-bit value encoded as a 2-element unsigned 64-bit tuple. The former is a counter which is incremented by 1 for every 4 64-bit randoms produced. The second is a key which determined the sequence produced. Using different keys produces independent sequences. {!SeedSequence} is used to produce a high-quality initial state for the key vector. The counter is set to 0. The preferred way to use Philox in parallel applications is to use the {!SeedSequence.spawn} function to obtain entropy values, and to use these to generate new instance of a Philox bitgenerator: {@ocaml[ open Bitgen let gens = SeedSequence.initialize [] |> SeedSequence.spawn 10 |> fst |> List.map Philox4x64.initialize ]} *) include Common.BITGEN val initialize_ctr : counter:uint64 * uint64 * uint64 * uint64 -> Seed.SeedSequence.t -> t (** Get the initial state of the generator using a 4-element unsigned 64-bit tuple as the bitgenerator's [counter] initial state as well as {!SeedSequence.t} for the initiale state of the generator's [key].*) val jump : t -> t (** [jump t] is equivalent to {m 2^{128}} calls to {!Philox4x64.next_uint64}. *) val advance : uint64 * uint64 * uint64 * uint64 -> t -> t (** [advance n] Advances the generator forward as if [n] draws have been made, and returns the new advanced state.*) end = struct type t = {key: key; ctr : counter; buffer : buffer; buffer_pos : int; ustore : uint32 option} and counter = uint64 array and key = uint64 array and buffer = uint64 array let rh0, rh1 = Uint128.(of_string "0xD2E7470EE14C6C93", of_string "0xCA5A826395121157") let k0, k1 = Uint64.(of_string "0x9E3779B97F4A7C15", of_string "0xBB67AE8584CAA73B") (* golden ratio and sqrt(3)-1 *) let mulhilo64 a b = let p = Uint128.(a * of_uint64 b) in Uint128.[|shift_right p 64 |> to_uint64; to_uint64 p|] (* Apply Philox's round function (r + 1) rounds for r >= 2. *) let rec rounds c k r = let c' = match mulhilo64 rh0 c.(0), mulhilo64 rh1 c.(2) with | x, y -> Uint64.[|logxor y.(0) c.(1) |> logxor k.(0); y.(1); logxor x.(0) c.(3) |> logxor k.(1); x.(1)|] in match r with | 0 -> c' | i -> rounds c' Uint64.[|k.(0) + k0; k.(1) + k1|] (i - 1) let next c = let open Uint64 in match c.(0) + one, c.(1) + one, c.(2) + one with | c0', c1', c2' when (c0' = zero && c1' = zero && c2' = zero) -> [|c0'; c1'; c2'; c.(3) + one|] | c0', c1', c2' when (c0' = zero && c1' = zero) -> [|c0'; c1'; c2'; c.(3)|] | c0', c1', _ when c0' = zero -> [|c0'; c1'; c.(2); c.(3)|] | c0', _, _ -> [|c0'; c.(1); c.(2); c.(3)|] let next_uint64 t = match t.buffer_pos < 4 with | true -> t.buffer.(t.buffer_pos), {t with buffer_pos = t.buffer_pos + 1} | false -> let ctr = next t.ctr in let buffer = rounds ctr t.key 9 in (* perform 10 rounds *) buffer.(0), {t with ctr; buffer; buffer_pos = 1} let next_uint32 t = match Common.next_uint32 ~next:next_uint64 t t.ustore with | u, s, ustore -> u, {s with 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 jump t = let c2' = Uint64.(t.ctr.(2) + one) in let ctr = match Uint64.(c2' = zero) with | true -> [|t.ctr.(0); t.ctr.(1); c2'; Uint64.(t.ctr.(3) + one)|] | false -> [|t.ctr.(0); t.ctr.(1); c2'; t.ctr.(3)|] in {t with ctr; ustore = None; buffer_pos = 4} let advance (d0, d1, d2, d3) t = let aux s x c = let x', c' = match Uint64.(x + one), c with | v, true when Uint64.(v = zero) -> v, true | v, true -> v, false | _, false -> x, c in match Uint64.(x' + s) with | v when (v < x' && c' = false) -> v, true | v -> v, c' in let c0', p0 = aux d0 t.ctr.(0) false in let c1', p1 = aux d1 t.ctr.(1) p0 in let c2', p2 = aux d2 t.ctr.(2) p1 in let c3', _ = aux d3 t.ctr.(3) p2 in {t with ctr = [|c0'; c1'; c2'; c3'|]; ustore = None; buffer_pos = 4} let initialize_ctr ~counter:(w, x, y, z) seed = {buffer_pos = 4; ctr = [|w; x; y; z|]; ustore = None; buffer = Array.make 4 Uint64.zero; key = Seed.SeedSequence.generate_64bit_state 2 seed} let initialize seed = initialize_ctr ~counter:Uint64.(zero, zero, zero, zero) seed end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>