From bea9ac010c7cbbfaab21a5d9e3c3144ebd4a69a6 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 26 Aug 2023 12:58:57 +0200 Subject: [PATCH] Updated random interface. --- lib/std/core/builtin.c3 | 5 + lib/std/math/math.random.c3 | 136 ++++++----- lib/std/math/random/math.lcg.c3 | 102 ++++++-- lib/std/math/random/math.mcg.c3 | 108 +++++++-- lib/std/math/random/math.msws.c3 | 271 +++++++++++++++++----- lib/std/math/random/math.pcg.c3 | 164 +++++++++++-- lib/std/math/random/math.seeder.c3 | 85 ++++--- lib/std/math/random/math.sfc.c3 | 202 +++++++++++++--- lib/std/math/random/math.simple_random.c3 | 53 +++-- 9 files changed, 849 insertions(+), 277 deletions(-) diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 99273abec..2b2e1280c 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -297,6 +297,11 @@ macro bool @ok(#expr) @builtin return true; } +macro char[] @to_byte_view(&value) @builtin +{ + return ((char*)value)[:$sizeof(*value)]; +} + macro uint int.hash(int i) => i; macro uint uint.hash(uint i) => i; macro uint short.hash(short s) => s; diff --git a/lib/std/math/math.random.c3 b/lib/std/math/math.random.c3 index 5de44cd5c..4dca68db8 100644 --- a/lib/std/math/math.random.c3 +++ b/lib/std/math/math.random.c3 @@ -2,23 +2,16 @@ module std::math; struct Random { - RandomInterface fns; - void* state; + RandomInterface* fns; } -fn uint any.next_random(void*, int bits) @interface; -fn void any.next_random_bytes(void*, char[] buffer) @interface; -fn void any.set_random_seed(void*, char[] seed) @interface; - def RandomSeedFn = fn void(Random*, char[] seed); def RandomNextBytesFn = fn void(Random*, char[] buffer); -def RandomNextFn = fn uint(Random*, int bits); struct RandomInterface { RandomSeedFn seed_fn; RandomNextBytesFn next_bytes_fn; - RandomNextFn next_fn; } /** @@ -28,66 +21,65 @@ struct RandomInterface fn void Random.next_bytes(&random, char[] buffer) { if (!buffer.len) return; - if (RandomNextBytesFn func = random.fns.next_bytes_fn) - { - func(random, buffer); - return; - } - if (RandomNextFn func = random.fns.next_fn) - { - usz current = 0; - while (current < buffer.len) - { - char[4] res = bitcast(func(random, 32), char[4]); - foreach (c : res) - { - buffer[current++] = c; - if (current == buffer.len) return; - } - } - return; - } - unreachable("Invalid Random type."); + random.fns.next_bytes_fn(random, buffer); } /** - * @param [&inout] random - * @require bits >= 0 && bits <= 32 + * @param [&inout] self **/ -fn uint Random.next(&random, int bits) +fn char Random.next_byte(&self) { - if (bits == 0) return 0; - if (RandomNextFn func = random.fns.next_fn) return func(random, bits); - int bytes = (bits + 7) / 8; - char[4] buffer; - char[] b = buffer[:bytes]; - assert(random.fns.next_bytes_fn, "Invalid Random"); - random.fns.next_bytes_fn(random, b) @inline; - uint next = 0; - foreach (char c : b) - { - next = next << 8 + c; - } - if (bits < 32) next >>= bytes * 8 - bits; - return next; + char result @noinit; + self.next_bytes(((char*)&result)[:1]); + return result; } /** - * @param [&inout] random + * @param [&inout] self **/ -fn ulong Random.next_long(&random) +fn uint Random.next_short(&self) { - char[8] buffer; - random.next_bytes(&buffer); - return bitcast(buffer, ulong); + ushort result @noinit; + self.next_bytes(((char*)&result)[:2]); + return result; } /** - * @param [&inout] random + * @param [&inout] self **/ -fn void Random.set_seed(&random, long seed) +fn uint Random.next_int(&self) { - random.fns.seed_fn(random, &&bitcast(seed, char[8])) @inline; + uint result @noinit; + self.next_bytes(((char*)&result)[:4]); + return result; +} + +/** + * @param [&inout] self + **/ +fn ulong Random.next_long(&self) +{ + ulong result @noinit; + self.next_bytes(((char*)&result)[:8]); + return result; +} + +/** + * @param [&inout] self + **/ +fn uint128 Random.next_uint128(&self) +{ + uint128 result @noinit; + self.next_bytes(((char*)&result)[:16]); + return result; +} + +/** + * @param [&inout] self + **/ +fn void Random.set_seed(&self, long seed) +{ + self.fns.seed_fn(self, &&bitcast(seed, char[8])) @inline; } /** @@ -100,24 +92,42 @@ fn void Random.set_seeds(&random, char[] seed) } /** - * @param [&inout] random + * @param [&inout] self **/ -fn bool Random.next_bool(&random) +fn bool Random.next_bool(&self) { - return random.next(1) != 0; + return self.next_byte() & 1 == 0; } -fn float Random.next_float(&r) +fn float Random.next_float(&self) { - return r.next(24) / (float)(1 << 24); + uint val = self.next_int() & (1 << 24 - 1); + return val / (float)(1 << 24); } -fn double Random.next_double(&r) + +fn double Random.next_double(&self) { - return (((long)(r.next(26)) << 27) + r.next(27)) * 0x1.0p-53; + ulong val = self.next_long() & (1UL << 53 - 1); + return val * 0x1.0p-53; } -fn uint Random.next_int(&r) +macro @random_value_to_bytes(#function, char[] bytes) { - return r.next(32) @inline; -} + 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(); +} \ No newline at end of file diff --git a/lib/std/math/random/math.lcg.c3 b/lib/std/math/random/math.lcg.c3 index f8ee59449..f3ea05bb4 100644 --- a/lib/std/math/random/math.lcg.c3 +++ b/lib/std/math/random/math.lcg.c3 @@ -14,16 +14,45 @@ const MUL_LCG16 @local = 0x915d; // TODO: Find good constant // -------------------------------- Lcg128_64 -------------------------------- -def Lcg128_64 = distinct uint128; - -fn void Lcg128_64.seed(&lcg, char[16] seed) +struct Lcg128Random { - *lcg = bitcast(seed, Lcg128_64); + inline Random random; + Lcg128RandomState state; } -fn ulong Lcg128_64.next(&lcg) +def Lcg128RandomState = distinct uint128; + +const RandomInterface LCG_128_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Lcg128Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Lcg128Random.next_bytes, +}; + +fn void Lcg128Random.init(&self) { - uint128* s = (uint128*)lcg; + self.random.fns = &LCG_128_RANDOM_INTERFACE; +} + +fn void Lcg128Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(uint128, input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Lcg128Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_long, bytes); +} + +fn void Lcg128RandomState.set_seed(&self, uint128 seed) @inline +{ + *self = (Lcg128RandomState)seed; +} + +fn ulong Lcg128RandomState.next_long(&self) +{ + uint128* s = (uint128*)self; ulong result = (ulong)(*s >> 64); *s = *s * MUL_LCG128 + ODD_PHI128; return result; @@ -32,16 +61,45 @@ fn ulong Lcg128_64.next(&lcg) // -------------------------------- Lcg64_32 -------------------------------- -def Lcg64_32 = distinct ulong; - -fn void Lcg64_32.seed(&lcg, char[8] seed) +struct Lcg64Random { - *lcg = bitcast(seed, Lcg64_32); + inline Random random; + Lcg64RandomState state; } -fn uint Lcg64_32.next(&lcg) +def Lcg64RandomState = distinct ulong; + +const RandomInterface LCG_64_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Lcg64Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Lcg64Random.next_bytes, +}; + +fn void Lcg64Random.init(&self) { - ulong* s = (ulong*)lcg; + self.random.fns = &LCG_64_RANDOM_INTERFACE; +} + +fn void Lcg64Random.set_seed(&self, char[] seed) +{ + self.state.set_seed(random::make_seed(ulong, seed)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Lcg64Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int, bytes); +} + +fn void Lcg64RandomState.set_seed(&self, ulong seed) +{ + *self = (Lcg64RandomState)seed; +} + +fn uint Lcg64RandomState.next_int(&self) +{ + ulong* s = (ulong*)self; uint result = (uint)(*s >> 32); *s = *s * MUL_LCG64 + ODD_PHI64; return result; @@ -50,16 +108,16 @@ fn uint Lcg64_32.next(&lcg) // -------------------------------- Lcg32_16 -------------------------------- -def Lcg32_16 = distinct uint; +def Lcg32RandomState = distinct uint; -fn void Lcg32_16.seed(&lcg, char[4] seed) +fn void Lcg32RandomState.set_seed(&self, uint seed) { - *lcg = bitcast(seed, Lcg32_16); + *self = (Lcg32RandomState)seed; } -fn ushort Lcg32_16.next(&lcg) +fn ushort Lcg32RandomState.next_short(&self) { - uint* s = (uint*)lcg; + uint* s = (uint*)self; ushort result = (ushort)(*s >> 16); *s = *s * MUL_LCG32 + ODD_PHI32; return result; @@ -68,16 +126,16 @@ fn ushort Lcg32_16.next(&lcg) // -------------------------------- Lcg16_8 -------------------------------- -def Lcg16_8 = distinct ushort; +def Lcg16RandomState = distinct ushort; -fn void Lcg16_8.seed(&lcg, char[2] seed) +fn void Lcg16RandomState.set_seed(&self, ushort seed) { - *lcg = bitcast(seed, Lcg16_8); + *self = (Lcg16RandomState)seed; } -fn char Lcg16_8.next(&lcg) +fn char Lcg16RandomState.next_byte(&self) { - ushort* s = (ushort*)lcg; + ushort* s = (ushort*)self; char result = (char)(*s >> 8); *s = *s * MUL_LCG16 + ODD_PHI16; return result; diff --git a/lib/std/math/random/math.mcg.c3 b/lib/std/math/random/math.mcg.c3 index 26f786247..d72ff8e22 100644 --- a/lib/std/math/random/math.mcg.c3 +++ b/lib/std/math/random/math.mcg.c3 @@ -7,70 +7,130 @@ const MUL_MCG16 @local = 0x93d5; // TODO: Find good constant // -------------------------------- Mcg128_64 -------------------------------- -def Mcg128_64 = distinct uint128; - -fn void Mcg128_64.seed(&mcg, char[16] seed) +struct Mcg128Random { - *mcg = bitcast(seed, Mcg128_64) | 1; + inline Random random; + Mcg128RandomState state; } -fn ulong Mcg128_64.next(&mcg) +def Mcg128RandomState = distinct uint128; + +const RandomInterface MCG_128_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Mcg128Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Mcg128Random.next_bytes, +}; + +fn void Mcg128Random.init(&self) { - uint128* s = (uint128*)mcg; + self.random.fns = &MCG_64_RANDOM_INTERFACE; +} + +fn void Mcg128Random.set_seed(&self, char[] seed) +{ + self.state.set_seed(random::make_seed(uint128, seed)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Mcg128Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_long, bytes); +} + +fn void Mcg128RandomState.set_seed(&self, uint128 seed) +{ + *self = (Mcg128RandomState)seed | 1; +} + +fn ulong Mcg128RandomState.next_long(&self) +{ + uint128* s = (uint128*)self; ulong result = (ulong)(*s >> 64); *s *= MUL_MCG128; return result; } -// -------------------------------- Mcg64_32 -------------------------------- -def Mcg64_32 = distinct ulong; -fn void Mcg64_32.seed(&mcg, char[8] seed) +// -------------------------------- Mcg64RandomState -------------------------------- + +struct Mcg64Random { - *mcg = bitcast(seed, Mcg64_32) | 1; + inline Random random; + Mcg64RandomState state; } -fn uint Mcg64_32.next(&mcg) +const RandomInterface MCG_64_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Mcg64Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Mcg64Random.next_bytes, +}; + +def Mcg64RandomState = distinct ulong; + +fn void Mcg64Random.init(&self) { - ulong* s = (ulong*)mcg; + self.random.fns = &MCG_64_RANDOM_INTERFACE; +} + +fn void Mcg64Random.set_seed(&self, char[] seed) +{ + self.state.set_seed(random::make_seed(ulong, seed)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Mcg64Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int, bytes); +} + +fn void Mcg64RandomState.set_seed(&self, ulong seed) +{ + *self = (Mcg64RandomState)self | 1; +} + +fn uint Mcg64RandomState.next_int(&self) +{ + ulong* s = (ulong*)self; uint result = (uint)(*s >> 32); *s *= MUL_MCG64; return result; } -// -------------------------------- Mcg32_16 -------------------------------- +// -------------------------------- Mcg32RandomState -------------------------------- -def Mcg32_16 = distinct uint; +def Mcg32RandomState = distinct uint; -fn void Mcg32_16.seed(&mcg, char[4] seed) +fn void Mcg32RandomState.set_seed(&self, uint seed) { - *mcg = bitcast(seed, Mcg32_16) | 1; + *self = (Mcg32RandomState)seed | 1; } -fn ushort Mcg32_16.next(&mcg) +fn ushort Mcg32RandomState.next_short(&self) { - uint* s = (uint*)mcg; + uint* s = (uint*)self; ushort result = (ushort)(*s >> 16); *s *= MUL_MCG32; return result; } -// -------------------------------- Mcg16_8 -------------------------------- +// -------------------------------- Mcg16RandomState -------------------------------- -def Mcg16_8 = distinct ushort; +def Mcg16RandomState = distinct ushort; -fn void Mcg16_8.seed(&mcg, char[2] seed) +fn void Mcg16RandomState.set_seed(&self, ushort seed) { - *mcg = bitcast(seed, Mcg16_8) | 1; + *self = (Mcg16RandomState)seed | 1; } -fn char Mcg16_8.next(&mcg) +fn char Mcg16RandomState.next_byte(&self) { - ushort* s = (ushort*)mcg; + ushort* s = (ushort*)self; char result = (char)(*s >> 8); *s *= MUL_MCG16; return result; diff --git a/lib/std/math/random/math.msws.c3 b/lib/std/math/random/math.msws.c3 index b1bf35201..b57ad3dd2 100644 --- a/lib/std/math/random/math.msws.c3 +++ b/lib/std/math/random/math.msws.c3 @@ -10,27 +10,57 @@ const ODD_PHI8 @local = 0x9f; // -------------------------------- Msws128 -------------------------------- -struct Msws128 { +struct Msws128Random +{ + inline Random random; + Msws128RandomState state; +} + +struct Msws128RandomState +{ uint128 state0, state1; uint128 weyl0, weyl1; } -fn void Msws128.seed(&msws, char[16 * 4] seed) +const RandomInterface MSWS_128_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Msws128Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Msws128Random.next_bytes, +}; + +fn void Msws128Random.init(&self) { - *msws = bitcast(seed, Msws128); + self.random.fns = &MSWS_128_RANDOM_INTERFACE; } -fn uint128 Msws128.next(&msws) +fn void Msws128Random.set_seed(&self, char[] input) { - uint128 s0 = msws.state0; - msws.state0 = msws.state0 * msws.state0 + msws.weyl0; - msws.state0 = msws.state0.rotr(64); - msws.weyl0 += ODD_PHI128; + self.state.set_seed(random::make_seed(uint128[4], input)); +} - msws.state1 = msws.state1 * msws.state1 + msws.weyl1; - uint128 s1 = msws.state1; - msws.state1 = msws.state1.rotr(64); - msws.weyl1 -= ODD_PHI128; +/** + * @require bytes.len > 0 + **/ +fn void Msws128Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int128, bytes); +} + +fn void Msws128RandomState.set_seed(&self, uint128[4] seed) +{ + *self = bitcast(seed, Msws128RandomState); +} + +fn uint128 Msws128RandomState.next_int128(&self) +{ + uint128 s0 = self.state0; + self.state0 = self.state0 * self.state0 + self.weyl0; + self.state0 = self.state0.rotr(64); + self.weyl0 += ODD_PHI128; + + self.state1 = self.state1 * self.state1 + self.weyl1; + uint128 s1 = self.state1; + self.state1 = self.state1.rotr(64); + self.weyl1 -= ODD_PHI128; return s0 + s1; } @@ -38,27 +68,57 @@ fn uint128 Msws128.next(&msws) // -------------------------------- Msws64 -------------------------------- -struct Msws64 { +struct Msws64Random +{ + inline Random random; + Msws64RandomState state; +} + +struct Msws64RandomState +{ ulong state0, state1; ulong weyl0, weyl1; } -fn void Msws64.seed(&msws, char[8 * 4] seed) +const RandomInterface MSWS_64_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Msws64Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Msws64Random.next_bytes, +}; + +fn void Msws64Random.init(&self) { - *msws = bitcast(seed, Msws64); + self.random.fns = &MSWS_64_RANDOM_INTERFACE; } -fn ulong Msws64.next(&msws) +fn void Msws64Random.set_seed(&self, char[] input) { - ulong s0 = msws.state0; - msws.state0 = msws.state0 * msws.state0 + msws.weyl0; - msws.state0 = msws.state0.rotr(32); - msws.weyl0 += ODD_PHI64; + self.state.set_seed(random::make_seed(ulong[4], input)); +} - msws.state1 = msws.state1 * msws.state1 + msws.weyl1; - ulong s1 = msws.state1; - msws.state1 = msws.state1.rotr(32); - msws.weyl1 -= ODD_PHI64; +/** + * @require bytes.len > 0 + **/ +fn void Msws64Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_long, bytes); +} + +fn void Msws64RandomState.set_seed(&self, ulong[4] seed) +{ + *self = bitcast(seed, Msws64RandomState); +} + +fn ulong Msws64RandomState.next_long(&self) +{ + ulong s0 = self.state0; + self.state0 = self.state0 * self.state0 + self.weyl0; + self.state0 = self.state0.rotr(32); + self.weyl0 += ODD_PHI64; + + self.state1 = self.state1 * self.state1 + self.weyl1; + ulong s1 = self.state1; + self.state1 = self.state1.rotr(32); + self.weyl1 -= ODD_PHI64; return s0 + s1; } @@ -66,27 +126,57 @@ fn ulong Msws64.next(&msws) // -------------------------------- Msws32 -------------------------------- -struct Msws32 { +struct Msws32Random +{ + inline Random random; + Msws32RandomState state; +} + +struct Msws32RandomState +{ uint state0, state1; uint weyl0, weyl1; } -fn void Msws32.seed(&msws, char[4 * 4] seed) +const RandomInterface MSWS_32_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Msws32Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Msws32Random.next_bytes, +}; + +fn void Msws32Random.init(&self) { - *msws = bitcast(seed, Msws32); + self.random.fns = &MSWS_32_RANDOM_INTERFACE; } -fn uint Msws32.next(&msws) +fn void Msws32Random.set_seed(&self, char[] input) { - uint s0 = msws.state0; - msws.state0 = msws.state0 * msws.state0 + msws.weyl0; - msws.state0 = msws.state0.rotr(16); - msws.weyl0 += ODD_PHI32; + self.state.set_seed(random::make_seed(uint[4], input)); +} - msws.state1 = msws.state1 * msws.state1 + msws.weyl1; - uint s1 = msws.state1; - msws.state1 = msws.state1.rotr(16); - msws.weyl1 -= ODD_PHI32; +/** + * @require bytes.len > 0 + **/ +fn void Msws32Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int, bytes); +} + +fn void Msws32RandomState.set_seed(&self, uint[4] seed) +{ + *self = bitcast(seed, Msws32RandomState); +} + +fn uint Msws32RandomState.next_int(&self) +{ + uint s0 = self.state0; + self.state0 = self.state0 * self.state0 + self.weyl0; + self.state0 = self.state0.rotr(16); + self.weyl0 += ODD_PHI32; + + self.state1 = self.state1 * self.state1 + self.weyl1; + uint s1 = self.state1; + self.state1 = self.state1.rotr(16); + self.weyl1 -= ODD_PHI32; return s0 + s1; } @@ -94,27 +184,58 @@ fn uint Msws32.next(&msws) // -------------------------------- Msws16 -------------------------------- -struct Msws16 { +struct Msws16Random +{ + inline Random random; + Msws16RandomState state; +} + +struct Msws16RandomState +{ ushort state0, state1; ushort weyl0, weyl1; } -fn void Msws16.seed(&msws, char[2 * 4] seed) +const RandomInterface MSWS_16_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Msws16Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Msws16Random.next_bytes, +}; + +fn void Msws16Random.init(&self) { - *msws = bitcast(seed, Msws16); + self.random.fns = &MSWS_16_RANDOM_INTERFACE; } -fn ushort Msws16.next(&msws) +fn void Msws16Random.set_seed(&self, char[] input) { - ushort s0 = msws.state0; - msws.state0 = msws.state0 * msws.state0 + msws.weyl0; - msws.state0 = msws.state0.rotr(8); - msws.weyl0 += ODD_PHI16; + self.state.set_seed(random::make_seed(ushort[4], input)); +} - msws.state1 = msws.state1 * msws.state1 + msws.weyl1; - ushort s1 = msws.state1; - msws.state1 = msws.state1.rotr(8); - msws.weyl1 -= ODD_PHI16; +/** + * @require bytes.len > 0 + **/ +fn void Msws16Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_short, bytes); +} + + +fn void Msws16RandomState.set_seed(&self, ushort[4] seed) +{ + *self = bitcast(seed, Msws16RandomState); +} + +fn ushort Msws16RandomState.next_short(&self) +{ + ushort s0 = self.state0; + self.state0 = self.state0 * self.state0 + self.weyl0; + self.state0 = self.state0.rotr(8); + self.weyl0 += ODD_PHI16; + + self.state1 = self.state1 * self.state1 + self.weyl1; + ushort s1 = self.state1; + self.state1 = self.state1.rotr(8); + self.weyl1 -= ODD_PHI16; return s0 + s1; } @@ -122,27 +243,57 @@ fn ushort Msws16.next(&msws) // -------------------------------- Msws8 -------------------------------- -struct Msws8 { +struct Msws8Random +{ + inline Random random; + Msws8RandomState state; +} + +struct Msws8RandomState +{ char state0, state1; char weyl0, weyl1; } -fn void Msws8.seed(&msws, char[1 * 4] seed) +const RandomInterface MSWS_8_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Msws8Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Msws8Random.next_bytes, +}; + +fn void Msws8Random.init(&self) { - *msws = bitcast(seed, Msws8); + self.random.fns = &MSWS_8_RANDOM_INTERFACE; } -fn char Msws8.next(&msws) +fn void Msws8Random.set_seed(&self, char[] input) { - char s0 = msws.state0; - msws.state0 = msws.state0 * msws.state0 + msws.weyl0; - msws.state0 = msws.state0.rotr(4); - msws.weyl0 += ODD_PHI8; + self.state.set_seed(random::make_seed(char[4], input)); +} - msws.state1 = msws.state1 * msws.state1 + msws.weyl1; - char s1 = msws.state1; - msws.state1 = msws.state1.rotr(4); - msws.weyl1 -= ODD_PHI8; +/** + * @require bytes.len > 0 + **/ +fn void Msws8Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_byte, bytes); +} + +fn void Msws8RandomState.set_seed(&msws, char[4] seed) +{ + *msws = bitcast(seed, Msws8RandomState); +} + +fn char Msws8RandomState.next_byte(&self) +{ + char s0 = self.state0; + self.state0 = self.state0 * self.state0 + self.weyl0; + self.state0 = self.state0.rotr(4); + self.weyl0 += ODD_PHI8; + + self.state1 = self.state1 * self.state1 + self.weyl1; + char s1 = self.state1; + self.state1 = self.state1.rotr(4); + self.weyl1 -= ODD_PHI8; return s0 + s1; } diff --git a/lib/std/math/random/math.pcg.c3 b/lib/std/math/random/math.pcg.c3 index 5d4de759f..b981f6ff7 100644 --- a/lib/std/math/random/math.pcg.c3 +++ b/lib/std/math/random/math.pcg.c3 @@ -14,17 +14,46 @@ const MUL_LCG16 @local = 0x915d; // TODO: Find good constant // -------------------------------- Pcg128_64 -------------------------------- -def Pcg128_64 = distinct uint128; - -fn void Pcg128_64.seed(&pcg, char[16] seed) +struct Pcg128Random { - *pcg = bitcast(seed, Pcg128_64); + inline Random random; + Pcg128RandomState state; } -fn ulong Pcg128_64.next(&pcg) +def Pcg128RandomState = distinct uint128; + +const RandomInterface PCG_128_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Pcg128Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Pcg128Random.next_bytes, +}; + +fn void Pcg128Random.init(&self) +{ + self.random.fns = &PCG_128_RANDOM_INTERFACE; +} + +fn void Pcg128Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(uint128, input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Pcg128Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_long, bytes); +} + +fn void Pcg128RandomState.set_seed(&self, uint128 seed) +{ + *self = (Pcg128RandomState)seed; +} + +fn ulong Pcg128RandomState.next_long(&self) { const ROT_SHIFT = 64 - 6; - uint128* s = (uint128*)pcg; + uint128* s = (uint128*)self; uint128 xor = *s ^ *s >> ((128 - ROT_SHIFT) / 2); char rot = (char)(*s >> (128 - 6)); *s = *s * MUL_LCG128 + ODD_PHI128; @@ -34,17 +63,46 @@ fn ulong Pcg128_64.next(&pcg) // -------------------------------- Pcg64_32 -------------------------------- -def Pcg64_32 = distinct ulong; - -fn void Pcg64_32.seed(&pcg, char[8] seed) +struct Pcg64Random { - *pcg = bitcast(seed, Pcg64_32); + inline Random random; + Pcg64RandomState state; } -fn uint Pcg64_32.next(&pcg) +def Pcg64RandomState = distinct ulong; + +const RandomInterface PCG_64_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Pcg64Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Pcg64Random.next_bytes, +}; + +fn void Pcg64Random.init(&self) +{ + self.random.fns = &PCG_64_RANDOM_INTERFACE; +} + +fn void Pcg64Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(ulong, input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Pcg64Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int, bytes); +} + +fn void Pcg64RandomState.set_seed(&self, ulong seed) +{ + *self = (Pcg64RandomState)seed; +} + +fn uint Pcg64RandomState.next_int(&self) { const ROT_SHIFT = 32 - 5; - ulong* s = (ulong*)pcg; + ulong* s = (ulong*)self; ulong xor = *s ^ *s >> ((64 - ROT_SHIFT) / 2); char rot = (char)(*s >> (64 - 5)); *s = *s * MUL_LCG64 + ODD_PHI64; @@ -54,17 +112,46 @@ fn uint Pcg64_32.next(&pcg) // -------------------------------- Pcg32_16 -------------------------------- -def Pcg32_16 = distinct uint; - -fn void Pcg32_16.seed(&pcg, char[4] seed) +struct Pcg32Random { - *pcg = bitcast(seed, Pcg32_16); + inline Random random; + Pcg32RandomState state; } -fn ushort Pcg32_16.next(&pcg) +def Pcg32RandomState = distinct uint; + +const RandomInterface PCG_32_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Pcg32Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Pcg32Random.next_bytes, +}; + +fn void Pcg32Random.init(&self) +{ + self.random.fns = &PCG_32_RANDOM_INTERFACE; +} + +fn void Pcg32Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(uint, input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Pcg32Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_short, bytes); +} + +fn void Pcg32RandomState.set_seed(&self, uint seed) +{ + *self = (Pcg32RandomState)seed; +} + +fn ushort Pcg32RandomState.next_short(&self) { const ROT_SHIFT = 16 - 4; - uint* s = (uint*)pcg; + uint* s = (uint*)self; uint xor = *s ^ *s >> ((32 - ROT_SHIFT) / 2); char rot = (char)(*s >> (32 - 4)); *s = *s * MUL_LCG32 + ODD_PHI32; @@ -74,17 +161,46 @@ fn ushort Pcg32_16.next(&pcg) // -------------------------------- Pcg16_8 -------------------------------- -def Pcg16_8 = distinct ushort; - -fn void Pcg16_8.seed(&pcg, char[2] seed) +struct Pcg16Random { - *pcg = bitcast(seed, Pcg16_8); + inline Random random; + Pcg16RandomState state; } -fn char Pcg16_8.next(&pcg) +def Pcg16RandomState = distinct ushort; + +const RandomInterface PCG_16_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Pcg16Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Pcg16Random.next_bytes, +}; + +fn void Pcg16Random.init(&self) +{ + self.random.fns = &PCG_16_RANDOM_INTERFACE; +} + +fn void Pcg16Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(ushort, input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Pcg16Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_byte, bytes); +} + +fn void Pcg16RandomState.set_seed(&self, ushort seed) +{ + *self = (Pcg16RandomState)seed; +} + +fn char Pcg16RandomState.next_byte(&self) { const ROT_SHIFT = 8 - 3; - ushort* s = (ushort*)pcg; + ushort* s = (ushort*)self; ushort xor = *s ^ *s >> ((16 - ROT_SHIFT) / 2); char rot = (char)(*s >> (16 - 3)); *s = *s * MUL_LCG16 + ODD_PHI16; diff --git a/lib/std/math/random/math.seeder.c3 b/lib/std/math/random/math.seeder.c3 index 743f5f502..38dcb47ac 100644 --- a/lib/std/math/random/math.seeder.c3 +++ b/lib/std/math/random/math.seeder.c3 @@ -1,53 +1,66 @@ -module std::math; +module std::math::random; const ODD_PHI64 @local = 0x9e3779b97f4a7c15; const MUL_MCG64 @local = 0xf1357aea2e62a9c5; const MUL_LCG64 @local = 0xd1342543de82ef95; +macro make_seed($Type, char[] input) +{ + $Type return_value; + seeder(input, @to_byte_view(return_value)); + return return_value; +} + // TODO: Make endian independent -fn char[] seeder(usz outChars, char[] input) +/** + * @param [in] input + * @param [inout] out_buffer + **/ +fn void seeder(char[] input, char[] out_buffer) { $if env::BIG_ENDIAN: $echo("Please note that the seeding function behaves differently on BE architectures."); $endif - // Init words - ulong[] words = malloc(ulong, (outChars + 7) / 8); - words[..] = ODD_PHI64; - - // Add word at a time - for (usz i = 0; i < input.len / 8; i++) + usz out_chars = out_buffer.len; + @pool() { - usz j = i % words.len; - words[j] -= bitcast(*(char[8]*)&input[i * 8], ulong) * MUL_LCG64; - words[j] ^= words[j] >> 25; - } + ulong[] words = tmalloc(ulong, (out_chars + 7) / 8); + words[..] = ODD_PHI64; - // Add rest of the bytes - usz remaining = input.len - input.len / 8 * 8; - if (remaining) - { - ulong rest = MUL_MCG64; - mem::copy(&rest, &input[^remaining], remaining); - words[^1] -= rest * MUL_LCG64; - words[^1] ^= words[^1] >> 25; - } + // Add word at a time + for (usz i = 0; i < input.len / 8; i++) + { + usz j = i % words.len; + words[j] -= bitcast(*(char[8]*)&input[i * 8], ulong) * MUL_LCG64; + words[j] ^= words[j] >> 25; + } - // Mix between words - for (isz i = words.len * 2 - 1; i >= 0; i--) - { - isz j = i % words.len; - words[j] -= words[(i + 1) % words.len] * MUL_LCG64; - words[j] ^= words[j] >> 25; - } + // Add rest of the bytes + usz remaining = input.len - input.len / 8 * 8; + if (remaining) + { + ulong rest = MUL_MCG64; + mem::copy(&rest, &input[^remaining], remaining); + words[^1] -= rest * MUL_LCG64; + words[^1] ^= words[^1] >> 25; + } - // Mix within words - for (usz i = 0; i < 2; i++) - { - usz j = i % words.len; - words[j] *= MUL_MCG64; - words[j] ^= words[j] >> 25; - } + // Mix between words + for (isz i = words.len * 2 - 1; i >= 0; i--) + { + isz j = i % words.len; + words[j] -= words[(i + 1) % words.len] * MUL_LCG64; + words[j] ^= words[j] >> 25; + } - return ((char*)words.ptr)[:words.len * 8]; + // Mix within words + for (usz i = 0; i < 2; i++) + { + usz j = i % words.len; + words[j] *= MUL_MCG64; + words[j] ^= words[j] >> 25; + } + out_buffer[..] = ((char*)words.ptr)[:out_chars]; + }; } diff --git a/lib/std/math/random/math.sfc.c3 b/lib/std/math/random/math.sfc.c3 index 8fef6c4b5..4d78d3182 100644 --- a/lib/std/math/random/math.sfc.c3 +++ b/lib/std/math/random/math.sfc.c3 @@ -10,16 +10,46 @@ const ODD_PHI8 @local = 0x9f; // -------------------------------- Sfc128 -------------------------------- -def Sfc128 = distinct uint128[4]; - -fn void Sfc128.seed(&sfc, char[16 * 4] seed) +struct Sfc128Random { - *sfc = bitcast(seed, Sfc128); + inline Random random; + Sfc128RandomState state; } -fn uint128 Sfc128.next(&sfc) // TODO: Find good constant +def Sfc128RandomState = distinct uint128[4]; + + +const RandomInterface SFC_128_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Sfc128Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Sfc128Random.next_bytes, +}; + +fn void Sfc128Random.init(&self) { - uint128* s = (uint128[4]*)sfc; + self.random.fns = &SFC_128_RANDOM_INTERFACE; +} + +fn void Sfc128Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(uint128[4], input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Sfc128Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int128, bytes); +} + +fn void Sfc128RandomState.set_seed(&self, uint128[4] seed) +{ + *self = (Sfc128RandomState)seed; +} + +fn uint128 Sfc128RandomState.next_int128(&self) // TODO: Find good constant +{ + uint128* s = (uint128[4]*)self; uint128 result = s[0] + s[1] + s[3]; s[0] = s[1] ^ s[1] >> 11; s[1] = s[2] + s[2] << 3; @@ -31,16 +61,46 @@ fn uint128 Sfc128.next(&sfc) // TODO: Find good constant // -------------------------------- Sfc64 -------------------------------- -def Sfc64 = distinct ulong[4]; -fn void Sfc64.seed(&sfc, char[8 * 4] seed) +struct Sfc64Random { - *sfc = bitcast(seed, Sfc64); + inline Random random; + Sfc64RandomState state; } -fn ulong Sfc64.next(&sfc) +def Sfc64RandomState = distinct ulong[4]; + +const RandomInterface SFC_64_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Sfc64Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Sfc64Random.next_bytes, +}; + +fn void Sfc64Random.init(&self) { - ulong* s = (ulong[4]*)sfc; + self.random.fns = &SFC_64_RANDOM_INTERFACE; +} + +fn void Sfc64Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(ulong[4], input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Sfc64Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_long, bytes); +} + +fn void Sfc64RandomState.set_seed(&self, ulong[4] seed) +{ + *self = (Sfc64RandomState)seed; +} + +fn ulong Sfc64RandomState.next_long(&self) +{ + ulong* s = (ulong[4]*)self; ulong result = s[0] + s[1] + s[3]; s[0] = s[1] ^ s[1] >> 11; s[1] = s[2] + s[2] << 3; @@ -52,14 +112,44 @@ fn ulong Sfc64.next(&sfc) // -------------------------------- Sfc32 -------------------------------- -def Sfc32 = distinct uint[4]; -fn void Sfc32.seed(&sfc, char[4 * 4] seed) +struct Sfc32Random { - *sfc = bitcast(seed, Sfc32); + inline Random random; + Sfc32RandomState state; } -fn uint Sfc32.next(&sfc) +def Sfc32RandomState = distinct uint[4]; + +const RandomInterface SFC_32_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Sfc32Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Sfc32Random.next_bytes, +}; + +fn void Sfc32Random.init(&self) +{ + self.random.fns = &SFC_32_RANDOM_INTERFACE; +} + +fn void Sfc32Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(uint[4], input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Sfc32Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_int, bytes); +} + +fn void Sfc32RandomState.set_seed(&self, uint[4] seed) +{ + *self = (Sfc32RandomState)seed; +} + +fn uint Sfc32RandomState.next_int(&sfc) { uint* s = (uint[4]*)sfc; uint result = s[0] + s[1] + s[3]; @@ -73,16 +163,45 @@ fn uint Sfc32.next(&sfc) // -------------------------------- Sfc16 -------------------------------- -def Sfc16 = distinct ushort[4]; - -fn void Sfc16.seed(&sfc, char[2 * 4] seed) +struct Sfc16Random { - *sfc = bitcast(seed, Sfc16); + inline Random random; + Sfc16RandomState state; } -fn ushort Sfc16.next(&sfc) +def Sfc16RandomState = distinct ushort[4]; + +const RandomInterface SFC_16_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Sfc16Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Sfc16Random.next_bytes, +}; + +fn void Sfc16Random.init(&self) { - ushort* s = (ushort[4]*)sfc; + self.random.fns = &SFC_16_RANDOM_INTERFACE; +} + +fn void Sfc16Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(ushort[4], input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Sfc16Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_short, bytes); +} + +fn void Sfc16RandomState.set_seed(&self, ushort[4] seed) +{ + *self = (Sfc16RandomState)seed; +} + +fn ushort Sfc16RandomState.next_short(&seed) +{ + ushort* s = (ushort[4]*)seed; ushort result = s[0] + s[1] + s[3]; s[0] = s[1] ^ s[1] >> 2; s[1] = s[2] + s[2] << 3; @@ -94,16 +213,45 @@ fn ushort Sfc16.next(&sfc) // -------------------------------- Sfc8 -------------------------------- -def Sfc8 = distinct char[4]; - -fn void Sfc8.seed(&sfc, char[1 * 4] seed) +struct Sfc8Random { - *sfc = bitcast(seed, Sfc8); + inline Random random; + Sfc8RandomState state; } -fn char Sfc8.next(&sfc) // TODO: Find better constants +def Sfc8RandomState = distinct char[4]; + +const RandomInterface SFC_8_RANDOM_INTERFACE @local = { + .seed_fn = (RandomSeedFn)&Sfc8Random.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&Sfc8Random.next_bytes, +}; + +fn void Sfc8Random.init(&self) { - char* s = (char[4]*)sfc; + self.random.fns = &SFC_8_RANDOM_INTERFACE; +} + +fn void Sfc8Random.set_seed(&self, char[] input) +{ + self.state.set_seed(random::make_seed(char[4], input)); +} + +/** + * @require bytes.len > 0 + **/ +fn void Sfc8Random.next_bytes(&self, char[] bytes) +{ + @random_value_to_bytes(self.state.next_byte, bytes); +} + +fn void Sfc8RandomState.set_seed(&self, char[4] seed) +{ + *self = (Sfc8RandomState)seed; +} + +fn char Sfc8RandomState.next_byte(&self) // TODO: Find better constants +{ + char* s = (char[4]*)self; char result = s[0] + s[1] + s[3]; s[0] = s[1] ^ s[1] >> 1; s[1] = s[2] + s[2] << 2; diff --git a/lib/std/math/random/math.simple_random.c3 b/lib/std/math/random/math.simple_random.c3 index 122ba6dfe..786d49904 100644 --- a/lib/std/math/random/math.simple_random.c3 +++ b/lib/std/math/random/math.simple_random.c3 @@ -1,38 +1,49 @@ module std::math; -def SimpleRandom = distinct ulong; +struct SimpleRandom +{ + inline Random random; + SimpleRandomState state; +} -const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D; -const long SIMPLE_RANDOM_ADDEND @local = 0xB; -const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1; +def SimpleRandomState = distinct ulong; RandomInterface simple_random_interface = { - .seed_fn = fn (random, seed) => ((SimpleRandom*)random.state).set_random_seed(seed), - .next_fn = fn (random, bits) => ((SimpleRandom*)random.state).next_random(bits) + .seed_fn = (RandomSeedFn)&SimpleRandom.set_seed, + .next_bytes_fn = (RandomNextBytesFn)&SimpleRandom.next_bytes }; -fn Random SimpleRandom.as_random(&random) + +/** + * @require bytes.len > 0 + **/ +fn void SimpleRandom.next_bytes(&self, char[] bytes) { - return { .fns = simple_random_interface, .state = random }; + @random_value_to_bytes(self.state.next_int, bytes); } -fn uint SimpleRandom.next_random(&r, int bits) @dynamic -{ - ulong nextseed = ((ulong)*r * SIMPLE_RANDOM_MULTIPLIER + SIMPLE_RANDOM_ADDEND) & SIMPLE_RANDOM_MASK; - *r = (SimpleRandom)nextseed; - return (uint)(nextseed >> (48 - bits)); -} - -fn void SimpleRandom.set_seed(&r, ulong seed) -{ - *r = (SimpleRandom)((seed ^ SIMPLE_RANDOM_MULTIPLIER) & SIMPLE_RANDOM_MASK); -} -fn void SimpleRandom.set_random_seed(&r, char[] seed) @dynamic +fn void SimpleRandom.set_seed(&self, char[] seed) { char[8] full; foreach (i, c : seed) { full[i % 8] ^= c; } - r.set_seed(bitcast(full, ulong)); + self.state.set_seed(bitcast(full, ulong)); } + +fn void SimpleRandomState.set_seed(&self, ulong seed) +{ + *self = (SimpleRandomState)(seed ^ SIMPLE_RANDOM_MULTIPLIER) & SIMPLE_RANDOM_MASK; +} + +fn uint SimpleRandomState.next_int(&self) +{ + ulong nextseed = ((ulong)*self * SIMPLE_RANDOM_MULTIPLIER + SIMPLE_RANDOM_ADDEND) & SIMPLE_RANDOM_MASK; + *self = (SimpleRandomState)nextseed; + return (uint)(nextseed >> (48 - 32)); +} + +const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D; +const long SIMPLE_RANDOM_ADDEND @local = 0xB; +const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1;