From 7ec17ecf98c5151b31f7b0a3e090d6b5a4d12c54 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:33:24 +0100 Subject: [PATCH] Update `core:math/rand` to use `context.random_generator` and remove `rand.Rand` --- base/runtime/random_generator.odin | 18 +- core/math/rand/distributions.odin | 120 ++++----- core/math/rand/exp.odin | 8 +- core/math/rand/normal.odin | 10 +- core/math/rand/rand.odin | 351 +++++--------------------- tests/core/hash/test_core_hash.odin | 3 +- tests/core/slice/test_core_slice.odin | 12 +- tests/internal/test_map.odin | 62 +++-- 8 files changed, 197 insertions(+), 387 deletions(-) diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 43d7ffd0c..205d7eb7e 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -29,6 +29,20 @@ random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Gener } +random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) { + if rg.procedure != nil { + rg.procedure(rg.data, .Reset, p) + } +} + +random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) { + if rg.procedure != nil { + p := p + rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)]) + } +} + + Default_Random_State :: struct { state: u64, inc: u64, @@ -93,9 +107,9 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, } } -default_random_generator :: proc "contextless" () -> Random_Generator { +default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator { return { procedure = default_random_generator_proc, - data = nil, + data = state, } } \ No newline at end of file diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin index 9365e8b76..a10ea3238 100644 --- a/core/math/rand/distributions.odin +++ b/core/math/rand/distributions.odin @@ -8,12 +8,12 @@ float32_uniform :: float32_range // Triangular Distribution // See: http://wikipedia.org/wiki/Triangular_distribution @(require_results) -float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 { +float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 { if hi-lo == 0 { return lo } lo, hi := lo, hi - u := float64(r) + u := float64() c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) if u > c { u = 1-u @@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 // Triangular Distribution // See: http://wikipedia.org/wiki/Triangular_distribution @(require_results) -float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 { +float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 { if hi-lo == 0 { return lo } lo, hi := lo, hi - u := float32(r) + u := float32() c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) if u > c { u = 1-u @@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 // Normal/Gaussian Distribution @(require_results) -float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { - return norm_float64(r) * stddev + mean +float64_normal :: proc(mean, stddev: f64) -> f64 { + return norm_float64() * stddev + mean } // Normal/Gaussian Distribution @(require_results) -float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { - return f32(float64_normal(f64(mean), f64(stddev), r)) +float32_normal :: proc(mean, stddev: f32) -> f32 { + return f32(float64_normal(f64(mean), f64(stddev))) } // Log Normal Distribution @(require_results) -float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { - return math.exp(float64_normal(mean, stddev, r)) +float64_log_normal :: proc(mean, stddev: f64) -> f64 { + return math.exp(float64_normal(mean, stddev)) } // Log Normal Distribution @(require_results) -float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { - return f32(float64_log_normal(f64(mean), f64(stddev), r)) +float32_log_normal :: proc(mean, stddev: f32) -> f32 { + return f32(float64_log_normal(f64(mean), f64(stddev))) } @@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { // 0 to positive infinity if lambda > 0 // negative infinity to 0 if lambda <= 0 @(require_results) -float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 { - return - math.ln(1 - float64(r)) / lambda +float64_exponential :: proc(lambda: f64) -> f64 { + return - math.ln(1 - float64()) / lambda } // Exponential Distribution // `lambda` is 1.0/(desired mean). It should be non-zero. @@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 { // 0 to positive infinity if lambda > 0 // negative infinity to 0 if lambda <= 0 @(require_results) -float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 { - return f32(float64_exponential(f64(lambda), r)) +float32_exponential :: proc(lambda: f32) -> f32 { + return f32(float64_exponential(f64(lambda))) } @@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 { // // mean is alpha*beta, variance is math.pow(alpha*beta, 2) @(require_results) -float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { +float64_gamma :: proc(alpha, beta: f64) -> f64 { if alpha <= 0 || beta <= 0 { panic(#procedure + ": alpha and beta must be > 0.0") } @@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { bbb := alpha - LOG4 ccc := alpha + ainv for { - u1 := float64(r) + u1 := float64() if !(1e-7 < u1 && u1 < 0.9999999) { continue } - u2 := 1 - float64(r) + u2 := 1 - float64() v := math.ln(u1 / (1 - u1)) / ainv x := alpha * math.exp(v) z := u1 * u1 * u2 @@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { } case alpha == 1: // float64_exponential(1/beta) - return -math.ln(1 - float64(r)) * beta + return -math.ln(1 - float64()) * beta case: // ALGORITHM GS of Statistical Computing - Kennedy & Gentle x: f64 for { - u := float64(r) + u := float64() b := (math.e + alpha) / math.e p := b * u if p <= 1 { @@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { } else { x = -math.ln((b - p) / alpha) } - u1 := float64(r) + u1 := float64() if p > 1 { if u1 <= math.pow(x, alpha-1) { break @@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { // // mean is alpha*beta, variance is math.pow(alpha*beta, 2) @(require_results) -float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_gamma(f64(alpha), f64(beta), r)) +float32_gamma :: proc(alpha, beta: f32) -> f32 { + return f32(float64_gamma(f64(alpha), f64(beta))) } @@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { // // Return values range between 0 and 1 @(require_results) -float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { +float64_beta :: proc(alpha, beta: f64) -> f64 { if alpha <= 0 || beta <= 0 { panic(#procedure + ": alpha and beta must be > 0.0") } // Knuth Vol 2 Ed 3 pg 134 "the beta distribution" - y := float64_gamma(alpha, 1.0, r) + y := float64_gamma(alpha, 1.0) if y != 0 { - return y / (y + float64_gamma(beta, 1.0, r)) + return y / (y + float64_gamma(beta, 1.0)) } return 0 } @@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { // // Return values range between 0 and 1 @(require_results) -float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_beta(f64(alpha), f64(beta), r)) +float32_beta :: proc(alpha, beta: f32) -> f32 { + return f32(float64_beta(f64(alpha), f64(beta))) } // Pareto distribution, `alpha` is the shape parameter. // https://wikipedia.org/wiki/Pareto_distribution @(require_results) -float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 { - return math.pow(1 - float64(r), -1.0 / alpha) +float64_pareto :: proc(alpha: f64) -> f64 { + return math.pow(1 - float64(), -1.0 / alpha) } // Pareto distribution, `alpha` is the shape parameter. // https://wikipedia.org/wiki/Pareto_distribution @(require_results) -float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_pareto(f64(alpha), r)) +float32_pareto :: proc(alpha, beta: f32) -> f32 { + return f32(float64_pareto(f64(alpha))) } // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. @(require_results) -float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { - u := 1 - float64(r) +float64_weibull :: proc(alpha, beta: f64) -> f64 { + u := 1 - float64() return alpha * math.pow(-math.ln(u), 1.0/beta) } // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. @(require_results) -float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_weibull(f64(alpha), f64(beta), r)) +float32_weibull :: proc(alpha, beta: f32) -> f32 { + return f32(float64_weibull(f64(alpha), f64(beta))) } @@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { // `kappa` is the concentration parameter which must be >= 0 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi @(require_results) -float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { +float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 { // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993. mu := mean_angle if kappa <= 1e-6 { - return math.TAU * float64(r) + return math.TAU * float64() } s := 0.5 / kappa t := s + math.sqrt(1 + s*s) z: f64 for { - u1 := float64(r) + u1 := float64() z = math.cos(math.TAU * 0.5 * u1) d := z / (t + z) - u2 := float64(r) + u2 := float64() if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) { break } @@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { q := 1.0 / t f := (q + z) / (1 + q*z) - u3 := float64(r) + u3 := float64() if u3 > 0.5 { return math.mod(mu + math.acos(f), math.TAU) } else { @@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { // `kappa` is the concentration parameter which must be >= 0 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi @(require_results) -float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 { - return f32(float64_von_mises(f64(mean_angle), f64(kappa), r)) +float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 { + return f32(float64_von_mises(f64(mean_angle), f64(kappa))) } // Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { +float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { assert(gamma > 0) // Calculated from the inverse CDF - return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0 + return math.tan(math.PI * (float64() - 0.5))*gamma + x_0 } // Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { - return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r)) +float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { + return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma))) } // Log Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { +float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { assert(gamma > 0) - return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0) + return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0) } // Log Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { - return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r)) +float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { + return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma))) } // Laplace Distribution // `b` is the scale where `b` > 0 @(require_results) -float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 { +float64_laplace :: proc(mean, b: f64) -> f64 { assert(b > 0) - p := float64(r)-0.5 + p := float64()-0.5 return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean } // Laplace Distribution // `b` is the scale where `b` > 0 @(require_results) -float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 { - return f32(float64_laplace(f64(mean), f64(b), r)) +float32_laplace :: proc(mean, b: f32) -> f32 { + return f32(float64_laplace(f64(mean), f64(b))) } @@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 { // `eta` is the shape, `b` is the scale // Both `eta` and `b` must be > 0 @(require_results) -float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 { +float64_gompertz :: proc(eta, b: f64) -> f64 { if eta <= 0 || b <= 0 { panic(#procedure + ": eta and b must be > 0.0") } - p := float64(r) + p := float64() return math.ln(1 - math.ln(1 - p)/eta)/b } // Gompertz Distribution // `eta` is the shape, `b` is the scale // Both `eta` and `b` must be > 0 @(require_results) -float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 { - return f32(float64_gompertz(f64(eta), f64(b), r)) +float32_gompertz :: proc(eta, b: f32) -> f32 { + return f32(float64_gompertz(f64(eta), f64(b))) } diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin index ebc849b2f..f30f11f50 100644 --- a/core/math/rand/exp.odin +++ b/core/math/rand/exp.odin @@ -16,7 +16,7 @@ import "core:math" // https://www.jstatsoft.org/article/view/v005i08 [web page] // @(require_results) -exp_float64 :: proc(r: ^Rand = nil) -> f64 { +exp_float64 :: proc() -> f64 { re :: 7.69711747013104972 @(static, rodata) @@ -199,16 +199,16 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 { } for { - j := uint32(r) + j := uint32() i := j & 0xFF x := f64(j) * f64(we[i]) if j < ke[i] { return x } if i == 0 { - return re - math.ln(float64(r)) + return re - math.ln(float64()) } - if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { + if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { return x } } diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index 31b9a2387..eefa013df 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -18,7 +18,7 @@ import "core:math" // https://www.jstatsoft.org/article/view/v005i08 [web page] // @(require_results) -norm_float64 :: proc(r: ^Rand = nil) -> f64 { +norm_float64 :: proc() -> f64 { rn :: 3.442619855899 @(static, rodata) @@ -116,7 +116,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { } for { - j := i32(uint32(r)) + j := i32(uint32()) i := j & 0x7f x := f64(j) * f64(wn[i]) if u32(abs(j)) < kn[i] { @@ -126,15 +126,15 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { if i == 0 { for { - x = -math.ln(float64(r)) * (1.0/ rn) - y := -math.ln(float64(r)) + x = -math.ln(float64()) * (1.0/ rn) + y := -math.ln(float64()) if y+y >= x*x { break } } return j > 0 ? rn + x : -rn - x } - if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) { + if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) { return x } } diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 3f85277e8..7bb78cc1d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -10,52 +10,18 @@ import "core:crypto" import "core:math" import "core:mem" -Rand :: struct { - state: u64, - inc: u64, - is_system: bool, +Default_Random_State :: runtime.Default_Random_State +default_random_generator :: runtime.default_random_generator + +create :: proc(seed: u64) -> (state: Default_Random_State) { + seed := seed + runtime.default_random_generator(&state) + runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)]) + return } -to_random_generator :: proc(r: ^Rand) -> runtime.Random_Generator { - return { - procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { - r := (^Rand)(data) - switch mode { - case .Read: - _ = read(p, r) - - case .Reset: - if r.is_system { - return - } - seed: u64 - runtime.mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p))) - init(r, seed) - - - case .Query_Info: - if len(p) != size_of(runtime.Random_Generator_Query_Info) { - return - } - info := (^runtime.Random_Generator_Query_Info)(raw_data(p)) - info^ += {.Uniform} - if r.is_system { - info^ += {.External_Entropy} - } else { - info^ += {.Resettable} - } - } - }, - data = r, - } -} - - -@(private) -global_rand := create(u64(intrinsics.read_cycle_counter())) - /* -Sets the seed used by the global random number generator. +Reset the seed used by the context.random_generator. Inputs: - seed: The seed value @@ -72,143 +38,46 @@ Example: Possible Output: 10 - */ +@(deprecated="Prefer `rand.reset`") set_global_seed :: proc(seed: u64) { - init(&global_rand, seed) + runtime.random_generator_reset_u64(context.random_generator, seed) } /* -Creates a new random number generator. +Reset the seed used by the context.random_generator. Inputs: -- seed: The seed value to create the random number generator with - -Returns: -- res: The created random number generator +- seed: The seed value Example: import "core:math/rand" import "core:fmt" - create_example :: proc() { - my_rand := rand.create(1) - fmt.println(rand.uint64(&my_rand)) + set_global_seed_example :: proc() { + rand.set_global_seed(1) + fmt.println(rand.uint64()) } Possible Output: 10 - */ -@(require_results) -create :: proc(seed: u64) -> (res: Rand) { - r: Rand - init(&r, seed) - return r +reset :: proc(seed: u64) { + runtime.random_generator_reset_u64(context.random_generator, seed) } -/* -Initialises a random number generator. - -Inputs: -- r: The random number generator to initialise -- seed: The seed value to initialise this random number generator - -Example: - import "core:math/rand" - import "core:fmt" - - init_example :: proc() { - my_rand: rand.Rand - rand.init(&my_rand, 1) - fmt.println(rand.uint64(&my_rand)) - } - -Possible Output: - - 10 - -*/ -init :: proc(r: ^Rand, seed: u64) { - r.state = 0 - r.inc = (seed << 1) | 1 - _random_u64(r) - r.state += seed - _random_u64(r) -} - -/* -Initialises a random number generator to use the system random number generator. -The system random number generator is platform specific, and not supported -on all targets. - -Inputs: -- r: The random number generator to use the system random number generator - -WARNING: Panics if the system random number generator is not supported. -Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant. - -Example: - import "core:crypto" - import "core:math/rand" - import "core:fmt" - - init_as_system_example :: proc() { - my_rand: rand.Rand - switch crypto.HAS_RAND_BYTES { - case true: - rand.init_as_system(&my_rand) - fmt.println(rand.uint64(&my_rand)) - case false: - fmt.println("system random not supported!") - } - } - -Possible Output: - - 10 - -*/ -init_as_system :: proc(r: ^Rand) { - if !crypto.HAS_RAND_BYTES { - panic(#procedure + " is not supported on this platform yet") - } - r.state = 0 - r.inc = 0 - r.is_system = true -} - @(private) -_random_u64 :: proc(r: ^Rand) -> u64 { - r := r - switch { - case r == nil: - if res: u64; runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) { - return res - } - - r = &global_rand - case r.is_system: - value: u64 - crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)]) - return value - } - - old_state := r.state - r.state = old_state * 6364136223846793005 + (r.inc|1) - xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 - rot := (old_state >> 59) - return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +_random_u64 :: proc() -> (res: u64) { + ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) + assert(ok, "uninitialized context.random_generator") + return } /* Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 32 bit value @@ -217,11 +86,7 @@ Example: import "core:fmt" uint32_example :: proc() { - // Using the global random number generator fmt.println(rand.uint32()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint32(&my_rand)) } Possible Output: @@ -231,14 +96,11 @@ Possible Output: */ @(require_results) -uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return u32(_random_u64(r)) } +uint32 :: proc() -> (val: u32) { return u32(_random_u64()) } /* Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 64 bit value @@ -247,11 +109,7 @@ Example: import "core:fmt" uint64_example :: proc() { - // Using the global random number generator fmt.println(rand.uint64()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint64(&my_rand)) } Possible Output: @@ -261,14 +119,11 @@ Possible Output: */ @(require_results) -uint64 :: proc(r: ^Rand = nil) -> (val: u64) { return _random_u64(r) } +uint64 :: proc() -> (val: u64) { return _random_u64() } /* Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 128 bit value @@ -277,11 +132,7 @@ Example: import "core:fmt" uint128_example :: proc() { - // Using the global random number generator fmt.println(rand.uint128()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint128(&my_rand)) } Possible Output: @@ -291,9 +142,9 @@ Possible Output: */ @(require_results) -uint128 :: proc(r: ^Rand = nil) -> (val: u128) { - a := u128(_random_u64(r)) - b := u128(_random_u64(r)) +uint128 :: proc() -> (val: u128) { + a := u128(_random_u64()) + b := u128(_random_u64()) return (a<<64) | b } @@ -301,9 +152,6 @@ uint128 :: proc(r: ^Rand = nil) -> (val: u128) { Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 31 bit value @@ -312,11 +160,7 @@ Example: import "core:fmt" int31_example :: proc() { - // Using the global random number generator fmt.println(rand.int31()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int31(&my_rand)) } Possible Output: @@ -325,15 +169,12 @@ Possible Output: 389 */ -@(require_results) int31 :: proc(r: ^Rand = nil) -> (val: i32) { return i32(uint32(r) << 1 >> 1) } +@(require_results) int31 :: proc() -> (val: i32) { return i32(uint32() << 1 >> 1) } /* Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 63 bit value @@ -342,11 +183,7 @@ Example: import "core:fmt" int63_example :: proc() { - // Using the global random number generator fmt.println(rand.int63()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int63(&my_rand)) } Possible Output: @@ -355,15 +192,12 @@ Possible Output: 389 */ -@(require_results) int63 :: proc(r: ^Rand = nil) -> (val: i64) { return i64(uint64(r) << 1 >> 1) } +@(require_results) int63 :: proc() -> (val: i64) { return i64(uint64() << 1 >> 1) } /* Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 127 bit value @@ -372,11 +206,7 @@ Example: import "core:fmt" int127_example :: proc() { - // Using the global random number generator fmt.println(rand.int127()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int127(&my_rand)) } Possible Output: @@ -385,14 +215,13 @@ Possible Output: 389 */ -@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) } +@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) } /* Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used. Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 31 bit value in the range `[0, n)` @@ -404,11 +233,7 @@ Example: import "core:fmt" int31_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int31_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int31_max(1024, &my_rand)) } Possible Output: @@ -418,17 +243,17 @@ Possible Output: */ @(require_results) -int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) { +int31_max :: proc(n: i32) -> (val: i32) { if n <= 0 { panic("Invalid argument to int31_max") } if n&(n-1) == 0 { - return int31(r) & (n-1) + return int31() & (n-1) } max := i32((1<<31) - 1 - (1<<31)%u32(n)) - v := int31(r) + v := int31() for v > max { - v = int31(r) + v = int31() } return v % n } @@ -438,7 +263,6 @@ Generates a random 63 bit value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 63 bit value in the range `[0, n)` @@ -450,11 +274,7 @@ Example: import "core:fmt" int63_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int63_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int63_max(1024, &my_rand)) } Possible Output: @@ -464,17 +284,17 @@ Possible Output: */ @(require_results) -int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) { +int63_max :: proc(n: i64) -> (val: i64) { if n <= 0 { panic("Invalid argument to int63_max") } if n&(n-1) == 0 { - return int63(r) & (n-1) + return int63() & (n-1) } max := i64((1<<63) - 1 - (1<<63)%u64(n)) - v := int63(r) + v := int63() for v > max { - v = int63(r) + v = int63() } return v % n } @@ -484,7 +304,6 @@ Generates a random 127 bit value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 127 bit value in the range `[0, n)` @@ -496,11 +315,7 @@ Example: import "core:fmt" int127_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int127_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int127_max(1024, &my_rand)) } Possible Output: @@ -510,17 +325,17 @@ Possible Output: */ @(require_results) -int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) { +int127_max :: proc(n: i128) -> (val: i128) { if n <= 0 { panic("Invalid argument to int127_max") } if n&(n-1) == 0 { - return int127(r) & (n-1) + return int127() & (n-1) } max := i128((1<<127) - 1 - (1<<127)%u128(n)) - v := int127(r) + v := int127() for v > max { - v = int127(r) + v = int127() } return v % n } @@ -530,7 +345,6 @@ Generates a random integer value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random integer value in the range `[0, n)` @@ -542,11 +356,7 @@ Example: import "core:fmt" int_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int_max(1024, &my_rand)) } Possible Output: @@ -556,23 +366,20 @@ Possible Output: */ @(require_results) -int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) { +int_max :: proc(n: int) -> (val: int) { if n <= 0 { panic("Invalid argument to int_max") } when size_of(int) == 4 { - return int(int31_max(i32(n), r)) + return int(int31_max(i32(n))) } else { - return int(int63_max(i64(n), r)) + return int(int63_max(i64(n))) } } /* Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random double floating point value in the range `[0, 1)` @@ -581,11 +388,7 @@ Example: import "core:fmt" float64_example :: proc() { - // Using the global random number generator fmt.println(rand.float64()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float64(&my_rand)) } Possible Output: @@ -594,14 +397,11 @@ Possible Output: 0.511 */ -@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) } +@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) } /* Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random single floating point value in the range `[0, 1)` @@ -610,11 +410,7 @@ Example: import "core:fmt" float32_example :: proc() { - // Using the global random number generator fmt.println(rand.float32()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float32(&my_rand)) } Possible Output: @@ -623,7 +419,7 @@ Possible Output: 0.511 */ -@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(int31_max(1<<24, r)) / (1 << 24) } +@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) } /* Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used. @@ -633,7 +429,6 @@ WARNING: Panics if `high < low` Inputs: - low: The lower bounds of the value, this value is inclusive - high: The upper bounds of the value, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random double floating point value in the range [low, high) @@ -643,11 +438,7 @@ Example: import "core:fmt" float64_range_example :: proc() { - // Using the global random number generator fmt.println(rand.float64_range(-10, 300)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float64_range(600, 900, &my_rand)) } Possible Output: @@ -656,9 +447,9 @@ Possible Output: 673.130 */ -@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) { +@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) { assert(low <= high, "low must be lower than or equal to high") - val = (high-low)*float64(r) + low + val = (high-low)*float64() + low if val >= high { val = max(low, high * (1 - math.F64_EPSILON)) } @@ -671,7 +462,6 @@ Generates a random single floating point value in the range `[low, high)` using Inputs: - low: The lower bounds of the value, this value is inclusive - high: The upper bounds of the value, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random single floating point value in the range [low, high) @@ -683,11 +473,7 @@ Example: import "core:fmt" float32_range_example :: proc() { - // Using the global random number generator fmt.println(rand.float32_range(-10, 300)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float32_range(600, 900, &my_rand)) } Possible Output: @@ -696,9 +482,9 @@ Possible Output: 673.130 */ -@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) { +@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) { assert(low <= high, "low must be lower than or equal to high") - val = (high-low)*float32(r) + low + val = (high-low)*float32() + low if val >= high { val = max(low, high * (1 - math.F32_EPSILON)) } @@ -711,7 +497,6 @@ Due to floating point precision there is no guarantee if the upper and lower bou Inputs: - p: The byte slice to fill -- r: The random number generator to use, or nil for the global generator Returns: - n: The number of bytes generated @@ -721,7 +506,6 @@ Example: import "core:fmt" read_example :: proc() { - // Using the global random number generator data: [8]byte n := rand.read(data[:]) fmt.println(n) @@ -735,12 +519,12 @@ Possible Output: */ @(require_results) -read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) { +read :: proc(p: []byte) -> (n: int) { pos := i8(0) val := i64(0) for n = 0; n < len(p); n += 1 { if pos == 0 { - val = int63(r) + val = int63() pos = 7 } p[n] = byte(val) @@ -757,7 +541,6 @@ Creates a slice of `int` filled with random values using the provided random num Inputs: - n: The size of the created slice -- r: The random number generator to use, or nil for the global generator - allocator: (default: context.allocator) Returns: @@ -770,16 +553,10 @@ Example: import "core:fmt" perm_example :: proc() -> (err: mem.Allocator_Error) { - // Using the global random number generator and using the context allocator data := rand.perm(4) or_return fmt.println(data) defer delete(data, context.allocator) - // Using local random number generator and temp allocator - my_rand := rand.create(1) - data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return - fmt.println(data_tmp) - return } @@ -790,10 +567,10 @@ Possible Output: */ @(require_results) -perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error { +perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error { m := make([]int, n, allocator) or_return for i := 0; i < n; i += 1 { - j := int_max(i+1, r) + j := int_max(i+1) m[i] = m[j] m[j] = i } @@ -805,14 +582,12 @@ Randomizes the ordering of elements for the provided slice. If no generator is p Inputs: - array: The slice to randomize -- r: The random number generator to use, or nil for the global generator Example: import "core:math/rand" import "core:fmt" shuffle_example :: proc() { - // Using the global random number generator data: [4]int = { 1, 2, 3, 4 } fmt.println(data) // the contents are in order rand.shuffle(data[:]) @@ -825,14 +600,14 @@ Possible Output: [2, 4, 3, 1] */ -shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) { +shuffle :: proc(array: $T/[]$E) { n := i64(len(array)) if n < 2 { return } for i := i64(n - 1); i > 0; i -= 1 { - j := int63_max(i + 1, r) + j := int63_max(i + 1) array[i], array[j] = array[j], array[i] } } @@ -842,7 +617,6 @@ Returns a random element from the provided slice. If no generator is provided th Inputs: - array: The slice to choose an element from -- r: The random number generator to use, or nil for the global generator Returns: - res: A random element from `array` @@ -852,7 +626,6 @@ Example: import "core:fmt" choice_example :: proc() { - // Using the global random number generator data: [4]int = { 1, 2, 3, 4 } fmt.println(rand.choice(data[:])) fmt.println(rand.choice(data[:])) @@ -869,17 +642,17 @@ Possible Output: */ @(require_results) -choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) { +choice :: proc(array: $T/[]$E) -> (res: E) { n := i64(len(array)) if n < 1 { return E{} } - return array[int63_max(n, r)] + return array[int63_max(n)] } @(require_results) -choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T +choice_enum :: proc($T: typeid) -> T where intrinsics.type_is_enum(T), size_of(T) <= 8, @@ -887,11 +660,11 @@ choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T { when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) && u64(max(T)) > u64(max(i64)) { - i := uint64(r) % u64(len(T)) + i := uint64() % u64(len(T)) i += u64(min(T)) return T(i) } else { - i := int63_max(i64(len(T)), r) + i := int63_max(i64(len(T))) i += i64(min(T)) return T(i) } diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index c332383e7..0255717a2 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -54,8 +54,9 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { // XXH3_128_update random_seed := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&random_seed) for len(b) > 0 { - update_size := min(len(b), rand.int_max(8192, &random_seed)) + update_size := min(len(b), rand.int_max(8192)) if update_size > 4096 { update_size %= 73 } diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 23de1b482..003021a8a 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -11,6 +11,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -21,7 +22,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64() } // Sort @@ -29,7 +30,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, t.seed) + rand.reset(t.seed) for v, i in f_idx { r_idx[v] = i @@ -45,7 +46,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { } } - idx_pass := vals[r_idx[i]] == rand.uint64(&r) + idx_pass := vals[r_idx[i]] == rand.uint64() testing.expect(t, idx_pass, "Expected index to have been sorted") if !idx_pass { break @@ -62,6 +63,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -72,7 +74,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64() } // Sort @@ -80,7 +82,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, t.seed) + rand.reset(t.seed) { indices := make([]int, test_size) diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index a9a8cf5d4..ab7e52f33 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -17,9 +17,10 @@ map_insert_random_key_value :: proc(t: ^testing.T) { unique_keys := 0 r := rand.create(t.seed + seed_incr) + context.random_generator = rand.default_random_generator(&r) for _ in 0..