//+------------------------------------------------------------------+ //| Xoshiro256.mqh | //| Copyright © 2025, Amr Ali | //| https://www.mql5.com/en/users/amrali | //+------------------------------------------------------------------+ #property copyright "Copyright © 2025, Amr Ali" #property link "https://www.mql5.com/en/users/amrali" #property version "1.10" #property description "Random number generation using the 64-bit Xoshiro256** algorithm." #property description "All-purpose rock-solid generator with excellent (sub-ns) speed." #ifndef XOSHIRO256_XOSHIRO256_MQH #define XOSHIRO256_XOSHIRO256_MQH // Updates: // 2023.01.06 - v.1.01 : Updated the class to use the more robust 64-bits xoshiro256** random number generator, instead of the 32-bits xoshiro128**. // 2025.08.01 - v.1.02 : Using a splitmix64 generator to seed the state to avoid correlation on similar seeds, as recommended by the original authors. // : Added a seedable constructor with a single ulong. The four 64-bit state variables would be generated using splitmix64 function. // : Increased performance of all the public methods for generating random numbers. // 2025.08.03 - v.1.03 : Added RandomDoubleHighRes, a new method for generating high-resolution random doubles in the range of [0.0, 1.0). // 2025.08.03 - v.1.04 : Faster generation of unbiased random integers in a range by using Lemire's fastrange method. // 2025.08.28 - v.1.05 : Improved RNG seeding mechanism to enforce strong bit-avalanche. // 2025.09.13 - v.1.06 : Allow richer initialization by using full entropy from all four 64-bit seeds independently. // 2025.09.16 - v.1.07 : Implemented SHA-256 based seeding (uses CryptEncode with CRYPT_HASH_SHA256) to preserve full 256-bit entropy. // 2025.09.19 - v.1.08 : Added sampling utilities (Shuffle, Sample, SampleWithReplacement) to the Xoshiro256 class. // 2025.10.02 - v.1.09 : Implemented a dedicated boundedUInt32 function using the 32-bit variant of Lemire's fastrange to improve performance. // 2026.06.05 - v.1.10 : Guard against all-zero state after seeding + Replaced the Box–Muller zero edge case. //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Modificaciones // 1. uinteger ya remplzao en funciaon a has.. // 2. rolt inline (solo un caso) // 3. Para SHA ahora se usa la lib CryptByLeo #include //+------------------------------------------------------------------+ //| Class Xoshiro256 | //| Provides an implementation of Xoshiro256** 64-bit PRNG. | //| Features: | //| - High-quality 64-bit state (256-bit total) | //| - Uniform random integers, longs, doubles | //| - High-resolution doubles suitable for scientific simulations | //| - Normal (Gaussian) random sampling | //| - Array utilities: Shuffle, Sample, SampleWithReplacement | //| Notes: | //| - RNG state must never be all zeros. | //| - Bounded integer methods use Lemire's fast-rejection algorithm | //| - RandomDoubleHighRes may be slower due to full coverage of [0,1)| //+------------------------------------------------------------------+ class Xoshiro256 { protected: // RNG internal state (four 64-bit words) ulong s0, s1, s2, s3; // For caching a 32-bit integer. uint uinteger; // Internal helper functions ulong rotl(const ulong value, const int count); public: // Constructor and destructor Xoshiro256(void); // Auto-seeded constructor ~Xoshiro256(void) {} // Setting the internal state for the generator void Seed(ulong seed); void Seed(ulong a, ulong b, ulong c, ulong d); string GetName() const { return "Xoshiro 256** 64-bit"; } // Final uint nextUInt32(void); ulong nextUInt64(void); }; //+------------------------------------------------------------------+ //| Initializes a new instance of the Xoshiro class with auto-seed. | //+------------------------------------------------------------------+ void Xoshiro256::Xoshiro256(void) { // collect multiple entropy sources (non-cryptographic) // --- merjoas: mejortes fuentes de entropía ulong a = (ulong)TerminalInfoInteger(TERMINAL_PING_LAST) ^ (ulong)TerminalInfoInteger(TERMINAL_MEMORY_USED); ulong b = (ulong)GetTickCount64(); ulong c = (ulong)TerminalInfoDouble(TERMINAL_RETRANSMISSION) * 100000ULL; ulong d = (ulong)MQLInfoInteger(MQL_GLOBAL_COUNTER); // initialize RNG state Seed(a, b, c, d); } //+------------------------------------------------------------------+ //| Sets the internal state for the generator with multiple | //| 64-bit seeds. | //+------------------------------------------------------------------+ void Xoshiro256::Seed(ulong a, ulong b, ulong c, ulong d) { union ULongWords { ulong words[4]; uchar bytes[32]; }; ULongWords entropy; entropy.words[0] = a; entropy.words[1] = b; entropy.words[2] = c; entropy.words[3] = d; // compute SHA-256 (32-byte) digest of entropy bytes ULongWords digest = {}; // -- remplazo a CryptEncode nativo //int result = CryptEncode(CRYPT_HASH_SHA256, entropy.bytes, entropy.bytes, digest.bytes); TSN::CCryptoHash::SHA256(entropy.bytes, 32, digest.bytes, 0); // fill RNG state from the hash digest s0 = digest.words[0]; s1 = digest.words[1]; s2 = digest.words[2]; s3 = digest.words[3]; uinteger = 0; // prevent all-zero state after seeding if((s0 | s1 | s2 | s3) == 0) { s0 = 0x9E3779B97F4A7C15 ^ a; // fallback s1 = 0xBF58476D1CE4E5B9 ^ b; s2 = 0x94D049BB133111EB ^ c; s3 = 0xD2B74407B1CE6E93 ^ d; if((s0 | s1 | s2 | s3) == 0) s0 = 1; } } //+------------------------------------------------------------------+ //| Sets the internal state for the generator with a single | //| 64-bit seed (e.g., for reproducibility). | //+------------------------------------------------------------------+ void Xoshiro256::Seed(ulong seed) { //Seed(seed, 0UL, 0UL, 0UL); Seed(seed, seed ^ 0x9E3779B97F4A7C15, seed ^ 0xBF58476D1CE4E5B9, seed ^ 0x94D049BB133111EB); } //+------------------------------------------------------------------+ //| Rotates bits of a 64-bit value to the left by count positions. | //+------------------------------------------------------------------+ #define XOSHIRO_ROTL(value, count) ((value << count) | (value >> (64 - count))) ulong Xoshiro256::rotl(const ulong value, const int count) { return XOSHIRO_ROTL(value, count); // (value << count) | (value >> (64 - count)); } //+------------------------------------------------------------------+ //| Uniformly distributed 64-bit unsigned long integer [0,ULONG_MAX] | //| This is the heart of the generator. | //+------------------------------------------------------------------+ ulong Xoshiro256::nextUInt64(void) { const ulong result = rotl(s1 * 5, 7) * 9; // Update the generator state const ulong t = s1 << 17; s2 ^= s0; s3 ^= s1; s1 ^= s2; s0 ^= s3; s2 ^= t; s3 = XOSHIRO_ROTL(s3, 45); return result; } //+------------------------------------------------------------------+ //| Uniformly distributed 32-bit unsigned integer [0, UINT_MAX] | //+------------------------------------------------------------------+ uint Xoshiro256::nextUInt32(void) { if(uinteger) // en caso no haya un valor previo { const uint t = uinteger; // temporal uinteger = 0; // reset return t; // retornamos } const ulong next = nextUInt64(); uinteger = (uint)(next >> 32); return (uint)(next); } //--- #undef XOSHIRO_ROTL #endif // XOSHIRO256_XOSHIRO256_MQH