Files
Odin/core/math/rand/rand.odin
2025-12-22 16:10:51 -05:00

1213 lines
27 KiB
Odin

// Random number generators.
package rand
import "base:intrinsics"
import "base:runtime"
import "core:math"
import "core:mem"
Generator :: runtime.Random_Generator
Generator_Query_Info :: runtime.Random_Generator_Query_Info
Default_Random_State :: runtime.Default_Random_State
/*
Returns an instance of the runtime pseudorandom generator. If no
initial state is provided, the PRNG will be lazily initialized with
entropy from the system entropy source on first-use.
The cryptographic security of the returned random number generator
is directly dependent on the quality of the initialization entropy.
Calling `reset`/`create` SHOULD be done with no seed/state, or
32-bytes of high-quality entropy.
WARNING:
- The lazy initialization will panic if there is no system entropy
source available.
- While the generator is cryptographically secure, developers SHOULD
prefer `crypto.random_generator()` for cryptographic use cases such
as key generation.
Inputs:
- state: Optional initial PRNG state.
Returns:
- A `Generator` instance.
*/
default_random_generator :: runtime.default_random_generator
@(require_results)
create_u64 :: proc(seed: u64) -> (state: Default_Random_State) {
seed := seed
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
return
}
@(require_results)
create_bytes :: proc(seed: []byte) -> (state: Default_Random_State) {
runtime.default_random_generator_proc(&state, .Reset, seed)
return
}
create :: proc {
create_u64,
create_bytes,
}
/*
Reset the seed used by the context.random_generator.
Inputs:
- seed: The seed value
Example:
import "core:math/rand"
import "core:fmt"
reset_example :: proc() {
rand.reset(1)
fmt.println(rand.uint64())
}
Possible Output:
10
*/
reset :: proc {
reset_u64,
reset_bytes,
}
reset_u64 :: proc(seed: u64, gen := context.random_generator) {
runtime.random_generator_reset_u64(gen, seed)
}
reset_bytes :: proc(bytes: []byte, gen := context.random_generator) {
runtime.random_generator_reset_bytes(gen, bytes)
}
query_info :: proc(gen := context.random_generator) -> Generator_Query_Info {
return runtime.random_generator_query_info(gen)
}
/*
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.
Returns:
- val: A random unsigned 32 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint32_example :: proc() {
fmt.println(rand.uint32())
}
Possible Output:
10
389
*/
@(require_results)
uint32 :: proc(gen := context.random_generator) -> (val: u32) {return u32(uint64(gen))}
/*
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.
Returns:
- val: A random unsigned 64 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint64_example :: proc() {
fmt.println(rand.uint64())
}
Possible Output:
10
389
*/
@(require_results)
uint64 :: proc(gen := context.random_generator) -> (val: u64) {
ok := runtime.random_generator_read_ptr(gen, &val, size_of(val))
assert(ok, "uninitialized gen/context.random_generator")
return
}
/*
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.
Returns:
- val: A random unsigned 128 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint128_example :: proc() {
fmt.println(rand.uint128())
}
Possible Output:
10
389
*/
@(require_results)
uint128 :: proc(gen := context.random_generator) -> (val: u128) {
a := u128(uint64(gen))
b := u128(uint64(gen))
return (a<<64) | b
}
/*
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.
Returns:
- val: A random 31 bit value
Example:
import "core:math/rand"
import "core:fmt"
int31_example :: proc() {
fmt.println(rand.int31())
}
Possible Output:
10
389
*/
@(require_results) int31 :: proc(gen := context.random_generator) -> (val: i32) { return i32(uint32(gen) << 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.
Returns:
- val: A random 63 bit value
Example:
import "core:math/rand"
import "core:fmt"
int63_example :: proc() {
fmt.println(rand.int63())
}
Possible Output:
10
389
*/
@(require_results) int63 :: proc(gen := context.random_generator) -> (val: i64) { return i64(uint64(gen) << 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.
Returns:
- val: A random 127 bit value
Example:
import "core:math/rand"
import "core:fmt"
int127_example :: proc() {
fmt.println(rand.int127())
}
Possible Output:
10
389
*/
@(require_results) int127 :: proc(gen := context.random_generator) -> (val: i128) { return i128(uint128(gen) << 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
Returns:
- val: A random 31 bit value in the range `[0, n)`
WARNING: Panics if n is less than or equal to 0
Example:
import "core:math/rand"
import "core:fmt"
int31_max_example :: proc() {
fmt.println(rand.int31_max(16))
}
Possible Output:
6
13
*/
@(require_results)
int31_max :: proc(n: i32, gen := context.random_generator) -> (val: i32) {
if n <= 0 {
panic("Invalid argument to int31_max")
}
if n&(n-1) == 0 {
return int31(gen) & (n-1)
}
max := i32((1<<31) - 1 - (1<<31)%u32(n))
v := int31(gen)
for v > max {
v = int31(gen)
}
return v % n
}
/*
Generates a random 63 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
Returns:
- val: A random 63 bit value in the range `[0, n)`
WARNING: Panics if n is less than or equal to 0
Example:
import "core:math/rand"
import "core:fmt"
int63_max_example :: proc() {
fmt.println(rand.int63_max(16))
}
Possible Output:
6
13
*/
@(require_results)
int63_max :: proc(n: i64, gen := context.random_generator) -> (val: i64) {
if n <= 0 {
panic("Invalid argument to int63_max")
}
if n&(n-1) == 0 {
return int63(gen) & (n-1)
}
max := i64((1<<63) - 1 - (1<<63)%u64(n))
v := int63(gen)
for v > max {
v = int63(gen)
}
return v % n
}
/*
Generates a random 127 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
Returns:
- val: A random 127 bit value in the range `[0, n)`
WARNING: Panics if n is less than or equal to 0
Example:
import "core:math/rand"
import "core:fmt"
int127_max_example :: proc() {
fmt.println(rand.int127_max(16))
}
Possible Output:
6
13
*/
@(require_results)
int127_max :: proc(n: i128, gen := context.random_generator) -> (val: i128) {
if n <= 0 {
panic("Invalid argument to int127_max")
}
if n&(n-1) == 0 {
return int127(gen) & (n-1)
}
max := i128((1<<127) - 1 - (1<<127)%u128(n))
v := int127(gen)
for v > max {
v = int127(gen)
}
return v % n
}
/*
Generates a random integer 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
Returns:
- val: A random integer value in the range `[0, n)`
WARNING: Panics if n is less than or equal to 0
Example:
import "core:math/rand"
import "core:fmt"
int_max_example :: proc() {
fmt.println(rand.int_max(16))
}
Possible Output:
6
13
*/
@(require_results)
int_max :: proc(n: int, gen := context.random_generator) -> (val: int) {
if n <= 0 {
panic("Invalid argument to int_max")
}
when size_of(int) == 4 {
return int(int31_max(i32(n), gen))
} else {
return int(int63_max(i64(n), gen))
}
}
/*
Generates a random 32 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
Returns:
- val: A random 32 bit value in the range `[0, n)`
WARNING: Panics if n is equal to 0
Example:
import "core:math/rand"
import "core:fmt"
uint32_max_example :: proc() {
fmt.println(rand.uint32_max(16))
}
Possible Output:
6
13
*/
@(require_results)
uint32_max :: proc(n: u32, gen := context.random_generator) -> (val: u32) {
if n == 0 {
panic("Invalid argument to uint32_max")
}
if n & (n - 1) == 0 {
return uint32(gen) & (n - 1)
}
min := (-n) % n
v := uint32(gen)
for v < min {
v = uint32(gen)
}
return v % n
}
/*
Generates a random 64 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
Returns:
- val: A random 64 bit value in the range `[0, n)`
WARNING: Panics if n is equal to 0
Example:
import "core:math/rand"
import "core:fmt"
uint64_max_example :: proc() {
fmt.println(rand.uint64_max(16))
}
Possible Output:
6
13
*/
@(require_results)
uint64_max :: proc(n: u64, gen := context.random_generator) -> (val: u64) {
if n == 0 {
panic("Invalid argument to uint64_max")
}
if n & (n - 1) == 0 {
return uint64(gen) & (n - 1)
}
min := (-n) % n
v := uint64(gen)
for v < min {
v = uint64(gen)
}
return v % n
}
/*
Generates a random 128 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
Returns:
- val: A random 128 bit value in the range `[0, n)`
WARNING: Panics if n is equal to 0
Example:
import "core:math/rand"
import "core:fmt"
uint128_max_example :: proc() {
fmt.println(rand.uint128_max(16))
}
Possible Output:
6
13
*/
@(require_results)
uint128_max :: proc(n: u128, gen := context.random_generator) -> (val: u128) {
if n == 0 {
panic("Invalid argument to uint128_max")
}
if n & (n - 1) == 0 {
return uint128(gen) & (n - 1)
}
min := (-n) % n
v := uint128(gen)
for v < min {
v = uint128(gen)
}
return v % n
}
/*
Generates a random integer 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
Returns:
- val: A random integer value in the range `[0, n)`
WARNING: Panics if n is equal to 0
Example:
import "core:math/rand"
import "core:fmt"
uint_max_example :: proc() {
fmt.println(rand.uint_max(16))
}
Possible Output:
6
13
*/
@(require_results)
uint_max :: proc(n: uint, gen := context.random_generator) -> (val: uint) {
if n <= 0 {
panic("Invalid argument to uint_max")
}
when size_of(int) == 4 {
return uint(uint32_max(u32(n), gen))
} else {
return uint(uint64_max(u64(n), gen))
}
}
/*
Generates a random unsigned 32 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 32 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
uint32_range_example :: proc() {
fmt.println(rand.uint32_range(5,15))
}
Possible Output:
6
13
*/
uint32_range :: proc(lo, hi: u32, gen := context.random_generator) -> (val: u32) {
assert(lo < hi, "Invalid arguments to uint32_range: lo must be less than hi")
range := hi - lo
if (range & (range - 1)) == 0 {
return lo + (uint32(gen) & (range - 1))
}
threshold := -range % range
v := uint32(gen)
for v < threshold {
v = uint32(gen)
}
return lo + (v % range)
}
/*
Generates a random unsigned 64 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 64 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
uint64_range_example :: proc() {
fmt.println(rand.uint64_range(5,15))
}
Possible Output:
6
13
*/
uint64_range :: proc(lo, hi: u64, gen := context.random_generator) -> (val: u64) {
assert(lo < hi, "Invalid arguments to uint64_range: lo must be less than hi")
range := hi - lo
if (range & (range - 1)) == 0 {
return lo + (uint64(gen) & (range - 1))
}
threshold := -range % range
v := uint64(gen)
for v < threshold {
v = uint64(gen)
}
return lo + (v % range)
}
/*
Generates a random unsigned 128 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 128 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
uint128_range_example :: proc() {
fmt.println(rand.uint128_range(5,15))
}
Possible Output:
6
13
*/
uint128_range :: proc(lo, hi: u128, gen := context.random_generator) -> (val: u128) {
assert(lo < hi, "Invalid arguments to uint128_range: lo must be less than hi")
range := hi - lo
if (range & (range - 1)) == 0 {
return lo + (uint128(gen) & (range - 1))
}
threshold := -range % range
v := uint128(gen)
for v < threshold {
v = uint128(gen)
}
return lo + (v % range)
}
/*
Generates a random unsigned integer value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random integer value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
uint_range_example :: proc() {
fmt.println(rand.uint_range(5,15))
}
Possible Output:
6
13
*/
@(require_results)
uint_range :: proc(lo, hi: uint, gen := context.random_generator) -> (val: uint) {
assert(lo < hi, "Invalid arguments to uint_range: lo must be less than hi")
when size_of(int) == 4 {
return uint(uint32_range(u32(lo), u32(hi), gen))
} else {
return uint(uint64_range(u64(lo), u64(hi), gen))
}
}
/*
Generates a random signed 32 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 32 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
int32_range_example :: proc() {
fmt.println(rand.int32_range(-10,10))
}
Possible Output:
6
-9
*/
int32_range :: proc(lo, hi: i32, gen := context.random_generator) -> (val: i32) {
assert(lo < hi, "Invalid arguments to int32_range: lo must be less than hi")
range := u32(hi) - u32(lo)
if (range & (range - 1)) == 0 {
return lo + i32(uint32(gen) & (range - 1))
}
threshold := -range % range
v := uint32(gen)
for v < threshold {
v = uint32(gen)
}
return lo + i32(v % range)
}
/*
Generates a random signed 64 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 64 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
int64_range_example :: proc() {
fmt.println(rand.int64_range(-10,10))
}
Possible Output:
6
-9
*/
int64_range :: proc(lo, hi: i64, gen := context.random_generator) -> (val: i64) {
assert(lo < hi, "Invalid arguments to int64_range: lo must be less than hi")
range := u64(hi) - u64(lo)
if (range & (range - 1)) == 0 {
return lo + i64(uint64(gen) & (range - 1))
}
threshold := -range % range
v := uint64(gen)
for v < threshold {
v = uint64(gen)
}
return lo + i64(v % range)
}
/*
Generates a random signed 128 bit value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random 128 bit value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
int128_range_example :: proc() {
fmt.println(rand.int128_range(-10,10))
}
Possible Output:
6
-9
*/
int128_range :: proc(lo, hi: i128, gen := context.random_generator) -> (val: i128) {
assert(lo < hi, "Invalid arguments to int128_range: lo must be less than hi")
range := u128(hi) - u128(lo)
if (range & (range - 1)) == 0 {
return lo + i128(uint128(gen) & (range - 1))
}
threshold := -range % range
v := uint128(gen)
for v < threshold {
v = uint128(gen)
}
return lo + i128(v % range)
}
/*
Generates a random signed integer value in the range `[lo, hi)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- lo: The lower bound of the generated number, this value is inclusice
- hi: The upper bound of the generated number, this value is exclusive
Returns:
- val: A random integer value in the range `[lo, hi)`
WARNING: Panics if `lo` is greater or equal to `hi`
Example:
import "core:math/rand"
import "core:fmt"
int_range_example :: proc() {
fmt.println(rand.int_range(-10,10))
}
Possible Output:
6
-9
*/
@(require_results)
int_range :: proc(lo, hi: int, gen := context.random_generator) -> (val: int) {
assert(lo < hi, "Invalid arguments to int_range: lo must be less than hi")
when size_of(int) == 4 {
return int(int32_range(i32(lo), i32(hi), gen))
} else {
return int(int64_range(i64(lo), i64(hi), gen))
}
}
/*
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.
Returns:
- val: A random double floating point value in the range `[0, 1)`
Example:
import "core:math/rand"
import "core:fmt"
float64_example :: proc() {
fmt.println(rand.float64())
}
Possible Output:
0.043
0.511
*/
@(require_results) float64 :: proc(gen := context.random_generator) -> (val: f64) { return f64(int63_max(1<<53, gen)) / (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.
Returns:
- val: A random single floating point value in the range `[0, 1)`
Example:
import "core:math/rand"
import "core:fmt"
float32_example :: proc() {
fmt.println(rand.float32())
}
Possible Output:
0.043
0.511
*/
@(require_results) float32 :: proc(gen := context.random_generator) -> (val: f32) { return f32(int31_max(1<<24, gen)) / (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.
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
Returns:
- val: A random double floating point value in the range [low, high)
Example:
import "core:math/rand"
import "core:fmt"
float64_range_example :: proc() {
fmt.println(rand.float64_range(-10, 300))
}
Possible Output:
15.312
273.15
*/
@(require_results) float64_range :: proc(low, high: f64, gen := context.random_generator) -> (val: f64) {
assert(low <= high, "low must be lower than or equal to high")
val = (high-low)*float64(gen) + low
if val >= high {
val = max(low, high * (1 - math.F64_EPSILON))
}
return
}
/*
Generates a random single 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.
Inputs:
- low: The lower bounds of the value, this value is inclusive
- high: The upper bounds of the value, this value is exclusive
Returns:
- val: A random single floating point value in the range [low, high)
WARNING: Panics if `high < low`
Example:
import "core:math/rand"
import "core:fmt"
float32_range_example :: proc() {
fmt.println(rand.float32_range(-10, 300))
}
Possible Output:
15.312
273.15
*/
@(require_results) float32_range :: proc(low, high: f32, gen := context.random_generator) -> (val: f32) {
assert(low <= high, "low must be lower than or equal to high")
val = (high-low)*float32(gen) + low
if val >= high {
val = max(low, high * (1 - math.F32_EPSILON))
}
return
}
/*
Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
Due to floating point precision there is no guarantee if the upper and lower bounds are inclusive/exclusive with the exact floating point value.
Inputs:
- p: The byte slice to fill
Returns:
- n: The number of bytes generated
Example:
import "core:math/rand"
import "core:fmt"
read_example :: proc() {
data: [8]byte
n := rand.read(data[:])
fmt.println(n)
fmt.println(data)
}
Possible Output:
8
[32, 4, 59, 7, 1, 2, 2, 119]
*/
@(require_results)
read :: proc(p: []byte, gen := context.random_generator) -> (n: int) {
if !runtime.random_generator_read_bytes(gen, p) {return 0}
return len(p)
}
/*
Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
*Allocates Using Provided Allocator*
Inputs:
- n: The size of the created slice
- allocator: (default: context.allocator)
Returns:
- res: A slice filled with random values
- err: An allocator error if one occured, `nil` otherwise
Example:
import "core:math/rand"
import "core:mem"
import "core:fmt"
perm_example :: proc() -> (err: mem.Allocator_Error) {
data := rand.perm(4) or_return
fmt.println(data)
defer delete(data, context.allocator)
return
}
Possible Output:
[7201011, 3, 9123, 231131]
[19578, 910081, 131, 7]
*/
@(require_results)
perm :: proc(n: int, allocator := context.allocator, gen := context.random_generator) -> (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, gen)
m[i] = m[j]
m[j] = i
}
return m, {}
}
/*
Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to randomize
Example:
import "core:math/rand"
import "core:fmt"
shuffle_example :: proc() {
data: [4]int = { 1, 2, 3, 4 }
fmt.println(data) // the contents are in order
rand.shuffle(data[:])
fmt.println(data) // the contents have been shuffled
}
Possible Output:
[1, 2, 3, 4]
[2, 4, 3, 1]
*/
shuffle :: proc(array: $T/[]$E, gen := context.random_generator) {
n := i64(len(array))
if n < 2 {
return
}
i := n - 1
for ; i > (1<<31 - 2); i -= 1 {
j := int63_max(i + 1, gen)
array[i], array[j] = array[j], array[i]
}
for ; i > 0; i -= 1 {
j := int31_max(i32(i + 1), gen)
array[i], array[j] = array[j], array[i]
}
}
/*
Returns a random element from the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to choose an element from
Returns:
- res: A random element from `array`
Example:
import "core:math/rand"
import "core:fmt"
choice_example :: proc() {
data: [4]int = { 1, 2, 3, 4 }
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
}
Possible Output:
3
2
2
4
*/
@(require_results)
choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
n := i64(len(array))
if n < 1 {
return E{}
}
return array[int63_max(n, gen)]
}
@(require_results)
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) {
when size_of(T) <= 8 && len(T) == cap(T) {
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
u64(max(T)) > u64(max(i64)) {
i := uint64(gen) % u64(len(T))
i += u64(min(T))
return T(i)
} else {
i := int63_max(i64(len(T)), gen)
i += i64(min(T))
return T(i)
}
} else {
values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values
return T(choice(values))
}
}
/*
Returns a random *set* bit from the provided `bit_set`.
Inputs:
- set: The `bit_set` to choose a random set bit from
Returns:
- res: The randomly selected bit, or the zero value if `ok` is `false`
- ok: Whether the bit_set was not empty and thus `res` is actually a random set bit
Example:
import "core:math/rand"
import "core:fmt"
choice_bit_set_example :: proc() {
Flags :: enum {
A,
B = 10,
C,
}
fmt.println(rand.choice_bit_set(bit_set[Flags]{}))
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B}))
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C}))
fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4}))
}
Possible Output:
A false
B true
C true
5 true
*/
@(require_results)
choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) {
total_set := card(set)
if total_set == 0 {
return {}, false
}
target := int_max(total_set, gen)
for value in set {
if target == 0 {
return value, true
}
target -= 1
}
return {}, false
}