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