PNRGByLeo/Src/Imp/Xho/Xho.mqh

192 lines
8.2 KiB
MQL5
Raw Permalink Normal View History

2026-08-31 21:32:45 -05:00
//+------------------------------------------------------------------+
//| 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
2026-09-01 09:13:18 -05:00
// 1. uinteger ya remplzao en funciaon a has..
2026-08-31 21:32:45 -05:00
// 2. rolt inline (solo un caso)
// 3. Para SHA ahora se usa la lib CryptByLeo
#include <TSN\\Crypto\\Hash\\SHA256.mqh>
//+------------------------------------------------------------------+
//| 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"; }
2026-09-01 09:13:18 -05:00
// Final
uint nextUInt32(void);
ulong nextUInt64(void);
2026-08-31 21:32:45 -05:00
};
//+------------------------------------------------------------------+
//| 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);
2026-09-01 08:23:33 -05:00
return (uint)(next);
2026-08-31 21:32:45 -05:00
}
2026-09-01 09:13:18 -05:00
2026-08-31 21:32:45 -05:00
//---
#undef XOSHIRO_ROTL
#endif // XOSHIRO256_XOSHIRO256_MQH