Zero  0.1.0
uniform_int_distribution.hpp
Go to the documentation of this file.
1 #ifndef __UNIFORM_INT_DISTRIBUTION_HPP
2 #define __UNIFORM_INT_DISTRIBUTION_HPP
3 
4 #include <type_traits>
5 #include <limits>
6 #include <cassert>
7 #include <cstdint>
8 #include <iostream>
9 #include <cmath>
10 #include "cgs/meta.hpp"
11 #include "gcem.hpp"
12 #include "w_debug.h"
13 #include <boost/integer.hpp>
14 #include <boost/random/uniform_int_distribution.hpp>
15 #include <boost/random/detail/uniform_int_float.hpp>
16 #include <boost/random/taus88.hpp>
17 #include <boost/random/mersenne_twister.hpp>
18 #include <boost/random/ranlux.hpp>
19 
21 
22  namespace details {
23 
24  template<typename int_type>
25  constexpr uint16_t log2(int_type n) {
26  return ((n < 2) ? 1 : 1 + log2(n / 2));
27  };
28  } // details
29 
65  template<class int_type = int>
67  static_assert(std::is_integral<int_type>::value
68  && !std::is_same<int_type, bool>::value,
69  "'int_type' is not an integral type or it is bool");
70 
71  public:
75  using result_type = int_type;
76 
80  struct param_type {
81  public:
86 
91  param_type(0) {};
92 
99  explicit param_type(int_type lowerLimit = 0, int_type upperLimit = std::numeric_limits<int_type>::max()) :
100  _lowerLimit(lowerLimit),
101  _upperLimit(upperLimit) {
102  assert(_lowerLimit < _upperLimit);
103  };
104 
110  int_type a() const noexcept {
111  return _lowerLimit;
112  }
113 
119  int_type b() const noexcept {
120  return _upperLimit;
121  }
122 
133  friend bool operator==(const param_type& parameters0, const param_type& parameters1) noexcept {
134  return parameters0._lowerLimit == parameters1._lowerLimit
135  && parameters0._upperLimit == parameters1._upperLimit;
136  }
137 
148  friend bool operator!=(const param_type& parameters0, const param_type& parameters1) noexcept {
149  return !(parameters0 == parameters1);
150  }
151 
152  private:
156  int_type _lowerLimit;
157 
161  int_type _upperLimit;
162  };
163 
169 
176  explicit biased_uniform_int_distribution(int_type lowerLimit,
177  int_type upperLimit = std::numeric_limits<int_type>::max()) :
178  _parameters(lowerLimit, upperLimit),
179  _offset(lowerLimit),
180  _range(upperLimit - lowerLimit),
181  _rangeBits(static_cast<uint16_t>(std::ceil(std::log2(_range + 2)))),
182  _fallbackDistribution(lowerLimit, upperLimit) {};
183 
189  explicit biased_uniform_int_distribution(const param_type& parameters) :
190  biased_uniform_int_distribution(parameters.a(), parameters.b()) {};
191 
197  void reset() const noexcept {};
198 
204  result_type a() const noexcept {
205  return _parameters.a();
206  };
207 
213  result_type b() const noexcept {
214  return _parameters.b();
215  };
216 
222  param_type param() const noexcept {
223  return _parameters;
224  };
225 
231  void param(const param_type& parameters) noexcept {
232  _parameters = parameters;
233  _offset = parameters.a();
234  _range = parameters.a() - parameters.b();
235  _rangeBits = static_cast<uint16_t>(std::ceil(std::log2(_range + 2)));
236  _fallbackDistribution.param(parameters);
237  };
238 
244  result_type min() const noexcept {
245  return this->a();
246  };
247 
253  result_type max() const noexcept {
254  return this->b();
255  }
256 
271  template<typename uniform_random_number_generator>
272  result_type operator()(uniform_random_number_generator& uniformRandomNumberGenerator) const {
273  // The output data type of the underlying PRNG:
274  using generator_type = typename uniform_random_number_generator::result_type;
275  // The unsigned equivalent of the output data type of the underlying PRNG:
276  // In case the output data type of the underlying PRNG is already unsigned, this should be the same or one
277  // with a higher bitwidth and in case the output data type of the underlying PRNG is float, this should be
278  // an unsigned integer data type with the same or a higher bitwidth.
279  using unsigned_generator_type = typename boost::uint_t<sizeof(generator_type) * CHAR_BIT>::fast;
280  // The unsigned equivalent of this random distribution facility's output type:
281  // In case the output type of this random distribution facility is already unsigned, this is the same type.
282  using unsigned_result_type = typename std::make_unsigned<result_type>::type;
283 
284  // Whether the actual output range of the underlying PRNG is known at compile-time:
285  constexpr bool minMaxAvailable = cgs::is_constexpr<uniform_random_number_generator::min>()
286  && cgs::is_constexpr<uniform_random_number_generator::max>();
287  // The length of the actual output range of the underlying PRNG or 0 if it outputs floats or if the range is
288  // not known at compile-time:
289  constexpr unsigned_generator_type generatorRange =
290  !minMaxAvailable || !std::is_integral<generator_type>::value ? 0
291  : gcem::abs(
292  static_cast<unsigned_generator_type>(uniform_random_number_generator::max())
293  - static_cast<unsigned_generator_type>(uniform_random_number_generator::min()));
294  // The effective bitwidth of the actual output range of the underlying integer PRNG if known at compile-time
295  // or the bitwidth of the output data type of the underlying PRNG:
296  constexpr uint16_t generatorBits = std::is_integral<generator_type>::value
298  ? !minMaxAvailable ? static_cast<uint16_t>(0)
299  : static_cast<uint16_t>(
300  details::log2<unsigned_generator_type>(generatorRange + 1) - 1)
301  : static_cast<uint16_t>(sizeof(generator_type) * CHAR_BIT);
302  // The bitwidth of the output data type of the underlying PRNG:
303  constexpr uint16_t generatorTypeBits = static_cast<uint16_t>(sizeof(generator_type) * CHAR_BIT);
304 
305  // The highest possible length of the output range of this random distribution facility (based on the range
306  // of the output data type):
307  constexpr unsigned_result_type resultRange = std::numeric_limits<unsigned_result_type>::max();
308  // The bitwidth of the output data type of this random distribution facility:
309  constexpr uint16_t resultBits = static_cast<uint16_t>(sizeof(result_type) * CHAR_BIT);
310 
311  // Unsigned and signed integer types with a bitwidth which is at least double the bitwidth of the one of the
312  // output data type of this random distribution facility:
313  using unsigned_double_width_result_type = typename boost::uint_t<resultBits * 2>::fast;
314  using signed_double_width_result_type = typename boost::int_t<resultBits * 2>::fast;
315 
316  // The output range of the underlying PRNG is known at compile-time and it is a PRNG generating integer
317  // numbers:
318  if constexpr (minMaxAvailable && std::is_integral<generator_type>::value) {
319  // The effective bitwidth of the output range of the PRNG is at least as high as the bitwidth of the
320  // output data type of this random distribution facility:
321  if constexpr (resultBits <= generatorBits) {
322  DBG1(<< "Best Case: " << typeid(uniformRandomNumberGenerator).name() << " (" << static_cast<uint32_t>(_rangeBits) << " <= log2(" << static_cast<uint64_t>(generatorRange) << ") = " << static_cast<uint32_t>(generatorBits) << ")");
323  unsigned_generator_type x = static_cast<unsigned_generator_type>(uniformRandomNumberGenerator()
325  if constexpr (generatorBits != details::log2(generatorRange)) {
326  constexpr unsigned_generator_type bitMask =
327  gcem::pow<unsigned_generator_type, unsigned_generator_type>(2, generatorBits) - 1;
328  x = x & bitMask;
329  }
330  if constexpr (std::is_unsigned<result_type>::value && resultBits <= 32 && generatorBits <= 32) {
331  unsigned_double_width_result_type m = static_cast<unsigned_double_width_result_type>(x)
332  * (static_cast<unsigned_double_width_result_type>(_range)
333  + static_cast<unsigned_double_width_result_type>(1));
334  return static_cast<result_type>(_offset + (m >> generatorBits));
335  } else if (std::is_unsigned<result_type>::value) {
336  __uint128_t m = static_cast<__uint128_t>(x)
337  * (static_cast<__uint128_t>(_range)
338  + static_cast<__uint128_t>(1));
339  return static_cast<result_type>(_offset + (m >> generatorBits));
340  } else if (std::is_signed<result_type>::value && resultBits <= 32 && generatorBits <= 32) {
341  signed_double_width_result_type m = static_cast<signed_double_width_result_type>(x)
342  * (static_cast<signed_double_width_result_type>(_range)
343  + static_cast<signed_double_width_result_type>(1));
344  return static_cast<result_type>(_offset + (m >> generatorBits));
345  } else {
346  __int128_t m = static_cast<__int128_t>(x)
347  * (static_cast<__int128_t>(_range)
348  + static_cast<__int128_t>(1));
349  return static_cast<result_type>(_offset + (m >> generatorBits));
350  }
351  // The effective bitwidth of the output range of the PRNG is lower than the one of the output data
352  // type of this random distribution facility. But the effective bitwidth of the output range of this
353  // random distribution facility (which is never known at compile-time) might still be lower than the
354  // effective bitwidth of the output range of the PRNG:
355  } else {
356  // The effective bitwidth of the output range of the PRNG is at least as high as the effective
357  // bitwidth of the output range of this random distribution facility:
358  if (_rangeBits <= generatorBits) {
359  DBG1(<< "176: " << typeid(uniformRandomNumberGenerator).name() << " (" << static_cast<uint32_t>(_rangeBits) << " <= log2(" << static_cast<uint64_t>(generatorRange) << ") = " << static_cast<uint32_t>(generatorBits) << ")");
360  unsigned_generator_type x = static_cast<unsigned_generator_type>(uniformRandomNumberGenerator()
362  if constexpr (generatorBits != details::log2(generatorRange)) {
363  constexpr unsigned_generator_type bitMask =
364  gcem::pow<unsigned_generator_type, unsigned_generator_type>(2, generatorBits) - 1;
365  x = x & bitMask;
366  }
367  if constexpr (std::is_unsigned<result_type>::value && resultBits <= 32 && generatorBits <= 32) {
368  unsigned_double_width_result_type m = static_cast<unsigned_double_width_result_type>(x)
369  * (static_cast<unsigned_double_width_result_type>(_range)
370  + static_cast<unsigned_double_width_result_type>(1));
371  return static_cast<result_type>(_offset + (m >> generatorBits));
372  } else if (std::is_unsigned<result_type>::value) {
373  __uint128_t m = static_cast<__uint128_t>(x)
374  * (static_cast<__uint128_t>(_range)
375  + static_cast<__uint128_t>(1));
376  return static_cast<result_type>(_offset + (m >> generatorBits));
377  } else if (std::is_signed<result_type>::value && resultBits <= 32 && generatorBits <= 32) {
378  signed_double_width_result_type m = static_cast<signed_double_width_result_type>(x)
379  * (static_cast<signed_double_width_result_type>(_range)
380  + static_cast<signed_double_width_result_type>(1));
381  return static_cast<result_type>(_offset + (m >> generatorBits));
382  } else {
383  __int128_t m = static_cast<__int128_t>(x)
384  * (static_cast<__int128_t>(_range)
385  + static_cast<__int128_t>(1));
386  return static_cast<result_type>(_offset + (m >> generatorBits));
387  }
388  // The effective bitwidth of the output range of the PRNG is lower than the effective bitwidth of
389  // the output range of this random distribution facility and therefore multiple random numbers
390  // generated by the underlying PRNG are required to get enough entropy for a biased uniformly
391  // distributed random integer in the output range:
392  } else {
393  DBG1(<< "205: " << typeid(uniformRandomNumberGenerator).name() << " (" << static_cast<uint32_t>(_rangeBits) << " > log2(" << static_cast<uint64_t>(generatorRange) << ") = " << static_cast<uint32_t>(generatorBits) << ")");
394  return _fallbackDistribution(uniformRandomNumberGenerator);
395  }
396  }
397  // The output range of the underlying PRNG is not known at compile-time or it is a PRNG generating floating
398  // point numbers:
399  } else {
400  unsigned_generator_type generatorRange;
401  if constexpr (std::is_integral<generator_type>::value) {
402  generatorRange = gcem::abs(
403  static_cast<unsigned_generator_type>(uniform_random_number_generator::max())
404  - static_cast<unsigned_generator_type>(uniform_random_number_generator::min()));
405  } else {
406 
407  generatorRange = gcem::abs(
408  static_cast<unsigned_generator_type>(boost::random::detail::uniform_int_float<
409  uniform_random_number_generator>::max())
410  - static_cast<unsigned_generator_type>(boost::random::detail::uniform_int_float<
411  uniform_random_number_generator>::min()));
412  }
413  uint16_t generatorBits = generatorRange == std::numeric_limits<unsigned_generator_type>::max()
414  ? static_cast<uint16_t>(sizeof(generator_type) * CHAR_BIT)
415  : static_cast<uint16_t>(std::floor(std::log2(generatorRange + 1)));
416 
417  // The effective bitwidth of the output range of the PRNG is at least as high as the effective bitwidth
418  // of the output range of this random distribution facility:
419  if (_rangeBits <= generatorBits) {
420  DBG1(<< "223: " << typeid(uniformRandomNumberGenerator).name() << " (" << static_cast<uint32_t>(_rangeBits) << " <= log2(" << static_cast<uint64_t>(generatorRange) << ") = " << static_cast<uint32_t>(generatorBits) << ")");
421  unsigned_generator_type x;
422  if constexpr (std::is_integral<generator_type>::value) {
423  x = static_cast<unsigned_generator_type>(uniformRandomNumberGenerator()
425  if (generatorBits < generatorTypeBits) {
426  unsigned_generator_type bitMask =
427  gcem::pow<unsigned_generator_type, unsigned_generator_type>(2, generatorBits) - 1;
428  x = x & bitMask;
429  }
430  } else {
431  boost::random::detail::uniform_int_float<uniform_random_number_generator> floatToInt(
432  uniformRandomNumberGenerator);
433 
434  x = static_cast<unsigned_generator_type>(floatToInt());
435  }
436  if constexpr (std::is_unsigned<result_type>::value && resultBits <= 32 && generatorTypeBits <= 32) {
437  unsigned_double_width_result_type m = static_cast<unsigned_double_width_result_type>(x)
438  * (static_cast<unsigned_double_width_result_type>(_range)
439  + static_cast<unsigned_double_width_result_type>(1));
440  return static_cast<result_type>(_offset + (m >> generatorBits));
441  } else if (std::is_unsigned<result_type>::value) {
442  __uint128_t m = static_cast<__uint128_t>(x)
443  * (static_cast<__uint128_t>(_range)
444  + static_cast<__uint128_t>(1));
445  return static_cast<result_type>(_offset + (m >> generatorBits));
446  } else if (std::is_signed<result_type>::value && resultBits <= 32 && generatorTypeBits <= 32) {
447  signed_double_width_result_type m = static_cast<signed_double_width_result_type>(x)
448  * (static_cast<signed_double_width_result_type>(_range)
449  + static_cast<signed_double_width_result_type>(1));
450  return static_cast<result_type>(_offset + (m >> generatorBits));
451  } else {
452  __int128_t m = static_cast<__int128_t>(x)
453  * (static_cast<__int128_t>(_range)
454  + static_cast<__int128_t>(1));
455  return static_cast<result_type>(_offset + (m >> generatorBits));
456  }
457  // The effective bitwidth of the output range of the PRNG is lower than the effective bitwidth of the
458  // output range of this random distribution facility and therefore multiple random numbers generated by
459  // the underlying PRNG are required to get enough entropy for a biased uniformly distributed random
460  // integer in the output range:
461  } else {
462  DBG1(<< "258: " << typeid(uniformRandomNumberGenerator).name() << " (" << static_cast<uint32_t>(_rangeBits) << " > log2(" << static_cast<uint64_t>(generatorRange) << ") = " << static_cast<uint32_t>(generatorBits) << ")");
463  return _fallbackDistribution(uniformRandomNumberGenerator);
464  }
465  }
466  };
467 
478  const biased_uniform_int_distribution<result_type>& distribution1) noexcept {
479  return distribution0._parameters == distribution1._parameters;
480  };
481 
492  const biased_uniform_int_distribution<result_type>& distribution1) noexcept {
493  return !(distribution0 == distribution1);
494  };
495 
509  template<class CharT, class Traits, class result_type>
510  friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& outputStream,
512  result_type>& distribution) {
513  outputStream << distribution._parameters._a << "<" << distribution._parameters._b << std::endl;
514 
515  return outputStream;
516  };
517 
534  template<class CharT, class Traits, class result_type>
535  friend std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& inputStream,
537  result_type>& distribution) {
538  result_type lowerBound;
539  char separator;
540  result_type upperBound;
541 
542  inputStream >> lowerBound >> separator >> upperBound;
543  if (separator == '<') {
544  distribution.param(param_type(lowerBound, upperBound));
545  }
546 
547  return inputStream;
548  };
549 
550  private:
554  param_type _parameters;
555 
561  int_type _offset;
562 
568  int_type _range;
569 
576  uint16_t _rangeBits;
577 
584  boost::random::uniform_int_distribution<int_type> _fallbackDistribution;
585  };
586 } // zero::uniform_int_distribution
587 
588 #endif // __UNIFORM_INT_DISTRIBUTION_HPP
result_type a() const noexcept
Returns the lower limit of the output range.
Definition: uniform_int_distribution.hpp:204
biased_uniform_int_distribution()
Constructs an unspecified biased uniform random integer distribution facility.
Definition: uniform_int_distribution.hpp:167
int_type _range
The length of the output range of this random distribution facility.
Definition: uniform_int_distribution.hpp:568
result_type min() const noexcept
Returns the minimum integer value returned by this random distribution facility.
Definition: uniform_int_distribution.hpp:244
int_type a() const noexcept
Returns the lower limit of the random distribution&#39;s output range.
Definition: uniform_int_distribution.hpp:110
Configuration of a random distribution facility.
Definition: uniform_int_distribution.hpp:80
STL namespace.
biased_uniform_int_distribution(int_type lowerLimit, int_type upperLimit=std::numeric_limits< int_type >::max())
Constructs a biased uniform random integer distribution facility with given lower and upper limits...
Definition: uniform_int_distribution.hpp:176
param_type(int_type lowerLimit=0, int_type upperLimit=std::numeric_limits< int_type >::max())
Constructs a configuration for a random distribution facility with given lower and upper limits...
Definition: uniform_int_distribution.hpp:99
int_type b() const noexcept
Returns the upper limit of the random distribution&#39;s output range.
Definition: uniform_int_distribution.hpp:119
bf_idx result_type
The output data type of this distribution facility.
Definition: uniform_int_distribution.hpp:75
param_type _parameters
The configuration of this random distribution facility.
Definition: uniform_int_distribution.hpp:554
friend bool operator!=(const biased_uniform_int_distribution< result_type > &distribution0, const biased_uniform_int_distribution< result_type > &distribution1) noexcept
Compares two biased uniform random integer distribution facilities for inequality.
Definition: uniform_int_distribution.hpp:491
result_type b() const noexcept
Returns the upper limit of the output range.
Definition: uniform_int_distribution.hpp:213
int_type _lowerLimit
The lower limit of the random distribution&#39;s output range.
Definition: uniform_int_distribution.hpp:156
friend bool operator==(const param_type &parameters0, const param_type &parameters1) noexcept
Compares two configuration for random distribution facilities for equality.
Definition: uniform_int_distribution.hpp:133
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &inputStream, const biased_uniform_int_distribution< result_type > &distribution)
Deserializes a random distribution facility from the given stream and apply its parameters to the giv...
Definition: uniform_int_distribution.hpp:535
const T max(const T x, const T y)
Definition: w_minmax.h:45
int_type _offset
The lower limit of output range of this random distribution facility.
Definition: uniform_int_distribution.hpp:561
#define DBG1(a)
Definition: w_debug.h:251
boost::random::uniform_int_distribution< int_type > _fallbackDistribution
The fallback random distribution facility.
Definition: uniform_int_distribution.hpp:584
param_type()
Constructs an unspecified configuration for a random distribution facility.
Definition: uniform_int_distribution.hpp:90
Definition: uniform_int_distribution.hpp:20
void reset() const noexcept
Resets the internal state of the random distribution facility.
Definition: uniform_int_distribution.hpp:197
uint16_t _rangeBits
The number of bits required to differentiate all the values in the output range of this random distri...
Definition: uniform_int_distribution.hpp:576
Distributes random numbers from a PRNG uniformly (but biased) distributed in a range.
Definition: uniform_int_distribution.hpp:66
param_type param() const noexcept
Returns the configuration of this random distribution facility.
Definition: uniform_int_distribution.hpp:222
int_type _upperLimit
The upper limit of the random distribution&#39;s output range.
Definition: uniform_int_distribution.hpp:161
void param(const param_type &parameters) noexcept
Changes this biased uniform random integer distribution facility according the given configuration...
Definition: uniform_int_distribution.hpp:231
const T min(const T x, const T y)
Definition: w_minmax.h:52
constexpr uint16_t log2(int_type n)
Definition: uniform_int_distribution.hpp:25
friend bool operator!=(const param_type &parameters0, const param_type &parameters1) noexcept
Compares two configuration for random distribution facilities for inequality.
Definition: uniform_int_distribution.hpp:148
result_type max() const noexcept
Returns the maximum integer value returned by this random distribution facility.
Definition: uniform_int_distribution.hpp:253
friend bool operator==(const biased_uniform_int_distribution< result_type > &distribution0, const biased_uniform_int_distribution< result_type > &distribution1) noexcept
Compares two biased uniform random integer distribution facilities for equality.
Definition: uniform_int_distribution.hpp:477
biased_uniform_int_distribution(const param_type &parameters)
Constructs a biased uniform random integer distribution facility based on a given configuration...
Definition: uniform_int_distribution.hpp:189
result_type operator()(uniform_random_number_generator &uniformRandomNumberGenerator) const
Generates the next biased uniformly distributed random integer in the given range from the given PRNG...
Definition: uniform_int_distribution.hpp:272