1 /* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
2
3 #ifndef MPT_RANDOM_RANDOM_HPP
4 #define MPT_RANDOM_RANDOM_HPP
5
6
7
8 #include "mpt/base/namespace.hpp"
9 #include "mpt/random/engine.hpp"
10
11 #include <type_traits>
12
13
14 namespace mpt {
15 inline namespace MPT_INLINE_NS {
16
17
18
19 template <typename T, typename Trng>
random(Trng & rng)20 inline T random(Trng & rng) {
21 static_assert(std::numeric_limits<T>::is_integer);
22 typedef typename std::make_unsigned<T>::type unsigned_T;
23 const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
24 unsigned_T result = 0;
25 for (std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) {
26 if constexpr (rng_bits < (sizeof(T) * 8)) {
27 constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
28 result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
29 } else {
30 result = static_cast<unsigned_T>(rng());
31 }
32 }
33 return static_cast<T>(result);
34 }
35
36 template <typename T, std::size_t required_entropy_bits, typename Trng>
random(Trng & rng)37 inline T random(Trng & rng) {
38 static_assert(std::numeric_limits<T>::is_integer);
39 typedef typename std::make_unsigned<T>::type unsigned_T;
40 const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
41 unsigned_T result = 0;
42 for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) {
43 if constexpr (rng_bits < (sizeof(T) * 8)) {
44 constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
45 result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
46 } else {
47 result = static_cast<unsigned_T>(rng());
48 }
49 }
50 if constexpr (required_entropy_bits >= (sizeof(T) * 8)) {
51 return static_cast<T>(result);
52 } else {
53 return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
54 }
55 }
56
57 template <typename T, typename Trng>
random(Trng & rng,std::size_t required_entropy_bits)58 inline T random(Trng & rng, std::size_t required_entropy_bits) {
59 static_assert(std::numeric_limits<T>::is_integer);
60 typedef typename std::make_unsigned<T>::type unsigned_T;
61 const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
62 unsigned_T result = 0;
63 for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) {
64 if constexpr (rng_bits < (sizeof(T) * 8)) {
65 constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
66 result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
67 } else {
68 result = static_cast<unsigned_T>(rng());
69 }
70 }
71 if (required_entropy_bits >= (sizeof(T) * 8)) {
72 return static_cast<T>(result);
73 } else {
74 return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
75 }
76 }
77
78 template <typename T>
79 struct uniform_real_distribution {
80 private:
81 T a;
82 T b;
83
84 public:
uniform_real_distributionmpt::MPT_INLINE_NS::uniform_real_distribution85 inline uniform_real_distribution(T a_, T b_)
86 : a(a_)
87 , b(b_) {
88 return;
89 }
90 template <typename Trng>
operator ()mpt::MPT_INLINE_NS::uniform_real_distribution91 inline T operator()(Trng & rng) const {
92 const int mantissa_bits = std::numeric_limits<T>::digits;
93 return ((b - a) * static_cast<T>(mpt::random<uint64, mantissa_bits>(rng)) / static_cast<T>((static_cast<uint64>(1u) << mantissa_bits))) + a;
94 }
95 };
96
97
98 template <typename T, typename Trng>
random(Trng & rng,T min,T max)99 inline T random(Trng & rng, T min, T max) {
100 static_assert(!std::numeric_limits<T>::is_integer);
101 typedef mpt::uniform_real_distribution<T> dis_type;
102 dis_type dis(min, max);
103 return static_cast<T>(dis(rng));
104 }
105
106
107
108 } // namespace MPT_INLINE_NS
109 } // namespace mpt
110
111
112
113 #endif // MPT_RANDOM_RANDOM_HPP
114