| @ -0,0 +1,306 @@ | |||
| /********************************************************************************************** | |||
| * | |||
| * rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG) | |||
| * | |||
| * FEATURES: | |||
| * - Pseudo-random values generation, 32 bits: [0..4294967295] | |||
| * - Sequence generation avoiding duplicate values | |||
| * - Using standard and proven prng algorithm (Xoshiro128**) | |||
| * - State initialized with a separate generator (SplitMix64) | |||
| * | |||
| * LIMITATIONS: | |||
| * - No negative numbers, up to the user to manage them | |||
| * | |||
| * POSSIBLE IMPROVEMENTS: | |||
| * - Support 64 bits generation | |||
| * | |||
| * ADDITIONAL NOTES: | |||
| * This library implements two pseudo-random number generation algorithms: | |||
| * | |||
| * - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c | |||
| * - SplitMix64 : https://prng.di.unimi.it/splitmix64.c | |||
| * | |||
| * SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed | |||
| * | |||
| * It's suggested to use SplitMix64 to initialize the state of the generators starting from | |||
| * a 64-bit seed, as research has shown that initialization must be performed with a generator | |||
| * radically different in nature from the one initialized to avoid correlation on similar seeds. | |||
| * | |||
| * CONFIGURATION: | |||
| * #define RPRAND_IMPLEMENTATION | |||
| * Generates the implementation of the library into the included file. | |||
| * If not defined, the library is in header only mode and can be included in other headers | |||
| * or source files without problems. But only ONE file should hold the implementation. | |||
| * | |||
| * DEPENDENCIES: none | |||
| * | |||
| * VERSIONS HISTORY: | |||
| * 1.0 (01-Jun-2023) First version | |||
| * | |||
| * | |||
| * LICENSE: zlib/libpng | |||
| * | |||
| * Copyright (c) 2023 Ramon Santamaria (@raysan5) | |||
| * | |||
| * This software is provided "as-is", without any express or implied warranty. In no event | |||
| * will the authors be held liable for any damages arising from the use of this software. | |||
| * | |||
| * Permission is granted to anyone to use this software for any purpose, including commercial | |||
| * applications, and to alter it and redistribute it freely, subject to the following restrictions: | |||
| * | |||
| * 1. The origin of this software must not be misrepresented; you must not claim that you | |||
| * wrote the original software. If you use this software in a product, an acknowledgment | |||
| * in the product documentation would be appreciated but is not required. | |||
| * | |||
| * 2. Altered source versions must be plainly marked as such, and must not be misrepresented | |||
| * as being the original software. | |||
| * | |||
| * 3. This notice may not be removed or altered from any source distribution. | |||
| * | |||
| **********************************************************************************************/ | |||
| #ifndef RPRAND_H | |||
| #define RPRAND_H | |||
| #define RPRAND_VERSION "1.0" | |||
| // Function specifiers in case library is build/used as a shared library (Windows) | |||
| // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll | |||
| #if defined(_WIN32) | |||
| #if defined(BUILD_LIBTYPE_SHARED) | |||
| #define RPRAND __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) | |||
| #elif defined(USE_LIBTYPE_SHARED) | |||
| #define RPRAND __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) | |||
| #endif | |||
| #endif | |||
| // Function specifiers definition | |||
| #ifndef RPRANDAPI | |||
| #define RPRANDAPI // Functions defined as 'extern' by default (implicit specifiers) | |||
| #endif | |||
| //---------------------------------------------------------------------------------- | |||
| // Defines and Macros | |||
| //---------------------------------------------------------------------------------- | |||
| // Allow custom memory allocators | |||
| #ifndef RPRAND_CALLOC | |||
| #define RPRAND_CALLOC(ptr,sz) calloc(ptr,sz) | |||
| #endif | |||
| #ifndef RPRAND_FREE | |||
| #define RPRAND_FREE(ptr) free(ptr) | |||
| #endif | |||
| // Simple log system to avoid RPNG_LOG() calls if required | |||
| // NOTE: Avoiding those calls, also avoids const strings memory usage | |||
| #define RPRAND_SHOW_LOG_INFO | |||
| #if defined(RPNG_SHOW_LOG_INFO) | |||
| #define RPRAND_LOG(...) printf(__VA_ARGS__) | |||
| #else | |||
| #define RPRAND_LOG(...) | |||
| #endif | |||
| //---------------------------------------------------------------------------------- | |||
| // Types and Structures Definition | |||
| //---------------------------------------------------------------------------------- | |||
| //... | |||
| #ifdef __cplusplus | |||
| extern "C" { // Prevents name mangling of functions | |||
| #endif | |||
| //---------------------------------------------------------------------------------- | |||
| // Global Variables Definition | |||
| //---------------------------------------------------------------------------------- | |||
| //... | |||
| //---------------------------------------------------------------------------------- | |||
| // Module Functions Declaration | |||
| //---------------------------------------------------------------------------------- | |||
| RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit | |||
| RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included | |||
| RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates | |||
| RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif // RPRAND_H | |||
| /*********************************************************************************** | |||
| * | |||
| * RPRAND IMPLEMENTATION | |||
| * | |||
| ************************************************************************************/ | |||
| #if defined(RPRAND_IMPLEMENTATION) | |||
| #include <stdlib.h> // Required for: calloc(), free() | |||
| #include <stdint.h> // Required for data types: uint32_t, uint64_t | |||
| //---------------------------------------------------------------------------------- | |||
| // Types and Structures Definition | |||
| //---------------------------------------------------------------------------------- | |||
| // ... | |||
| //---------------------------------------------------------------------------------- | |||
| // Global Variables Definition | |||
| //---------------------------------------------------------------------------------- | |||
| static uint64_t rprand_seed = 0; // SplitMix64 actual seed | |||
| static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64 | |||
| //---------------------------------------------------------------------------------- | |||
| // Module internal functions declaration | |||
| //---------------------------------------------------------------------------------- | |||
| static uint32_t rprand_xoshiro(void); // Xoshiro128** generator (uses global rprand_state) | |||
| static uint64_t rprand_splitmix64(void); // SplitMix64 generator (uses seed to generate rprand_state) | |||
| //---------------------------------------------------------------------------------- | |||
| // Module functions definition | |||
| //---------------------------------------------------------------------------------- | |||
| // Set rprand_state for Xoshiro128** | |||
| // NOTE: We use a custom generation algorithm using SplitMix64 | |||
| void rprand_set_seed(unsigned long long seed) | |||
| { | |||
| rprand_seed = (uint64_t)seed; // Set SplitMix64 seed for further use | |||
| // To generate the Xoshiro128** state, we use SplitMix64 generator first | |||
| // We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB | |||
| rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff); | |||
| rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); | |||
| rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff); | |||
| rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); | |||
| } | |||
| // Get random value within a range, min and max included | |||
| unsigned int rprand_get_value(int min, int max) | |||
| { | |||
| unsigned int value = rprand_xoshiro()%(max - min) + min; | |||
| return value; | |||
| } | |||
| // Load pseudo-random numbers sequence with no duplicates | |||
| unsigned int *rprand_load_sequence(unsigned int count, int min, int max) | |||
| { | |||
| unsigned int *sequence = NULL; | |||
| if (count > (max - min)) | |||
| { | |||
| RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); | |||
| //count = (max - min); | |||
| return sequence; | |||
| } | |||
| sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); | |||
| uint32_t value = 0; | |||
| int value_count = 0; | |||
| bool value_is_dup = false; | |||
| for (int i = 0; value_count < count; i++) | |||
| { | |||
| value = rprand_xoshiro()%(max - min) + min; | |||
| value_is_dup = false; | |||
| for (int j = 0; j < value_count; j++) | |||
| { | |||
| if (sequence[j] == value) | |||
| { | |||
| value_is_dup = true; | |||
| break; | |||
| } | |||
| } | |||
| if (!value_is_dup) | |||
| { | |||
| sequence[value_count] = value; | |||
| value_count++; | |||
| } | |||
| } | |||
| return sequence; | |||
| } | |||
| // Unload pseudo-random numbers sequence | |||
| void rprand_unload_sequence(unsigned int *sequence) | |||
| { | |||
| RPRAND_FREE(sequence); | |||
| sequence = NULL; | |||
| } | |||
| //---------------------------------------------------------------------------------- | |||
| // Module internal functions definition | |||
| //---------------------------------------------------------------------------------- | |||
| static inline uint32_t rprand_rotate_left(const uint32_t x, int k) | |||
| { | |||
| return (x << k) | (x >> (32 - k)); | |||
| } | |||
| // Xoshiro128** generator info: | |||
| // | |||
| // Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) | |||
| // | |||
| // To the extent possible under law, the author has dedicated all copyright | |||
| // and related and neighboring rights to this software to the public domain | |||
| // worldwide. This software is distributed without any warranty. | |||
| // | |||
| // See <http://creativecommons.org/publicdomain/zero/1.0/>. | |||
| // | |||
| // This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid | |||
| // generators. It has excellent speed, a state size (128 bits) that is | |||
| // large enough for mild parallelism, and it passes all tests we are aware | |||
| // of. | |||
| // | |||
| // Note that version 1.0 had mistakenly s[0] instead of s[1] as state | |||
| // word passed to the scrambler. | |||
| // | |||
| // For generating just single-precision (i.e., 32-bit) floating-point | |||
| // numbers, xoshiro128+ is even faster. | |||
| // | |||
| // The state must be seeded so that it is not everywhere zero. | |||
| // | |||
| uint32_t rprand_xoshiro(void) | |||
| { | |||
| const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9; | |||
| const uint32_t t = rprand_state[1] << 9; | |||
| rprand_state[2] ^= rprand_state[0]; | |||
| rprand_state[3] ^= rprand_state[1]; | |||
| rprand_state[1] ^= rprand_state[2]; | |||
| rprand_state[0] ^= rprand_state[3]; | |||
| rprand_state[2] ^= t; | |||
| rprand_state[3] = rprand_rotate_left(rprand_state[3], 11); | |||
| return result; | |||
| } | |||
| // SplitMix64 generator info: | |||
| // | |||
| // Written in 2015 by Sebastiano Vigna (vigna@acm.org) | |||
| // | |||
| // To the extent possible under law, the author has dedicated all copyright | |||
| // and related and neighboring rights to this software to the public domain | |||
| // worldwide. This software is distributed without any warranty. | |||
| // | |||
| // See <http://creativecommons.org/publicdomain/zero/1.0/>. | |||
| // | |||
| // | |||
| // This is a fixed-increment version of Java 8's SplittableRandom generator | |||
| // See http://dx.doi.org/10.1145/2714064.2660195 and | |||
| // http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html | |||
| // | |||
| // It is a very fast generator passing BigCrush, and it can be useful if | |||
| // for some reason you absolutely want 64 bits of state. | |||
| uint64_t rprand_splitmix64() | |||
| { | |||
| uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15); | |||
| z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9; | |||
| z = (z ^ (z >> 27))*0x94d049bb133111eb; | |||
| return z ^ (z >> 31); | |||
| } | |||
| #endif // RPRAND_IMPLEMENTATION | |||