splitmix64.hpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Copyright Sebastiano Vigna 2015.
  3. * Copyright Matt Borland 2022.
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. */
  9. #ifndef BOOST_RANDOM_SPLITMIX64_HPP
  10. #define BOOST_RANDOM_SPLITMIX64_HPP
  11. #include <cstdint>
  12. #include <cstdlib>
  13. #include <limits>
  14. #include <array>
  15. #include <string>
  16. #include <ios>
  17. #include <type_traits>
  18. namespace boost { namespace random {
  19. /**
  20. * This is a fixed-increment version of Java 8's SplittableRandom generator
  21. * See http://dx.doi.org/10.1145/2714064.2660195 and
  22. * http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
  23. * It is a very fast generator passing BigCrush, and it can be useful if
  24. * for some reason you absolutely want 64 bits of state; otherwise, we
  25. * rather suggest to use a xoroshiro128+ (for moderately parallel
  26. * computations) or xorshift1024* (for massively parallel computations)
  27. * generator.
  28. */
  29. class splitmix64
  30. {
  31. private:
  32. std::uint64_t state_;
  33. inline std::uint64_t concatenate(std::uint32_t word1, std::uint32_t word2) noexcept
  34. {
  35. return static_cast<std::uint64_t>(word1) << 32 | word2;
  36. }
  37. public:
  38. using result_type = std::uint64_t;
  39. using seed_type = std::uint64_t;
  40. // Required for old Boost.Random concept
  41. static constexpr bool has_fixed_range {false};
  42. /** Seeds the generator with the default seed. */
  43. void seed(result_type value = 0) noexcept
  44. {
  45. if (value == 0)
  46. {
  47. state_ = UINT64_C(0xA164B43C8F634A13);
  48. }
  49. else
  50. {
  51. state_ = value;
  52. }
  53. }
  54. /**
  55. * Seeds the generator with 32-bit values produced by @c seq.generate().
  56. */
  57. template <typename Sseq, typename std::enable_if<!std::is_convertible<Sseq, std::uint64_t>::value, bool>::type = true>
  58. void seed(Sseq& seq)
  59. {
  60. std::array<std::uint32_t, 2> seeds;
  61. seq.generate(seeds.begin(), seeds.end());
  62. state_ = concatenate(seeds[0], seeds[1]);
  63. }
  64. /**
  65. * Seeds the generator with 64-bit values produced by @c seq.generate().
  66. */
  67. template <typename Sseq, typename std::enable_if<!std::is_convertible<Sseq, splitmix64>::value, bool>::type = true>
  68. explicit splitmix64(Sseq& seq)
  69. {
  70. seed(seq);
  71. }
  72. /** Seeds the generator with a user provided seed. */
  73. template <typename T, typename std::enable_if<std::is_convertible<T, std::uint64_t>::value, bool>::type = true>
  74. void seed(T value = 0) noexcept
  75. {
  76. seed(static_cast<std::uint64_t>(value));
  77. }
  78. /** Seeds the generator with a user provided seed. */
  79. explicit splitmix64(std::uint64_t state = 0) noexcept
  80. {
  81. seed(state);
  82. }
  83. splitmix64(const splitmix64& other) = default;
  84. splitmix64& operator=(const splitmix64& other) = default;
  85. /** Returns the next value of the generator. */
  86. inline result_type next() noexcept
  87. {
  88. std::uint64_t z {state_ += UINT64_C(0x9E3779B97F4A7C15)};
  89. z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
  90. z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);
  91. return z ^ (z >> 31);
  92. }
  93. /** Returns the next value of the generator. */
  94. inline result_type operator()() noexcept
  95. {
  96. return next();
  97. }
  98. /** Advances the state of the generator by @c z. */
  99. inline void discard(std::uint64_t z) noexcept
  100. {
  101. for (std::uint64_t i {}; i < z; ++i)
  102. {
  103. next();
  104. }
  105. }
  106. /**
  107. * Returns true if the two generators will produce identical
  108. * sequences of values.
  109. */
  110. inline friend bool operator==(const splitmix64& lhs, const splitmix64& rhs) noexcept
  111. {
  112. return lhs.state_ == rhs.state_;
  113. }
  114. /**
  115. * Returns true if the two generators will produce different
  116. * sequences of values.
  117. */
  118. inline friend bool operator!=(const splitmix64& lhs, const splitmix64& rhs) noexcept
  119. {
  120. return !(lhs == rhs);
  121. }
  122. /** Writes a @c splitmix64 to a @c std::ostream. */
  123. template <typename CharT, typename Traits>
  124. inline friend std::basic_ostream<CharT,Traits>& operator<<(std::basic_ostream<CharT,Traits>& ost,
  125. const splitmix64& e)
  126. {
  127. ost << e.state_;
  128. return ost;
  129. }
  130. /** Writes a @c splitmix64 to a @c std::istream. */
  131. template <typename CharT, typename Traits>
  132. inline friend std::basic_istream<CharT,Traits>& operator>>(std::basic_istream<CharT,Traits>& ist,
  133. splitmix64& e)
  134. {
  135. std::string sstate;
  136. CharT val;
  137. while (ist >> val)
  138. {
  139. if (std::isdigit(val))
  140. {
  141. sstate.push_back(val);
  142. }
  143. }
  144. e.state_ = std::strtoull(sstate.c_str(), nullptr, 10);
  145. return ist;
  146. }
  147. /** Fills a range with random values */
  148. template <typename FIter>
  149. inline void generate(FIter first, FIter last) noexcept
  150. {
  151. while (first != last)
  152. {
  153. *first++ = next();
  154. }
  155. }
  156. /**
  157. * Returns the largest value that the @c splitmix64
  158. * can produce.
  159. */
  160. static constexpr result_type (max)() noexcept
  161. {
  162. return (std::numeric_limits<std::uint64_t>::max)();
  163. }
  164. /**
  165. * Returns the smallest value that the @c splitmix64
  166. * can produce.
  167. */
  168. static constexpr result_type (min)() noexcept
  169. {
  170. return (std::numeric_limits<std::uint64_t>::min)();
  171. }
  172. };
  173. }} // Namespace boost::random
  174. #endif // BOOST_RANDOM_SPLITMIX64_HPP