diff --git a/lib/std/math/math_random.c3 b/lib/std/math/math_random.c3 index 0b2eaa9fc..f5d2c4d4d 100644 --- a/lib/std/math/math_random.c3 +++ b/lib/std/math/math_random.c3 @@ -1,18 +1,15 @@ +/** + * 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; -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 bool is_random(random) => $assignable(random, Random*); - /** * @require is_random(random) **/ @@ -21,12 +18,19 @@ 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 max (not including max). + * * @require is_random(random) **/ macro int next(random, int max) @@ -34,6 +38,11 @@ macro int next(random, int max) return (int)(next_double(random) * max); } +def DefaultRandom = Sfc64Random; + +/** + * Get a default random value between 0 and max (not including max) + **/ fn int rand(int max) @builtin { tlocal Sfc64Random default_random; @@ -45,15 +54,20 @@ fn int rand(int max) @builtin } return next(&default_random, max); } + /** + * Get 'true' or 'false' + * * @require is_random(random) **/ macro bool next_bool(random) { - return random.next_byte() & 1; + 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) @@ -63,6 +77,8 @@ macro float next_float(random) } /** + * Get a double between 0 and 1.0, not including 1.0. + * * @require is_random(random) **/ macro double next_double(random) @@ -71,6 +87,9 @@ macro double next_double(random) return val * 0x1.0p-53; } +// True if the value is a Random. +macro bool is_random(random) => $assignable(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; @@ -95,3 +114,16 @@ macro @random_value_to_bytes(#function, char[] bytes) } 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); +} diff --git a/releasenotes.md b/releasenotes.md index 82e5d52e3..fd9dbd7e4 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -13,6 +13,7 @@ ### Fixes - Broken WASM library code. +- Regression: Invalid is_random implementation due to changes in 0.6. ### Stdlib changes None diff --git a/test/unit/stdlib/math/random.c3 b/test/unit/stdlib/math/random.c3 new file mode 100644 index 000000000..884ec04e0 --- /dev/null +++ b/test/unit/stdlib/math/random.c3 @@ -0,0 +1,35 @@ +module std::math::random @test; +import std::math; + +fn void test_regular_random() +{ + for (int i = 0; i < 100; i++) assert(rand(45) < 45 && rand(45) >= 0); +} + +fn void test_next_bool() +{ + DefaultRandom rand; + random::seed_entropy(&rand); + for (int i = 0; i < 100; i++) random::next_bool(&rand); +} + +fn void test_next() +{ + DefaultRandom rand; + random::seed_entropy(&rand); + for (int i = 0; i < 100; i++) assert(random::next(&rand, 100) < 100.0 && random::next(&rand, 100) >= 0); +} + +fn void test_next_double() +{ + DefaultRandom rand; + random::seed_entropy(&rand); + for (int i = 0; i < 100; i++) assert(random::next_double(&rand) < 1.0 && random::next_double(&rand) >= 0); +} + +fn void test_next_float() +{ + DefaultRandom rand; + random::seed_entropy(&rand); + for (int i = 0; i < 100; i++) assert(random::next_float(&rand) < 1.0 && random::next_float(&rand) >= 0); +} \ No newline at end of file