192 lines
8.2 KiB
MQL5
192 lines
8.2 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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 <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"; }
|
|
|
|
// 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
|