From f46c3ab38a939314ba532397a2d952a1927ccbbd Mon Sep 17 00:00:00 2001 From: Abdelrahman Said Date: Wed, 16 Sep 2026 00:10:18 +0100 Subject: [PATCH] Extract splitmix64 PRNG --- CHANGELOG.md | 2 + src/prng/splitmix64/splitmix64.c | 97 ++++++++++++++++++++++++++++++++ src/prng/splitmix64/splitmix64.h | 18 ++++++ src/prng/wapp_prng.c | 1 + src/prng/wapp_prng.h | 1 + src/prng/xorshift/xorshift.c | 95 ++++--------------------------- 6 files changed, 129 insertions(+), 85 deletions(-) create mode 100644 src/prng/splitmix64/splitmix64.c create mode 100644 src/prng/splitmix64/splitmix64.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b975a2..65bc119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `wpU8Const`, `wpU16Const`, `wpU32Const`, `wpU64Const`, `wpI8Const`, `wpI16Const`, `wpI32Const`, and `wpI64Const` - `wpPrngXorshiftInitWithSeed` to allow for user control with PRNG - `wpPrngXorshift256InRange`, `wpPrngXorshift256ssInRange`, `wpPrngXorshift256pInRange`, `wpPrngXorshift256Choice`, `wpPrngXorshift256ssChoice` and `wpPrngXorshift256pChoice` utilities +- `WpSplitmix64State` +- `wpPrngSplitmix64Init`, `wpPrngSplitmix64InitWithSeed`, `wpPrngSplitmix64`, `wpPrngSplitmix64InRange`, `wpPrngSplitmix64Choice` - `wpStr8Clear` ### Changed diff --git a/src/prng/splitmix64/splitmix64.c b/src/prng/splitmix64/splitmix64.c new file mode 100644 index 0000000..9549b0e --- /dev/null +++ b/src/prng/splitmix64/splitmix64.c @@ -0,0 +1,97 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "splitmix64.h" +#include "../../common/aliases/aliases.h" +#include "../../common/assert/assert.h" +#include "../../common/platform/platform.h" +#include +#include + +wp_intern void seedOsGenerator(u64 seed); +wp_intern u64 genRandomNumber(void); + +WpSplitmix64State wpPrngSplitmix64Init(void) { + wp_persist b8 seeded = false; + if (!seeded) { + seeded = true; + seedOsGenerator(0); + } + + return (WpSplitmix64State){ .seed = genRandomNumber() }; +} + +WpSplitmix64State wpPrngSplitmix64InitWithSeed(u64 seed) { + return (WpSplitmix64State){ .seed = seed }; +} + +u64 wpPrngSplitmix64(WpSplitmix64State *state) { + state->seed += 0x9E3779B97f4A7C15; + + u64 result = state->seed; + result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; + result = (result ^ (result >> 27)) * 0x94D049BB133111EB; + + return result ^ (result >> 31); +} + +u64 wpPrngSplitmix64InRange(WpSplitmix64State *state, u64 start, u64 end) { + return wpPrngSplitmix64(state) % (end - start) + start; +} + +u64 wpPrngSplitmix64Choice(WpSplitmix64State *state, u64 available_choices) { + return wpPrngSplitmix64(state) % available_choices; +} + +#if defined(WP_PLATFORM_C) && WP_PLATFORM_C_VERSION >= WP_PLATFORM_C11_VERSION +#ifdef WP_PLATFORM_POSIX +wp_intern void seedOsGenerator(u64 seed) { + if (seed == 0) { + struct timespec ts = {0}; + int result = clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + wpRuntimeAssert(result == 0, "Invalid seed value"); + seed = (u64)ts.tv_nsec; + } + + srand48(seed); +} + +wp_intern u64 genRandomNumber(void) { + return lrand48(); +} +#else +wp_intern void seedOsGenerator(u64 seed) { + if (seed == 0) { + struct timespec ts = {0}; + int result = timespec_get(&ts, TIME_UTC); + wpRuntimeAssert(result != 0, "Invalid seed value"); + seed = (u64)ts.tv_nsec; + } + + srand(seed); +} + +wp_intern u64 genRandomNumber(void) { + i32 n1 = rand(); + i32 n2 = rand(); + + return (((u64)n1) << 32 | (u64)n2); +} +#endif // !WP_PLATFORM_POSIX +#else +wp_intern void seedOsGenerator(u64 seed) { + if (seed == 0) { + time_t result = time(NULL); + wpRuntimeAssert(result != (time_t)(-1), "Invalid seed value"); + seed = (u64)result; + } + + srand(seed); +} + +wp_intern u64 genRandomNumber(void) { + i32 n1 = rand(); + i32 n2 = rand(); + + return (((u64)n1) << 32 | (u64)n2); +} +#endif // !WP_PLATFORM_C diff --git a/src/prng/splitmix64/splitmix64.h b/src/prng/splitmix64/splitmix64.h new file mode 100644 index 0000000..15e3ba6 --- /dev/null +++ b/src/prng/splitmix64/splitmix64.h @@ -0,0 +1,18 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef SPLITMIX64_H +#define SPLITMIX64_H + +#include "../../common/aliases/aliases.h" + +typedef struct WpSplitmix64State { + u64 seed; +} WpSplitmix64State; + +WpSplitmix64State wpPrngSplitmix64Init(void); +WpSplitmix64State wpPrngSplitmix64InitWithSeed(u64 seed); +u64 wpPrngSplitmix64(WpSplitmix64State *state); +u64 wpPrngSplitmix64InRange(WpSplitmix64State *state, u64 start, u64 end); +u64 wpPrngSplitmix64Choice(WpSplitmix64State *state, u64 available_choices); + +#endif // !SPLITMIX64_H diff --git a/src/prng/wapp_prng.c b/src/prng/wapp_prng.c index e5081be..6c05c2c 100644 --- a/src/prng/wapp_prng.c +++ b/src/prng/wapp_prng.c @@ -3,6 +3,7 @@ #ifndef WAPP_PRNG_C #define WAPP_PRNG_C +#include "splitmix64/splitmix64.c" #include "xorshift/xorshift.c" #endif // !WAPP_PRNG_C diff --git a/src/prng/wapp_prng.h b/src/prng/wapp_prng.h index e64dfac..2ba8fe3 100644 --- a/src/prng/wapp_prng.h +++ b/src/prng/wapp_prng.h @@ -3,6 +3,7 @@ #ifndef WAPP_PRNG_H #define WAPP_PRNG_H +#include "splitmix64/splitmix64.h" #include "xorshift/xorshift.h" #include "../common/wapp_common.h" diff --git a/src/prng/xorshift/xorshift.c b/src/prng/xorshift/xorshift.c index e87bdc0..74a0787 100644 --- a/src/prng/xorshift/xorshift.c +++ b/src/prng/xorshift/xorshift.c @@ -1,40 +1,29 @@ // vim:fileencoding=utf-8:foldmethod=marker #include "xorshift.h" +#include "../splitmix64/splitmix64.h" #include "../../common/aliases/aliases.h" -#include "../../common/assert/assert.h" -#include "../../common/platform/platform.h" #include "../../common/misc/misc_utils.h" #include #include -typedef struct SplitMix64State SplitMix64State; -struct SplitMix64State { - u64 seed; -}; - -wp_intern u64 splitMix64(SplitMix64State *state); -wp_intern void seedOsGenerator(u64 seed); -wp_intern u64 genRandomNumber(void); - WpXor256State wpPrngXorshiftInit(void) { return wpPrngXorshiftInitWithSeed(0); } WpXor256State wpPrngXorshiftInitWithSeed(u64 seed) { - wp_persist b8 seeded = false; - if (!seeded) { - seeded = true; - seedOsGenerator(seed); + WpSplitmix64State sm64; + if (seed > 0) { + sm64 = wpPrngSplitmix64InitWithSeed(seed); + } else { + sm64 = wpPrngSplitmix64Init(); } - SplitMix64State sm64 = {.seed = genRandomNumber()}; - return (WpXor256State){ - .x = splitMix64(&sm64), - .y = splitMix64(&sm64), - .z = splitMix64(&sm64), - .w = splitMix64(&sm64), + .x = wpPrngSplitmix64(&sm64), + .y = wpPrngSplitmix64(&sm64), + .z = wpPrngSplitmix64(&sm64), + .w = wpPrngSplitmix64(&sm64), }; } @@ -102,67 +91,3 @@ u64 wpPrngXorshift256ssChoice(WpXor256State *state, u64 available_choices) { u64 wpPrngXorshift256pChoice(WpXor256State *state, u64 available_choices) { return wpPrngXorshift256p(state) % available_choices; } - -wp_intern u64 splitMix64(SplitMix64State *state) { - state->seed += 0x9E3779B97f4A7C15; - - u64 result = state->seed; - result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; - result = (result ^ (result >> 27)) * 0x94D049BB133111EB; - - return result ^ (result >> 31); -} - -#if defined(WP_PLATFORM_C) && WP_PLATFORM_C_VERSION >= WP_PLATFORM_C11_VERSION -#ifdef WP_PLATFORM_POSIX -wp_intern void seedOsGenerator(u64 seed) { - if (seed == 0) { - struct timespec ts = {0}; - int result = clock_gettime(CLOCK_MONOTONIC_RAW, &ts); - wpRuntimeAssert(result == 0, "Invalid seed value"); - seed = (u64)ts.tv_nsec; - } - - srand48(seed); -} - -wp_intern u64 genRandomNumber(void) { - return lrand48(); -} -#else -wp_intern void seedOsGenerator(u64 seed) { - if (seed == 0) { - struct timespec ts = {0}; - int result = timespec_get(&ts, TIME_UTC); - wpRuntimeAssert(result != 0, "Invalid seed value"); - seed = (u64)ts.tv_nsec; - } - - srand(seed); -} - -wp_intern u64 genRandomNumber(void) { - i32 n1 = rand(); - i32 n2 = rand(); - - return (((u64)n1) << 32 | (u64)n2); -} -#endif // !WP_PLATFORM_POSIX -#else -wp_intern void seedOsGenerator(u64 seed) { - if (seed == 0) { - time_t result = time(NULL); - wpRuntimeAssert(result != (time_t)(-1), "Invalid seed value"); - seed = (u64)result; - } - - srand(seed); -} - -wp_intern u64 genRandomNumber(void) { - i32 n1 = rand(); - i32 n2 = rand(); - - return (((u64)n1) << 32 | (u64)n2); -} -#endif // !WP_PLATFORM_C