Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+118
View File
@@ -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`.
+10
View File
@@ -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
}
+49
View File
@@ -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'")
}
}
+155
View File
@@ -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('')
}
+64
View File
@@ -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]
}
+130
View File
@@ -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
}
}
}
+114
View File
@@ -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
}
+130
View File
@@ -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) }
}
+185
View File
@@ -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() }
}
+327
View File
@@ -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
}
}
}
+96
View File
@@ -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() {
}
+317
View File
@@ -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
}
}
}
+97
View File
@@ -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() {
}
+324
View File
@@ -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{}))
}
+230
View File
@@ -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()
}
}
}
}
+66
View File
@@ -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()
}
}
+813
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
// vtest build: present_node?
import rand
fn test_string() {
res := rand.string(4)
assert res.len == 4
println(res)
}
+103
View File
@@ -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
}
+136
View File
@@ -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
}
+536
View File
@@ -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)
}
+44
View File
@@ -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
}
+96
View File
@@ -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
}
}
}
+124
View File
@@ -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() {
}
+15
View File
@@ -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()
}
+381
View File
@@ -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
}
}
}
+16
View File
@@ -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
}
+87
View File
@@ -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
}
+317
View File
@@ -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
}
}
}
+21
View File
@@ -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{}))
}