diff --git a/cmd/tools/bench/wyhash.v b/cmd/tools/bench/wyhash.v index 203b83cbf2..f25132dbfa 100644 --- a/cmd/tools/bench/wyhash.v +++ b/cmd/tools/bench/wyhash.v @@ -12,11 +12,11 @@ fn main() { println('Generating $sample_size strings between $min_str_len - $max_str_len chars long...') mut bytepile := []byte{} for _ in 0 .. sample_size * max_str_len { - bytepile << byte(40 + rand.next(125 - 40)) + bytepile << byte(rand.int_in_range(40, 125)) } mut str_lens := []int{} for _ in 0 .. sample_size { - str_lens << min_str_len + rand.next(max_str_len - min_str_len) + str_lens << rand.int_in_range(min_str_len, max_str_len) } println('Hashing each of the generated strings...') t0 := time.ticks() diff --git a/cmd/tools/vtest-fixed.v b/cmd/tools/vtest-fixed.v index 47fa82583a..661b5d66c1 100644 --- a/cmd/tools/vtest-fixed.v +++ b/cmd/tools/vtest-fixed.v @@ -22,6 +22,7 @@ const ( ] skip_on_windows = [ 'vlib/orm/orm_test.v', + 'vlib/net/websocket/ws_test.v', ] skip_on_non_windows = []string{} skip_on_macos = []string{} diff --git a/examples/path_tracing.v b/examples/path_tracing.v index d207e53f71..0f1fa16c77 100644 --- a/examples/path_tracing.v +++ b/examples/path_tracing.v @@ -236,7 +236,7 @@ fn intersect(r Ray, spheres &Sphere, nspheres int) (bool, f64, int){ // some casual random function, try to avoid the 0 fn rand_f64() f64 { - x := (C.rand()+1) & 0x3FFF_FFFF + x := (rand.intn(cache_len)+1) & 0x3FFF_FFFF return f64(x)/f64(0x3FFF_FFFF) } @@ -318,7 +318,7 @@ fn radiance(r Ray, depthi int, scene_id int) Vec { //r1 := f64(2.0 * math.pi) * rand_f64() // tabbed speed-up - r1 := C.rand() & cache_mask + r1 := rand.intn(cache_len) & cache_mask r2 := rand_f64() r2s := math.sqrt(r2) @@ -465,9 +465,8 @@ fn main() { height = os.args[5].int() } - // init the rand, using the same seed allows to obtain the same result in different runs - // change the seed from 2020 for different results - rand.seed(2020) + // change the seed for a different result + rand.seed([u32(2020), 0]) t1:=time.ticks() diff --git a/examples/quick_sort.v b/examples/quick_sort.v index c92413bf79..45ff0eb08d 100644 --- a/examples/quick_sort.v +++ b/examples/quick_sort.v @@ -7,11 +7,10 @@ const ( ) fn main() { - rand.seed(int(time.now().unix)) - rand.next(gen_max) // skip the first + rand.intn(gen_max) // skip the first mut arr := []int{} for _ in 0..gen_len { - arr << rand.next(gen_max) + arr << rand.intn(gen_max) } println('length of random array is $arr.len') println('before quick sort whether array is sorted: ${is_sorted(arr)}') diff --git a/examples/random_ips.v b/examples/random_ips.v index f99bee151d..1585ca9aa3 100644 --- a/examples/random_ips.v +++ b/examples/random_ips.v @@ -2,9 +2,7 @@ import rand import time fn main() { - rand.seed(int(time.now().unix)) - for _ in 0..10 { - println('${rand.next(255)}.${rand.next(255)}.${rand.next(255)}.${rand.next(255)}') + println('${rand.intn(255)}.${rand.intn(255)}.${rand.intn(255)}.${rand.intn(255)}') } } diff --git a/examples/tetris/tetris.v b/examples/tetris/tetris.v index f040835501..836aa69f25 100644 --- a/examples/tetris/tetris.v +++ b/examples/tetris/tetris.v @@ -193,7 +193,6 @@ fn main() { fn (mut g Game) init_game() { g.parse_tetros() - rand.seed(int(time.now().unix)) g.generate_tetro() g.field = [] // Generate the field, fill it with 0's, add -1's on each edge @@ -300,7 +299,7 @@ fn (mut g Game) delete_completed_line(y int) { fn (mut g Game) generate_tetro() { g.pos_y = 0 g.pos_x = field_width / 2 - tetro_size / 2 - g.tetro_idx = rand.next(b_tetros.len) + g.tetro_idx = rand.intn(b_tetros.len) g.rotation_idx = 0 g.get_tetro() } diff --git a/examples/vcasino/vcasino.v b/examples/vcasino/vcasino.v index 0026e9c2c5..e638230ef0 100644 --- a/examples/vcasino/vcasino.v +++ b/examples/vcasino/vcasino.v @@ -81,7 +81,7 @@ fn get_bet(money int) int { } bet = line.int() if bet <= 0 { - println('error: $line is not heigher than 1.') + println('error: $line is not higher than 1.') continue } else if bet > money { println('error: $line is more money than you have.') @@ -92,8 +92,7 @@ fn get_bet(money int) int { fn run_wheel(bet_nbr int, _bet int) int { mut bet := _bet - rand.seed(int(time.now().unix)) - winning_nbr := rand.next(50) + winning_nbr := rand.intn(50) print('Roulette Wheel spinning... and stops on the number $winning_nbr which is a ') if winning_nbr % 2 == 1 { println(odd) @@ -115,7 +114,7 @@ fn run_wheel(bet_nbr int, _bet int) int { fn is_broke(money int) bool { if money <= 0 { - println('You\'broke, the game is over..') + println('You\'re broke, the game is over..') return false } else { quit := Options{'yes', 'y'} diff --git a/vlib/bitfield/bitfield_test.v b/vlib/bitfield/bitfield_test.v index 9ded88c4e3..8e486436b5 100644 --- a/vlib/bitfield/bitfield_test.v +++ b/vlib/bitfield/bitfield_test.v @@ -19,16 +19,15 @@ fn test_bf_set_clear_toggle_get() { } fn test_bf_and_not_or_xor() { - rand.seed(int(time.now().unix)) len := 80 mut input1 := bitfield.new(len) mut input2 := bitfield.new(len) mut i := 0 for i < len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input1.set_bit(i) } - if rand.next(2) == 1{ + if rand.intn(2) == 1{ input2.set_bit(i) } i++ @@ -46,11 +45,10 @@ fn test_bf_and_not_or_xor() { } fn test_clone_cmp() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) } } @@ -60,11 +58,10 @@ fn test_clone_cmp() { } fn test_slice_join() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) } } @@ -83,12 +80,11 @@ fn test_slice_join() { } fn test_pop_count() { - rand.seed(int(time.now().unix)) len := 80 mut count0 := 0 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) count0++ } @@ -98,13 +94,12 @@ fn test_pop_count() { } fn test_hamming() { - rand.seed(int(time.now().unix)) len := 80 mut count := 0 mut input1 := bitfield.new(len) mut input2 := bitfield.new(len) for i in 0..len { - match rand.next(4) { + match rand.intn(4) { 0, 1 { input1.set_bit(i) count++ @@ -138,11 +133,10 @@ fn test_bf_from_bytes() { } fn test_bf_from_str() { - rand.seed(int(time.now().unix)) len := 80 mut input := '' for _ in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input = input + '1' } else { @@ -160,11 +154,10 @@ fn test_bf_from_str() { } fn test_bf_bf2str() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) } } @@ -188,7 +181,6 @@ fn test_bf_bf2str() { } fn test_bf_set_all() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) input.set_all() @@ -202,11 +194,10 @@ fn test_bf_set_all() { } fn test_bf_clear_all() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) } } @@ -221,11 +212,10 @@ fn test_bf_clear_all() { } fn test_bf_reverse() { - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { input.set_bit(i) } } @@ -241,11 +231,10 @@ fn test_bf_reverse() { } fn test_bf_resize() { - rand.seed(int(time.now().unix)) len := 80 - mut input := bitfield.new(rand.next(len) + 1) + mut input := bitfield.new(rand.intn(len) + 1) for _ in 0..100 { - input.resize(rand.next(len) + 1) + input.resize(rand.intn(len) + 1) input.set_bit(input.get_size() - 1) } assert input.get_bit(input.get_size() - 1) == 1 @@ -259,7 +248,6 @@ fn test_bf_pos() { * all haystacks here contain exactly one instanse of needle, * so search should return non-negative-values **/ - rand.seed(int(time.now().unix)) len := 80 mut result := 1 for i := 1; i < len; i++ { // needle size @@ -269,13 +257,13 @@ fn test_bf_pos() { // fill the needle with random values for k in 0..i { - if rand.next(2) == 1 { + if rand.intn(2) == 1 { needle.set_bit(k) } } // make sure the needle contains at least one set bit, selected randomly - r := rand.next(i) + r := rand.intn(i) needle.set_bit(r) // create the haystack, make sure it contains the needle @@ -322,11 +310,10 @@ fn test_bf_rotate() { } fn test_bf_printing(){ - rand.seed(int(time.now().unix)) len := 80 mut input := bitfield.new(len) for i in 0..len { - if rand.next(2) == 0 { + if rand.intn(2) == 0 { input.set_bit(i) } } diff --git a/vlib/net/websocket/utils.v b/vlib/net/websocket/utils.v index 213ac77fc5..d3c1c6117c 100644 --- a/vlib/net/websocket/utils.v +++ b/vlib/net/websocket/utils.v @@ -1,8 +1,6 @@ module websocket -import time import rand -import math import crypto.sha1 import encoding.base64 @@ -20,10 +18,7 @@ fn htonl64(payload_len u64) byteptr { } fn create_masking_key() []byte { - t := time.ticks() - tseq := t % 23237671 - mut rnd := rand.new_pcg32(u64(t), u64(tseq)) - mask_bit := byte(rnd.bounded_next(u32(math.max_i32))) + mask_bit := byte(rand.intn(255)) buf := [`0`].repeat(4) C.memcpy(buf.data, &mask_bit, 4) return buf @@ -46,7 +41,7 @@ fn get_nonce(nonce_size int) string { mut nonce := []byte{len: nonce_size, cap: nonce_size} alphanum := '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz' for i in 0 .. nonce_size { - nonce[i] = alphanum[rand.next(61)] + nonce[i] = alphanum[rand.intn(alphanum.len)] } return tos(nonce.data, nonce.len).clone() } diff --git a/vlib/rand/README.md b/vlib/rand/README.md new file mode 100644 index 0000000000..cd5948f8ba --- /dev/null +++ b/vlib/rand/README.md @@ -0,0 +1,46 @@ +# Quickstart + +The V `rand` module provides two main ways in which users can generate pseudorandom numbers: + +1. Through top-level functions in the `rand` module. + - `import rand` - Import the `rand` module. + - `rand.seed(seed_data)` to seed (optional). + - Use `rand.int()`, `rand.u32n(max)`, etc. +2. Through a generator of choice. The PRNGs are included in their respective submodules. + - `import rand.pcg32` - Import the module of the PRNG required. + - `mut rng := pcg32.PCG32RNG{}` - Initialize the struct. Note that the **`mut`** is important. + - `rng.seed(seed_data)` - optionally seed it with an array of `u32` values. + - Use `rng.int()`, `rng.u32n(max)`, etc. + +# 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. This is often useful for simulations that need the same starting seed. + +If you need truly random numbers that are going to be used for cryptography, use the `crypto.rand` module. + +# Guaranteed functions + +The following 21 functions are guaranteed to be supported by `rand` as well as the individual PRNGs. + +- `seed(seed_data)` where `seed_data` is an array of `u32` values. Different generators require different number of bits as the initial seed. The smallest is 32-bits, required by `sys.SysRNG`. Most others require 64-bits or 2 `u32` values. +- `u32()`, `u64()`, `int()`, `i64()`, `f32()`, `f64()` +- `u32n(max)`, `u64n(max)`, `intn(max)`, `i64n(max)`, `f32n(max)`, `f64n(max)` +- `u32_in_range(min, max)`, `u64_in_range(min, max)`, `int_in_range(min, max)`, `i64_in_range(min, max)`, `f32_in_range(min, max)`, `f64_in_range(min, max)` +- `int31()`, `int63()` + +# Utility Functions + +All the generators are time-seeded. The helper functions publicly available in `rand.util` 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. + +# 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... diff --git a/vlib/rand/mt19937.v b/vlib/rand/mt19937/mt19937.v similarity index 98% rename from vlib/rand/mt19937.v rename to vlib/rand/mt19937/mt19937.v index 26afac7109..2b97154c69 100644 --- a/vlib/rand/mt19937.v +++ b/vlib/rand/mt19937/mt19937.v @@ -1,9 +1,10 @@ // Copyright (c) 2019-2020 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 +module mt19937 import math.bits +import rand.util /* C++ functions for MT19937, with initialization improved 2002/2/10. @@ -58,7 +59,7 @@ const ( // A generator that uses the Mersenne Twister algorithm with period 2^19937 pub struct MT19937RNG { mut: - state []u64 = calculate_state(time_seed_array(2), mut []u64{len: nn}) + state []u64 = calculate_state(util.time_seed_array(2), mut []u64{len: nn}) mti int = nn next_rnd u32 = 0 has_next bool = false diff --git a/vlib/rand/mt19937_test.v b/vlib/rand/mt19937/mt19937_test.v similarity index 85% rename from vlib/rand/mt19937_test.v rename to vlib/rand/mt19937/mt19937_test.v index 068af67a52..e9811f10d3 100644 --- a/vlib/rand/mt19937_test.v +++ b/vlib/rand/mt19937/mt19937_test.v @@ -1,5 +1,6 @@ -import rand +import mt19937 import math +import rand.util const ( range_limit = 40 @@ -14,7 +15,7 @@ const ( ) fn mt19937_basic_test() { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed([u32(0xdeadbeef)]) target := [956529277, 3842322136, 3319553134, 1843186657, 2704993644, 595827513, 938518626, 1676224337, 3221315650, 1819026461] @@ -26,7 +27,7 @@ fn mt19937_basic_test() { fn gen_randoms(seed_data []u32, bound int) []u64 { bound_u64 := u64(bound) mut randoms := [u64(0)].repeat(20) - mut rnd := rand.MT19937RNG{} + mut rnd := mt19937.MT19937RNG{} rnd.seed(seed_data) for i in 0 .. 20 { randoms[i] = rnd.u64n(bound_u64) @@ -35,7 +36,7 @@ fn gen_randoms(seed_data []u32, bound int) []u64 { } fn test_mt19937_reproducibility() { - seed_data := rand.time_seed_array(2) + seed_data := util.time_seed_array(2) randoms1 := gen_randoms(seed_data, 1000) randoms2 := gen_randoms(seed_data, 1000) assert randoms1.len == randoms2.len @@ -61,7 +62,7 @@ fn test_mt19937_variability() { // at fault, try changing the seed values. Repeated values are // improbable but not impossible. for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -73,7 +74,7 @@ fn test_mt19937_variability() { } } -fn check_uniformity_u64(mut rng rand.MT19937RNG, range u64) { +fn check_uniformity_u64(mut rng mt19937.MT19937RNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -91,7 +92,7 @@ fn check_uniformity_u64(mut rng rand.MT19937RNG, range u64) { fn test_mt19937_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for range in ranges { check_uniformity_u64(mut rng, u64(range)) @@ -99,7 +100,7 @@ fn test_mt19937_uniformity_u64() { } } -fn check_uniformity_f64(mut rng rand.MT19937RNG) { +fn check_uniformity_f64(mut rng mt19937.MT19937RNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -116,19 +117,19 @@ fn check_uniformity_f64(mut rng rand.MT19937RNG) { fn test_mt19937_uniformity_f64() { // The f64 version for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) check_uniformity_f64(mut rng) } } fn test_mt19937_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -138,7 +139,7 @@ fn test_mt19937_u32n() { fn test_mt19937_u64n() { max := u64(379091181005) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -149,13 +150,13 @@ fn test_mt19937_u64n() { } fn test_mt19937_u32_in_range() { - max := 484468466 - min := 316846 + max := u32(484468466) + min := u32(316846) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32_in_range(u32(min), u32(max)) + value := rng.u32_in_range(min, max) assert value >= min assert value < max } @@ -166,7 +167,7 @@ fn test_mt19937_u64_in_range() { max := u64(216468454685163) min := u64(6848646868) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -180,7 +181,7 @@ fn test_mt19937_int31() { max_u31 := 0x7FFFFFFF sign_mask := 0x80000000 for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int31() @@ -196,7 +197,7 @@ fn test_mt19937_int63() { max_u63 := i64(0x7FFFFFFFFFFFFFFF) sign_mask := i64(0x8000000000000000) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int63() @@ -210,7 +211,7 @@ fn test_mt19937_int63() { fn test_mt19937_intn() { max := 2525642 for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.intn(max) @@ -223,7 +224,7 @@ fn test_mt19937_intn() { fn test_mt19937_i64n() { max := i64(3246727724653636) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -237,7 +238,7 @@ fn test_mt19937_int_in_range() { min := -4252 max := 1034 for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -251,7 +252,7 @@ fn test_mt19937_i64_in_range() { min := i64(-24095) max := i64(324058) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -263,7 +264,7 @@ fn test_mt19937_i64_in_range() { fn test_mt19937_f32() { for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32() @@ -275,7 +276,7 @@ fn test_mt19937_f32() { fn test_mt19937_f64() { for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64() @@ -288,7 +289,7 @@ fn test_mt19937_f64() { fn test_mt19937_f32n() { max := f32(357.0) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32n(max) @@ -301,7 +302,7 @@ fn test_mt19937_f32n() { fn test_mt19937_f64n() { max := 1.52e6 for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64n(max) @@ -315,7 +316,7 @@ fn test_mt19937_f32_in_range() { min := f32(-24.0) max := f32(125.0) for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32_in_range(min, max) @@ -329,7 +330,7 @@ fn test_mt19937_f64_in_range() { min := -548.7 max := 5015.2 for seed in seeds { - mut rng := rand.MT19937RNG{} + mut rng := mt19937.MT19937RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64_in_range(min, max) diff --git a/vlib/rand/musl_rng.v b/vlib/rand/musl/musl_rng.v similarity index 97% rename from vlib/rand/musl_rng.v rename to vlib/rand/musl/musl_rng.v index 8adb0211b8..9146127624 100644 --- a/vlib/rand/musl_rng.v +++ b/vlib/rand/musl/musl_rng.v @@ -1,14 +1,15 @@ // Copyright (c) 2019-2020 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 +module musl import math.bits +import rand.util // Ported from https://git.musl-libc.org/cgit/musl/tree/src/prng/rand_r.c pub struct MuslRNG { mut: - state u32 = time_seed_32() + state u32 = util.time_seed_32() } pub fn (mut rng MuslRNG) seed(seed_data []u32) { @@ -186,13 +187,13 @@ pub fn (mut rng MuslRNG) i64_in_range(min, max i64) i64 { // rng.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng MuslRNG) f32() f32 { - return f32(rng.u32()) / max_u32_as_f32 + return f32(rng.u32()) / util.max_u32_as_f32 } // rng.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng MuslRNG) f64() f64 { - return f64(rng.u64()) / max_u64_as_f64 + return f64(rng.u64()) / util.max_u64_as_f64 } // rng.f32n() returns a pseudorandom f32 value in [0, max) diff --git a/vlib/rand/musl_rng_test.v b/vlib/rand/musl/musl_rng_test.v similarity index 84% rename from vlib/rand/musl_rng_test.v rename to vlib/rand/musl/musl_rng_test.v index c59514a742..0bcd2c4302 100644 --- a/vlib/rand/musl_rng_test.v +++ b/vlib/rand/musl/musl_rng_test.v @@ -1,5 +1,6 @@ -import rand +import musl import math +import rand.util const ( range_limit = 40 @@ -16,7 +17,7 @@ const ( fn gen_randoms(seed_data []u32, bound int) []u64 { bound_u64 := u64(bound) mut randoms := [u64(0)].repeat(20) - mut rnd := rand.MuslRNG{} + mut rnd := musl.MuslRNG{} rnd.seed(seed_data) for i in 0 .. 20 { randoms[i] = rnd.u64n(bound_u64) @@ -25,7 +26,7 @@ fn gen_randoms(seed_data []u32, bound int) []u64 { } fn test_musl_reproducibility() { - seed_data := rand.time_seed_array(1) + seed_data := util.time_seed_array(1) randoms1 := gen_randoms(seed_data, 1000) randoms2 := gen_randoms(seed_data, 1000) assert randoms1.len == randoms2.len @@ -51,7 +52,7 @@ fn test_musl_variability() { // at fault, try changing the seed values. Repeated values are // improbable but not impossible. for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -63,7 +64,7 @@ fn test_musl_variability() { } } -fn check_uniformity_u64(mut rng rand.MuslRNG, range u64) { +fn check_uniformity_u64(mut rng musl.MuslRNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -81,7 +82,7 @@ fn check_uniformity_u64(mut rng rand.MuslRNG, range u64) { fn test_musl_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for range in ranges { check_uniformity_u64(mut rng, u64(range)) @@ -89,7 +90,7 @@ fn test_musl_uniformity_u64() { } } -fn check_uniformity_f64(mut rng rand.MuslRNG) { +fn check_uniformity_f64(mut rng musl.MuslRNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -106,19 +107,19 @@ fn check_uniformity_f64(mut rng rand.MuslRNG) { fn test_musl_uniformity_f64() { // The f64 version for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) check_uniformity_f64(mut rng) } } fn test_musl_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -128,7 +129,7 @@ fn test_musl_u32n() { fn test_musl_u64n() { max := u64(379091181005) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -139,13 +140,13 @@ fn test_musl_u64n() { } fn test_musl_u32_in_range() { - max := 484468466 - min := 316846 + max := u32(484468466) + min := u32(316846) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32_in_range(u64(min), u64(max)) + value := rng.u32_in_range(min, max) assert value >= min assert value < max } @@ -156,7 +157,7 @@ fn test_musl_u64_in_range() { max := u64(216468454685163) min := u64(6848646868) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -170,7 +171,7 @@ fn test_musl_int31() { max_u31 := 0x7FFFFFFF sign_mask := 0x80000000 for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int31() @@ -186,7 +187,7 @@ fn test_musl_int63() { max_u63 := i64(0x7FFFFFFFFFFFFFFF) sign_mask := i64(0x8000000000000000) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int63() @@ -200,7 +201,7 @@ fn test_musl_int63() { fn test_musl_intn() { max := 2525642 for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.intn(max) @@ -213,7 +214,7 @@ fn test_musl_intn() { fn test_musl_i64n() { max := i64(3246727724653636) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -227,7 +228,7 @@ fn test_musl_int_in_range() { min := -4252 max := 1034 for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -241,7 +242,7 @@ fn test_musl_i64_in_range() { min := i64(-24095) max := i64(324058) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -253,7 +254,7 @@ fn test_musl_i64_in_range() { fn test_musl_f32() { for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32() @@ -265,7 +266,7 @@ fn test_musl_f32() { fn test_musl_f64() { for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64() @@ -278,10 +279,10 @@ fn test_musl_f64() { fn test_musl_f32n() { max := f32(357.0) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32n(max) assert value >= 0.0 assert value < max } @@ -291,10 +292,10 @@ fn test_musl_f32n() { fn test_musl_f64n() { max := 1.52e6 for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64n(max) assert value >= 0.0 assert value < max } @@ -305,10 +306,10 @@ fn test_musl_f32_in_range() { min := f32(-24.0) max := f32(125.0) for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32_in_range(min, max) assert value >= min assert value < max } @@ -319,10 +320,10 @@ fn test_musl_f64_in_range() { min := -548.7 max := 5015.2 for seed in seeds { - mut rng := rand.MuslRNG{} + mut rng := musl.MuslRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64_in_range(min, max) assert value >= min assert value < max } diff --git a/vlib/rand/pcg32.v b/vlib/rand/pcg32/pcg32.v similarity index 92% rename from vlib/rand/pcg32.v rename to vlib/rand/pcg32/pcg32.v index 96fa6f88c1..1063f5586a 100644 --- a/vlib/rand/pcg32.v +++ b/vlib/rand/pcg32/pcg32.v @@ -1,26 +1,17 @@ // Copyright (c) 2019-2020 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 +module pcg32 + +import rand.util // 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 { mut: - state u64 = u64(0x853c49e6748fea9b) ^ time_seed_64() - inc u64 = u64(0xda3e39cb94b95bdb) ^ time_seed_64() -} - -// TODO: Remove in Phase 2 of reorganizing Random -pub fn new_pcg32(init_state, init_seq u64) PCG32RNG { - mut rng := PCG32RNG{} - rng.seed([u32(init_state), u32(init_state >> 32), u32(init_seq), u32(init_seq >> 32)]) - return rng -} - -pub fn (mut rng PCG32RNG) bounded_next(bound u32) u32 { - return rng.u32n(bound) + state u64 = u64(0x853c49e6748fea9b) ^ util.time_seed_64() + inc u64 = u64(0xda3e39cb94b95bdb) ^ util.time_seed_64() } // rng.seed(seed_data) - seed the PCG32RNG with 4 u32 values. @@ -184,13 +175,13 @@ pub fn (mut rng PCG32RNG) i64_in_range(min, max i64) i64 { // rng.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng PCG32RNG) f32() f32 { - return f32(rng.u32()) / max_u32_as_f32 + return f32(rng.u32()) / util.max_u32_as_f32 } // rng.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng PCG32RNG) f64() f64 { - return f64(rng.u64()) / max_u64_as_f64 + return f64(rng.u64()) / util.max_u64_as_f64 } // rng.f32n() returns a pseudorandom f32 value in [0, max) diff --git a/vlib/rand/pcg32_test.v b/vlib/rand/pcg32/pcg32_test.v similarity index 83% rename from vlib/rand/pcg32_test.v rename to vlib/rand/pcg32/pcg32_test.v index 642f6c419d..be116b6c8c 100644 --- a/vlib/rand/pcg32_test.v +++ b/vlib/rand/pcg32/pcg32_test.v @@ -1,5 +1,6 @@ -import rand import math +import pcg32 +import rand.util const ( range_limit = 40 @@ -15,7 +16,7 @@ const ( fn gen_randoms(seed_data []u32, bound int) []u32 { mut randoms := []u32{len: 20} - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed_data) for i in 0 .. 20 { randoms[i] = rng.u32n(u32(bound)) @@ -24,8 +25,8 @@ fn gen_randoms(seed_data []u32, bound int) []u32 { } fn test_pcg32_reproducibility() { - randoms1 := gen_randoms(rand.time_seed_array(4), 1000) - randoms2 := gen_randoms(rand.time_seed_array(4), 1000) + randoms1 := gen_randoms(util.time_seed_array(4), 1000) + randoms2 := gen_randoms(util.time_seed_array(4), 1000) assert randoms1.len == randoms2.len len := randoms1.len for i in 0 .. len { @@ -49,7 +50,7 @@ fn test_pcg32_variability() { // at fault, try changing the seed values. Repeated values are // improbable but not impossible. for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -61,7 +62,7 @@ fn test_pcg32_variability() { } } -fn check_uniformity_u64(mut rng rand.PCG32RNG, range u64) { +fn check_uniformity_u64(mut rng pcg32.PCG32RNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -79,7 +80,7 @@ fn check_uniformity_u64(mut rng rand.PCG32RNG, range u64) { fn test_pcg32_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for range in ranges { check_uniformity_u64(mut rng, u64(range)) @@ -87,7 +88,7 @@ fn test_pcg32_uniformity_u64() { } } -fn check_uniformity_f64(mut rng rand.PCG32RNG) { +fn check_uniformity_f64(mut rng pcg32.PCG32RNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -104,19 +105,19 @@ fn check_uniformity_f64(mut rng rand.PCG32RNG) { fn test_pcg32_uniformity_f64() { // The f64 version for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) check_uniformity_f64(mut rng) } } fn test_pcg32_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -126,7 +127,7 @@ fn test_pcg32_u32n() { fn test_pcg32_u64n() { max := u64(379091181005) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -137,10 +138,10 @@ fn test_pcg32_u64n() { } fn test_pcg32_u32_in_range() { - max := 484468466 - min := 316846 + max := u64(484468466) + min := u64(316846) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u32_in_range(u64(min), u64(max)) @@ -154,7 +155,7 @@ fn test_pcg32_u64_in_range() { max := u64(216468454685163) min := u64(6848646868) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -168,7 +169,7 @@ fn test_pcg32_int31() { max_u31 := 0x7FFFFFFF sign_mask := 0x80000000 for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int31() @@ -184,7 +185,7 @@ fn test_pcg32_int63() { max_u63 := i64(0x7FFFFFFFFFFFFFFF) sign_mask := i64(0x8000000000000000) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int63() @@ -198,7 +199,7 @@ fn test_pcg32_int63() { fn test_pcg32_intn() { max := 2525642 for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.intn(max) @@ -211,7 +212,7 @@ fn test_pcg32_intn() { fn test_pcg32_i64n() { max := i64(3246727724653636) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -225,7 +226,7 @@ fn test_pcg32_int_in_range() { min := -4252 max := 1034 for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -239,7 +240,7 @@ fn test_pcg32_i64_in_range() { min := i64(-24095) max := i64(324058) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -251,7 +252,7 @@ fn test_pcg32_i64_in_range() { fn test_pcg32_f32() { for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32() @@ -263,7 +264,7 @@ fn test_pcg32_f32() { fn test_pcg32_f64() { for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64() @@ -276,10 +277,10 @@ fn test_pcg32_f64() { fn test_pcg32_f32n() { max := f32(357.0) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32n(max) assert value >= 0.0 assert value < max } @@ -289,10 +290,10 @@ fn test_pcg32_f32n() { fn test_pcg32_f64n() { max := 1.52e6 for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64n(max) assert value >= 0.0 assert value < max } @@ -303,10 +304,10 @@ fn test_pcg32_f32_in_range() { min := f32(-24.0) max := f32(125.0) for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32_in_range(min, max) assert value >= min assert value < max } @@ -317,10 +318,10 @@ fn test_pcg32_f64_in_range() { min := -548.7 max := 5015.2 for seed in seeds { - mut rng := rand.PCG32RNG{} + mut rng := pcg32.PCG32RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64_in_range(min, max) assert value >= min assert value < max } diff --git a/vlib/rand/rand.v b/vlib/rand/rand.v index d734bae99f..b5de9309c7 100644 --- a/vlib/rand/rand.v +++ b/vlib/rand/rand.v @@ -3,72 +3,128 @@ // that can be found in the LICENSE file. module rand -// TODO: Remove these functions once done: -// 1. C.rand() -// 2. seed() -// 3. next() -// 4. rand_r() -// fn C.rand() int -pub fn seed(s int) { - C.srand(s) -} - -pub fn next(max int) int { - return C.rand() % max -} - -// rand_r returns a pseudo-random number; -// writes a result value to the seed argument. -pub fn rand_r(seed &int) int { - ns := *seed * 1103515245 + 12345 - unsafe { - (*seed) = ns - } - return ns & 0x7fffffff -} +import rand.util +import rand.wyrand +// Configuration struct for creating a new instance of the default RNG. pub struct PRNGConfigStruct { - seed []u32 = time_seed_array(2) + seed []u32 = util.time_seed_array(2) } -pub fn new_default(config PRNGConfigStruct) &WyRandRNG { - rng := &WyRandRNG{} +__global default_rng &wyrand.WyRandRNG +fn init() { + default_rng = new_default({}) +} + +// 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. +pub fn new_default(config PRNGConfigStruct) &wyrand.WyRandRNG { + rng := &wyrand.WyRandRNG{} rng.seed(config.seed) return rng } -// rand_f32 return a random f32 between 0 and max -[deprecated] -pub fn rand_f32(max f32) f32 { - return rand_uniform_f32() * max +// seed sets the given array of `u32` values as the seed for the `default_rng`. +pub fn seed(seed []u32) { + default_rng.seed(seed) } -// rand_f32 return a random f32 in range min and max -[deprecated] -pub fn rand_f32_in_range(min, max f32) f32 { - return min + rand_uniform_f32() * (max - min) +// u32() returns a uniformly distributed u32 in _[0, 232)_ +pub fn u32() u32 { + return default_rng.u32() } -// rand_f64 return a random f64 between 0 (inclusive) and max (exclusive) -[deprecated] -pub fn rand_f64(max f64) f64 { - return rand_uniform_f64() * max +// u64() returns a uniformly distributed u64 in _[0, 264)_ +pub fn u64() u64 { + return default_rng.u64() } -// rand_f64 return a random f64 in range min (inclusive) and max (exclusive) -[deprecated] -pub fn rand_f64_in_range(min, max f64) f64 { - return min + rand_uniform_f64() * (max - min) +// u32n(max) returns a uniformly distributed pseudorandom 32-bit signed positive u32 in _[0, max)_ +pub fn u32n(max u32) u32 { + return default_rng.u32n(max) } -// rand_uniform_f32 returns a uniformly distributed f32 in the range 0 (inclusive) and 1 (exclusive) -[deprecated] -pub fn rand_uniform_f32() f32 { - return f32(C.rand()) / f32(C.RAND_MAX) +// u64n(max) returns a uniformly distributed pseudorandom 64-bit signed positive u64 in _[0, max)_ +pub fn u64n(max u64) u64 { + return default_rng.u64n(max) } -// rand_uniform_f64 returns a uniformly distributed f64 in the range 0 (inclusive) and 1 (exclusive) -[deprecated] -pub fn rand_uniform_f64() f64 { - return f64(C.rand()) / f64(C.RAND_MAX) +// u32_in_range(min, max) returns a uniformly distributed pseudorandom 32-bit unsigned u32 in _[min, max)_ +pub fn u32_in_range(min, max u32) u32 { + return default_rng.u32_in_range(min, max) +} + +// u64_in_range(min, max) returns a uniformly distributed pseudorandom 64-bit unsigned u64 in _[min, max)_ +pub fn u64_in_range(min, max u64) u64 { + return default_rng.u64_in_range(min, max) +} + +// int() returns a uniformly distributed pseudorandom 32-bit signed (possibly negative) int +pub fn int() int { + return default_rng.int() +} + +// intn(max) returns a uniformly distributed pseudorandom 32-bit signed positive int in _[0, max)_ +pub fn intn(max int) int { + return default_rng.intn(max) +} + +// int_in_range(min, max) returns a uniformly distributed pseudorandom +// 32-bit signed int in [min, max). Both min and max can be negative, but we must have _min < max_. +pub fn int_in_range(min, max int) int { + return default_rng.int_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(max) returns a uniformly distributed pseudorandom 64-bit signed positive i64 in _[0, max)_ +pub fn i64n(max i64) i64 { + return default_rng.i64n(max) +} + +// i64_in_range(min, max) returns a uniformly distributed pseudorandom 64-bit signed int in _[min, max)_ +pub fn i64_in_range(min, max i64) i64 { + return default_rng.i64_in_range(min, max) +} + +// int63() returns a uniformly distributed pseudorandom 63-bit signed positive int +pub fn int63() i64 { + return default_rng.int63() +} + +// f32() returns a uniformly distributed 32-bit floating point in _[0, 1)_ +pub fn f32() f32 { + return default_rng.f32() +} + +// f64() returns a uniformly distributed 64-bit floating point in _[0, 1)_ +pub fn f64() f64 { + return default_rng.f64() +} + +// f32n() returns a uniformly distributed 32-bit floating point in _[0, max)_ +pub fn f32n(max f32) f32 { + return default_rng.f32n(max) +} + +// f64n() returns a uniformly distributed 64-bit floating point in _[0, max)_ +pub fn f64n(max f64) f64 { + return default_rng.f64n(max) +} + +// f32_in_range(min, max) returns a uniformly distributed 32-bit floating point in _[min, max)_ +pub fn f32_in_range(min, max f32) f32 { + return default_rng.f32_in_range(min, max) +} + +// f64_in_range(min, max) returns a uniformly distributed 64-bit floating point in _[min, max)_ +pub fn f64_in_range(min, max f64) f64 { + return default_rng.f64_in_range(min, max) } diff --git a/vlib/rand/random_numbers_test.v b/vlib/rand/random_numbers_test.v index fde6b0c576..6d6e5506ad 100644 --- a/vlib/rand/random_numbers_test.v +++ b/vlib/rand/random_numbers_test.v @@ -1,56 +1,177 @@ import rand -import math const ( rnd_count = 40 - seeds = [42, 256] + 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) + } + return values +} + fn test_rand_reproducibility() { for seed in seeds { - mut randoms1 := gen_randoms(seed) - mut randoms2 := gen_randoms(seed) - assert_randoms_equal(randoms1, randoms2) + 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_r_reproducibility() { - for seed in seeds { - mut randoms1 := gen_randoms_r(seed) - mut randoms2 := gen_randoms_r(seed) - assert_randoms_equal(randoms1, randoms2) - } -} - -fn test_rand_r_seed_update() { - seed := 10 +fn test_rand_u32n() { + max := u32(16384) for _ in 0 .. rnd_count { - prev_seed := seed - _ = rand.rand_r(&seed) - assert prev_seed != seed + value := rand.u32n(max) + assert value >= 0 + assert value < max } } -fn gen_randoms(seed int) []int { - mut randoms := [0].repeat(rnd_count) - rand.seed(seed) - for i in 0 .. rnd_count { - randoms[i] = rand.next(100) - } - return randoms -} - -fn gen_randoms_r(seed int) []int { - mut randoms := [0].repeat(rnd_count) - for i in 0 .. rnd_count { - randoms[i] = rand.rand_r(&seed) - } - return randoms -} - -fn assert_randoms_equal(r1, r2 []int) { - for i in 0 .. rnd_count { - assert r1[i] == r2[i] +fn test_rand_u64n() { + max := u64(379091181005) + for _ in 0 .. rnd_count { + value := rand.u64n(max) + 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) + 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) + assert value >= min + assert value < max + } +} + +fn test_rand_intn() { + max := 2525642 + for _ in 0 .. rnd_count { + value := rand.intn(max) + assert value >= 0 + assert value < max + } +} + +fn test_rand_i64n() { + max := i64(3246727724653636) + for _ in 0 .. rnd_count { + value := rand.i64n(max) + 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) + 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) + assert value >= min + assert value < max + } +} + +fn test_rand_int31() { + max_u31 := 0x7FFFFFFF + sign_mask := 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(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) + assert value >= 0.0 + assert value < max + } +} + +fn test_rand_f64n() { + max := f64(1.52e6) + for _ in 0 .. rnd_count { + value := rand.f64n(max) + 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) + 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) + assert value >= min + assert value < max + } +} diff --git a/vlib/rand/splitmix64.v b/vlib/rand/splitmix64/splitmix64.v similarity index 94% rename from vlib/rand/splitmix64.v rename to vlib/rand/splitmix64/splitmix64.v index 4012f78dfe..4f1956b794 100644 --- a/vlib/rand/splitmix64.v +++ b/vlib/rand/splitmix64/splitmix64.v @@ -1,12 +1,14 @@ // Copyright (c) 2019-2020 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 +module splitmix64 + +import rand.util // Ported from http://xoshiro.di.unimi.it/splitmix64.c pub struct SplitMix64RNG { mut: - state u64 = time_seed_64() + state u64 = util.time_seed_64() has_extra bool = false extra u32 } @@ -30,7 +32,7 @@ pub fn (mut rng SplitMix64RNG) u32() u32 { return rng.extra } full_value := rng.u64() - lower := u32(full_value & lower_mask) + lower := u32(full_value & util.lower_mask) upper := u32(full_value >> 32) rng.extra = upper rng.has_extra = true @@ -119,13 +121,13 @@ pub fn (mut rng SplitMix64RNG) i64() i64 { // rng.int31() returns a pseudorandom 31-bit int which is non-negative [inline] pub fn (mut rng SplitMix64RNG) int31() int { - return int(rng.u32() & u31_mask) // Set the 32nd bit to 0. + return int(rng.u32() & util.u31_mask) // Set the 32nd bit to 0. } // rng.int63() returns a pseudorandom 63-bit int which is non-negative [inline] pub fn (mut rng SplitMix64RNG) int63() i64 { - return i64(rng.u64() & u63_mask) // Set the 64th bit to 0. + return i64(rng.u64() & util.u63_mask) // Set the 64th bit to 0. } // rng.intn(max) returns a pseudorandom int that lies in [0, max) @@ -172,13 +174,13 @@ pub fn (mut rng SplitMix64RNG) i64_in_range(min, max i64) i64 { // rng.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng SplitMix64RNG) f32() f32 { - return f32(rng.u32()) / max_u32_as_f32 + return f32(rng.u32()) / util.max_u32_as_f32 } // rng.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng SplitMix64RNG) f64() f64 { - return f64(rng.u64()) / max_u64_as_f64 + return f64(rng.u64()) / util.max_u64_as_f64 } // rng.f32n() returns a pseudorandom f32 value in [0, max) diff --git a/vlib/rand/splitmix64_test.v b/vlib/rand/splitmix64/splitmix64_test.v similarity index 79% rename from vlib/rand/splitmix64_test.v rename to vlib/rand/splitmix64/splitmix64_test.v index f8da0be13c..bfd2895b5d 100644 --- a/vlib/rand/splitmix64_test.v +++ b/vlib/rand/splitmix64/splitmix64_test.v @@ -1,5 +1,6 @@ -import rand import math +import splitmix64 +import rand.util const ( range_limit = 40 @@ -16,7 +17,7 @@ const ( fn gen_randoms(seed_data []u32, bound int) []u64 { bound_u64 := u64(bound) mut randoms := [u64(0)].repeat(20) - mut rnd := rand.SplitMix64RNG{} + mut rnd := splitmix64.SplitMix64RNG{} rnd.seed(seed_data) for i in 0 .. 20 { randoms[i] = rnd.u64n(bound_u64) @@ -25,7 +26,7 @@ fn gen_randoms(seed_data []u32, bound int) []u64 { } fn test_splitmix64_reproducibility() { - seed_data := rand.time_seed_array(2) + seed_data := util.time_seed_array(2) randoms1 := gen_randoms(seed_data, 1000) randoms2 := gen_randoms(seed_data, 1000) assert randoms1.len == randoms2.len @@ -51,7 +52,7 @@ fn test_splitmix64_variability() { // at fault, try changing the seed values. Repeated values are // improbable but not impossible. for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -63,7 +64,7 @@ fn test_splitmix64_variability() { } } -fn check_uniformity_u64(mut rng rand.SplitMix64RNG, range u64) { +fn check_uniformity_u64(mut rng splitmix64.SplitMix64RNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -81,7 +82,7 @@ fn check_uniformity_u64(mut rng rand.SplitMix64RNG, range u64) { fn test_splitmix64_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for range in ranges { check_uniformity_u64(mut rng, u64(range)) @@ -89,7 +90,7 @@ fn test_splitmix64_uniformity_u64() { } } -fn check_uniformity_f64(mut rng rand.SplitMix64RNG) { +fn check_uniformity_f64(mut rng splitmix64.SplitMix64RNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -106,19 +107,19 @@ fn check_uniformity_f64(mut rng rand.SplitMix64RNG) { fn test_splitmix64_uniformity_f64() { // The f64 version for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) check_uniformity_f64(mut rng) } } fn test_splitmix64_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -128,7 +129,7 @@ fn test_splitmix64_u32n() { fn test_splitmix64_u64n() { max := u64(379091181005) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -139,13 +140,13 @@ fn test_splitmix64_u64n() { } fn test_splitmix64_u32_in_range() { - max := 484468466 - min := 316846 + max := u32(484468466) + min := u32(316846) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32_in_range(u32(min), u32(max)) + value := rng.u32_in_range(min, max) assert value >= min assert value < max } @@ -156,7 +157,7 @@ fn test_splitmix64_u64_in_range() { max := u64(216468454685163) min := u64(6848646868) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -170,7 +171,7 @@ fn test_splitmix64_int31() { max_u31 := 0x7FFFFFFF sign_mask := 0x80000000 for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int31() @@ -186,7 +187,7 @@ fn test_splitmix64_int63() { max_u63 := i64(0x7FFFFFFFFFFFFFFF) sign_mask := i64(0x8000000000000000) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int63() @@ -197,10 +198,10 @@ fn test_splitmix64_int63() { } } -fn test_splimix64_intn() { +fn test_splitmix64_intn() { max := 2525642 for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.intn(max) @@ -210,10 +211,10 @@ fn test_splimix64_intn() { } } -fn test_splimix64_i64n() { +fn test_splitmix64_i64n() { max := i64(3246727724653636) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -223,11 +224,11 @@ fn test_splimix64_i64n() { } } -fn test_splimix64_int_in_range() { +fn test_splitmix64_int_in_range() { min := -4252 max := 230549862 for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -237,11 +238,11 @@ fn test_splimix64_int_in_range() { } } -fn test_splimix64_i64_in_range() { +fn test_splitmix64_i64_in_range() { min := i64(-24095) max := i64(324058) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -253,7 +254,7 @@ fn test_splimix64_i64_in_range() { fn test_splitmix64_f32() { for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32() @@ -265,7 +266,7 @@ fn test_splitmix64_f32() { fn test_splitmix64_f64() { for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64() @@ -278,10 +279,10 @@ fn test_splitmix64_f64() { fn test_splitmix64_f32n() { max := f32(357.0) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32n(max) assert value >= 0.0 assert value < max } @@ -291,10 +292,10 @@ fn test_splitmix64_f32n() { fn test_splitmix64_f64n() { max := 1.52e6 for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64n(max) assert value >= 0.0 assert value < max } @@ -305,10 +306,10 @@ fn test_splitmix64_f32_in_range() { min := f32(-24.0) max := f32(125.0) for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32_in_range(min, max) assert value >= min assert value < max } @@ -319,10 +320,10 @@ fn test_splitmix64_f64_in_range() { min := -548.7 max := 5015.2 for seed in seeds { - mut rng := rand.SplitMix64RNG{} + mut rng := splitmix64.SplitMix64RNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64_in_range(min, max) assert value >= min assert value < max } diff --git a/vlib/rand/system_rng.c.v b/vlib/rand/sys/system_rng.c.v similarity index 92% rename from vlib/rand/system_rng.c.v rename to vlib/rand/sys/system_rng.c.v index 0561b9bb9f..5ef2343683 100644 --- a/vlib/rand/system_rng.c.v +++ b/vlib/rand/sys/system_rng.c.v @@ -1,9 +1,10 @@ // Copyright (c) 2019-2020 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 +module sys import math.bits +import rand.util // Implementation note: // ==================== @@ -27,20 +28,6 @@ fn calculate_iterations_for(bits int) int { return base + extra } -// Size constants to avoid importing the entire math module -const ( - max_u32 = 0xFFFFFFFF - max_u64 = 0xFFFFFFFFFFFFFFFF - max_u32_as_f32 = f32(max_u32) - max_u64_as_f64 = f64(max_u64) -) - -// Masks for fast modular division -const ( - u31_mask = u32(0x7FFFFFFF) - u63_mask = u64(0x7FFFFFFFFFFFFFFF) -) - // C.rand returns a pseudorandom integer from 0 (inclusive) to C.RAND_MAX (exclusive) fn C.rand() int @@ -49,7 +36,7 @@ fn C.rand() int // SysRNG is the PRNG provided by default in the libc implementiation that V uses. pub struct SysRNG { mut: - seed u32 = time_seed_32() + seed u32 = util.time_seed_32() } // r.seed() sets the seed of the accepting SysRNG to the given data. @@ -188,13 +175,13 @@ pub fn (r SysRNG) i64() i64 { // r.int31() returns a pseudorandom 31-bit int which is non-negative [inline] pub fn (r SysRNG) int31() int { - return int(r.u32() & u31_mask) // Set the 32nd bit to 0. + return int(r.u32() & util.u31_mask) // Set the 32nd bit to 0. } // r.int63() returns a pseudorandom 63-bit int which is non-negative [inline] pub fn (r SysRNG) int63() i64 { - return i64(r.u64() & u63_mask) // Set the 64th bit to 0. + return i64(r.u64() & util.u63_mask) // Set the 64th bit to 0. } // r.intn(max) returns a pseudorandom int that lies in [0, max) @@ -241,13 +228,13 @@ pub fn (r SysRNG) i64_in_range(min, max i64) i64 { // r.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (r SysRNG) f32() f32 { - return f32(r.u32()) / max_u32_as_f32 + return f32(r.u32()) / util.max_u32_as_f32 } // r.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (r SysRNG) f64() f64 { - return f64(r.u64()) / max_u64_as_f64 + return f64(r.u64()) / util.max_u64_as_f64 } // r.f32n() returns a pseudorandom f32 value in [0, max) diff --git a/vlib/rand/system_rng.js.v b/vlib/rand/sys/system_rng.js.v similarity index 97% rename from vlib/rand/system_rng.js.v rename to vlib/rand/sys/system_rng.js.v index 833ba81596..05ebcba352 100644 --- a/vlib/rand/system_rng.js.v +++ b/vlib/rand/sys/system_rng.js.v @@ -1,7 +1,7 @@ // Copyright (c) 2019-2020 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 +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 diff --git a/vlib/rand/system_rng_test.v b/vlib/rand/sys/system_rng_test.v similarity index 86% rename from vlib/rand/system_rng_test.v rename to vlib/rand/sys/system_rng_test.v index 0309edf066..d6c2cacea0 100644 --- a/vlib/rand/system_rng_test.v +++ b/vlib/rand/sys/system_rng_test.v @@ -1,5 +1,5 @@ -import rand import math +import sys const ( range_limit = 40 @@ -13,7 +13,7 @@ const ( inv_sqrt_12 = 1.0 / math.sqrt(12) ) -fn get_n_randoms(n int, r rand.SysRNG) []int { +fn get_n_randoms(n int, r sys.SysRNG) []int { mut ints := []int{cap: n} for _ in 0 .. n { ints << r.int() @@ -28,8 +28,8 @@ fn test_sys_rng_reproducibility() { // seed for another batch of data. for seed in seeds { seed_data := [seed] - mut r1 := rand.SysRNG{} - mut r2 := rand.SysRNG{} + mut r1 := sys.SysRNG{} + mut r2 := sys.SysRNG{} r1.seed(seed_data) ints1 := get_n_randoms(value_count, r1) r2.seed(seed_data) @@ -55,7 +55,7 @@ fn test_sys_rng_variability() { // improbable but not impossible. for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -67,7 +67,7 @@ fn test_sys_rng_variability() { } } -fn check_uniformity_u64(rng rand.SysRNG, range u64) { +fn check_uniformity_u64(rng sys.SysRNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -88,7 +88,7 @@ fn test_sys_rng_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for range in ranges { check_uniformity_u64(rng, u64(range)) @@ -96,7 +96,7 @@ fn test_sys_rng_uniformity_u64() { } } -fn check_uniformity_f64(rng rand.SysRNG) { +fn check_uniformity_f64(rng sys.SysRNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -114,20 +114,20 @@ fn test_sys_rng_uniformity_f64() { // The f64 version for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) check_uniformity_f64(rng) } } fn test_sys_rng_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -138,7 +138,7 @@ fn test_sys_rng_u64n() { max := u64(379091181005) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -149,14 +149,14 @@ fn test_sys_rng_u64n() { } fn test_sys_rng_u32_in_range() { - max := 484468466 - min := 316846 + max := u32(484468466) + min := u32(316846) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.u32_in_range(u32(min), u32(max)) + value := rng.u32_in_range(min, max) assert value >= min assert value < max } @@ -168,7 +168,7 @@ fn test_sys_rng_u64_in_range() { min := u64(6848646868) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -182,7 +182,7 @@ fn test_sys_rng_intn() { max := 2525642 for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.intn(max) @@ -196,7 +196,7 @@ fn test_sys_rng_i64n() { max := i64(3246727724653636) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -211,7 +211,7 @@ fn test_sys_rng_int_in_range() { max := 23054962 for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -226,7 +226,7 @@ fn test_sys_rng_i64_in_range() { max := i64(324058) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -241,7 +241,7 @@ fn test_sys_rng_int31() { sign_mask := 0x80000000 for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.int31() @@ -258,7 +258,7 @@ fn test_sys_rng_int63() { sign_mask := i64(0x8000000000000000) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.int63() @@ -272,7 +272,7 @@ fn test_sys_rng_int63() { fn test_sys_rng_f32() { for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.f32() @@ -285,7 +285,7 @@ fn test_sys_rng_f32() { fn test_sys_rng_f64() { for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { value := rng.f64() @@ -299,10 +299,10 @@ fn test_sys_rng_f32n() { max := f32(357.0) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32n(max) assert value >= 0.0 assert value < max } @@ -313,10 +313,10 @@ fn test_sys_rng_f64n() { max := 1.52e6 for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64n(max) assert value >= 0.0 assert value < max } @@ -328,10 +328,10 @@ fn test_sys_rng_f32_in_range() { max := f32(125.0) for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32_in_range(min, max) assert value >= min assert value < max } @@ -343,10 +343,10 @@ fn test_sys_rng_f64_in_range() { max := 5015.2 for seed in seeds { seed_data := [seed] - mut rng := rand.SysRNG{} + mut rng := sys.SysRNG{} rng.seed(seed_data) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64_in_range(min, max) assert value >= min assert value < max } diff --git a/vlib/rand/util.v b/vlib/rand/util/util.v similarity index 56% rename from vlib/rand/util.v rename to vlib/rand/util/util.v index cc021181cd..1bdadd9c44 100644 --- a/vlib/rand/util.v +++ b/vlib/rand/util/util.v @@ -1,13 +1,19 @@ // Copyright (c) 2019-2020 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 +module util import time // Commonly used constants across RNGs -const ( - lower_mask = u64(0x00000000ffffffff) +pub const ( + lower_mask = u64(0x00000000FFFFFFFF) + max_u32 = 0xFFFFFFFF + max_u64 = 0xFFFFFFFFFFFFFFFF + max_u32_as_f32 = f32(max_u32) + 1 + max_u64_as_f64 = f64(max_u64) + 1 + u31_mask = u32(0x7FFFFFFF) + u63_mask = u64(0x7FFFFFFFFFFFFFFF) ) // Constants taken from Numerical Recipes @@ -16,7 +22,7 @@ fn nr_next(prev u32) u32 { return prev * 1664525 + 1013904223 } -// utility function that return the required number of u32s generated from system time +// time_seed_array is a utility function that returns the required number of u32s generated from system time [inline] pub fn time_seed_array(count int) []u32 { mut seed := u32(time.now().unix_time()) @@ -28,13 +34,15 @@ pub fn time_seed_array(count int) []u32 { return seed_data } +// time_seed_32 returns a 32-bit seed geenrated from system time [inline] -fn time_seed_32() u32 { +pub fn time_seed_32() u32 { return time_seed_array(1)[0] } +// time_seed_64 returns a 64-bit seed geenrated from system time [inline] -fn time_seed_64() u64 { +pub fn time_seed_64() u64 { seed_data := time_seed_array(2) lower := u64(seed_data[0]) upper := u64(seed_data[1]) diff --git a/vlib/rand/wyrand.v b/vlib/rand/wyrand/wyrand.v similarity index 94% rename from vlib/rand/wyrand.v rename to vlib/rand/wyrand/wyrand.v index df682a7108..778b1de443 100644 --- a/vlib/rand/wyrand.v +++ b/vlib/rand/wyrand/wyrand.v @@ -1,9 +1,10 @@ // Copyright (c) 2019-2020 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 +module wyrand import math.bits +import rand.util import hash.wyhash // Redefinition of some constants that we will need for pseudorandom number generation @@ -15,7 +16,7 @@ const ( // RNG based on the WyHash hashing algorithm pub struct WyRandRNG { mut: - state u64 = time_seed_64() + state u64 = util.time_seed_64() has_extra bool = false extra u32 } @@ -30,7 +31,6 @@ pub fn (mut rng WyRandRNG) seed(seed_data []u32) { rng.has_extra = false } - // rng.u32() updates the PRNG state and returns the next pseudorandom u32 [inline] pub fn (mut rng WyRandRNG) u32() u32 { @@ -39,7 +39,7 @@ pub fn (mut rng WyRandRNG) u32() u32 { return rng.extra } full_value := rng.u64() - lower := u32(full_value & lower_mask) + lower := u32(full_value & util.lower_mask) upper := u32(full_value >> 32) rng.extra = upper rng.has_extra = true @@ -148,13 +148,13 @@ pub fn (mut rng WyRandRNG) i64() i64 { // rng.int31() returns a pseudorandom 31-bit int which is non-negative [inline] pub fn (mut rng WyRandRNG) int31() int { - return int(rng.u32() & u31_mask) // Set the 32nd bit to 0. + return int(rng.u32() & util.u31_mask) // Set the 32nd bit to 0. } // rng.int63() returns a pseudorandom 63-bit int which is non-negative [inline] pub fn (mut rng WyRandRNG) int63() i64 { - return i64(rng.u64() & u63_mask) // Set the 64th bit to 0. + return i64(rng.u64() & util.u63_mask) // Set the 64th bit to 0. } // rng.intn(max) returns a pseudorandom int that lies in [0, max) @@ -201,13 +201,13 @@ pub fn (mut rng WyRandRNG) i64_in_range(min, max i64) i64 { // rng.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng WyRandRNG) f32() f32 { - return f32(rng.u32()) / max_u32_as_f32 + return f32(rng.u32()) / util.max_u32_as_f32 } // rng.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) [inline] pub fn (mut rng WyRandRNG) f64() f64 { - return f64(rng.u64()) / max_u64_as_f64 + return f64(rng.u64()) / util.max_u64_as_f64 } // rng.f32n() returns a pseudorandom f32 value in [0, max) diff --git a/vlib/rand/wyrand_test.v b/vlib/rand/wyrand/wyrand_test.v similarity index 83% rename from vlib/rand/wyrand_test.v rename to vlib/rand/wyrand/wyrand_test.v index 2eaa1d3014..42ee859e62 100644 --- a/vlib/rand/wyrand_test.v +++ b/vlib/rand/wyrand/wyrand_test.v @@ -1,5 +1,6 @@ -import rand import math +import rand.util +import wyrand const ( range_limit = 40 @@ -16,7 +17,7 @@ const ( fn gen_randoms(seed_data []u32, bound int) []u64 { bound_u64 := u64(bound) mut randoms := [u64(0)].repeat(20) - mut rnd := rand.WyRandRNG{} + mut rnd := wyrand.WyRandRNG{} rnd.seed(seed_data) for i in 0 .. 20 { randoms[i] = rnd.u64n(bound_u64) @@ -25,7 +26,7 @@ fn gen_randoms(seed_data []u32, bound int) []u64 { } fn test_wyrand_reproducibility() { - seed_data := rand.time_seed_array(2) + seed_data := util.time_seed_array(2) randoms1 := gen_randoms(seed_data, 1000) randoms2 := gen_randoms(seed_data, 1000) assert randoms1.len == randoms2.len @@ -51,7 +52,7 @@ fn test_wyrand_variability() { // at fault, try changing the seed values. Repeated values are // improbable but not impossible. for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) mut values := []u64{cap: value_count} for i in 0 .. value_count { @@ -63,7 +64,7 @@ fn test_wyrand_variability() { } } -fn check_uniformity_u64(mut rng rand.WyRandRNG, range u64) { +fn check_uniformity_u64(mut rng wyrand.WyRandRNG, range u64) { range_f64 := f64(range) expected_mean := range_f64 / 2.0 mut variance := 0.0 @@ -81,7 +82,7 @@ fn check_uniformity_u64(mut rng rand.WyRandRNG, range u64) { fn test_wyrand_uniformity_u64() { ranges := [14019545, 80240, 130] for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for range in ranges { check_uniformity_u64(mut rng, u64(range)) @@ -89,7 +90,7 @@ fn test_wyrand_uniformity_u64() { } } -fn check_uniformity_f64(mut rng rand.WyRandRNG) { +fn check_uniformity_f64(mut rng wyrand.WyRandRNG) { expected_mean := 0.5 mut variance := 0.0 for _ in 0 .. sample_size { @@ -106,19 +107,19 @@ fn check_uniformity_f64(mut rng rand.WyRandRNG) { fn test_wyrand_uniformity_f64() { // The f64 version for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) check_uniformity_f64(mut rng) } } fn test_wyrand_u32n() { - max := 16384 + max := u32(16384) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32n(u32(max)) + value := rng.u32n(max) assert value >= 0 assert value < max } @@ -128,7 +129,7 @@ fn test_wyrand_u32n() { fn test_wyrand_u64n() { max := u64(379091181005) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64n(max) @@ -139,13 +140,13 @@ fn test_wyrand_u64n() { } fn test_wyrand_u32_in_range() { - max := 484468466 - min := 316846 + max := u32(484468466) + min := u32(316846) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.u32_in_range(u32(min), u32(max)) + value := rng.u32_in_range(min, max) assert value >= min assert value < max } @@ -156,7 +157,7 @@ fn test_wyrand_u64_in_range() { max := u64(216468454685163) min := u64(6848646868) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.u64_in_range(min, max) @@ -170,7 +171,7 @@ fn test_wyrand_int31() { max_u31 := 0x7FFFFFFF sign_mask := 0x80000000 for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int31() @@ -186,7 +187,7 @@ fn test_wyrand_int63() { max_u63 := i64(0x7FFFFFFFFFFFFFFF) sign_mask := i64(0x8000000000000000) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int63() @@ -200,7 +201,7 @@ fn test_wyrand_int63() { fn test_wyrand_intn() { max := 2525642 for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.intn(max) @@ -213,7 +214,7 @@ fn test_wyrand_intn() { fn test_wyrand_i64n() { max := i64(3246727724653636) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64n(max) @@ -227,7 +228,7 @@ fn test_wyrand_int_in_range() { min := -4252 max := 1034 for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.int_in_range(min, max) @@ -241,7 +242,7 @@ fn test_wyrand_i64_in_range() { min := i64(-24095) max := i64(324058) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.i64_in_range(min, max) @@ -253,7 +254,7 @@ fn test_wyrand_i64_in_range() { fn test_wyrand_f32() { for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f32() @@ -265,7 +266,7 @@ fn test_wyrand_f32() { fn test_wyrand_f64() { for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { value := rng.f64() @@ -278,10 +279,10 @@ fn test_wyrand_f64() { fn test_wyrand_f32n() { max := f32(357.0) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32n(max) assert value >= 0.0 assert value < max } @@ -291,10 +292,10 @@ fn test_wyrand_f32n() { fn test_wyrand_f64n() { max := 1.52e6 for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64n(max) assert value >= 0.0 assert value < max } @@ -305,10 +306,10 @@ fn test_wyrand_f32_in_range() { min := f32(-24.0) max := f32(125.0) for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f32() + value := rng.f32_in_range(min, max) assert value >= min assert value < max } @@ -319,10 +320,10 @@ fn test_wyrand_f64_in_range() { min := -548.7 max := 5015.2 for seed in seeds { - mut rng := rand.WyRandRNG{} + mut rng := wyrand.WyRandRNG{} rng.seed(seed) for _ in 0 .. range_limit { - value := rng.f64() + value := rng.f64_in_range(min, max) assert value >= min assert value < max } diff --git a/vlib/time/misc/misc.v b/vlib/time/misc/misc.v index a004ecc8f8..619c2dd47b 100644 --- a/vlib/time/misc/misc.v +++ b/vlib/time/misc/misc.v @@ -8,5 +8,5 @@ const ( ) // random returns a random time struct in *the past*. pub fn random() time.Time { - return time.unix(rand.next(int(start_time_unix))) + return time.unix(int(rand.u64n(start_time_unix))) } diff --git a/vlib/time/misc/misc_test.v b/vlib/time/misc/misc_test.v index 211fce7275..9bcc8ad3ae 100644 --- a/vlib/time/misc/misc_test.v +++ b/vlib/time/misc/misc_test.v @@ -3,7 +3,7 @@ import rand fn test_random() { // guarantee CI test stability, by seeding the random number generator with a known seed - rand.seed(0) + rand.seed([u32(0), 0]) t1 := tmisc.random() t2 := tmisc.random() t3 := tmisc.random()