mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
185 lines
3.8 KiB
Plaintext
185 lines
3.8 KiB
Plaintext
<*
|
|
Randoms:
|
|
General usage -
|
|
1. Create a Random (see std/math/random for various alternatives), or pick DefaultRandom
|
|
2. Define it : `DefaultRandom my_random;`
|
|
3. Seed it: `random::seed(&my_random, some_seed);` or : `rand::seed_entropy(&my_random);`
|
|
4. Use it with the functions in random: `float random_float = random::next_float(&my_random);`
|
|
|
|
For just a simple integer between 0 and n (not including n), you can use `rand(n)`.
|
|
*>
|
|
module std::math::random;
|
|
|
|
<*
|
|
@require is_random(random)
|
|
*>
|
|
macro void seed(random, seed)
|
|
{
|
|
random.set_seed(@as_char_view(seed));
|
|
}
|
|
|
|
<*
|
|
Seed the random with some best effort entropy.
|
|
|
|
@require is_random(random)
|
|
*>
|
|
macro void seed_entropy(random)
|
|
{
|
|
random.set_seed(&&entropy());
|
|
}
|
|
|
|
<*
|
|
Get the next value between 0 and range (not including range).
|
|
|
|
@require is_random(random)
|
|
@require range > 0
|
|
*>
|
|
macro int next(random, uint range)
|
|
{
|
|
if (range == 1) return 0;
|
|
uint mask = ~0U;
|
|
range--;
|
|
mask >>= range.clz();
|
|
uint x @noinit;
|
|
do
|
|
{
|
|
x = random.next_int() & mask;
|
|
}
|
|
while (x > range);
|
|
return x;
|
|
}
|
|
|
|
<*
|
|
Get a random in the range [min, max], both included.
|
|
|
|
@require is_random(random)
|
|
@require max >= min
|
|
*>
|
|
macro int next_in_range(random, int min, int max)
|
|
{
|
|
return next(random, max - min + 1) + min;
|
|
}
|
|
|
|
alias DefaultRandom = Sfc64Random;
|
|
|
|
tlocal Sfc64Random default_random @private;
|
|
tlocal bool default_random_initialized @private = false;
|
|
|
|
<*
|
|
Seed the default random function.
|
|
*>
|
|
fn void srand(ulong seed) @builtin
|
|
{
|
|
default_random.set_seed(@as_char_view(seed));
|
|
default_random_initialized = true;
|
|
}
|
|
|
|
<*
|
|
Get a default random value between 0 and range (not including range)
|
|
*>
|
|
fn int rand(int range) @builtin
|
|
{
|
|
init_default_random();
|
|
return next(&default_random, range);
|
|
}
|
|
|
|
<*
|
|
Get a random in the range, both included.
|
|
@require max >= min
|
|
*>
|
|
fn int rand_in_range(int min, int max) @builtin
|
|
{
|
|
init_default_random();
|
|
return next_in_range(&default_random, min, max);
|
|
}
|
|
|
|
fn double rnd() @builtin
|
|
{
|
|
init_default_random();
|
|
ulong val = default_random.next_long() & (1UL << 53 - 1);
|
|
return val * 0x1.0p-53;
|
|
}
|
|
|
|
<*
|
|
Get 'true' or 'false'
|
|
|
|
@require is_random(random)
|
|
*>
|
|
macro bool next_bool(random)
|
|
{
|
|
return (bool)(random.next_byte() & 1);
|
|
}
|
|
|
|
<*
|
|
Get a float between 0 and 1.0, not including 1.0.
|
|
|
|
@require is_random(random)
|
|
*>
|
|
macro float next_float(random)
|
|
{
|
|
uint val = random.next_int() & (1U << 24 - 1);
|
|
return val * 0x1.0p-24f;
|
|
}
|
|
|
|
<*
|
|
Get a double between 0 and 1.0, not including 1.0.
|
|
|
|
@require is_random(random)
|
|
*>
|
|
macro double next_double(random)
|
|
{
|
|
ulong val = random.next_long() & (1UL << 53 - 1);
|
|
return val * 0x1.0p-53;
|
|
}
|
|
|
|
// True if the value is a Random.
|
|
macro bool is_random(random) => @assignable_to(random, Random);
|
|
|
|
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
|
|
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
|
|
macro uint @short_to_int(#function) => (uint)#function << 16 + #function;
|
|
macro ushort @char_to_short(#function) => (ushort)#function << 8 + #function;
|
|
|
|
macro @random_value_to_bytes(#function, char[] bytes)
|
|
{
|
|
var $byte_size = $sizeof(#function());
|
|
usz len = bytes.len;
|
|
// Same size or smaller? Then just copy.
|
|
while (len > 0)
|
|
{
|
|
var value = #function();
|
|
if (len <= $byte_size)
|
|
{
|
|
bytes[..] = ((char*)&value)[:len];
|
|
return;
|
|
}
|
|
bytes[:$byte_size] = ((char*)&value)[:$byte_size];
|
|
len -= $byte_size;
|
|
bytes = bytes[$byte_size..];
|
|
}
|
|
unreachable();
|
|
}
|
|
|
|
// This is the interface to implement for Random implementations, usually
|
|
// it is not invoked directly.
|
|
interface Random
|
|
{
|
|
fn void set_seed(char[] input);
|
|
fn char next_byte();
|
|
fn ushort next_short();
|
|
fn uint next_int();
|
|
fn ulong next_long();
|
|
fn uint128 next_int128();
|
|
fn void next_bytes(char[] buffer);
|
|
}
|
|
|
|
|
|
macro init_default_random() @private
|
|
{
|
|
if (!default_random_initialized)
|
|
{
|
|
seed_entropy(&default_random);
|
|
default_random_initialized = true;
|
|
}
|
|
}
|