Skip to content

Commit 7d17c6d

Browse files
medeirosdevguitargeek
authored andcommitted
[math] Make TRandom compatible with UniformRandomBitGenerator
TRandom and its subclasses can now be used directly with standard library algorithms like std::shuffle and std::uniform_int_distribution. This is done by adding the three required members to TRandom: result_type, min(), max(), and operator(). The operator() delegates to the virtual Rndm(), so polymorphism is preserved across all TRandom subclasses. Fixes #7979
1 parent 546ba89 commit 7d17c6d

2 files changed

Lines changed: 69 additions & 7 deletions

File tree

math/mathcore/inc/TRandom.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
//////////////////////////////////////////////////////////////////////////
2424

2525
#include "TNamed.h"
26+
#include <limits>
2627

2728
class TRandom : public TNamed, public ROOT::Math::TRandomEngine {
2829

@@ -56,6 +57,16 @@ class TRandom : public TNamed, public ROOT::Math::TRandomEngine {
5657
virtual Double_t Uniform(Double_t x1, Double_t x2);
5758
virtual void WriteRandom(const char *filename) const;
5859

60+
// std::UniformRandomBitGenerator interface -- makes TRandom usable directly
61+
// with std::shuffle, std::uniform_int_distribution and similar.
62+
using result_type = UInt_t;
63+
static constexpr result_type min() { return 0; }
64+
static constexpr result_type max() { return std::numeric_limits<UInt_t>::max(); }
65+
// NOTE: Rndm() returns a double in ]0,1[, so converting back to UInt_t
66+
// has a small precision loss. Subclasses with access to raw integer output
67+
// could override this for better accuracy.
68+
result_type operator()() { return static_cast<result_type>(Rndm() * (static_cast<double>(max()) + 1.0)); }
69+
5970
ClassDefOverride(TRandom,3) //Simple Random number generator (periodicity = 10**9)
6071
};
6172

math/mathcore/test/testMathRandom.cxx

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
//#include "TRandomNew3.h"
1616

1717
#include "TStopwatch.h"
18+
#include <algorithm>
1819
#include <iostream>
19-
20+
#include <numeric>
2021
#include <random>
22+
#include <type_traits>
2123

2224
using namespace ROOT::Math;
2325

@@ -193,21 +195,70 @@ bool test4() {
193195
}
194196

195197

198+
bool test5() {
199+
std::cout << "\nTesting TRandom std::UniformRandomBitGenerator interface" << std::endl;
200+
201+
TRandom3 rng(42);
202+
203+
static_assert(std::is_same<TRandom::result_type, UInt_t>::value, "result_type must be UInt_t");
204+
205+
if (TRandom::min() != 0) {
206+
std::cout << "TRandom::min() is not 0" << std::endl;
207+
return false;
208+
}
209+
if (TRandom::max() != std::numeric_limits<UInt_t>::max()) {
210+
std::cout << "TRandom::max() is wrong" << std::endl;
211+
return false;
212+
}
213+
214+
for (int i = 0; i < 10000; i++) {
215+
auto v = rng();
216+
if (v < TRandom::min() || v > TRandom::max()) {
217+
std::cout << "operator() returned out-of-range value: " << v << std::endl;
218+
return false;
219+
}
220+
}
221+
222+
std::vector<int> vec(10);
223+
std::iota(vec.begin(), vec.end(), 1);
224+
std::shuffle(vec.begin(), vec.end(), rng);
225+
std::sort(vec.begin(), vec.end());
226+
for (int i = 0; i < 10; i++) {
227+
if (vec[i] != i + 1) {
228+
std::cout << "std::shuffle corrupted the elements" << std::endl;
229+
return false;
230+
}
231+
}
232+
233+
std::uniform_int_distribution<int> dist(0, 99);
234+
for (int i = 0; i < 10000; i++) {
235+
int v = dist(rng);
236+
if (v < 0 || v > 99) {
237+
std::cout << "std::uniform_int_distribution produced out-of-range value: " << v << std::endl;
238+
return false;
239+
}
240+
}
241+
242+
std::cout << "TRandom std interface: OK" << std::endl;
243+
return true;
244+
}
245+
196246
bool testMathRandom() {
197247

198-
248+
199249
bool ret = true;
200250
std::cout << "testing generating " << NR << " numbers " << std::endl;
201251

202-
ret &= test1();
203-
ret &= test2();
204-
ret &= test3();
205-
ret &= test4();
252+
ret &= test1();
253+
ret &= test2();
254+
ret &= test3();
255+
ret &= test4();
256+
ret &= test5();
206257

207258
if (!ret) Error("testMathRandom","Test Failed");
208259
else
209260
std::cout << "\nTestMathRandom: OK \n";
210-
return ret;
261+
return ret;
211262
}
212263

213264
int main() {

0 commit comments

Comments
 (0)