//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2026, Niquel Mendoza | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza" #property link "https://www.mql5.com" #property strict #ifndef PRNGBYLEO_SRC_MAIN_MQH #define PRNGBYLEO_SRC_MAIN_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Original API design and implementation: Copyright © 2025, Amr Ali // (https://forge.mql5.io/amrali/Xoshiro256) // Refactored into a generic template backend by Niquel Mendoza, 2026 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // convencion backend a de tener /* uint nextUInt32(void); ulong nextUInt64(void); Se deben de tener dichas funciones. */ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template class CRandom : public TBackend { private: ulong mulhi(const ulong x, const ulong y, ulong &lowPart); public: CRandom(void) {} ~CRandom(void) {} //--- Boundend for (32 \ 64) uint boundedUInt32(const uint bound); ulong boundedUInt64(const ulong bound); //--- Public methods for generating random numbers // Integer __forceinline int RandomInteger(void); // Random integer [0, INT_MAX] __forceinline int RandomInteger(const int max); // Random integer [0, max) __forceinline int RandomInteger(const int min, const int max); // Random integer [min, max) // Long __forceinline long RandomLong(void); // Random long [0, LONG_MAX] __forceinline long RandomLong(const long max); // Random long [0, max) __forceinline long RandomLong(const long min, const long max); // Random long [min, max) // Double __forceinline double RandomDouble(void); // Random double [0.0, 1.0) double RandomDouble(const double max); // Random double [0.0, max) double RandomDouble(const double min, const double max); // Random double [min, max) // Double High Res double RandomDoubleHighRes(void); // Random double [0.0, 1.0) double RandomDoubleHighRes(const double max); // Random double [0.0, max) double RandomDoubleHighRes(const double min, const double max); // Random double [min, max) // Boolean __forceinline bool RandomBoolean(void); // Random true/false (equal probability) bool RandomBoolean(const double prob_true); // Random true/false (with prob_true) // Normal double RandomNormal(void); // Random double (normal distribution with mean 0, std dev 1) __forceinline double RandomNormal(const double mu, const double sigma); // Random double (normal distribution) // Sampling utilities template void Shuffle(T &arr[]); // Shuffle array arr[] in place using Fisher-Yates template bool Sample(const T &arr[], T &dest[], int k); // Sample k unique items from arr[] without replacement template bool SampleWithReplacement(const T &arr[], T &dest[], int k); }; //+------------------------------------------------------------------+ //| Uniformly distributed random 32-bit INT, in range [0, INT_MAX] | //+------------------------------------------------------------------+ template __forceinline int CRandom::RandomInteger(void) { return (int)(nextUInt32() >> 1); } //+------------------------------------------------------------------+ //| Uniformly distributed random 32-bit INT, in the range [0, max) | //| i.e., inclusive of 0 and exclusive of max. | //+------------------------------------------------------------------+ template __forceinline int CRandom::RandomInteger(const int max) { return (max > 0) ? (int)boundedUInt32(max) : 0; } //+------------------------------------------------------------------+ //| Uniformly distributed random 32-bit INT, in the range [min, max) | //| i.e., inclusive of min and exclusive of max. | //+------------------------------------------------------------------+ template __forceinline int CRandom::RandomInteger(const int min, const int max) { return (max > min) ? (int)boundedUInt32(max - min) + min : min; } //+------------------------------------------------------------------+ //| Uniformly distributed random 64-bit LONG, in range [0, LONG_MAX] | //+------------------------------------------------------------------+ template __forceinline long CRandom::RandomLong(void) { return (long)(nextUInt64() >> 1); } //+------------------------------------------------------------------+ //| Uniformly distributed random 64-bit LONG, in the range [0, max) | //| i.e., inclusive of 0 and exclusive of max. | //+------------------------------------------------------------------+ template __forceinline long CRandom::RandomLong(const long max) { return (max > 0) ? (long)boundedUInt64(max) : 0; } //+------------------------------------------------------------------+ //| Uniformly distributed random 64-bit LONG, in the range [min, max)| //| i.e., inclusive of min and exclusive of max. | //+------------------------------------------------------------------+ template __forceinline long CRandom::RandomLong(const long min, const long max) { return (max > min) ? (long)boundedUInt64(max - min) + min : min; } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [0.0, 1.0) | //| i.e., inclusive of 0.0 and exclusive of 1.0. | //| Doubles are rounded down to the nearest multiple of 1/2^53. | //+------------------------------------------------------------------+ template __forceinline double CRandom::RandomDouble() { return (nextUInt64() >> 11) * (1.0 / (1ull << 53)); } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [0.0, max) | //| i.e., inclusive of 0.0 and exclusive of max. | //+------------------------------------------------------------------+ template double CRandom::RandomDouble(const double max) { if(max <= 0) return 0; double r; do { r = RandomDouble() * max; } while(r >= max); // Check for rounding error return r; } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [min, max) | //| i.e., inclusive of min and exclusive of max. | //+------------------------------------------------------------------+ template double CRandom::RandomDouble(const double min, const double max) { if(max <= min) return min; double r; double range = max - min; do { r = RandomDouble() * range + min; } while(r >= max); return r; } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [0.0, 1.0) | //| i.e., inclusive of 0.0 and exclusive of 1.0. | //| | //| https://docs.python.org/3/library/random.html#recipe | //| http://mumble.net/~campbell/tmp/random_real.c | //| | //| Uses an alternative precise sampling method that is capable of | //| generating all possible values in the interval [0, 1) that can | //| be represented by a double precision float, a feature important | //| for scientific simulation and other high-precision applications. | //| Note however that this method is slower than RandomDouble(). | //+------------------------------------------------------------------+ template double CRandom::RandomDoubleHighRes() { const ulong mantissa = 0x10000000000000 | nextUInt64() >> 12; // 2^52...2^53-1 double power2 = 0x20000000000000; // 2^53 uint x; while((x = nextUInt32()) == 0) { power2 *= 0x100000000; // 2^32 } // bitmask for rightmost 1-bit (geometric distribution) x &= (0u - x); power2 *= x; return mantissa / power2; } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [0.0, max) | //| i.e., inclusive of 0.0 and exclusive of max. | //+------------------------------------------------------------------+ template double CRandom::RandomDoubleHighRes(const double max) { if(max <= 0) return 0; double r; do { r = RandomDoubleHighRes() * max; } while(r >= max); return r; } //+------------------------------------------------------------------+ //| Uniformly distributed random double in the range [min, max) | //| i.e., inclusive of min and exclusive of max. | //+------------------------------------------------------------------+ template double CRandom::RandomDoubleHighRes(const double min, const double max) { if(max <= min) return min; double r; const double range = max - min; do { r = RandomDoubleHighRes() * range + min; } while(r >= max); return r; } //+------------------------------------------------------------------+ //| Uniformly distributed random true/false with equal probability | //+------------------------------------------------------------------+ template __forceinline bool CRandom::RandomBoolean(void) { // Use a high bit since the low bits are less random. return (nextUInt32() & 0x80000000) != 0; } //+------------------------------------------------------------------+ //| Random true/false where 'true' has the probability of prob_true | //+------------------------------------------------------------------+ template bool CRandom::RandomBoolean(const double prob_true) { if(prob_true < 0.0) return false; if(prob_true >= 1.0) return true; return RandomDouble() < prob_true; } //+------------------------------------------------------------------+ //| Random normal sample with specified mean and standard deviation | //+------------------------------------------------------------------+ template __forceinline double CRandom::RandomNormal(const double mu, const double sigma) { return mu + sigma * RandomNormal(); } //+------------------------------------------------------------------+ //| Random normal sample with mean 0 and standard deviation 1 | //+------------------------------------------------------------------+ template double CRandom::RandomNormal(void) { // Use Box-Muller algorithm double u1; do { u1 = RandomDouble(); } while(u1 == 0.0); const double u2 = RandomDouble(); const double r = ::MathSqrt(-2.0 * ::MathLog(u1)); const double theta = 2.0 * M_PI * u2; return r * ::MathSin(theta); } //+------------------------------------------------------------------+ //| Shuffle the order of array elements in-place. | //| To shuffle to a new array, use Sample(a, dest, ArraySize(a)). | //+------------------------------------------------------------------+ template template void CRandom::Shuffle(T &arr[]) { // Fisher-Yates shuffle int size = ArraySize(arr); for(int i = 0; i < size - 1; i++) { // Select random index and swap items [j] and [i] const int j = RandomInteger(i, size); const T tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } //+------------------------------------------------------------------+ //| Sample k unique items from array (without replacement) | //| k cannot be larger than the size of arr[]. | //+------------------------------------------------------------------+ template template bool CRandom::Sample(const T &arr[], T &dest[], int k) { int size = ArraySize(arr); if(size == 0 || k <= 0) return false; if(k > size) k = size; // cannot sample more than available if(ArrayResize(dest, k) != k) return false; // Make a temporary index array int idx[]; if(ArrayResize(idx, size) != size) return false; for(int i = 0; i < size; i++) idx[i] = i; // Select the first k random unique indices for(int i = 0; i < k; i++) { // Fisher-Yates shuffle on indices const int j = RandomInteger(i, size); const int tmp = idx[i]; idx[i] = idx[j]; idx[j] = tmp; dest[i] = arr[idx[i]]; } return true; } //+------------------------------------------------------------------+ //| Sample k items from array with replacement. | //| A sampled item is replaced back into arr[], so that the same | //| item can be selected more than once (duplicate selections). | //+------------------------------------------------------------------+ template template bool CRandom::SampleWithReplacement(const T &arr[], T &dest[], int k) { int size = ArraySize(arr); if(size == 0 || k <= 0) return false; if(ArrayResize(dest, k) != k) return false; // Pick a random index from arr[] for(int i = 0; i < k; i++) { const int ind = RandomInteger(0, size); dest[i] = arr[ind]; } return true; } //+------------------------------------------------------------------------+ //| Uniformly distributed random 64-bit ULONG, r, where 0 <= r < bound | //| This implementation uses a fast rejection method (Lemire's fastrange). | //| Source: https://github.com/lemire/fastrange | //+------------------------------------------------------------------------+ template ulong CRandom::boundedUInt64(const ulong bound) { ulong l; ulong m = mulhi(nextUInt64(), bound, l); if(l < bound) { ulong t = (0ull - bound) % bound; while(l < t) { m = mulhi(nextUInt64(), bound, l); } } return m; } //+------------------------------------------------------------------------+ //| Uniformly distributed random 32-bit UINT, r, where 0 <= r < bound | //| 32-bit variant of Lemire's fastrange. | //+------------------------------------------------------------------------+ template uint CRandom::boundedUInt32(const uint bound) { ulong m = nextUInt32() * (ulong)bound; uint l = (uint) m; if(l < bound) { uint t = (0u - bound) % bound; while(l < t) { m = nextUInt32() * (ulong)bound; l = (uint) m; } } return (uint)(m >> 32); } //+------------------------------------------------------------------+ //| Emulate 64x64 bit multiplication to 128 bit result. | //| Returns high part of product, and low part is set in `lowPart`. | //| github.com/numpy/numpy/blob/main/numpy/random/src/pcg64/pcg64.h | //+------------------------------------------------------------------+ template ulong CRandom::mulhi(const ulong x, const ulong y, ulong &lowPart) { lowPart = x * y; // Lower 64 bits are straightforward clock-arithmetic. ulong x0 = x & 0xFFFFFFFF; ulong x1 = x >> 32; ulong y0 = y & 0xFFFFFFFF; ulong y1 = y >> 32; ulong w0 = x0 * y0; ulong t = x1 * y0 + (w0 >> 32); ulong w1 = t & 0xFFFFFFFF; ulong w2 = t >> 32; w1 += x0 * y1; ulong hi = x1 * y1 + w2 + (w1 >> 32); return hi; } //+------------------------------------------------------------------+ } #endif // PRNGBYLEO_SRC_MAIN_MQH //+------------------------------------------------------------------+