mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-29 15:15:08 +00:00
132 lines
3.1 KiB
Odin
132 lines
3.1 KiB
Odin
package rand
|
|
|
|
import "base:intrinsics"
|
|
import "base:runtime"
|
|
|
|
/*
|
|
The state for a xoshiro256** pseudorandom generator.
|
|
*/
|
|
Xoshiro256_Random_State :: struct {
|
|
s: [4]u64,
|
|
}
|
|
|
|
xoshiro256_random_generator_proc :: proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
|
@(require_results)
|
|
read_u64 :: proc "contextless" (r: ^Xoshiro256_Random_State) -> u64 {
|
|
// xoshiro256** output function and state transition
|
|
|
|
result := rotate_left64(r.s[1] * 5, 7) * 9
|
|
t := r.s[1] << 17
|
|
|
|
r.s[2] = r.s[2] ~ r.s[0]
|
|
r.s[3] = r.s[3] ~ r.s[1]
|
|
r.s[1] = r.s[1] ~ r.s[2]
|
|
r.s[0] = r.s[0] ~ r.s[3]
|
|
r.s[2] = r.s[2] ~ t
|
|
r.s[3] = rotate_left64(r.s[3], 45)
|
|
|
|
return result
|
|
|
|
rotate_left64 :: proc "contextless" (x: u64, k: int) -> u64 {
|
|
n :: 64
|
|
s := uint(k) & (n-1)
|
|
return x << s | x >> (n-s)
|
|
}
|
|
}
|
|
|
|
@(thread_local)
|
|
global_rand_seed: Xoshiro256_Random_State
|
|
|
|
init :: proc "contextless" (r: ^Xoshiro256_Random_State, seed: u64) {
|
|
// splitmix64 to expand a 64-bit seed into 256 bits of state
|
|
sm64_next :: proc "contextless" (s: ^u64) -> u64 {
|
|
s^ += 0x9E3779B97F4A7C15
|
|
z := s^
|
|
z = (z ~ (z >> 30)) * 0xBF58476D1CE4E5B9
|
|
z = (z ~ (z >> 27)) * 0x94D049BB133111EB
|
|
return z ~ (z >> 31)
|
|
}
|
|
|
|
local_seed := seed
|
|
r.s[0] = sm64_next(&local_seed)
|
|
r.s[1] = sm64_next(&local_seed)
|
|
r.s[2] = sm64_next(&local_seed)
|
|
r.s[3] = sm64_next(&local_seed)
|
|
// Extremely unlikely all zero; ensure non-zero state
|
|
if (r.s[0] | r.s[1] | r.s[2] | r.s[3]) == 0 {
|
|
// force a minimal non-zero tweak
|
|
r.s[0] = 1
|
|
}
|
|
}
|
|
|
|
r: ^Xoshiro256_Random_State = ---
|
|
if data == nil {
|
|
r = &global_rand_seed
|
|
} else {
|
|
r = cast(^Xoshiro256_Random_State)data
|
|
}
|
|
|
|
switch mode {
|
|
case .Read:
|
|
if (r.s[0] | r.s[1] | r.s[2] | r.s[3]) == 0 {
|
|
init(r, u64(intrinsics.read_cycle_counter()))
|
|
}
|
|
|
|
switch len(p) {
|
|
case size_of(u64):
|
|
// Fast path for a 64-bit destination.
|
|
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
|
case:
|
|
// All other cases.
|
|
n := len(p) / size_of(u64)
|
|
buff := ([^]u64)(raw_data(p))[:n]
|
|
for &e in buff {
|
|
intrinsics.unaligned_store(&e, read_u64(r))
|
|
}
|
|
// Handle remaining bytes
|
|
rem := len(p) % size_of(u64)
|
|
if rem > 0 {
|
|
val := read_u64(r)
|
|
tail := p[len(p) - rem:]
|
|
for &b in tail {
|
|
b = byte(val)
|
|
val >>= 8
|
|
}
|
|
}
|
|
}
|
|
|
|
case .Reset:
|
|
seed: u64 = 0
|
|
runtime.mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
|
|
init(r, seed)
|
|
|
|
case .Query_Info:
|
|
if len(p) != size_of(Generator_Query_Info) {
|
|
return
|
|
}
|
|
info := (^Generator_Query_Info)(raw_data(p))
|
|
info^ += {.Uniform, .Resettable}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns an instance of the xoshiro256** pseudorandom generator. If no
|
|
initial state is provided, the PRNG will be lazily initialized with the
|
|
system timestamp counter on first-use.
|
|
|
|
WARNING: This random number generator is NOT cryptographically secure.
|
|
|
|
Inputs:
|
|
- state: Optional initial PRNG state.
|
|
|
|
Returns:
|
|
- A `Generator` instance.
|
|
*/
|
|
@(require_results)
|
|
xoshiro256_random_generator :: proc "contextless" (state: ^Xoshiro256_Random_State = nil) -> Generator {
|
|
return {
|
|
procedure = xoshiro256_random_generator_proc,
|
|
data = state,
|
|
}
|
|
}
|