Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,118 @@
|
||||
# Description
|
||||
|
||||
The V `rand` module provides two main ways in which users can generate pseudorandom numbers:
|
||||
|
||||
## Direct Access Through The `rand` Module
|
||||
|
||||
```
|
||||
// Import the rand module
|
||||
import rand
|
||||
|
||||
...
|
||||
|
||||
// Optionally seed the default generator
|
||||
rand.seed([u32(3223878742), 1732001562])
|
||||
|
||||
...
|
||||
|
||||
// Use the top-level functions
|
||||
rand.u32n(100)!
|
||||
rand.int() // among others ...
|
||||
```
|
||||
|
||||
## Through A Generator Of Choice
|
||||
|
||||
```
|
||||
// Import the rand module
|
||||
import rand
|
||||
import rand.seed
|
||||
|
||||
// Import the module of the generator you want to use
|
||||
import rand.pcg32
|
||||
|
||||
...
|
||||
|
||||
// Initialise the generator struct (note the `mut`)
|
||||
mut rng := &rand.PRNG(pcg32.PCG32RNG{})
|
||||
|
||||
// Optionally seed the generator
|
||||
rng.seed(seed.time_seed_array(pcg32.seed_len))
|
||||
|
||||
...
|
||||
|
||||
// Use functions of your choice
|
||||
rng.u32n(100)!
|
||||
rng.int() // among others ...
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
You can change the default generator to a different one. The only requirement is that
|
||||
the generator must implement the `PRNG` interface. See `get_current_rng()` and `set_rng()`.
|
||||
|
||||
> **Note**
|
||||
> The global PRNG is not thread safe. It is recommended to use separate generators for
|
||||
> separate threads in multi-threaded applications.
|
||||
|
||||
There are only a few extra functions that are defined only in this top-level `rand` module.
|
||||
Otherwise, there is feature parity between the generator functions and the top-level functions.
|
||||
|
||||
# General Background
|
||||
|
||||
A PRNG is a Pseudo Random Number Generator.
|
||||
Computers cannot generate truly random numbers without an external source of noise or entropy.
|
||||
We can use algorithms to generate sequences of seemingly random numbers,
|
||||
but their outputs will always be deterministic, according to the seed values.
|
||||
|
||||
This is often useful for simulations that need the same starting seeds.
|
||||
You may be debugging a program and want to restart it with the same
|
||||
seeds, or you want to verify a working program is still
|
||||
operating identically after compiler or operating system updates.
|
||||
|
||||
If you need truly random numbers that are going to be used for cryptography,
|
||||
use the `crypto.rand` module.
|
||||
|
||||
# Seeding Functions
|
||||
|
||||
All the generators are initialized with time-based seeds.
|
||||
The helper functions publicly available in `rand.seed` module are:
|
||||
|
||||
1. `time_seed_array()` - returns a `[]u32` that can be directly plugged into the `seed()` functions.
|
||||
2. `time_seed_32()` and `time_seed_64()` - 32-bit and 64-bit values respectively
|
||||
that are generated from the current time.
|
||||
|
||||
When composing your own seeds, use "typical" u32 numbers, not small numbers. This
|
||||
is especially important for PRNGs with large state, such as `mt19937`. You can create
|
||||
random unsigned integers with openssl `rand` or with `v repl` as follows:
|
||||
|
||||
```
|
||||
$ openssl rand -hex 4
|
||||
e3655862
|
||||
$ openssl rand -hex 4
|
||||
97c4b1db
|
||||
$ v repl
|
||||
>>> import rand
|
||||
>>> [rand.u32(),rand.u32()]
|
||||
[2132382944, 2443871665]
|
||||
```
|
||||
|
||||
# Caveats
|
||||
|
||||
Note that the `sys.SysRNG` struct (in the C backend) uses `C.srand()` which sets the seed globally.
|
||||
Consequently, all instances of the RNG will be affected.
|
||||
This problem does not arise for the other RNGs.
|
||||
A workaround (if you _must_ use the libc RNG) is to:
|
||||
|
||||
1. Seed the first instance.
|
||||
2. Generate all values required.
|
||||
3. Seed the second instance.
|
||||
4. Generate all values required.
|
||||
5. And so on...
|
||||
|
||||
# Notes
|
||||
|
||||
[Math interval](<https://en.wikipedia.org/wiki/Interval_(mathematics)#Including_or_excluding_endpoints>)
|
||||
notation is used throughout the function documentation to denote what numbers ranges include.
|
||||
|
||||
An example of `[0, max)` thus denotes a range with all possible values
|
||||
between `0` and `max` **including** 0 but **excluding** `max`.
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module buffer
|
||||
|
||||
pub struct PRNGBuffer {
|
||||
mut:
|
||||
bytes_left int
|
||||
buffer u64
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module config
|
||||
|
||||
import rand.seed
|
||||
|
||||
// PRNGConfigStruct is a configuration struct for creating a new instance of the default RNG.
|
||||
// Note that the RNGs may have a different number of u32s required for seeding. The default
|
||||
// generator WyRand used 64 bits, ie. 2 u32s so that is the default. In case your desired generator
|
||||
// uses a different number of u32s, use the `seed.time_seed_array()` method with the correct
|
||||
// number of u32s.
|
||||
@[params]
|
||||
pub struct PRNGConfigStruct {
|
||||
pub:
|
||||
seed_ []u32 = seed.time_seed_array(2)
|
||||
}
|
||||
|
||||
// Configuration struct for generating normally distributed floats. The default value for
|
||||
// `mu` is 0 and the default value for `sigma` is 1.
|
||||
@[params]
|
||||
pub struct NormalConfigStruct {
|
||||
pub:
|
||||
mu f64 = 0.0
|
||||
sigma f64 = 1.0
|
||||
}
|
||||
|
||||
// Configuration struct for the shuffle functions.
|
||||
// The start index is inclusive and the end index is exclusive.
|
||||
// Set the end to 0 to shuffle until the end of the array.
|
||||
@[params]
|
||||
pub struct ShuffleConfigStruct {
|
||||
pub:
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
// validate_for is a helper function for validating the configuration struct for the given array.
|
||||
pub fn (config ShuffleConfigStruct) validate_for[T](a []T) ! {
|
||||
if config.start < 0 || config.start >= a.len {
|
||||
return error("argument 'config.start' must be in range [0, a.len)")
|
||||
}
|
||||
if config.end < 0 || config.end > a.len {
|
||||
return error("argument 'config.end' must be in range [0, a.len]")
|
||||
}
|
||||
if config.end != 0 && config.end <= config.start {
|
||||
return error("argument 'config.end' must be greater than 'config.start'")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
module cuid2
|
||||
|
||||
import rand
|
||||
import time
|
||||
import strconv
|
||||
import crypto.sha3
|
||||
import math.big
|
||||
import os
|
||||
|
||||
const default_id_length = 24
|
||||
const min_id_length = 2
|
||||
const max_id_length = 32
|
||||
// ~22k hosts before 50% chance of initial counter collision
|
||||
const max_session_count = 476782367
|
||||
|
||||
// Cuid2Generator can be used to get secure, collision-resistant ids optimized for horizontal scaling and performance. Next generation UUIDs.
|
||||
pub struct Cuid2Generator {
|
||||
mut:
|
||||
// A counter that will be used to affect the entropy of
|
||||
// successive id generation calls
|
||||
session_counter i64
|
||||
// A unique string that will be used by the Cuid generator
|
||||
// to help prevent collisions when generating Cuids in a
|
||||
// distributed system.
|
||||
fingerprint string
|
||||
pub mut:
|
||||
// A PRNG that has a PRNG interface
|
||||
prng &rand.PRNG = rand.get_current_rng()
|
||||
// Length of the generated Cuid, min = 2, max = 32, default = 24
|
||||
length int = default_id_length
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct Cuid2Param {
|
||||
pub mut:
|
||||
// A PRNG that has a PRNG interface
|
||||
prng &rand.PRNG = rand.get_current_rng()
|
||||
// Length of the generated Cuid, min = 2, max = 32, default = 24
|
||||
length int = default_id_length
|
||||
}
|
||||
|
||||
// new Create a cuid2 UUID generator.
|
||||
pub fn new(param Cuid2Param) Cuid2Generator {
|
||||
return Cuid2Generator{
|
||||
prng: param.prng
|
||||
length: param.length
|
||||
}
|
||||
}
|
||||
|
||||
// generate Generate a new cuid2 UUID.
|
||||
// It is an alias to function `cuid2()`
|
||||
pub fn (mut g Cuid2Generator) generate() string {
|
||||
return g.cuid2()
|
||||
}
|
||||
|
||||
// cuid2 generates a random (cuid2) UUID.
|
||||
// Secure, collision-resistant ids optimized for horizontal
|
||||
// scaling and performance. Next generation UUIDs.
|
||||
// Ported from https://github.com/paralleldrive/cuid2
|
||||
pub fn (mut g Cuid2Generator) cuid2() string {
|
||||
if g.length < min_id_length || g.length > max_id_length {
|
||||
panic('cuid2 length(${g.length}) out of range: min=${min_id_length}, max=${max_id_length}')
|
||||
}
|
||||
|
||||
mut prng := g.prng
|
||||
first_letter := prng.string(1).to_lower()
|
||||
now := strconv.format_int(time.now().unix_milli(), 36)
|
||||
if g.session_counter == 0 {
|
||||
// First call, init session counter, fingerprint.
|
||||
g.session_counter = i64(prng.f64() * max_session_count)
|
||||
g.fingerprint = create_fingerprint(mut prng, get_environment_key_string())
|
||||
}
|
||||
g.session_counter = g.session_counter + 1
|
||||
count := strconv.format_int(g.session_counter, 36)
|
||||
|
||||
// The salt should be long enough to be globally unique
|
||||
// across the full length of the hash. For simplicity,
|
||||
// we use the same length as the intended id output.
|
||||
salt := create_entropy(g.length, mut prng)
|
||||
hash_input := now + salt + count + g.fingerprint
|
||||
hash_digest := first_letter + hash(hash_input)[1..g.length]
|
||||
return hash_digest
|
||||
}
|
||||
|
||||
// next generates a new cuid2 UUID.
|
||||
// It is an alias to function `cuid2()`
|
||||
pub fn (mut g Cuid2Generator) next() ?string {
|
||||
return g.cuid2()
|
||||
}
|
||||
|
||||
// is_cuid checks whether a given `cuid` has a valid form and length.
|
||||
pub fn is_cuid(cuid string) bool {
|
||||
if cuid.len < min_id_length || cuid.len > max_id_length {
|
||||
return false
|
||||
}
|
||||
|
||||
// first letter should in [a..z]
|
||||
if cuid[0] < u8(`a`) || cuid[0] > u8(`z`) {
|
||||
return false
|
||||
}
|
||||
|
||||
// other letter should in [a..z,0..9]
|
||||
for letter in cuid[1..] {
|
||||
if (letter >= u8(`a`) && letter <= u8(`z`)) || (letter >= u8(`0`) && letter <= u8(`9`)) {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn create_entropy(length int, mut prng rand.PRNG) string {
|
||||
mut entropy := ''
|
||||
for entropy.len < length {
|
||||
randomness := i64(prng.f64() * 36)
|
||||
entropy += strconv.format_int(randomness, 36)
|
||||
}
|
||||
return entropy
|
||||
}
|
||||
|
||||
// create_fingerprint This is a fingerprint of the host environment.
|
||||
// It is used to help prevent collisions when generating ids in a
|
||||
// distributed system. If no global object is available, you can
|
||||
// pass in your own, or fall back on a random string.
|
||||
fn create_fingerprint(mut prng rand.PRNG, env_key_string string) string {
|
||||
mut source_string := create_entropy(max_id_length, mut prng)
|
||||
if env_key_string.len > 0 {
|
||||
source_string += env_key_string
|
||||
}
|
||||
source_string_hash := hash(source_string)
|
||||
return source_string_hash[1..]
|
||||
}
|
||||
|
||||
fn hash(input string) string {
|
||||
mut h := sha3.new512() or { panic(err) }
|
||||
h.write(input.bytes()) or { panic(err) }
|
||||
hash_digest := h.checksum()
|
||||
|
||||
// Drop the first character because it will bias
|
||||
// the histogram to the left.
|
||||
return big.integer_from_bytes(hash_digest).radix_str(36)[1..]
|
||||
}
|
||||
|
||||
fn get_environment_key_string() string {
|
||||
env := os.environ()
|
||||
mut keys := []string{}
|
||||
|
||||
// Discard values of environment variables
|
||||
for _, variable in env {
|
||||
index := variable.index('=') or { variable.len }
|
||||
key := variable[..index]
|
||||
keys << key
|
||||
}
|
||||
return keys.join('')
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
module cuid2
|
||||
|
||||
import rand.musl
|
||||
import rand.mt19937
|
||||
|
||||
fn test_cuid2() {
|
||||
// default prng(wyrand), default id length = 24
|
||||
mut g24 := new()
|
||||
uuid24 := g24.generate()
|
||||
assert uuid24.len == 24
|
||||
assert is_cuid(uuid24)
|
||||
|
||||
// default prng(wyrand), id length = 2
|
||||
mut g2 := new(length: 2)
|
||||
uuid2 := g2.generate()
|
||||
assert uuid2.len == 2
|
||||
assert is_cuid(uuid2)
|
||||
|
||||
// default prng(wyrand), id length = 32
|
||||
mut g32 := new(length: 32)
|
||||
uuid32 := g32.generate()
|
||||
assert uuid32.len == 32
|
||||
assert is_cuid(uuid32)
|
||||
|
||||
// musl prng, id length = 28
|
||||
mut g_musl := new(prng: &musl.MuslRNG{}, length: 28)
|
||||
uuid_musl := g_musl.generate()
|
||||
assert uuid_musl.len == 28
|
||||
assert is_cuid(uuid_musl)
|
||||
|
||||
// mt19937 prng, default id length = 24
|
||||
mut g_mt19937 := new(prng: &mt19937.MT19937RNG{})
|
||||
uuid_mt19937 := g_mt19937.generate()
|
||||
assert uuid_mt19937.len == 24
|
||||
assert is_cuid(uuid_mt19937)
|
||||
|
||||
// successive calls
|
||||
// default prng(wyrand), default id length = 24
|
||||
mut g := new()
|
||||
mut ids := []string{}
|
||||
for id in g {
|
||||
eprintln(id)
|
||||
// id length should be default length(24)
|
||||
assert id.len == 24
|
||||
assert is_cuid(id)
|
||||
|
||||
ids << id
|
||||
if ids.len == 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// successive calls to g.next() in a row should be unique
|
||||
assert ids[0] != ids[1]
|
||||
assert ids[0] != ids[2]
|
||||
assert ids[0] != ids[3]
|
||||
assert ids[0] != ids[4]
|
||||
assert ids[1] != ids[2]
|
||||
assert ids[1] != ids[3]
|
||||
assert ids[1] != ids[4]
|
||||
assert ids[2] != ids[3]
|
||||
assert ids[2] != ids[4]
|
||||
assert ids[3] != ids[4]
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import math
|
||||
import rand
|
||||
|
||||
// The sample size to be used
|
||||
const count = 2000
|
||||
// Accepted error is within 5% of the actual values.
|
||||
const error = 0.05
|
||||
// The seeds used (for reproducible testing)
|
||||
const seeds = [[u32(0x24ffff), 0xabcd], [u32(0x141024), 0x42851],
|
||||
[u32(0x1452), 0x90cd]]
|
||||
|
||||
fn test_bernoulli() {
|
||||
ps := [0.0, 0.1, 1.0 / 3.0, 0.5, 0.8, 17.0 / 18.0, 1.0]
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for p in ps {
|
||||
mut successes := 0
|
||||
for _ in 0 .. count {
|
||||
if rand.bernoulli(p) or { false } {
|
||||
successes++
|
||||
}
|
||||
}
|
||||
assert math.abs(f64(successes) / count - p) < error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_binomial() {
|
||||
ns := [100, 200, 1000]
|
||||
ps := [0.0, 0.5, 0.95, 1.0]
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for n in ns {
|
||||
for p in ps {
|
||||
np := n * p
|
||||
npq := np * (1 - p)
|
||||
|
||||
mut sum := 0
|
||||
mut var := 0.0
|
||||
for _ in 0 .. count {
|
||||
x := rand.binomial(n, p) or { 0 }
|
||||
sum += x
|
||||
dist := (x - np)
|
||||
var += dist * dist
|
||||
}
|
||||
|
||||
assert math.abs(f64(sum / count) - np) / n < error
|
||||
assert math.abs(f64(var / count) - npq) / n < error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_normal_pair() {
|
||||
mus := [0, 10, 100, -40]
|
||||
sigmas := [1, 2, 40, 5]
|
||||
total := 2 * count
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for mu in mus {
|
||||
for sigma in sigmas {
|
||||
mut sum := 0.0
|
||||
mut var := 0.0
|
||||
for _ in 0 .. count {
|
||||
x, y := rand.normal_pair(mu: mu, sigma: sigma) or { 0.0, 0.0 }
|
||||
sum += x + y
|
||||
dist_x := x - mu
|
||||
dist_y := y - mu
|
||||
var += dist_x * dist_x
|
||||
var += dist_y * dist_y
|
||||
}
|
||||
|
||||
variance := sigma * sigma
|
||||
assert math.abs(f64(sum / total) - mu) / sigma < 1
|
||||
assert math.abs(f64(var / total) - variance) / variance < 2 * error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_normal() {
|
||||
mus := [0, 10, 100, -40, 20]
|
||||
sigmas := [1, 2, 5]
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for mu in mus {
|
||||
for sigma in sigmas {
|
||||
mut sum := 0.0
|
||||
mut var := 0.0
|
||||
for _ in 0 .. count {
|
||||
x := rand.normal(mu: mu, sigma: sigma) or { 0.0 }
|
||||
sum += x
|
||||
dist := x - mu
|
||||
var += dist * dist
|
||||
}
|
||||
|
||||
variance := sigma * sigma
|
||||
assert math.abs(f64(sum / count) - mu) / sigma < 1
|
||||
assert math.abs(f64(var / count) - variance) / variance < 2 * error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_exponential() {
|
||||
lambdas := [1.0, 10, 1 / 20.0, 1 / 10000.0, 1 / 524.0, 200]
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for lambda in lambdas {
|
||||
mu := 1 / lambda
|
||||
variance := mu * mu
|
||||
mut sum := 0.0
|
||||
mut var := 0.0
|
||||
for _ in 0 .. count {
|
||||
x := rand.exponential(lambda)
|
||||
sum += x
|
||||
dist := x - mu
|
||||
var += dist * dist
|
||||
}
|
||||
|
||||
assert math.abs((f64(sum / count) - mu) / mu) < error
|
||||
assert math.abs((f64(var / count) - variance) / variance) < 2 * error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import rand
|
||||
|
||||
struct Histo {
|
||||
mut:
|
||||
lo f64
|
||||
ct int
|
||||
}
|
||||
|
||||
// The sample size to be used; keep cpu time less than 5 seconds
|
||||
const count = 9100100
|
||||
// Two sets of seeds
|
||||
const seeds = [[u32(2742798260), 2159764996], [u32(2135051596), 958016781]]
|
||||
|
||||
fn test_f32() {
|
||||
mut histo := []Histo{}
|
||||
mut candidate := f32(0.0)
|
||||
|
||||
histo << Histo{1.0e-9, 0}
|
||||
histo << Histo{1.0e-6, 0}
|
||||
histo << Histo{1.0e-4, 0}
|
||||
histo << Histo{1.0e-2, 0}
|
||||
histo << Histo{1.0e00, 0}
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for _ in 0 .. count {
|
||||
candidate = rand.f32()
|
||||
for mut p in histo {
|
||||
if candidate < p.lo {
|
||||
p.ct += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println(' f32 ')
|
||||
println(histo)
|
||||
assert histo[0].ct == 4
|
||||
assert histo[1].ct == 21
|
||||
assert histo[2].ct == 1821
|
||||
assert histo[3].ct == 182583
|
||||
assert histo[4].ct == 18200200
|
||||
for mut p in histo {
|
||||
p.ct = 0
|
||||
}
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for _ in 0 .. count {
|
||||
candidate = rand.f32cp()
|
||||
for mut p in histo {
|
||||
if candidate < p.lo {
|
||||
p.ct += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println(' f32cp')
|
||||
println(histo)
|
||||
assert histo[0].ct == 0
|
||||
assert histo[1].ct == 22
|
||||
assert histo[2].ct == 1829
|
||||
assert histo[3].ct == 142203
|
||||
assert histo[4].ct == 18200200
|
||||
}
|
||||
|
||||
fn test_f64() {
|
||||
mut histo := []Histo{}
|
||||
mut candidate := f64(0.0)
|
||||
|
||||
histo << Histo{1.0e-9, 0}
|
||||
histo << Histo{1.0e-6, 0}
|
||||
histo << Histo{1.0e-4, 0}
|
||||
histo << Histo{1.0e-2, 0}
|
||||
histo << Histo{1.0e00, 0}
|
||||
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for _ in 0 .. count {
|
||||
candidate = rand.f64()
|
||||
for mut p in histo {
|
||||
if candidate < p.lo {
|
||||
p.ct += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println(' f64 ')
|
||||
println(histo)
|
||||
assert histo[0].ct == 0
|
||||
assert histo[1].ct == 25
|
||||
assert histo[2].ct == 1763
|
||||
assert histo[3].ct == 182552
|
||||
assert histo[4].ct == 18200200
|
||||
for mut p in histo {
|
||||
p.ct = 0
|
||||
}
|
||||
for seed in seeds {
|
||||
rand.seed(seed)
|
||||
for _ in 0 .. count {
|
||||
candidate = rand.f64cp()
|
||||
for mut p in histo {
|
||||
if candidate < p.lo {
|
||||
p.ct += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println(' f64cp')
|
||||
println(histo)
|
||||
assert histo[0].ct == 0
|
||||
assert histo[1].ct == 22
|
||||
assert histo[2].ct == 1787
|
||||
assert histo[3].ct == 182160
|
||||
assert histo[4].ct == 18200200
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module rand
|
||||
|
||||
// NOTE: mini_math.v exists, so that we can avoid `import math`,
|
||||
// just for the math.log and math.sqrt functions needed for the
|
||||
// non uniform random number redistribution functions.
|
||||
// Importing math is relatively heavy, both in terms of compilation
|
||||
// speed (more source to process), and in terms of increases in the
|
||||
// generated executable sizes (if the rest of the program does not use
|
||||
// math already; many programs do not need math, for example the
|
||||
// compiler itself does not, while needing random number generation.
|
||||
|
||||
const sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974
|
||||
|
||||
@[inline]
|
||||
fn msqrt(a f64) f64 {
|
||||
if a == 0 {
|
||||
return a
|
||||
}
|
||||
mut x := a
|
||||
z, ex := frexp(x)
|
||||
w := x
|
||||
// approximate square root of number between 0.5 and 1
|
||||
// relative error of approximation = 7.47e-3
|
||||
x = 4.173075996388649989089e-1 + 5.9016206709064458299663e-1 * z // adjust for odd powers of 2
|
||||
if (ex & 1) != 0 {
|
||||
x *= sqrt2
|
||||
}
|
||||
x = scalbn(x, ex >> 1)
|
||||
// newton iterations
|
||||
x = 0.5 * (x + w / x)
|
||||
x = 0.5 * (x + w / x)
|
||||
x = 0.5 * (x + w / x)
|
||||
return x
|
||||
}
|
||||
|
||||
// a simplified approximation (without the edge cases), see math.log
|
||||
fn mlog(a f64) f64 {
|
||||
ln2_lo := 1.90821492927058770002e-10
|
||||
ln2_hi := 0.693147180369123816490
|
||||
l1 := 0.6666666666666735130
|
||||
l2 := 0.3999999999940941908
|
||||
l3 := 0.2857142874366239149
|
||||
l4 := 0.2222219843214978396
|
||||
l5 := 0.1818357216161805012
|
||||
l6 := 0.1531383769920937332
|
||||
l7 := 0.1479819860511658591
|
||||
x := a
|
||||
mut f1, mut ki := frexp(x)
|
||||
if f1 < sqrt2 / 2 {
|
||||
f1 *= 2
|
||||
ki--
|
||||
}
|
||||
f := f1 - 1
|
||||
k := f64(ki)
|
||||
s := f / (2 + f)
|
||||
s2 := s * s
|
||||
s4 := s2 * s2
|
||||
t1 := s2 * (l1 + s4 * (l3 + s4 * (l5 + s4 * l7)))
|
||||
t2 := s4 * (l2 + s4 * (l4 + s4 * l6))
|
||||
r := t1 + t2
|
||||
hfsq := 0.5 * f * f
|
||||
return k * ln2_hi - ((hfsq - (s * (hfsq + r) + k * ln2_lo)) - f)
|
||||
}
|
||||
|
||||
fn frexp(x f64) (f64, int) {
|
||||
mut y := f64_bits(x)
|
||||
ee := int((y >> 52) & 0x7ff)
|
||||
if ee == 0 {
|
||||
if x != 0.0 {
|
||||
x1p64 := f64_from_bits(u64(0x43f0000000000000))
|
||||
z, e_ := frexp(x * x1p64)
|
||||
return z, e_ - 64
|
||||
}
|
||||
return x, 0
|
||||
} else if ee == 0x7ff {
|
||||
return x, 0
|
||||
}
|
||||
e_ := ee - 0x3fe
|
||||
y &= u64(0x800fffffffffffff)
|
||||
y |= u64(0x3fe0000000000000)
|
||||
return f64_from_bits(y), e_
|
||||
}
|
||||
|
||||
fn scalbn(x f64, n_ int) f64 {
|
||||
mut n := n_
|
||||
x1p1023 := f64_from_bits(u64(0x7fe0000000000000))
|
||||
x1p53 := f64_from_bits(u64(0x4340000000000000))
|
||||
x1p_1022 := f64_from_bits(u64(0x0010000000000000))
|
||||
|
||||
mut y := x
|
||||
if n > 1023 {
|
||||
y *= x1p1023
|
||||
n -= 1023
|
||||
if n > 1023 {
|
||||
y *= x1p1023
|
||||
n -= 1023
|
||||
if n > 1023 {
|
||||
n = 1023
|
||||
}
|
||||
}
|
||||
} else if n < -1022 {
|
||||
/*
|
||||
make sure final n < -53 to avoid double
|
||||
rounding in the subnormal range
|
||||
*/
|
||||
y *= x1p_1022 * x1p53
|
||||
n += 1022 - 53
|
||||
if n < -1022 {
|
||||
y *= x1p_1022 * x1p53
|
||||
n += 1022 - 53
|
||||
if n < -1022 {
|
||||
n = -1022
|
||||
}
|
||||
}
|
||||
}
|
||||
return y * f64_from_bits(u64((0x3ff + n)) << 52)
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn f64_from_bits(b u64) f64 {
|
||||
return *unsafe { &f64(&b) }
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn f64_bits(f f64) u64 {
|
||||
return *unsafe { &u64(&f) }
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module mt19937
|
||||
|
||||
import rand.buffer
|
||||
import rand.seed
|
||||
|
||||
/*
|
||||
C++ functions for MT19937, with initialization improved 2002/2/10.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
This is a faster version by taking Shawn Cokus's optimization,
|
||||
Matthe Bellew's simplification, Isaku Wada's real version.
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
pub const seed_len = 2
|
||||
|
||||
const nn = 312
|
||||
const mm = 156
|
||||
const matrix_a = 0xB5026F5AA96619E9
|
||||
const um = 0xFFFFFFFF80000000
|
||||
const lm = 0x7FFFFFFF
|
||||
const inv_f64_limit = 1.0 / 9007199254740992.0
|
||||
|
||||
// MT19937RNG is generator that uses the Mersenne Twister algorithm with period 2^19937.
|
||||
// **NOTE**: The RNG is not seeded when instantiated so remember to seed it before use.
|
||||
pub struct MT19937RNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state []u64 = get_first_state(seed.time_seed_array(2))
|
||||
mti int = nn
|
||||
}
|
||||
|
||||
fn get_first_state(seed_data []u32) []u64 {
|
||||
mut state := []u64{len: nn}
|
||||
calculate_state(seed_data, mut state)
|
||||
return state
|
||||
}
|
||||
|
||||
// calculate_state returns a random state array calculated from the `seed_data`.
|
||||
@[ignore_overflow]
|
||||
fn calculate_state(seed_data []u32, mut state []u64) {
|
||||
lo := u64(seed_data[0])
|
||||
hi := u64(seed_data[1])
|
||||
state[0] = u64((hi << 32) | lo)
|
||||
for j := 1; j < nn; j++ {
|
||||
state[j] = u64(6364136223846793005) * (state[j - 1] ^ (state[j - 1] >> 62)) + u64(j)
|
||||
}
|
||||
}
|
||||
|
||||
// seed sets the current random state based on `seed_data`.
|
||||
// seed expects `seed_data` to be only two `u32`s in little-endian format as [lower, higher].
|
||||
pub fn (mut rng MT19937RNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 2 {
|
||||
eprintln('mt19937 needs only two 32bit integers as seed: [lower, higher]')
|
||||
exit(1)
|
||||
}
|
||||
calculate_state(seed_data, mut rng.state)
|
||||
rng.mti = nn
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng MT19937RNG) u8() u8 {
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
rng.buffer = rng.u64()
|
||||
rng.bytes_left = 7
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16bit int in range `[0, 2¹⁶)`.
|
||||
@[inline]
|
||||
pub fn (mut rng MT19937RNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 6
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom 32bit int in range `[0, 2³²)`.
|
||||
@[inline]
|
||||
pub fn (mut rng MT19937RNG) u32() u32 {
|
||||
// Can we take a whole u32 out of the buffer?
|
||||
if rng.bytes_left >= 4 {
|
||||
rng.bytes_left -= 4
|
||||
value := u32(rng.buffer)
|
||||
rng.buffer >>= 32
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 32
|
||||
rng.bytes_left = 4
|
||||
return u32(ans)
|
||||
}
|
||||
|
||||
const mag01 = [u64(0), u64(matrix_a)]
|
||||
|
||||
// u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`.
|
||||
@[direct_array_access; ignore_overflow; inline]
|
||||
pub fn (mut rng MT19937RNG) u64() u64 {
|
||||
mut x := u64(0)
|
||||
mut i := int(0)
|
||||
if rng.mti >= nn {
|
||||
for i = 0; i < nn - mm; i++ {
|
||||
x = (rng.state[i] & um) | (rng.state[i + 1] & lm)
|
||||
rng.state[i] = rng.state[i + mm] ^ (x >> 1) ^ mag01[int(x & 1)]
|
||||
}
|
||||
for i < nn - 1 {
|
||||
x = (rng.state[i] & um) | (rng.state[i + 1] & lm)
|
||||
rng.state[i] = rng.state[i + (mm - nn)] ^ (x >> 1) ^ mag01[int(x & 1)]
|
||||
i++
|
||||
}
|
||||
x = (rng.state[nn - 1] & um) | (rng.state[0] & lm)
|
||||
rng.state[nn - 1] = rng.state[mm - 1] ^ (x >> 1) ^ mag01[int(x & 1)]
|
||||
rng.mti = 0
|
||||
}
|
||||
x = rng.state[rng.mti]
|
||||
rng.mti++
|
||||
x ^= (x >> 29) & 0x5555555555555555
|
||||
x ^= (x << 17) & 0x71D67FFFEDA60000
|
||||
x ^= (x << 37) & 0xFFF7EEE000000000
|
||||
x ^= (x >> 43)
|
||||
return x
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng MT19937RNG) block_size() int {
|
||||
return 64
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng MT19937RNG) free() {
|
||||
unsafe { rng.state.free() }
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.mt19937
|
||||
import rand.seed
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(0xcafebabe), u32(0xdeadbeef)], [u32(0xc0de), u32(0xfeed)]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn mt19937_basic_test() {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed([u32(0xdeadbeef)])
|
||||
target := [u32(956529277), 3842322136, 3319553134, 1843186657, 2704993644, 595827513, 938518626,
|
||||
1676224337, 3221315650, 1819026461]
|
||||
for i := 0; i < 10; i++ {
|
||||
assert target[i] == rng.u32()
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u64 {
|
||||
bound_u64 := u64(bound)
|
||||
mut randoms := []u64{len: (20)}
|
||||
mut rnd := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rnd.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rnd.u64n(bound_u64) or { panic("Couldn't obtain random u64") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_mt19937_reproducibility() {
|
||||
seed_data := seed.time_seed_array(2)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
assert randoms1[i] == randoms2[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain random u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_mt19937_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_mt19937_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain random u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain random u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(min, max) or { panic("Couldn't obtain random u32 in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain random u64 in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain random int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain random i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_int_in_range() {
|
||||
min := -4252
|
||||
max := 1034
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain random int in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain random i64 in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain random f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain random f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain random f32 in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_mt19937_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain random f64 in range.") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module musl
|
||||
|
||||
import rand.seed
|
||||
import rand.buffer
|
||||
|
||||
pub const seed_len = 1
|
||||
|
||||
// MuslRNG ported from https://git.musl-libc.org/cgit/musl/tree/src/prng/rand_r.c .
|
||||
pub struct MuslRNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state u32 = seed.time_seed_32()
|
||||
}
|
||||
|
||||
// seed sets the current random state based on `seed_data`.
|
||||
// seed expects `seed_data` to be only one `u32`.
|
||||
pub fn (mut rng MuslRNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 1 {
|
||||
eprintln('MuslRNG needs only one unsigned 32-bit integer as a seed.')
|
||||
exit(1)
|
||||
}
|
||||
rng.state = seed_data[0]
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng MuslRNG) u8() u8 {
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
rng.buffer = rng.u32()
|
||||
rng.bytes_left = 3
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16-bit unsigned integer (`u16`).
|
||||
@[inline]
|
||||
pub fn (mut rng MuslRNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u32()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 2
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// temper returns a tempered value based on `prev` value.
|
||||
@[inline]
|
||||
fn temper(prev u32) u32 {
|
||||
mut x := prev
|
||||
x ^= x >> 11
|
||||
x ^= (x << 7) & 0x9D2C5680
|
||||
x ^= (x << 15) & 0xEFC60000
|
||||
x ^= (x >> 18)
|
||||
return x
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom 32-bit unsigned integer (`u32`).
|
||||
@[ignore_overflow]
|
||||
pub fn (mut rng MuslRNG) u32() u32 {
|
||||
rng.state = rng.state * 1103515245 + 12345
|
||||
// We are not dividing by 2 (or shifting right by 1)
|
||||
// because we want all 32-bits of random data
|
||||
return temper(rng.state)
|
||||
}
|
||||
|
||||
// u64 returns a pseudorandom 64-bit unsigned integer (`u64`).
|
||||
@[inline]
|
||||
pub fn (mut rng MuslRNG) u64() u64 {
|
||||
return u64(rng.u32()) | (u64(rng.u32()) << 32)
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng MuslRNG) block_size() int {
|
||||
return 32
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng MuslRNG) free() {
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.musl
|
||||
import rand.seed
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(42)], [u32(256)]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u64 {
|
||||
bound_u64 := u64(bound)
|
||||
mut randoms := []u64{len: (20)}
|
||||
mut rnd := &rand.PRNG(&musl.MuslRNG{})
|
||||
rnd.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rnd.u64n(bound_u64) or { panic("Couldn't obtain u64") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_musl_reproducibility() {
|
||||
seed_data := seed.time_seed_array(1)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
assert randoms1[i] == randoms2[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_musl_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_musl_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(min, max) or { panic("Couldn't obtain u32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_int_in_range() {
|
||||
min := -4252
|
||||
max := 1034
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_musl_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&musl.MuslRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module pcg32
|
||||
|
||||
import rand.seed
|
||||
import rand.buffer
|
||||
|
||||
pub const seed_len = 4
|
||||
|
||||
// PCG32RNG is ported from http://www.pcg-random.org/download.html .
|
||||
// https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c, and
|
||||
// https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.h
|
||||
pub struct PCG32RNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state u64 = u64(0x853c49e6748fea9b) ^ seed.time_seed_64()
|
||||
inc u64 = (u64(0xda3e39cb94b95bdb) ^ seed.time_seed_64()) | u64(1)
|
||||
}
|
||||
|
||||
// seed seeds the PCG32RNG with 4 `u32` values.
|
||||
// The first 2 represent the 64-bit initial state as `[lower 32 bits, higher 32 bits]`
|
||||
// The last 2 represent the 64-bit stream/step of the PRNG.
|
||||
@[ignore_overflow]
|
||||
pub fn (mut rng PCG32RNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 4 {
|
||||
eprintln('PCG32RNG needs 4 u32s to be seeded. First two the initial state and the last two the stream/step. Both in little endian format: [lower, higher].')
|
||||
exit(1)
|
||||
}
|
||||
init_state := u64(seed_data[0]) | (u64(seed_data[1]) << 32)
|
||||
init_seq := u64(seed_data[2]) | (u64(seed_data[3]) << 32)
|
||||
rng.state = u64(0)
|
||||
rng.inc = (init_seq << u64(1)) | u64(1)
|
||||
rng.u32()
|
||||
rng.state += init_state
|
||||
rng.u32()
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng PCG32RNG) u8() u8 {
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
rng.buffer = rng.u32()
|
||||
rng.bytes_left = 3
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16-bit unsigned integer (`u16`).
|
||||
@[inline]
|
||||
pub fn (mut rng PCG32RNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u32()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 2
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom unsigned `u32`.
|
||||
@[ignore_overflow; inline]
|
||||
pub fn (mut rng PCG32RNG) u32() u32 {
|
||||
oldstate := rng.state
|
||||
rng.state = oldstate * (6364136223846793005) + rng.inc
|
||||
xorshifted := u32(((oldstate >> u64(18)) ^ oldstate) >> u64(27))
|
||||
rot := u32(oldstate >> u64(59))
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & u32(31)))
|
||||
}
|
||||
|
||||
// u64 returns a pseudorandom 64-bit unsigned `u64`.
|
||||
@[inline]
|
||||
pub fn (mut rng PCG32RNG) u64() u64 {
|
||||
return u64(rng.u32()) | (u64(rng.u32()) << 32)
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng PCG32RNG) block_size() int {
|
||||
return 32
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng PCG32RNG) free() {
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.pcg32
|
||||
import rand.seed
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(42), 242, 267, 14195], [u32(256), 340, 1451, 1505]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u32 {
|
||||
mut randoms := []u32{len: 20}
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rng.u32n(u32(bound)) or { panic("Couldn't obtain u32") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_pcg32_reproducibility() {
|
||||
seed_data := seed.time_seed_array(4)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
r1 := randoms1[i]
|
||||
r2 := randoms2[i]
|
||||
assert r1 == r2
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_pcg32_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_pcg32_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(u32(min), u32(max)) or {
|
||||
panic("Couldn't obtain u32 in range")
|
||||
}
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_int_in_range() {
|
||||
min := -4252
|
||||
max := 1034
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcg32_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_change_default_random_generator() {
|
||||
rand.set_rng(&rand.PRNG(&pcg32.PCG32RNG{}))
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
module rand
|
||||
|
||||
import time
|
||||
|
||||
// uuid_v4 generates a random (v4) UUID.
|
||||
// See https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
|
||||
// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4
|
||||
pub fn uuid_v4() string {
|
||||
rand_1 := default_rng.u64()
|
||||
rand_2 := default_rng.u64()
|
||||
return internal_uuid(4, rand_1, rand_2)
|
||||
}
|
||||
|
||||
@[direct_array_access; inline]
|
||||
fn internal_uuid(version u8, rand_1 u64, rand_2 u64) string {
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | rand_1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | rand_1 | ver | rand_1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |var| rand_2 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | rand_2 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
mut parts := [8]u16{}
|
||||
parts[0] = u16(rand_1 >> 48)
|
||||
parts[1] = u16(rand_1 >> 32)
|
||||
parts[2] = u16(rand_1 >> 16)
|
||||
parts[3] = u16(rand_1)
|
||||
parts[4] = u16(rand_2 >> 48)
|
||||
parts[5] = u16(rand_2 >> 32)
|
||||
parts[6] = u16(rand_2 >> 16)
|
||||
parts[7] = u16(rand_2)
|
||||
|
||||
parts[3] = (parts[3] & 0x0FFF) | (u16(version) << 12) // set version
|
||||
parts[4] = (parts[4] & 0x3FFF) | 0x8000 // set variant = 0b10
|
||||
|
||||
mut buf := unsafe { malloc_noscan(37) }
|
||||
mut start := 0
|
||||
unsafe {
|
||||
for i in 0 .. 8 {
|
||||
val := parts[i]
|
||||
buf[start] = hex_chars[(val >> 12) & 0xF]
|
||||
buf[start + 1] = hex_chars[(val >> 8) & 0xF]
|
||||
buf[start + 2] = hex_chars[(val >> 4) & 0xF]
|
||||
buf[start + 3] = hex_chars[val & 0xF]
|
||||
start += 4
|
||||
// insert `-` at specified locations
|
||||
if start in [8, 13, 18, 23]! {
|
||||
buf[start] = `-`
|
||||
start++
|
||||
}
|
||||
}
|
||||
buf[36] = 0
|
||||
return buf.vstring_with_len(36)
|
||||
}
|
||||
}
|
||||
|
||||
// uuid_v7 generates a time-ordered (v7) UUID.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7
|
||||
pub fn uuid_v7() string {
|
||||
timestamp_48 := u64(time.now().unix_milli()) << 16
|
||||
rand_1 := timestamp_48 | default_rng.u16()
|
||||
rand_2 := default_rng.u64()
|
||||
return internal_uuid(7, rand_1, rand_2)
|
||||
}
|
||||
|
||||
pub struct UUIDSession {
|
||||
mut:
|
||||
counter u8 // 6 bits session counter
|
||||
}
|
||||
|
||||
// new_uuid_v7_session create a new session for generating uuid_v7.
|
||||
// The 12 bits `rand_a` in the RFC 9652, is replaced by 6 bits
|
||||
// sub-millisecond timestamp + 6 bits session counter.
|
||||
// See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=78c5e141e9c139fc2ff36a220334e4aa25e1b0eb
|
||||
pub fn new_uuid_v7_session() UUIDSession {
|
||||
return UUIDSession{}
|
||||
}
|
||||
|
||||
// next get a new uuid_v7 from current session.
|
||||
@[ignore_overflow]
|
||||
pub fn (mut u UUIDSession) next() string {
|
||||
timestamp := u64(time.now().unix_nano())
|
||||
// make place for holding 4 bits `version`
|
||||
timestamp_shift_4bits := (timestamp & 0xFFFF_FFFF_FFFF_0000) | ((timestamp & 0x0000_0000_0000_FFFF) >> 4)
|
||||
rand_1 := (timestamp_shift_4bits & 0xFFFF_FFFF_FFFF_FFC0) | u64(u.counter & 0x3F) // 6 bits session counter
|
||||
rand_2 := default_rng.u64()
|
||||
|
||||
u.counter++
|
||||
|
||||
return internal_uuid(7, rand_1, rand_2)
|
||||
}
|
||||
|
||||
const ulid_encoding = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
||||
|
||||
@[direct_array_access]
|
||||
fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string {
|
||||
buflen := 26
|
||||
mut buf := unsafe { malloc_noscan(27) }
|
||||
mut t := unix_time_milli
|
||||
mut i := 9
|
||||
for i >= 0 {
|
||||
unsafe {
|
||||
buf[i] = ulid_encoding[int(t & 0x1F)]
|
||||
}
|
||||
t = t >> 5
|
||||
i--
|
||||
}
|
||||
// first rand set
|
||||
mut x := rng.u64()
|
||||
i = 10
|
||||
for i < 19 {
|
||||
unsafe {
|
||||
buf[i] = ulid_encoding[int(x & 0x1F)]
|
||||
}
|
||||
x = x >> 5
|
||||
i++
|
||||
}
|
||||
// second rand set
|
||||
x = rng.u64()
|
||||
for i < 26 {
|
||||
unsafe {
|
||||
buf[i] = ulid_encoding[int(x & 0x1F)]
|
||||
}
|
||||
x = x >> 5
|
||||
i++
|
||||
}
|
||||
unsafe {
|
||||
buf[26] = 0
|
||||
return buf.vstring_with_len(buflen)
|
||||
}
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn internal_string_from_set(mut rng PRNG, charset string, len int) string {
|
||||
if len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut buf := unsafe { malloc_noscan(len + 1) }
|
||||
for i in 0 .. len {
|
||||
unsafe {
|
||||
buf[i] = charset[rng.u32() % u32(charset.len)]
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
buf[len] = 0
|
||||
}
|
||||
return unsafe { buf.vstring_with_len(len) }
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn internal_fill_buffer_from_set(mut rng PRNG, charset string, mut buf []u8) {
|
||||
if buf.len == 0 {
|
||||
return
|
||||
}
|
||||
blen := buf.len
|
||||
for i in 0 .. blen {
|
||||
unsafe {
|
||||
buf[i] = charset[rng.u32() % u32(charset.len)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deinit() {
|
||||
unsafe {
|
||||
default_rng.free() // free the implementation
|
||||
free(default_rng) // free the interface wrapper itself
|
||||
}
|
||||
}
|
||||
|
||||
// init initializes the default RNG.
|
||||
fn init() {
|
||||
default_rng = new_default()
|
||||
at_exit(deinit) or {}
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn read_32(mut rng PRNG, mut buf []u8) {
|
||||
p32 := unsafe { &u32(buf.data) }
|
||||
u32s := buf.len / 4
|
||||
for i in 0 .. u32s {
|
||||
unsafe {
|
||||
*(p32 + i) = rng.u32()
|
||||
}
|
||||
}
|
||||
for i in u32s * 4 .. buf.len {
|
||||
buf[i] = rng.u8()
|
||||
}
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn read_64(mut rng PRNG, mut buf []u8) {
|
||||
p64 := unsafe { &u64(buf.data) }
|
||||
if u64(p64) & 0xF != 0 {
|
||||
for i in 0 .. buf.len {
|
||||
buf[i] = rng.u8()
|
||||
}
|
||||
return
|
||||
}
|
||||
u64s := buf.len / 8
|
||||
for i in 0 .. u64s {
|
||||
unsafe {
|
||||
*(p64 + i) = rng.u64()
|
||||
}
|
||||
}
|
||||
for i in u64s * 8 .. buf.len {
|
||||
buf[i] = rng.u8()
|
||||
}
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn read_internal(mut rng PRNG, mut buf []u8) {
|
||||
match rng.block_size() {
|
||||
32 {
|
||||
read_32(mut rng, mut buf)
|
||||
}
|
||||
64 {
|
||||
read_64(mut rng, mut buf)
|
||||
}
|
||||
else {
|
||||
for i in 0 .. buf.len {
|
||||
buf[i] = rng.u8()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
module rand
|
||||
|
||||
// init initializes the default RNG.
|
||||
fn init() {
|
||||
default_rng = new_default()
|
||||
}
|
||||
|
||||
fn internal_fill_buffer_from_set(mut _ PRNG, _ string, mut _ []u8) {
|
||||
panic('todo')
|
||||
}
|
||||
|
||||
// fn internal_string_from_set(mut rng PRNG, charset string, len int) string {
|
||||
fn internal_string_from_set(mut _ PRNG, charset string, len int) string {
|
||||
result := ''
|
||||
#
|
||||
#const characters = charset.str;
|
||||
#const charactersLength = characters.length;
|
||||
#for (let i = 0;i < len.val;i++)
|
||||
#result.str += characters.charAt(Math.random() * charactersLength);
|
||||
|
||||
_ = charset
|
||||
_ = len
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const ulid_encoding = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
||||
|
||||
fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string {
|
||||
mut buf := []u8{cap: 27}
|
||||
mut t := unix_time_milli
|
||||
mut i := 9
|
||||
for i >= 0 {
|
||||
buf[i] = ulid_encoding[int(t & 0x1f)]
|
||||
t = t >> 5
|
||||
i--
|
||||
}
|
||||
|
||||
mut x := rng.u64()
|
||||
i = 10
|
||||
for i < 19 {
|
||||
buf[i] = ulid_encoding[int(x & 0x1f)]
|
||||
|
||||
x = x >> 5
|
||||
i++
|
||||
}
|
||||
|
||||
x = rng.u64()
|
||||
for i < 26 {
|
||||
buf[i] = ulid_encoding[int(x & 0x1f)]
|
||||
x = x >> 5
|
||||
i++
|
||||
}
|
||||
|
||||
res := ''
|
||||
println(buf)
|
||||
#res.str = buf.arr.arr.map(String.fromCharCode).join('')
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn read_internal(mut rng PRNG, mut buf []u8) {
|
||||
for i in 0 .. buf.len {
|
||||
buf[i] = rng.u8()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,813 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
@[has_globals]
|
||||
module rand
|
||||
|
||||
import math.bits
|
||||
import rand.config
|
||||
import rand.wyrand
|
||||
import time
|
||||
|
||||
// PRNG is a common interface for all PRNGs that can be used seamlessly with the rand modules's API.
|
||||
// It defines all the methods that a PRNG (in the vlib or custom made) must
|
||||
// implement in order to ensure that _all_ functions can be used with the generator.
|
||||
pub interface PRNG {
|
||||
mut:
|
||||
seed(seed_data []u32)
|
||||
u8() u8
|
||||
u16() u16
|
||||
u32() u32
|
||||
u64() u64
|
||||
block_size() int
|
||||
free()
|
||||
}
|
||||
|
||||
// default_rng is used by the module level public APIs like rand.u8() etc.
|
||||
__global default_rng &PRNG
|
||||
|
||||
// new_default returns a new instance of the default RNG. If the seed is not provided, the current time will be used to seed the instance.
|
||||
@[manualfree]
|
||||
pub fn new_default(config_ config.PRNGConfigStruct) &PRNG {
|
||||
mut rng := &wyrand.WyRandRNG{}
|
||||
rng.seed(config_.seed_)
|
||||
unsafe { config_.seed_.free() }
|
||||
return &PRNG(rng)
|
||||
}
|
||||
|
||||
// get_current_rng returns the PRNG instance currently in use. If it is not changed, it will be an instance of wyrand.WyRandRNG.
|
||||
pub fn get_current_rng() &PRNG {
|
||||
return default_rng
|
||||
}
|
||||
|
||||
// set_rng changes the default RNG from wyrand.WyRandRNG (or whatever the last RNG was).
|
||||
// Note that this new RNG must be seeded manually with a constant seed or the
|
||||
// `seed.time_seed_array()` method. Also, it is recommended to store the old RNG in a variable and
|
||||
// should be restored if work with the custom RNG is complete. It is not necessary to restore if the
|
||||
// program terminates soon afterwards.
|
||||
pub fn set_rng(rng &PRNG) {
|
||||
default_rng = unsafe { rng }
|
||||
}
|
||||
|
||||
// seed sets the given array of `u32` values as the seed for the `default_rng`.
|
||||
// Note: the default_rng is already seeded with a *time dependent value*,
|
||||
// so if you just need some randomness, for a game/simulation, and you do not need
|
||||
// reproducibility, just use it, without calling rand.seed() to do explicit seeding.
|
||||
// The default_rng is an instance of WyRandRNG which takes 2 u32 values.
|
||||
// When using a custom RNG, make sure to use the correct number of u32s.
|
||||
pub fn seed(seed []u32) {
|
||||
default_rng.seed(seed)
|
||||
}
|
||||
|
||||
// bytes returns a buffer of `bytes_needed` random bytes
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) bytes(bytes_needed int) ![]u8 {
|
||||
if bytes_needed < 0 {
|
||||
return error('can not read < 0 random bytes')
|
||||
}
|
||||
|
||||
mut buffer := []u8{len: bytes_needed}
|
||||
read_internal(mut rng, mut buffer)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
// read fills in `buf` with a maximum of `buf.len` random bytes.
|
||||
pub fn (mut rng PRNG) read(mut buf []u8) {
|
||||
read_internal(mut rng, mut buf)
|
||||
}
|
||||
|
||||
// i32n returns a uniformly distributed pseudorandom 32-bit signed positive `i32` in range `[0, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i32n(max i32) !i32 {
|
||||
return i32(rng.intn(max)!)
|
||||
}
|
||||
|
||||
// u32n returns a uniformly distributed pseudorandom 32-bit unsigned positive `u32` in range `[0, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) u32n(max u32) !u32 {
|
||||
if max == 0 {
|
||||
return error('max must be positive integer')
|
||||
}
|
||||
// Owing to the pigeon-hole principle, we can't simply do
|
||||
// val := rng.u32() % max.
|
||||
// It'll wreck the properties of the distribution unless
|
||||
// max evenly divides 2^32. So we divide evenly to
|
||||
// the closest power of two. Then we loop until we find
|
||||
// an int in the required range
|
||||
bit_len := bits.len_32(max)
|
||||
if _unlikely_(bit_len == 32) {
|
||||
for {
|
||||
value := rng.u32()
|
||||
if value < max {
|
||||
return value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mask := if _unlikely_(bit_len == 31) {
|
||||
u32(0x7FFFFFFF)
|
||||
} else {
|
||||
(u32(1) << (bit_len + 1)) - 1
|
||||
}
|
||||
for {
|
||||
value := rng.u32() & mask
|
||||
if value < max {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return u32(0)
|
||||
}
|
||||
|
||||
// u64n returns a uniformly distributed pseudorandom 64-bit signed positive `u64` in range `[0, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) u64n(max u64) !u64 {
|
||||
if max == 0 {
|
||||
return error('max must be positive integer')
|
||||
}
|
||||
bit_len := bits.len_64(max)
|
||||
if _unlikely_(bit_len == 64) {
|
||||
for {
|
||||
value := rng.u64()
|
||||
if value < max {
|
||||
return value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mask := if _unlikely_(bit_len == 63) {
|
||||
u64(0x7FFFFFFFFFFFFFFF)
|
||||
} else {
|
||||
(u64(1) << (bit_len + 1)) - 1
|
||||
}
|
||||
for {
|
||||
value := rng.u64() & mask
|
||||
if value < max {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
return u64(0)
|
||||
}
|
||||
|
||||
// u32_in_range returns a uniformly distributed pseudorandom 32-bit unsigned `u32` in range `[min, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) u32_in_range(min u32, max u32) !u32 {
|
||||
if max <= min {
|
||||
return error('max must be greater than min')
|
||||
}
|
||||
return min + rng.u32n(max - min)!
|
||||
}
|
||||
|
||||
// u64_in_range returns a uniformly distributed pseudorandom 64-bit unsigned `u64` in range `[min, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) u64_in_range(min u64, max u64) !u64 {
|
||||
if max <= min {
|
||||
return error('max must be greater than min')
|
||||
}
|
||||
return min + rng.u64n(max - min)!
|
||||
}
|
||||
|
||||
// i8 returns a (possibly negative) pseudorandom 8-bit `i8`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i8() i8 {
|
||||
return i8(rng.u8())
|
||||
}
|
||||
|
||||
// i16 returns a (possibly negative) pseudorandom 16-bit `i16`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i16() i16 {
|
||||
return i16(rng.u16())
|
||||
}
|
||||
|
||||
// i32 returns a (possibly negative) pseudorandom 32-bit `i32`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i32() i32 {
|
||||
return i32(rng.u32())
|
||||
}
|
||||
|
||||
// int returns a (possibly negative) pseudorandom 32-bit `int`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) int() int {
|
||||
return int(rng.u32())
|
||||
}
|
||||
|
||||
// i64 returns a (possibly negative) pseudorandom 64-bit `i64`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i64() i64 {
|
||||
return i64(rng.u64())
|
||||
}
|
||||
|
||||
// int31 returns a positive pseudorandom 31-bit `int`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) int31() int {
|
||||
return int(rng.u32() & u32(0x7FFFFFFF)) // Set the 32nd bit to 0.
|
||||
}
|
||||
|
||||
// int63 returns a positive pseudorandom 63-bit `i64`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) int63() i64 {
|
||||
return i64(rng.u64() & u64(0x7FFFFFFFFFFFFFFF)) // Set the 64th bit to 0.
|
||||
}
|
||||
|
||||
// intn returns a pseudorandom `int` in range `[0, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) intn(max int) !int {
|
||||
if max <= 0 {
|
||||
return error('max has to be positive.')
|
||||
}
|
||||
return int(rng.u32n(u32(max))!)
|
||||
}
|
||||
|
||||
// i64n returns a pseudorandom int that lies in `[0, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i64n(max i64) !i64 {
|
||||
if max <= 0 {
|
||||
return error('max has to be positive.')
|
||||
}
|
||||
return i64(rng.u64n(u64(max))!)
|
||||
}
|
||||
|
||||
// int_in_range returns a pseudorandom `int` in range `[min, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) int_in_range(min int, max int) !int {
|
||||
if max <= min {
|
||||
return error('max must be greater than min')
|
||||
}
|
||||
// This supports negative ranges like [-10, -5) because the difference is positive
|
||||
return min + rng.intn(max - min)!
|
||||
}
|
||||
|
||||
// int_in_range returns a pseudorandom `int` in range `[min, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i32_in_range(min i32, max i32) !i32 {
|
||||
if max <= min {
|
||||
return error('max must be greater than min')
|
||||
}
|
||||
// This supports negative ranges like [-10, -5) because the difference is positive
|
||||
return min + i32(rng.intn(max - min)!)
|
||||
}
|
||||
|
||||
// i64_in_range returns a pseudorandom `i64` in range `[min, max)`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) i64_in_range(min i64, max i64) !i64 {
|
||||
if max <= min {
|
||||
return error('max must be greater than min')
|
||||
}
|
||||
return min + rng.i64n(max - min)!
|
||||
}
|
||||
|
||||
// smallest mantissa with exponent 0 (un normalized)
|
||||
const reciprocal_2_23rd = 1.0 / f64(u32(1) << 23)
|
||||
const reciprocal_2_52nd = 1.0 / f64(u64(1) << 52)
|
||||
const ieee754_mantissa_f32_mask = (u32(1) << 23) - 1 // 23 bits for f32
|
||||
const ieee754_mantissa_f64_mask = (u64(1) << 52) - 1 // 52 bits for f64
|
||||
|
||||
// f32 returns a pseudorandom `f32` value in range `[0, 1)`
|
||||
// using rng.u32() multiplied by an f64 constant.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f32() f32 {
|
||||
return f32((rng.u32() >> 9) * reciprocal_2_23rd)
|
||||
}
|
||||
|
||||
// f32cp returns a pseudorandom `f32` value in range `[0, 1)`
|
||||
// with full precision (mantissa random between 0 and 1
|
||||
// and the exponent varies as well.)
|
||||
// See https://allendowney.com/research/rand/ for background on the method.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f32cp() f32 {
|
||||
mut x := rng.u32()
|
||||
mut exp := u32(126)
|
||||
mut mask := u32(1) << 31
|
||||
|
||||
// check if prng returns 0; rare but keep looking for precision
|
||||
if _unlikely_(x == 0) {
|
||||
x = rng.u32()
|
||||
exp -= 31
|
||||
}
|
||||
// count leading one bits and scale exponent accordingly
|
||||
for {
|
||||
if x & mask != 0 {
|
||||
mask >>= 1
|
||||
exp -= 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// if we used any high-order mantissa bits; replace x
|
||||
if exp < (126 - 8) {
|
||||
x = rng.u32()
|
||||
}
|
||||
|
||||
// Assumes little-endian IEEE floating point.
|
||||
x = (exp << 23) | (x >> 8) & ieee754_mantissa_f32_mask
|
||||
return bits.f32_from_bits(x)
|
||||
}
|
||||
|
||||
// f64 returns a pseudorandom `f64` value in range `[0, 1)`
|
||||
// using rng.u64() multiplied by a constant.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f64() f64 {
|
||||
return f64((rng.u64() >> 12) * reciprocal_2_52nd)
|
||||
}
|
||||
|
||||
// f64cp returns a pseudorandom `f64` value in range `[0, 1)`
|
||||
// with full precision (mantissa random between 0 and 1
|
||||
// and the exponent varies as well.)
|
||||
// See https://allendowney.com/research/rand/ for background on the method.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f64cp() f64 {
|
||||
mut x := rng.u64()
|
||||
mut exp := u64(1022)
|
||||
mut mask := u64(1) << 63
|
||||
mut bitcount := u32(0)
|
||||
|
||||
// check if prng returns 0; unlikely.
|
||||
if _unlikely_(x == 0) {
|
||||
x = rng.u64()
|
||||
exp -= 31
|
||||
}
|
||||
// count leading one bits and scale exponent accordingly
|
||||
for {
|
||||
if x & mask != 0 {
|
||||
mask >>= 1
|
||||
bitcount += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
exp -= bitcount
|
||||
if bitcount > 11 {
|
||||
x = rng.u64()
|
||||
}
|
||||
x = (exp << 52) | (x & ieee754_mantissa_f64_mask)
|
||||
return bits.f64_from_bits(x)
|
||||
}
|
||||
|
||||
// f32n returns a pseudorandom `f32` value in range `[0, max]`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f32n(max f32) !f32 {
|
||||
if max < 0 {
|
||||
return error('max has to be non-negative.')
|
||||
}
|
||||
return rng.f32() * max
|
||||
}
|
||||
|
||||
// f64n returns a pseudorandom `f64` value in range `[0, max]`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f64n(max f64) !f64 {
|
||||
if max < 0 {
|
||||
return error('max has to be non-negative.')
|
||||
}
|
||||
return rng.f64() * max
|
||||
}
|
||||
|
||||
// f32_in_range returns a pseudorandom `f32` in range `[min, max]`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f32_in_range(min f32, max f32) !f32 {
|
||||
if max < min {
|
||||
return error('max must be greater than or equal to min')
|
||||
}
|
||||
return min + rng.f32n(max - min)!
|
||||
}
|
||||
|
||||
// i64_in_range returns a pseudorandom `i64` in range `[min, max]`.
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) f64_in_range(min f64, max f64) !f64 {
|
||||
if max < min {
|
||||
return error('max must be greater than or equal to min')
|
||||
}
|
||||
return min + rng.f64n(max - min)!
|
||||
}
|
||||
|
||||
// ulid generates an unique lexicographically sortable identifier.
|
||||
// See https://github.com/ulid/spec .
|
||||
// Note: ULIDs can leak timing information, if you make them public, because
|
||||
// you can infer the rate at which some resource is being created, like
|
||||
// users or business transactions.
|
||||
// (https://news.ycombinator.com/item?id=14526173)
|
||||
pub fn (mut rng PRNG) ulid() string {
|
||||
return internal_ulid_at_millisecond(mut rng, u64(time.utc().unix_milli()))
|
||||
}
|
||||
|
||||
// ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_milli`.
|
||||
pub fn (mut rng PRNG) ulid_at_millisecond(unix_time_milli u64) string {
|
||||
return internal_ulid_at_millisecond(mut rng, unix_time_milli)
|
||||
}
|
||||
|
||||
// string_from_set returns a string of length `len` containing random characters sampled from the given `charset`.
|
||||
pub fn (mut rng PRNG) string_from_set(charset string, len int) string {
|
||||
return internal_string_from_set(mut rng, charset, len)
|
||||
}
|
||||
|
||||
// string returns a string of length `len` containing random characters in range `[a-zA-Z]`.
|
||||
pub fn (mut rng PRNG) string(len int) string {
|
||||
return internal_string_from_set(mut rng, english_letters, len)
|
||||
}
|
||||
|
||||
// hex returns a hexadecimal number of length `len` containing random characters in range `[a-f0-9]`.
|
||||
pub fn (mut rng PRNG) hex(len int) string {
|
||||
return internal_string_from_set(mut rng, hex_chars, len)
|
||||
}
|
||||
|
||||
// ascii returns a random string of the printable ASCII characters with length `len`.
|
||||
pub fn (mut rng PRNG) ascii(len int) string {
|
||||
return internal_string_from_set(mut rng, ascii_chars, len)
|
||||
}
|
||||
|
||||
// fill_buffer_from_set fills the mutable `buf` with random characters from the given `charset`
|
||||
@[inline]
|
||||
pub fn (mut rng PRNG) fill_buffer_from_set(charset string, mut buf []u8) {
|
||||
internal_fill_buffer_from_set(mut rng, charset, mut buf)
|
||||
}
|
||||
|
||||
// bernoulli returns true with a probability p. Note that 0 <= p <= 1.
|
||||
pub fn (mut rng PRNG) bernoulli(p f64) !bool {
|
||||
if p < 0 || p > 1 {
|
||||
return error('${p} is not a valid probability value.')
|
||||
}
|
||||
return rng.f64() <= p
|
||||
}
|
||||
|
||||
// normal returns a normally distributed pseudorandom f64 with mean `mu` and standard deviation `sigma`.
|
||||
// By default, `mu` is 0.0 and `sigma` is 1.0.
|
||||
// NOTE: Use normal_pair() instead if you're generating a lot of normal variates.
|
||||
pub fn (mut rng PRNG) normal(conf config.NormalConfigStruct) !f64 {
|
||||
x, _ := rng.normal_pair(conf)!
|
||||
return x
|
||||
}
|
||||
|
||||
// normal_pair returns a pair of normally distributed pseudorandom f64 with mean `mu` and standard deviation `sigma`.
|
||||
// By default, `mu` is 0.0 and `sigma` is 1.0.
|
||||
pub fn (mut rng PRNG) normal_pair(conf config.NormalConfigStruct) !(f64, f64) {
|
||||
if conf.sigma <= 0 {
|
||||
return error('Standard deviation must be positive')
|
||||
}
|
||||
// This is an implementation of the Marsaglia polar method
|
||||
// See: https://doi.org/10.1137%2F1006063
|
||||
// Also: https://en.wikipedia.org/wiki/Marsaglia_polar_method
|
||||
for {
|
||||
u := rng.f64_in_range(-1, 1) or { 0.0 }
|
||||
v := rng.f64_in_range(-1, 1) or { 0.0 }
|
||||
|
||||
s := u * u + v * v
|
||||
if s >= 1 || s == 0 {
|
||||
continue
|
||||
}
|
||||
t := msqrt(-2 * mlog(s) / s)
|
||||
x := conf.mu + conf.sigma * t * u
|
||||
y := conf.mu + conf.sigma * t * v
|
||||
return x, y
|
||||
}
|
||||
return error('Implementation error. Please file an issue.')
|
||||
}
|
||||
|
||||
// binomial returns the number of successful trials out of n when the probability of success for each trial is p.
|
||||
pub fn (mut rng PRNG) binomial(n int, p f64) !int {
|
||||
if p < 0 || p > 1 {
|
||||
return error('${p} is not a valid probability value.')
|
||||
}
|
||||
mut count := 0
|
||||
for _ in 0 .. n {
|
||||
if rng.bernoulli(p)! {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// exponential returns an exponentially distributed random number with the rate parameter lambda.
|
||||
// It is expected that lambda is positive.
|
||||
pub fn (mut rng PRNG) exponential(lambda f64) f64 {
|
||||
if lambda <= 0 {
|
||||
panic('The rate (lambda) must be positive.')
|
||||
}
|
||||
// Use the inverse transform sampling method
|
||||
return -mlog(rng.f64()) / lambda
|
||||
}
|
||||
|
||||
// shuffle randomly permutates the elements in `a`. The range for shuffling is
|
||||
// optional and the entire array is shuffled by default. Leave the end as 0 to
|
||||
// shuffle all elements until the end.
|
||||
@[direct_array_access]
|
||||
pub fn (mut rng PRNG) shuffle[T](mut a []T, config_ config.ShuffleConfigStruct) ! {
|
||||
config_.validate_for(a)!
|
||||
new_end := if config_.end == 0 { a.len } else { config_.end }
|
||||
|
||||
// We implement the Fisher-Yates shuffle:
|
||||
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
|
||||
|
||||
for i in config_.start .. new_end - 1 {
|
||||
x := rng.int_in_range(i, new_end) or { i }
|
||||
// swap
|
||||
a_i := a[i]
|
||||
a[i] = a[x]
|
||||
a[x] = a_i
|
||||
}
|
||||
}
|
||||
|
||||
// shuffle_clone returns a random permutation of the elements in `a`.
|
||||
// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
|
||||
pub fn (mut rng PRNG) shuffle_clone[T](a []T, config_ config.ShuffleConfigStruct) ![]T {
|
||||
mut res := a.clone()
|
||||
rng.shuffle[T](mut res, config_)!
|
||||
return res
|
||||
}
|
||||
|
||||
// choose samples k elements from the array without replacement.
|
||||
// This means the indices cannot repeat and it restricts the sample size to be less than or equal to the size of the given array.
|
||||
// Note that if the array has repeating elements, then the sample may have repeats as well.
|
||||
pub fn (mut rng PRNG) choose[T](array []T, k int) ![]T {
|
||||
n := array.len
|
||||
if k > n {
|
||||
return error('Cannot choose ${k} elements without replacement from a ${n}-element array.')
|
||||
}
|
||||
mut results := []T{len: k}
|
||||
mut indices := []int{len: n, init: index}
|
||||
rng.shuffle[int](mut indices)!
|
||||
for i in 0 .. k {
|
||||
results[i] = array[indices[i]]
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// element returns a random element from the given array.
|
||||
// Note that all the positions in the array have an equal chance of being selected. This means that if the array has repeating elements, then the probability of selecting a particular element is not uniform.
|
||||
pub fn (mut rng PRNG) element[T](array []T) !T {
|
||||
if array.len == 0 {
|
||||
return error('Cannot choose an element from an empty array.')
|
||||
}
|
||||
return array[rng.intn(array.len)!]
|
||||
}
|
||||
|
||||
// sample samples k elements from the array with replacement.
|
||||
// This means the elements can repeat and the size of the sample may exceed the size of the array.
|
||||
pub fn (mut rng PRNG) sample[T](array []T, k int) []T {
|
||||
mut results := []T{len: k}
|
||||
for i in 0 .. k {
|
||||
results[i] = array[rng.intn(array.len) or { 0 }]
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// u8 returns a uniformly distributed pseudorandom 8-bit unsigned positive `u8`.
|
||||
pub fn u8() u8 {
|
||||
return default_rng.u8()
|
||||
}
|
||||
|
||||
// u16 returns a uniformly distributed pseudorandom 16-bit unsigned positive `u16`.
|
||||
pub fn u16() u16 {
|
||||
return default_rng.u16()
|
||||
}
|
||||
|
||||
// u32 returns a uniformly distributed `u32` in range `[0, 2³²)`.
|
||||
pub fn u32() u32 {
|
||||
return default_rng.u32()
|
||||
}
|
||||
|
||||
// u64 returns a uniformly distributed `u64` in range `[0, 2⁶⁴)`.
|
||||
pub fn u64() u64 {
|
||||
return default_rng.u64()
|
||||
}
|
||||
|
||||
// u32n returns a uniformly distributed pseudorandom 32-bit signed positive `u32` in range `[0, max)`.
|
||||
pub fn u32n(max u32) !u32 {
|
||||
return default_rng.u32n(max)
|
||||
}
|
||||
|
||||
// u64n returns a uniformly distributed pseudorandom 64-bit signed positive `u64` in range `[0, max)`.
|
||||
pub fn u64n(max u64) !u64 {
|
||||
return default_rng.u64n(max)
|
||||
}
|
||||
|
||||
// u32_in_range returns a uniformly distributed pseudorandom 32-bit unsigned `u32` in range `[min, max)`.
|
||||
pub fn u32_in_range(min u32, max u32) !u32 {
|
||||
return default_rng.u32_in_range(min, max)
|
||||
}
|
||||
|
||||
// u64_in_range returns a uniformly distributed pseudorandom 64-bit unsigned `u64` in range `[min, max)`.
|
||||
pub fn u64_in_range(min u64, max u64) !u64 {
|
||||
return default_rng.u64_in_range(min, max)
|
||||
}
|
||||
|
||||
// i8 returns a uniformly distributed pseudorandom 8-bit signed (possibly negative) `i8`.
|
||||
pub fn i8() i8 {
|
||||
return default_rng.i8()
|
||||
}
|
||||
|
||||
// i16 returns a uniformly distributed pseudorandom 16-bit signed (possibly negative) `i16`.
|
||||
pub fn i16() i16 {
|
||||
return default_rng.i16()
|
||||
}
|
||||
|
||||
// i32 returns a uniformly distributed pseudorandom 32-bit signed (possibly negative) `i32`.
|
||||
pub fn i32() i32 {
|
||||
return default_rng.i32()
|
||||
}
|
||||
|
||||
// int returns a uniformly distributed pseudorandom 32-bit signed (possibly negative) `int`.
|
||||
pub fn int() int {
|
||||
return default_rng.int()
|
||||
}
|
||||
|
||||
// i32n returns a uniformly distributed pseudorandom 32-bit signed positive `i32` in range `[0, max)`.
|
||||
pub fn i32n(max i32) !i32 {
|
||||
return default_rng.i32n(max)
|
||||
}
|
||||
|
||||
// intn returns a uniformly distributed pseudorandom 32-bit signed positive `int` in range `[0, max)`.
|
||||
pub fn intn(max int) !int {
|
||||
return default_rng.intn(max)
|
||||
}
|
||||
|
||||
// int_in_range returns a uniformly distributed pseudorandom 32-bit signed int in range `[min, max)`.
|
||||
// Both `min` and `max` can be negative, but we must have `min < max`.
|
||||
pub fn int_in_range(min int, max int) !int {
|
||||
return default_rng.int_in_range(min, max)
|
||||
}
|
||||
|
||||
// int_in_range returns a uniformly distributed pseudorandom 32-bit signed int in range `[min, max)`.
|
||||
// Both `min` and `max` can be negative, but we must have `min < max`.
|
||||
pub fn i32_in_range(min i32, max i32) !i32 {
|
||||
return default_rng.i32_in_range(min, max)
|
||||
}
|
||||
|
||||
// int31 returns a uniformly distributed pseudorandom 31-bit signed positive `int`.
|
||||
pub fn int31() int {
|
||||
return default_rng.int31()
|
||||
}
|
||||
|
||||
// i64 returns a uniformly distributed pseudorandom 64-bit signed (possibly negative) `i64`.
|
||||
pub fn i64() i64 {
|
||||
return default_rng.i64()
|
||||
}
|
||||
|
||||
// i64n returns a uniformly distributed pseudorandom 64-bit signed positive `i64` in range `[0, max)`.
|
||||
pub fn i64n(max i64) !i64 {
|
||||
return default_rng.i64n(max)
|
||||
}
|
||||
|
||||
// i64_in_range returns a uniformly distributed pseudorandom 64-bit signed `i64` in range `[min, max)`.
|
||||
pub fn i64_in_range(min i64, max i64) !i64 {
|
||||
return default_rng.i64_in_range(min, max)
|
||||
}
|
||||
|
||||
// int63 returns a uniformly distributed pseudorandom 63-bit signed positive `i64`.
|
||||
pub fn int63() i64 {
|
||||
return default_rng.int63()
|
||||
}
|
||||
|
||||
// f32 returns a uniformly distributed 32-bit floating point in range `[0, 1)`.
|
||||
pub fn f32() f32 {
|
||||
return default_rng.f32()
|
||||
}
|
||||
|
||||
// f32cp returns a uniformly distributed 32-bit floating point in range `[0, 1)` with full precision mantissa.
|
||||
pub fn f32cp() f32 {
|
||||
return default_rng.f32cp()
|
||||
}
|
||||
|
||||
// f64 returns a uniformly distributed 64-bit floating point in range `[0, 1)`.
|
||||
pub fn f64() f64 {
|
||||
return default_rng.f64()
|
||||
}
|
||||
|
||||
// f64 returns a uniformly distributed 64-bit floating point in range `[0, 1)` with full precision mantissa.
|
||||
pub fn f64cp() f64 {
|
||||
return default_rng.f64cp()
|
||||
}
|
||||
|
||||
// f32n returns a uniformly distributed 32-bit floating point in range `[0, max)`.
|
||||
pub fn f32n(max f32) !f32 {
|
||||
return default_rng.f32n(max)
|
||||
}
|
||||
|
||||
// f64n returns a uniformly distributed 64-bit floating point in range `[0, max)`.
|
||||
pub fn f64n(max f64) !f64 {
|
||||
return default_rng.f64n(max)
|
||||
}
|
||||
|
||||
// f32_in_range returns a uniformly distributed 32-bit floating point in range `[min, max)`.
|
||||
pub fn f32_in_range(min f32, max f32) !f32 {
|
||||
return default_rng.f32_in_range(min, max)
|
||||
}
|
||||
|
||||
// f64_in_range returns a uniformly distributed 64-bit floating point in range `[min, max)`.
|
||||
pub fn f64_in_range(min f64, max f64) !f64 {
|
||||
return default_rng.f64_in_range(min, max)
|
||||
}
|
||||
|
||||
// bytes returns a buffer of `bytes_needed` random bytes.
|
||||
pub fn bytes(bytes_needed int) ![]u8 {
|
||||
return default_rng.bytes(bytes_needed)
|
||||
}
|
||||
|
||||
// read fills in `buf` a maximum of `buf.len` random bytes.
|
||||
pub fn read(mut buf []u8) {
|
||||
read_internal(mut default_rng, mut buf)
|
||||
}
|
||||
|
||||
const english_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
const hex_chars = '0123456789abcdef'
|
||||
const ascii_chars = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^_`abcdefghijklmnopqrstuvwxyz{|}~'
|
||||
|
||||
// ulid generates an unique lexicographically sortable identifier.
|
||||
// See https://github.com/ulid/spec .
|
||||
// Note: ULIDs can leak timing information, if you make them public, because
|
||||
// you can infer the rate at which some resource is being created, like
|
||||
// users or business transactions.
|
||||
// (https://news.ycombinator.com/item?id=14526173)
|
||||
pub fn ulid() string {
|
||||
return default_rng.ulid()
|
||||
}
|
||||
|
||||
// ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_milli`.
|
||||
pub fn ulid_at_millisecond(unix_time_milli u64) string {
|
||||
return default_rng.ulid_at_millisecond(unix_time_milli)
|
||||
}
|
||||
|
||||
// string_from_set returns a string of length `len` containing random characters sampled from the given `charset`.
|
||||
pub fn string_from_set(charset string, len int) string {
|
||||
return default_rng.string_from_set(charset, len)
|
||||
}
|
||||
|
||||
// fill_buffer_from_set fills the array `buf` with random characters sampled from the given `charset`
|
||||
@[inline]
|
||||
pub fn fill_buffer_from_set(charset string, mut buf []u8) {
|
||||
default_rng.fill_buffer_from_set(charset, mut buf)
|
||||
}
|
||||
|
||||
// string returns a string of length `len` containing random characters in range `[a-zA-Z]`.
|
||||
pub fn string(len int) string {
|
||||
return string_from_set(english_letters, len)
|
||||
}
|
||||
|
||||
// hex returns a hexadecimal number of length `len` containing random characters in range `[a-f0-9]`.
|
||||
pub fn hex(len int) string {
|
||||
return string_from_set(hex_chars, len)
|
||||
}
|
||||
|
||||
// ascii returns a random string of the printable ASCII characters with length `len`.
|
||||
pub fn ascii(len int) string {
|
||||
return string_from_set(ascii_chars, len)
|
||||
}
|
||||
|
||||
// shuffle randomly permutates the elements in `a`.
|
||||
// The range for shuffling is optional and the entire array is shuffled by default.
|
||||
// Leave the end as 0 to shuffle all elements until the end.
|
||||
pub fn shuffle[T](mut a []T, config_ config.ShuffleConfigStruct) ! {
|
||||
default_rng.shuffle[T](mut a, config_)!
|
||||
}
|
||||
|
||||
// shuffle_clone returns a random permutation of the elements in `a`.
|
||||
// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
|
||||
pub fn shuffle_clone[T](a []T, config_ config.ShuffleConfigStruct) ![]T {
|
||||
return default_rng.shuffle_clone[T](a, config_)
|
||||
}
|
||||
|
||||
// choose samples k elements from the array without replacement.
|
||||
// This means the indices cannot repeat and it restricts the sample size to be less than or equal to the size of the given array.
|
||||
// Note that if the array has repeating elements, then the sample may have repeats as well.
|
||||
pub fn choose[T](array []T, k int) ![]T {
|
||||
return default_rng.choose[T](array, k)
|
||||
}
|
||||
|
||||
// element returns a random element from the given array.
|
||||
// Note that all the positions in the array have an equal chance of being selected. This means that if the array has repeating elements, then the probability of selecting a particular element is not uniform.
|
||||
pub fn element[T](array []T) !T {
|
||||
return default_rng.element[T](array)
|
||||
}
|
||||
|
||||
// sample samples k elements from the array with replacement.
|
||||
// This means the elements can repeat and the size of the sample may exceed the size of the array.
|
||||
pub fn sample[T](array []T, k int) []T {
|
||||
return default_rng.sample[T](array, k)
|
||||
}
|
||||
|
||||
// bernoulli returns true with a probability p. Note that 0 <= p <= 1.
|
||||
pub fn bernoulli(p f64) !bool {
|
||||
return default_rng.bernoulli(p)
|
||||
}
|
||||
|
||||
// normal returns a normally distributed pseudorandom f64 with mean `mu` and standard deviation `sigma`.
|
||||
// By default, `mu` is 0.0 and `sigma` is 1.0.
|
||||
// NOTE: Use normal_pair() instead if you're generating a lot of normal variates.
|
||||
pub fn normal(config_ config.NormalConfigStruct) !f64 {
|
||||
return default_rng.normal(config_)
|
||||
}
|
||||
|
||||
// normal_pair returns a pair of normally distributed pseudorandom f64 with mean `mu` and standard deviation `sigma`.
|
||||
// By default, `mu` is 0.0 and `sigma` is 1.0.
|
||||
pub fn normal_pair(config_ config.NormalConfigStruct) !(f64, f64) {
|
||||
return default_rng.normal_pair(config_)
|
||||
}
|
||||
|
||||
// binomial returns the number of successful trials out of n when the probability of success for each trial is p.
|
||||
pub fn binomial(n int, p f64) !int {
|
||||
return default_rng.binomial(n, p)
|
||||
}
|
||||
|
||||
// exponential returns an exponentially distributed random number with the rate parameter `lambda`.
|
||||
// It is expected that lambda is positive.
|
||||
pub fn exponential(lambda f64) f64 {
|
||||
return default_rng.exponential(lambda)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// vtest build: present_node?
|
||||
import rand
|
||||
|
||||
fn test_string() {
|
||||
res := rand.string(4)
|
||||
assert res.len == 4
|
||||
println(res)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import rand
|
||||
|
||||
fn test_rand_bytes() {
|
||||
mut randoms := []string{}
|
||||
for i in 0 .. 100 {
|
||||
x := rand.bytes(i)!.hex()
|
||||
if x.len > 0 {
|
||||
randoms << x
|
||||
}
|
||||
assert x.len == i * 2
|
||||
}
|
||||
mut differences := 0
|
||||
for idx in 1 .. randoms.len {
|
||||
start := randoms[idx]#[0..8]
|
||||
prev_start := randoms[idx - 1]#[0..8]
|
||||
if start != prev_start {
|
||||
differences++
|
||||
}
|
||||
}
|
||||
assert differences > 95 // normally around 98
|
||||
dump(differences)
|
||||
}
|
||||
|
||||
fn test_prng_rand_bytes() {
|
||||
mut randoms := []string{}
|
||||
mut rng := rand.get_current_rng()
|
||||
for i in 0 .. 100 {
|
||||
x := rng.bytes(i)!.hex()
|
||||
if x.len > 0 {
|
||||
randoms << x
|
||||
}
|
||||
assert x.len == i * 2
|
||||
}
|
||||
mut differences := 0
|
||||
for idx in 1 .. randoms.len {
|
||||
start := randoms[idx]#[0..8]
|
||||
prev_start := randoms[idx - 1]#[0..8]
|
||||
if start != prev_start {
|
||||
differences++
|
||||
}
|
||||
}
|
||||
assert differences > 95 // normally around 98
|
||||
dump(differences)
|
||||
}
|
||||
|
||||
fn test_rand_read() {
|
||||
max := 50
|
||||
mut a := []u8{len: max}
|
||||
mut differences := 0
|
||||
for j in 1 .. max {
|
||||
start := '00'.repeat(j)
|
||||
for k in j + 1 .. max {
|
||||
end := '00'.repeat(max - k)
|
||||
middle := '00'.repeat(k - j)
|
||||
// eprintln('> j: ${j} | k: ${k} | start: ${start} | middle: ${middle} | end: ${end}')
|
||||
for i in 0 .. max {
|
||||
a[i] = 0
|
||||
}
|
||||
assert a[j..k].hex() == middle
|
||||
for i in 0 .. 10 {
|
||||
rand.read(mut a[j..k])
|
||||
// dump(a.hex())
|
||||
assert a[0..j].hex() == start
|
||||
assert a[k..].hex() == end
|
||||
if a[j..k].hex() != middle {
|
||||
differences++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dump(differences)
|
||||
assert differences > 11700 // normally around 11758
|
||||
}
|
||||
|
||||
fn test_prng_rand_read() {
|
||||
max := 50
|
||||
mut a := []u8{len: max}
|
||||
mut differences := 0
|
||||
mut rng := rand.get_current_rng()
|
||||
for j in 1 .. max {
|
||||
start := '00'.repeat(j)
|
||||
for k in j + 1 .. max {
|
||||
end := '00'.repeat(max - k)
|
||||
middle := '00'.repeat(k - j)
|
||||
// eprintln('> j: ${j} | k: ${k} | start: ${start} | middle: ${middle} | end: ${end}')
|
||||
for i in 0 .. max {
|
||||
a[i] = 0
|
||||
}
|
||||
assert a[j..k].hex() == middle
|
||||
for i in 0 .. 10 {
|
||||
rng.read(mut a[j..k])
|
||||
// dump(a.hex())
|
||||
assert a[0..j].hex() == start
|
||||
assert a[k..].hex() == end
|
||||
if a[j..k].hex() != middle {
|
||||
differences++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dump(differences)
|
||||
assert differences > 11700 // normally around 11758
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import time
|
||||
import rand
|
||||
|
||||
fn validate_separators(uuid string) {
|
||||
assert uuid[8] == `-`
|
||||
assert uuid[13] == `-`
|
||||
assert uuid[18] == `-`
|
||||
assert uuid[23] == `-`
|
||||
assert uuid.len == 36
|
||||
}
|
||||
|
||||
// uuid_v4:
|
||||
fn test_rand_uuid_v4() {
|
||||
uuid1 := rand.uuid_v4()
|
||||
uuid2 := rand.uuid_v4()
|
||||
uuid3 := rand.uuid_v4()
|
||||
validate_separators(uuid1)
|
||||
validate_separators(uuid2)
|
||||
validate_separators(uuid3)
|
||||
assert uuid1 != uuid2
|
||||
assert uuid1 != uuid3
|
||||
assert uuid2 != uuid3
|
||||
for i in 0 .. 1000 {
|
||||
x := rand.uuid_v4()
|
||||
// check the version field is always 4:
|
||||
assert x[14] == `4`
|
||||
// and the clock_seq_hi_and_reserved field is valid too:
|
||||
assert x[19] in [`8`, `9`, `a`, `b`]
|
||||
validate_separators(x)
|
||||
}
|
||||
}
|
||||
|
||||
// uuid_v7:
|
||||
fn test_rand_uuid_v7() {
|
||||
uuid1 := rand.uuid_v7()
|
||||
uuid2 := rand.uuid_v7()
|
||||
uuid3 := rand.uuid_v7()
|
||||
validate_separators(uuid1)
|
||||
validate_separators(uuid2)
|
||||
validate_separators(uuid3)
|
||||
assert uuid1 != uuid2
|
||||
assert uuid1 != uuid3
|
||||
assert uuid2 != uuid3
|
||||
for i in 0 .. 1000 {
|
||||
x := rand.uuid_v7()
|
||||
// check the version field is always 7:
|
||||
assert x[14] == `7`
|
||||
// and variant field is always 0b10:
|
||||
assert x[19] in [`8`, `9`, `a`, `b`]
|
||||
validate_separators(x)
|
||||
}
|
||||
}
|
||||
|
||||
// uuid_v7_session:
|
||||
fn test_rand_uuid_v7_session() {
|
||||
mut u := rand.new_uuid_v7_session()
|
||||
uuid1 := u.next()
|
||||
uuid2 := u.next()
|
||||
uuid3 := u.next()
|
||||
assert uuid1 != uuid2
|
||||
assert uuid1 != uuid3
|
||||
assert uuid2 != uuid3
|
||||
assert uuid1.len == 36
|
||||
assert uuid2.len == 36
|
||||
assert uuid3.len == 36
|
||||
mut prev_counter := `3`
|
||||
for i in 0 .. 1000 {
|
||||
x := u.next()
|
||||
// check the version field is always 7:
|
||||
assert x[14] == `7`
|
||||
// and variant field is always 0b10:
|
||||
assert x[19] in [`8`, `9`, `a`, `b`]
|
||||
|
||||
// verify counter increase
|
||||
assert x[17] == prev_counter
|
||||
if prev_counter == `9` {
|
||||
prev_counter = `a`
|
||||
} else if prev_counter == `f` {
|
||||
prev_counter = `0`
|
||||
} else {
|
||||
prev_counter++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ulids:
|
||||
fn test_ulids_are_unique() {
|
||||
ulid1 := rand.ulid()
|
||||
ulid2 := rand.ulid()
|
||||
ulid3 := rand.ulid()
|
||||
assert ulid1.len == 26
|
||||
assert ulid2.len == 26
|
||||
assert ulid3.len == 26
|
||||
assert ulid1 != ulid2
|
||||
assert ulid1 != ulid3
|
||||
assert ulid2 != ulid3
|
||||
}
|
||||
|
||||
fn test_ulids_max_start_character_is_ok() {
|
||||
ulid1 := rand.ulid()
|
||||
// the largest valid ULID encoded in Base32 is 7ZZZZZZZZZZZZZZZZZZZZZZZZZ
|
||||
assert (int(ulid1[0]) - 48) <= 7
|
||||
}
|
||||
|
||||
fn test_ulids_generated_in_the_same_millisecond_have_the_same_prefix() {
|
||||
t := u64(time.utc().unix_milli())
|
||||
mut ulid1 := ''
|
||||
mut ulid2 := ''
|
||||
mut ulid3 := ''
|
||||
ulid1 = rand.ulid_at_millisecond(t)
|
||||
ulid2 = rand.ulid_at_millisecond(t)
|
||||
ulid3 = rand.ulid_at_millisecond(t)
|
||||
ulid1_prefix := ulid1[0..10]
|
||||
ulid2_prefix := ulid2[0..10]
|
||||
ulid3_prefix := ulid3[0..10]
|
||||
assert ulid1_prefix == ulid2_prefix
|
||||
assert ulid1_prefix == ulid3_prefix
|
||||
}
|
||||
|
||||
fn test_ulids_should_be_lexicographically_ordered_when_not_in_same_millisecond() {
|
||||
ulid1 := rand.ulid()
|
||||
time.sleep(1 * time.millisecond)
|
||||
ulid2 := rand.ulid()
|
||||
time.sleep(1 * time.millisecond)
|
||||
ulid3 := rand.ulid()
|
||||
mut all := [ulid3, ulid2, ulid1]
|
||||
// eprintln('all before: ${all}')
|
||||
all.sort()
|
||||
// eprintln('all after: ${all}')
|
||||
s1 := all[0]
|
||||
s2 := all[1]
|
||||
s3 := all[2]
|
||||
assert s1 == ulid1
|
||||
assert s2 == ulid2
|
||||
assert s3 == ulid3
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
import rand
|
||||
import rand.splitmix64
|
||||
import rand.musl
|
||||
import rand.mt19937
|
||||
|
||||
const rnd_count = 40
|
||||
const seeds = [[u32(42), 0], [u32(256), 0]]
|
||||
|
||||
fn get_n_random_ints(seed_data []u32, n int) []int {
|
||||
mut values := []int{cap: n}
|
||||
rand.seed(seed_data)
|
||||
for _ in 0 .. n {
|
||||
values << rand.intn(n) or { panic("Couldn't obtain int") }
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
fn test_rand_reproducibility() {
|
||||
for seed in seeds {
|
||||
array1 := get_n_random_ints(seed, 1000)
|
||||
array2 := get_n_random_ints(seed, 1000)
|
||||
assert array1.len == array2.len
|
||||
assert array1 == array2
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_u32n() {
|
||||
max := u32(16384)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_u64n() {
|
||||
max := u64(379091181005)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.u32_in_range(min, max) or { panic("Couldn't obtain u32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_intn() {
|
||||
max := 2525642
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_i32n() {
|
||||
max := i32(2525642)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.i32n(max) or { panic("Couldn't obtain i32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_int_in_range() {
|
||||
min := -4252
|
||||
max := 23054962
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_i32_in_range() {
|
||||
min := i32(-4252)
|
||||
max := i32(23054962)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.i32_in_range(min, max) or { panic("Couldn't obtain i32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f32() {
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f64() {
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f32n() {
|
||||
max := f32(357.0)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f64n() {
|
||||
max := f64(1.52e6)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_f64_in_range() {
|
||||
min := f64(-548.7)
|
||||
max := f64(5015.2)
|
||||
for _ in 0 .. rnd_count {
|
||||
value := rand.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_u8() {
|
||||
mut all := []u8{}
|
||||
for _ in 0 .. 256 {
|
||||
x := rand.u8()
|
||||
assert x >= 0
|
||||
assert x <= 255
|
||||
all << x
|
||||
}
|
||||
all.sort(a < b)
|
||||
assert all[0] != all[255]
|
||||
assert all[0] != all[128]
|
||||
}
|
||||
|
||||
fn test_rand_u16() {
|
||||
mut all := []u16{}
|
||||
mut same_as_previous := 0
|
||||
mut previous := u16(0)
|
||||
for _ in 0 .. 65536 {
|
||||
x := rand.u16()
|
||||
assert x >= 0
|
||||
assert x <= 65535
|
||||
all << x
|
||||
if previous == x {
|
||||
same_as_previous++
|
||||
// dump(previous)
|
||||
// dump(x)
|
||||
}
|
||||
previous = x
|
||||
}
|
||||
assert same_as_previous < 1000
|
||||
all.sort(a < b)
|
||||
assert all[0] != all[65535]
|
||||
assert all[0] != all[32768]
|
||||
// dump( all[0] )
|
||||
// dump( all[65535] )
|
||||
// dump( all[32768] )
|
||||
}
|
||||
|
||||
const string_count = 25
|
||||
|
||||
fn test_rand_string_from_set() {
|
||||
sets := [
|
||||
'0123456789',
|
||||
'qwertyuiop',
|
||||
'abcdefghijklmnopqrstuvwxyz',
|
||||
]
|
||||
for charset in sets {
|
||||
for _ in 0 .. string_count {
|
||||
len := rand.intn(rnd_count) or { panic("Couldn't obtain int") }
|
||||
str := rand.string_from_set(charset, len)
|
||||
assert str.len == len
|
||||
for character in str {
|
||||
position := charset.index(character.ascii_str()) or { -1 }
|
||||
assert position > -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_fill_buffer_from_set() {
|
||||
rand.seed([u32(0), 1])
|
||||
outputs := [
|
||||
[u8(50), 53, 57, 49, 53, 49, 56, 52, 57, 57],
|
||||
[u8(55), 51, 52, 52, 50, 49, 53, 56, 52, 57],
|
||||
[u8(56), 53, 52, 48, 52, 48, 57, 49, 50, 53],
|
||||
[u8(52), 55, 53, 57, 54, 56, 49, 49, 49, 54],
|
||||
[u8(50), 55, 50, 57, 50, 56, 57, 52, 52, 48],
|
||||
[u8(49), 48, 48, 57, 57, 53, 49, 54, 50, 50],
|
||||
[u8(49), 57, 50, 57, 51, 49, 49, 53, 54, 53],
|
||||
[u8(56), 49, 52, 50, 56, 51, 48, 55, 54, 50],
|
||||
[u8(50), 55, 54, 55, 48, 54, 50, 56, 51, 52],
|
||||
[u8(51), 48, 50, 55, 54, 54, 49, 54, 54, 50],
|
||||
[u8(50), 51, 51, 57, 50, 48, 56, 49, 54, 55],
|
||||
[u8(51), 54, 51, 55, 56, 50, 54, 48, 51, 51],
|
||||
]
|
||||
for output in outputs {
|
||||
mut buf := []u8{len: 10}
|
||||
rand.fill_buffer_from_set('0123456789', mut buf)
|
||||
assert buf == output
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_string() {
|
||||
rand.seed([u32(0), 1])
|
||||
outputs := [
|
||||
'KNffDjUSbjLVyqkNLuklqtsEq',
|
||||
'yHxahszRjWILjfqmLoTsIPCaS',
|
||||
'JuuHjDLeuEjPMxhRzRUdOnegw',
|
||||
'NOJeMKbelMQAgBijGqLgGXgcy',
|
||||
'IRnHmuMXuddWLBsyeejpORynj',
|
||||
'SNyWxnrFYoWyOSLnIxTzkxdlq',
|
||||
'WzzZoOlnqzGKnUASSnlVqMtEg',
|
||||
'CveNYBaLaEwguzgwLxilypSDD',
|
||||
'XTBFsDOTHmTXcXjdmOqSAuAXz',
|
||||
'MFoKXRXLQSeebMegDyUJyaHfu',
|
||||
'EzaZjBJJWdGrASWqEPRRNQmgy',
|
||||
'gZvLtGiyCYQSKxWBMEqVvTytn',
|
||||
]
|
||||
for output in outputs {
|
||||
assert rand.string(25) == output
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_hex() {
|
||||
rand.seed([u32(0), 1])
|
||||
outputs := [
|
||||
'035d15ec991bc42f502bcba28',
|
||||
'49383ad7d8a51d44929ae9c04',
|
||||
'30cdddd88e1963fb9367858e6',
|
||||
'34f86c983ae6a38904dac56e8',
|
||||
'6b1d08e94fb053688c1f47491',
|
||||
'0700b91fea4808116b5deb7bc',
|
||||
'c1dbac7941cc16ec81b70ef2a',
|
||||
'c1cba301066e81a2df43cf051',
|
||||
'51b7adc9dcd9695f004c686d1',
|
||||
'a764df15e0009e02d02f88598',
|
||||
'2d8393b743092c806537724a0',
|
||||
'6b59704086e84f4b62cb11cb1',
|
||||
'aebc14a5d7c2d447e6282f7ff',
|
||||
'9ca8885813e42cb4380efeb84',
|
||||
]
|
||||
for output in outputs {
|
||||
assert rand.hex(25) == output
|
||||
}
|
||||
}
|
||||
|
||||
fn test_rand_ascii() {
|
||||
rand.seed([u32(0), 1])
|
||||
outputs := [
|
||||
r'Yj6x`haolJh8UwGP.gq,Uj+\I',
|
||||
r'5F@!@WF@tAulVN5-FqF;u"Y-9',
|
||||
r'vo1xB>.MIu2lGj~f&$a4wNYeC',
|
||||
r',M0o#M*QHa\myH{Bkkp#s&7/I',
|
||||
r'abRN)Iq`)@b_*jKU_1x$Iv-ZF',
|
||||
r'-@wQzJR0y+%{As"pqz.:Sz,L%',
|
||||
r'1px~?MLh16UAVWO51>X~%3n7S',
|
||||
r'Ed!vev=B-?OS)"W}N8gH5zU.R',
|
||||
r'FR~8;&wB`!NFCRR,_IsIG{y|l',
|
||||
]
|
||||
for output in outputs {
|
||||
assert rand.ascii(25) == output
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_same_output(mut rng rand.PRNG) {
|
||||
trng := rng.type_name()
|
||||
println('> ensure a ${trng} instance works as a global generator, and produces the same output as a local one...')
|
||||
for _ in 0 .. 100 {
|
||||
assert rand.i8() == rng.i8()
|
||||
assert rand.i16() == rng.i16()
|
||||
assert rand.int() == rng.int()
|
||||
assert rand.intn(45) or { 0 } == rng.intn(45) or { 0 }
|
||||
assert rand.i32() == rng.i32()
|
||||
assert rand.i32n(45) or { 0 } == rng.i32n(45) or { 0 }
|
||||
assert rand.i64() == rng.i64()
|
||||
assert rand.i64n(45) or { 0 } == rng.i64n(45) or { 0 }
|
||||
//
|
||||
assert rand.u8() == rng.u8()
|
||||
assert rand.u16() == rng.u16()
|
||||
assert rand.u32() == rng.u32()
|
||||
assert rand.u32n(45) or { 0 } == rng.u32n(45) or { 0 }
|
||||
assert rand.u64() == rng.u64()
|
||||
assert rand.u64n(45) or { 0 } == rng.u64n(45) or { 0 }
|
||||
//
|
||||
assert rand.f32() == rng.f32()
|
||||
assert rand.f32n(45) or { 0 } == rng.f32n(45) or { 0 }
|
||||
assert rand.f64() == rng.f64()
|
||||
assert rand.f64n(45) or { 0 } == rng.f64n(45) or { 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn test_new_global_rng() {
|
||||
old := rand.get_current_rng()
|
||||
|
||||
// MuslRNG
|
||||
mut rng1a := musl.MuslRNG{}
|
||||
mut rng1b := musl.MuslRNG{}
|
||||
seed1 := [u32(1234)]
|
||||
|
||||
rand.set_rng(rng1a)
|
||||
rand.seed(seed1)
|
||||
rng1b.seed(seed1)
|
||||
ensure_same_output(mut rng1b)
|
||||
|
||||
// SplitMix64RNG
|
||||
mut rng2a := splitmix64.SplitMix64RNG{}
|
||||
mut rng2b := splitmix64.SplitMix64RNG{}
|
||||
seed2 := [u32(2325), 14]
|
||||
|
||||
rand.set_rng(rng2a)
|
||||
rand.seed(seed2)
|
||||
rng2b.seed(seed2)
|
||||
ensure_same_output(mut rng2b)
|
||||
|
||||
// MT19937RNG
|
||||
mut rng3a := mt19937.MT19937RNG{}
|
||||
mut rng3b := mt19937.MT19937RNG{}
|
||||
seed3 := [u32(0xcafe), 234]
|
||||
|
||||
rand.set_rng(rng3a)
|
||||
rand.seed(seed3)
|
||||
rng3b.seed(seed3)
|
||||
ensure_same_output(mut rng3b)
|
||||
|
||||
rand.set_rng(old)
|
||||
}
|
||||
|
||||
fn test_shuffle() {
|
||||
mut arrays := [][]int{}
|
||||
arrays << [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
arrays << [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
for seed in seeds {
|
||||
a := get_n_random_ints(seed, 10)
|
||||
arrays << a
|
||||
}
|
||||
mut digits := []map[int]int{len: 10}
|
||||
for digit in 0 .. 10 {
|
||||
digits[digit] = {}
|
||||
for idx in 0 .. 10 {
|
||||
digits[digit][idx] = 0
|
||||
}
|
||||
}
|
||||
for mut a in arrays {
|
||||
o := a.clone()
|
||||
for _ in 0 .. 100 {
|
||||
rand.shuffle(mut a) or { panic('shuffle failed') }
|
||||
assert *a != o
|
||||
for idx in 0 .. 10 {
|
||||
digits[idx][a[idx]]++
|
||||
}
|
||||
}
|
||||
}
|
||||
for digit in 1 .. 10 {
|
||||
assert digits[0] != digits[digit]
|
||||
}
|
||||
for digit in 0 .. 10 {
|
||||
for idx in 0 .. 10 {
|
||||
assert digits[digit][idx] >= 10
|
||||
}
|
||||
// eprintln('digits[${digit}]: ${digits[digit]}')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_shuffle_partial() {
|
||||
mut a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
mut b := a.clone()
|
||||
|
||||
rand.shuffle(mut a, start: 4)!
|
||||
assert a[..4] == b[..4]
|
||||
|
||||
a = b.clone()
|
||||
rand.shuffle(mut a, start: 3, end: 7)!
|
||||
assert a[..3] == b[..3]
|
||||
assert a[7..] == b[7..]
|
||||
}
|
||||
|
||||
fn test_shuffle_clone() {
|
||||
original := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
mut a := original.clone()
|
||||
mut results := [][]int{}
|
||||
for _ in 0 .. 10 {
|
||||
results << rand.shuffle_clone(a) or { panic('shuffle failed') }
|
||||
}
|
||||
assert original == a
|
||||
for idx in 1 .. 10 {
|
||||
assert results[idx].len == 10
|
||||
assert results[idx] != results[0]
|
||||
assert results[idx] != original
|
||||
}
|
||||
}
|
||||
|
||||
fn test_choose() {
|
||||
lengths := [1, 3, 4, 5, 6, 7]
|
||||
a := ['one', 'two', 'three', 'four', 'five', 'six', 'seven']
|
||||
for length in lengths {
|
||||
b := rand.choose(a, length)!
|
||||
assert b.len == length
|
||||
for element in b {
|
||||
assert element in a
|
||||
// make sure every element occurs once
|
||||
mut count := 0
|
||||
for e in b {
|
||||
if e == element {
|
||||
count++
|
||||
}
|
||||
}
|
||||
assert count == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sample() {
|
||||
k := 20
|
||||
a := ['heads', 'tails']
|
||||
b := rand.sample(a, k)
|
||||
assert b.len == k
|
||||
for element in b {
|
||||
assert element in a
|
||||
}
|
||||
}
|
||||
|
||||
fn test_element1() {
|
||||
a := ['one', 'two', 'four', 'five', 'six', 'seven']
|
||||
for _ in 0 .. 30 {
|
||||
e := rand.element(a)!
|
||||
assert e in a
|
||||
assert 'three' != e
|
||||
}
|
||||
}
|
||||
|
||||
fn test_element2() {
|
||||
for _ in 0 .. 30 {
|
||||
e := rand.element([1, 2, 5, 6, 7, 8])!
|
||||
assert e in [1, 2, 5, 6, 7, 8]
|
||||
assert 3 != e
|
||||
assert 4 != e
|
||||
}
|
||||
}
|
||||
|
||||
fn test_proper_masking() {
|
||||
under32 := []int{len: 10, init: index * 0 + rand.intn(1073741823)!}
|
||||
assert under32 != [0].repeat(10)
|
||||
|
||||
over32 := []int{len: 10, init: index * 0 + rand.intn(1073741824)!}
|
||||
assert over32 != [0].repeat(10)
|
||||
|
||||
under64 := []i64{len: 10, init: index * 0 + rand.i64n(i64(4611686018427387903))!}
|
||||
assert under64 != [i64(0)].repeat(10)
|
||||
|
||||
over64 := []i64{len: 10, init: index * 0 + rand.i64n(i64(4611686018427387904))!}
|
||||
assert over64 != [i64(0)].repeat(10)
|
||||
|
||||
almost_full32 := []int{len: 10, init: index * 0 + rand.intn(2147483647)!}
|
||||
assert almost_full32 != [0].repeat(10)
|
||||
|
||||
almost_full64 := []i64{len: 10, init: index * 0 + rand.i64n(i64(9223372036854775807))!}
|
||||
assert almost_full64 != [i64(0)].repeat(10)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module seed
|
||||
|
||||
import time
|
||||
|
||||
// nr_next returns a next value based on the previous value `prev`.
|
||||
@[ignore_overflow; inline]
|
||||
fn nr_next(prev u32) u32 {
|
||||
return prev * 1664525 + 1013904223
|
||||
}
|
||||
|
||||
// time_seed_array returns the required number of u32s generated from system time.
|
||||
pub fn time_seed_array(count int) []u32 {
|
||||
ctime := time.sys_mono_now()
|
||||
mut seed := u32(ctime >> 32 ^ (ctime & 0x0000_0000_FFFF_FFFF))
|
||||
mut seed_data := []u32{cap: count}
|
||||
for _ in 0 .. count {
|
||||
seed = nr_next(seed)
|
||||
seed_data << nr_next(seed)
|
||||
}
|
||||
return seed_data
|
||||
}
|
||||
|
||||
// time_seed_32 returns a 32-bit seed generated from system time.
|
||||
@[manualfree]
|
||||
pub fn time_seed_32() u32 {
|
||||
sa := time_seed_array(1)
|
||||
res := sa[0]
|
||||
unsafe { sa.free() }
|
||||
return res
|
||||
}
|
||||
|
||||
// time_seed_64 returns a 64-bit seed generated from system time.
|
||||
@[manualfree]
|
||||
pub fn time_seed_64() u64 {
|
||||
seed_data := time_seed_array(2)
|
||||
lower := u64(seed_data[0])
|
||||
upper := u64(seed_data[1])
|
||||
unsafe { seed_data.free() }
|
||||
res := lower | (upper << 32)
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module splitmix64
|
||||
|
||||
import rand.seed
|
||||
import rand.buffer
|
||||
|
||||
pub const seed_len = 2
|
||||
|
||||
// SplitMix64RNG is ported from http://xoshiro.di.unimi.it/splitmix64.c .
|
||||
pub struct SplitMix64RNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state u64 = seed.time_seed_64()
|
||||
bytes_left int
|
||||
buffer u64
|
||||
}
|
||||
|
||||
// seed sets the seed of the accepting SplitMix64RNG to the given data in little-endian format (i.e. lower 32 bits are in [0] and higher 32 bits in [1]).
|
||||
pub fn (mut rng SplitMix64RNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 2 {
|
||||
eprintln('SplitMix64RNG needs 2 32-bit unsigned integers as the seed.')
|
||||
exit(1)
|
||||
}
|
||||
rng.state = seed_data[0] | (u64(seed_data[1]) << 32)
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng SplitMix64RNG) u8() u8 {
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
rng.buffer = rng.u64()
|
||||
rng.bytes_left = 7
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16bit int in range `[0, 2¹⁶)`.
|
||||
@[inline]
|
||||
pub fn (mut rng SplitMix64RNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 6
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom 32bit int in range `[0, 2³²)`.
|
||||
@[inline]
|
||||
pub fn (mut rng SplitMix64RNG) u32() u32 {
|
||||
if rng.bytes_left >= 4 {
|
||||
rng.bytes_left -= 4
|
||||
value := u32(rng.buffer)
|
||||
rng.buffer >>= 32
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 32
|
||||
rng.bytes_left = 4
|
||||
return u32(ans)
|
||||
}
|
||||
|
||||
// u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`.
|
||||
@[ignore_overflow; inline]
|
||||
pub fn (mut rng SplitMix64RNG) u64() u64 {
|
||||
rng.state += (0x9e3779b97f4a7c15)
|
||||
mut z := rng.state
|
||||
z = (z ^ (z >> u64(30))) * 0xbf58476d1ce4e5b9
|
||||
z = (z ^ (z >> u64(27))) * 0x94d049bb133111eb
|
||||
return z ^ (z >> (31))
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng SplitMix64RNG) block_size() int {
|
||||
return 64
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng SplitMix64RNG) free() {
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.splitmix64
|
||||
import rand.seed
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(42), 0], [u32(256), 0]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u64 {
|
||||
bound_u64 := u64(bound)
|
||||
mut randoms := []u64{len: (20)}
|
||||
mut rnd := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rnd.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rnd.u64n(bound_u64) or { panic("Couldn't obtain u64") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_splitmix64_reproducibility() {
|
||||
seed_data := seed.time_seed_array(2)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
assert randoms1[i] == randoms2[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_splitmix64_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_splitmix64_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(min, max) or { panic("Couldn't obtain u32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_int_in_range() {
|
||||
min := -4252
|
||||
max := 230549862
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_splitmix64_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module sys
|
||||
|
||||
import math.bits
|
||||
import rand.buffer
|
||||
import rand.seed
|
||||
|
||||
// Implementation note:
|
||||
// ====================
|
||||
// C.rand returns a pseudorandom integer from 0 (inclusive) to C.RAND_MAX (exclusive)
|
||||
// C.rand() is okay to use within its defined range.
|
||||
// (See: https://web.archive.org/web/20180801210127/http://eternallyconfuzzled.com/arts/jsw_art_rand.aspx)
|
||||
// The problem is, this value varies with the libc implementation. On windows,
|
||||
// for example, RAND_MAX is usually a measly 32767, whereas on (newer) linux it's generally
|
||||
// 2147483647. The repetition period also varies wildly. In order to provide more entropy
|
||||
// without altering the underlying algorithm too much, this implementation simply
|
||||
// requests for more random bits until the necessary width for the integers is achieved.
|
||||
|
||||
pub const seed_len = 1
|
||||
|
||||
const rand_limit = u64(C.RAND_MAX)
|
||||
const rand_bitsize = bits.len_64(rand_limit)
|
||||
const rand_bytesize = rand_bitsize / 8
|
||||
const u16_iter_count = calculate_iterations_for(16)
|
||||
const u32_iter_count = calculate_iterations_for(32)
|
||||
const u64_iter_count = calculate_iterations_for(64)
|
||||
|
||||
fn calculate_iterations_for(bits_ int) int {
|
||||
base := bits_ / rand_bitsize
|
||||
extra := if bits_ % rand_bitsize == 0 { 0 } else { 1 }
|
||||
return base + extra
|
||||
}
|
||||
|
||||
// SysRNG is the PRNG provided by default in the libc implementiation that V uses.
|
||||
pub struct SysRNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
seed u32 = seed.time_seed_32()
|
||||
}
|
||||
|
||||
// r.seed() sets the seed of the accepting SysRNG to the given data.
|
||||
pub fn (mut r SysRNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 1 {
|
||||
eprintln('SysRNG needs one 32-bit unsigned integer as the seed.')
|
||||
exit(1)
|
||||
}
|
||||
r.seed = seed_data[0]
|
||||
C.srand(r.seed)
|
||||
}
|
||||
|
||||
// r.default_rand() exposes the default behavior of the system's RNG
|
||||
// (equivalent to calling C.rand()). Recommended for testing/comparison
|
||||
// b/w V and other languages using libc and not for regular use.
|
||||
// This is also a one-off feature of SysRNG, similar to the global seed
|
||||
// situation. Other generators will not have this.
|
||||
@[inline]
|
||||
pub fn (r SysRNG) default_rand() int {
|
||||
return C.rand()
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut r SysRNG) u8() u8 {
|
||||
if r.bytes_left >= 1 {
|
||||
r.bytes_left -= 1
|
||||
value := u8(r.buffer)
|
||||
r.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
r.buffer = u64(r.default_rand())
|
||||
r.bytes_left = rand_bytesize - 1
|
||||
value := u8(r.buffer)
|
||||
r.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a uniformly distributed pseudorandom 16-bit unsigned positive `u16`.
|
||||
@[inline]
|
||||
pub fn (mut r SysRNG) u16() u16 {
|
||||
if r.bytes_left >= 2 {
|
||||
r.bytes_left -= 2
|
||||
value := u16(r.buffer)
|
||||
r.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
mut result := u16(C.rand())
|
||||
for i in 1 .. u16_iter_count {
|
||||
result = result ^ (u16(C.rand()) << (rand_bitsize * i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// u32 returns a uniformly distributed pseudorandom 32-bit unsigned positive `u32`.
|
||||
@[inline]
|
||||
pub fn (r SysRNG) u32() u32 {
|
||||
mut result := u32(C.rand())
|
||||
for i in 1 .. u32_iter_count {
|
||||
result = result ^ (u32(C.rand()) << (rand_bitsize * i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// u64 returns a uniformly distributed pseudorandom 64-bit unsigned positive `u64`.
|
||||
@[inline]
|
||||
pub fn (r SysRNG) u64() u64 {
|
||||
mut result := u64(C.rand())
|
||||
for i in 1 .. u64_iter_count {
|
||||
result = result ^ (u64(C.rand()) << (rand_bitsize * i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (r SysRNG) block_size() int {
|
||||
return rand_bitsize
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng SysRNG) free() {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module sys
|
||||
|
||||
// Until there's a portable, JS has a seeded way to produce random numbers
|
||||
// and not just Math.random(), use any of the existing implementations
|
||||
// as the System's RNG
|
||||
type SysRNG = WyRandRNG
|
||||
|
||||
// In the JS version, we simply return the same int as is normally generated.
|
||||
@[inline]
|
||||
pub fn (r SysRNG) default_rand() int {
|
||||
return r.int()
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.sys
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [u32(42), 256]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn get_n_randoms(n int, mut r rand.PRNG) []int {
|
||||
mut ints := []int{cap: n}
|
||||
for _ in 0 .. n {
|
||||
ints << r.int()
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
fn test_sys_rng_reproducibility() {
|
||||
// Note that C.srand() sets the seed globally.
|
||||
// So the order of seeding matters. It is recommended
|
||||
// to obtain all necessary data first, then set the
|
||||
// seed for another batch of data.
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut r1 := &rand.PRNG(&sys.SysRNG{})
|
||||
mut r2 := &rand.PRNG(&sys.SysRNG{})
|
||||
r1.seed(seed_data)
|
||||
ints1 := get_n_randoms(value_count, mut r1)
|
||||
r2.seed(seed_data)
|
||||
ints2 := get_n_randoms(value_count, mut r2)
|
||||
assert ints1 == ints2
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_sys_rng_uniformity_u64() {
|
||||
$if windows {
|
||||
// On Windows, C.rand() has RAND_MAX = 32767 (15 bits). The XOR-based
|
||||
// extension to u64 is inherently biased, so this uniformity check
|
||||
// cannot pass with the system RNG.
|
||||
return
|
||||
}
|
||||
// This assumes that C.rand() produces uniform results to begin with.
|
||||
// If the failure persists, report an issue on GitHub
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_sys_rng_uniformity_f64() {
|
||||
$if windows {
|
||||
// See test_sys_rng_uniformity_u64 above: Windows C.rand() has a
|
||||
// 15-bit range, so the derived f64 distribution is not uniform enough
|
||||
// to pass the statistical check.
|
||||
return
|
||||
}
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(min, max) or { panic("Couldn't obtain u32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_i32n() {
|
||||
max := i32(2525642)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i32n(max) or { panic("Couldn't obtain i32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_int_in_range() {
|
||||
min := -4252
|
||||
max := 23054962
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_i32_in_range() {
|
||||
min := i32(-4252)
|
||||
max := i32(23054962)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i32_in_range(min, max) or { panic("Couldn't obtain i32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f32() {
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f64() {
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_sys_rng_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
seed_data := [seed]
|
||||
mut rng := &rand.PRNG(&sys.SysRNG{})
|
||||
rng.seed(seed_data)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
module wyrand
|
||||
|
||||
import hash
|
||||
|
||||
pub fn (mut r WyRandRNG) free() {}
|
||||
|
||||
@[ignore_overflow; inline]
|
||||
pub fn (mut rng WyRandRNG) u64() u64 {
|
||||
unsafe {
|
||||
mut seed1 := rng.state
|
||||
seed1 += wyp0
|
||||
rng.state = seed1
|
||||
return hash.wymum(seed1 ^ wyp1, seed1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module wyrand
|
||||
|
||||
import rand.buffer
|
||||
import rand.seed
|
||||
|
||||
// Redefinition of some constants that we will need for pseudorandom number generation.
|
||||
const wyp0 = u64(0x2d358dccaa6c78a5)
|
||||
const wyp1 = u64(0x8bb84b93962eacc9)
|
||||
|
||||
pub const seed_len = 2
|
||||
|
||||
// WyRandRNG is a RNG based on the WyHash hashing algorithm.
|
||||
pub struct WyRandRNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state u64 = seed.time_seed_64()
|
||||
bytes_left int
|
||||
buffer u64
|
||||
}
|
||||
|
||||
// seed sets the seed, needs only two `u32`s in little-endian format as [lower, higher].
|
||||
pub fn (mut rng WyRandRNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 2 {
|
||||
eprintln('WyRandRNG needs 2 32-bit unsigned integers as the seed.')
|
||||
exit(1)
|
||||
}
|
||||
rng.state = seed_data[0] | (u64(seed_data[1]) << 32)
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng WyRandRNG) u8() u8 {
|
||||
// Can we extract a value from the buffer?
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
// Add a new value to the buffer
|
||||
rng.buffer = rng.u64()
|
||||
rng.bytes_left = 7
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16bit int in range `[0, 2¹⁶)`.
|
||||
@[inline]
|
||||
pub fn (mut rng WyRandRNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 6
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom 32bit int in range `[0, 2³²)`.
|
||||
@[inline]
|
||||
pub fn (mut rng WyRandRNG) u32() u32 {
|
||||
if rng.bytes_left >= 4 {
|
||||
rng.bytes_left -= 4
|
||||
value := u32(rng.buffer)
|
||||
rng.buffer >>= 32
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 32
|
||||
rng.bytes_left = 4
|
||||
return u32(ans)
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng WyRandRNG) block_size() int {
|
||||
return 64
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
import math
|
||||
import rand
|
||||
import rand.seed
|
||||
import rand.wyrand
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(42), 0], [u32(256), 0]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u64 {
|
||||
bound_u64 := u64(bound)
|
||||
mut randoms := []u64{len: (20)}
|
||||
mut rnd := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rnd.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rnd.u64n(bound_u64) or { panic("Couldn't obtain u64") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_wyrand_reproducibility() {
|
||||
seed_data := seed.time_seed_array(2)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
assert randoms1[i] == randoms2[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_wyrand_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_wyrand_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(min, max) or { panic("Couldn't obtain u32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_int_in_range() {
|
||||
min := -4252
|
||||
max := 1034
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_wyrand_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
module wyrand
|
||||
|
||||
fn C._wymix(u64, u64) u64
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
// WyRandRNG contains no heap-allocated fields, so this is a no-op.
|
||||
@[unsafe]
|
||||
pub fn (mut rng WyRandRNG) free() {
|
||||
}
|
||||
|
||||
// u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`.
|
||||
@[ignore_overflow; inline]
|
||||
pub fn (mut rng WyRandRNG) u64() u64 {
|
||||
unsafe {
|
||||
mut seed1 := rng.state
|
||||
seed1 += wyp0
|
||||
rng.state = seed1
|
||||
return C._wymix(seed1 ^ wyp1, seed1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2022 John Lloyd. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module xoroshiro128pp
|
||||
|
||||
import rand.seed
|
||||
import rand.buffer
|
||||
|
||||
pub const seed_len = 4 // u32, that is
|
||||
|
||||
fn rotl(x u64, k int) u64 {
|
||||
return (x << k) | (x >> (64 - k))
|
||||
}
|
||||
|
||||
// XOROS128PPRNG is ported from https://prng.di.unimi.it/xoroshiro128plusplus.c .
|
||||
pub struct XOROS128PPRNG {
|
||||
buffer.PRNGBuffer
|
||||
mut:
|
||||
state0 u64 = u64(0x853c49e6748fea9b) ^ seed.time_seed_64()
|
||||
state1 u64 = u64(0xda3e39cb94b95bdb) ^ seed.time_seed_64()
|
||||
}
|
||||
|
||||
// seed seeds the XOROS128PPRNG with 4 `u32` values.
|
||||
pub fn (mut rng XOROS128PPRNG) seed(seed_data []u32) {
|
||||
if seed_data.len != 4 {
|
||||
eprintln('XOROS128PPRNG needs 4 u32s to be seeded.')
|
||||
exit(1)
|
||||
}
|
||||
init_state0 := u64(seed_data[0]) | (u64(seed_data[1]) << 32)
|
||||
init_state1 := u64(seed_data[2]) | (u64(seed_data[3]) << 32)
|
||||
rng.state0 = init_state0
|
||||
rng.state1 = init_state1
|
||||
rng.u64()
|
||||
rng.u64()
|
||||
rng.bytes_left = 0
|
||||
rng.buffer = 0
|
||||
}
|
||||
|
||||
// byte returns a uniformly distributed pseudorandom 8-bit unsigned `byte`.
|
||||
@[inline]
|
||||
pub fn (mut rng XOROS128PPRNG) u8() u8 {
|
||||
if rng.bytes_left >= 1 {
|
||||
rng.bytes_left -= 1
|
||||
value := u8(rng.buffer)
|
||||
rng.buffer >>= 8
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 8
|
||||
rng.bytes_left = 7
|
||||
return u8(ans)
|
||||
}
|
||||
|
||||
// u16 returns a pseudorandom 16-bit unsigned integer (`u16`).
|
||||
@[inline]
|
||||
pub fn (mut rng XOROS128PPRNG) u16() u16 {
|
||||
if rng.bytes_left >= 2 {
|
||||
rng.bytes_left -= 2
|
||||
value := u16(rng.buffer)
|
||||
rng.buffer >>= 16
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 16
|
||||
rng.bytes_left = 6
|
||||
return u16(ans)
|
||||
}
|
||||
|
||||
// u32 returns a pseudorandom unsigned `u32`.
|
||||
@[inline]
|
||||
pub fn (mut rng XOROS128PPRNG) u32() u32 {
|
||||
if rng.bytes_left >= 4 {
|
||||
rng.bytes_left -= 4
|
||||
value := u32(rng.buffer)
|
||||
rng.buffer >>= 32
|
||||
return value
|
||||
}
|
||||
ans := rng.u64()
|
||||
rng.buffer = ans >> 32
|
||||
rng.bytes_left = 4
|
||||
return u32(ans)
|
||||
}
|
||||
|
||||
// u64 returns a pseudorandom 64-bit unsigned `u64`.
|
||||
@[ignore_overflow; inline]
|
||||
pub fn (mut rng XOROS128PPRNG) u64() u64 {
|
||||
oldstate0 := rng.state0
|
||||
mut oldstate1 := rng.state1
|
||||
res := rotl(oldstate0 + oldstate1, 17) + oldstate0
|
||||
oldstate1 = oldstate1 ^ oldstate0
|
||||
rng.state0 = rotl(oldstate0, 49) ^ oldstate1 ^ (oldstate1 << 21)
|
||||
rng.state1 = rotl(oldstate1, 28)
|
||||
return res
|
||||
}
|
||||
|
||||
// block_size returns the number of bits that the RNG can produce in a single iteration.
|
||||
@[inline]
|
||||
pub fn (mut rng XOROS128PPRNG) block_size() int {
|
||||
return 64
|
||||
}
|
||||
|
||||
// free should be called when the generator is no longer needed.
|
||||
@[unsafe]
|
||||
pub fn (mut rng XOROS128PPRNG) free() {
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
module xoroshiro128pp
|
||||
|
||||
import math
|
||||
import rand
|
||||
import rand.seed
|
||||
|
||||
const range_limit = 40
|
||||
const value_count = 1000
|
||||
const seeds = [[u32(42), 242, 267, 14195], [u32(256), 340, 1451, 1505]]
|
||||
|
||||
const sample_size = 1000
|
||||
const stats_epsilon = 0.05
|
||||
const inv_sqrt_12 = 1.0 / math.sqrt(12)
|
||||
|
||||
fn gen_randoms(seed_data []u32, bound int) []u32 {
|
||||
mut randoms := []u32{len: 20}
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed_data)
|
||||
for i in 0 .. 20 {
|
||||
randoms[i] = rng.u32n(u32(bound)) or { panic("Couldn't obtain u32") }
|
||||
}
|
||||
return randoms
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_reproducibility() {
|
||||
seed_data := seed.time_seed_array(4)
|
||||
randoms1 := gen_randoms(seed_data, 1000)
|
||||
randoms2 := gen_randoms(seed_data, 1000)
|
||||
assert randoms1.len == randoms2.len
|
||||
len := randoms1.len
|
||||
for i in 0 .. len {
|
||||
r1 := randoms1[i]
|
||||
r2 := randoms2[i]
|
||||
assert r1 == r2
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_variability() {
|
||||
// If this test fails and if it is certainly not the implementation
|
||||
// at fault, try changing the seed values. Repeated values are
|
||||
// improbable but not impossible.
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
mut values := []u64{cap: value_count}
|
||||
for i in 0 .. value_count {
|
||||
value := rng.u64()
|
||||
assert value !in values
|
||||
assert values.len == i
|
||||
values << value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_u64(mut rng rand.PRNG, range u64) {
|
||||
range_f64 := f64(range)
|
||||
expected_mean := range_f64 / 2.0
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := f64(rng.u64n(range) or { panic("Couldn't obtain u64") }) - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := range_f64 * inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_uniformity_u64() {
|
||||
ranges := [14019545, 80240, 130]
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for range in ranges {
|
||||
check_uniformity_u64(mut rng, u64(range))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uniformity_f64(mut rng rand.PRNG) {
|
||||
expected_mean := 0.5
|
||||
mut variance := 0.0
|
||||
for _ in 0 .. sample_size {
|
||||
diff := rng.f64() - expected_mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= sample_size - 1
|
||||
sigma := math.sqrt(variance)
|
||||
expected_sigma := inv_sqrt_12
|
||||
error := (sigma - expected_sigma) / expected_sigma
|
||||
assert math.abs(error) < stats_epsilon
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_uniformity_f64() {
|
||||
// The f64 version
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
check_uniformity_f64(mut rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_u32n() {
|
||||
max := u32(16384)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32n(max) or { panic("Couldn't obtain u32") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_u64n() {
|
||||
max := u64(379091181005)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64n(max) or { panic("Couldn't obtain u64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_u32_in_range() {
|
||||
max := u32(484468466)
|
||||
min := u32(316846)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u32_in_range(u32(min), u32(max)) or {
|
||||
panic("Couldn't obtain u32 in range")
|
||||
}
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_u64_in_range() {
|
||||
max := u64(216468454685163)
|
||||
min := u64(6848646868)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.u64_in_range(min, max) or { panic("Couldn't obtain u64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_int31() {
|
||||
max_u31 := int(0x7FFFFFFF)
|
||||
sign_mask := int(u32(0x80000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int31()
|
||||
assert value >= 0
|
||||
assert value <= max_u31
|
||||
// This statement ensures that the sign bit is zero
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_int63() {
|
||||
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
|
||||
sign_mask := i64(u64(0x8000000000000000))
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int63()
|
||||
assert value >= 0
|
||||
assert value <= max_u63
|
||||
assert (value & sign_mask) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_intn() {
|
||||
max := 2525642
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.intn(max) or { panic("Couldn't obtain int") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_i64n() {
|
||||
max := i64(3246727724653636)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64n(max) or { panic("Couldn't obtain i64") }
|
||||
assert value >= 0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_int_in_range() {
|
||||
min := -4252
|
||||
max := 1034
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.int_in_range(min, max) or { panic("Couldn't obtain int in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_i64_in_range() {
|
||||
min := i64(-24095)
|
||||
max := i64(324058)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.i64_in_range(min, max) or { panic("Couldn't obtain i64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f32() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f64() {
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64()
|
||||
assert value >= 0.0
|
||||
assert value < 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f32n() {
|
||||
max := f32(357.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32n(max) or { panic("Couldn't obtain f32") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f64n() {
|
||||
max := 1.52e6
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64n(max) or { panic("Couldn't obtain f64") }
|
||||
assert value >= 0.0
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f32_in_range() {
|
||||
min := f32(-24.0)
|
||||
max := f32(125.0)
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f32_in_range(min, max) or { panic("Couldn't obtain f32 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_xoroshiro128pp_f64_in_range() {
|
||||
min := -548.7
|
||||
max := 5015.2
|
||||
for seed in seeds {
|
||||
mut rng := &rand.PRNG(&XOROS128PPRNG{})
|
||||
rng.seed(seed)
|
||||
for _ in 0 .. range_limit {
|
||||
value := rng.f64_in_range(min, max) or { panic("Couldn't obtain f64 in range") }
|
||||
assert value >= min
|
||||
assert value < max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_change_default_random_generator() {
|
||||
rand.set_rng(&rand.PRNG(&XOROS128PPRNG{}))
|
||||
}
|
||||
Reference in new issue
Block a user