You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

pcg_engine.h 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
  15. #define ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
  16. #include <type_traits>
  17. #include "absl/base/config.h"
  18. #include "absl/meta/type_traits.h"
  19. #include "absl/numeric/bits.h"
  20. #include "absl/numeric/int128.h"
  21. #include "absl/random/internal/fastmath.h"
  22. #include "absl/random/internal/iostream_state_saver.h"
  23. namespace absl
  24. {
  25. ABSL_NAMESPACE_BEGIN
  26. namespace random_internal
  27. {
  28. // pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in
  29. // C++. PCG combines a linear congruential generator (LCG) with output state
  30. // mixing functions to generate each random variate. pcg_engine supports only a
  31. // single sequence (oneseq), and does not support streams.
  32. //
  33. // pcg_engine is parameterized by two types:
  34. // Params, which provides the multiplier and increment values;
  35. // Mix, which mixes the state into the result.
  36. //
  37. template<typename Params, typename Mix>
  38. class pcg_engine
  39. {
  40. static_assert(std::is_same<typename Params::state_type, typename Mix::state_type>::value, "Class-template absl::pcg_engine must be parameterized by "
  41. "Params and Mix with identical state_type");
  42. static_assert(std::is_unsigned<typename Mix::result_type>::value, "Class-template absl::pcg_engine must be parameterized by "
  43. "an unsigned Mix::result_type");
  44. using params_type = Params;
  45. using mix_type = Mix;
  46. using state_type = typename Mix::state_type;
  47. public:
  48. // C++11 URBG interface:
  49. using result_type = typename Mix::result_type;
  50. static constexpr result_type(min)()
  51. {
  52. return (std::numeric_limits<result_type>::min)();
  53. }
  54. static constexpr result_type(max)()
  55. {
  56. return (std::numeric_limits<result_type>::max)();
  57. }
  58. explicit pcg_engine(uint64_t seed_value = 0)
  59. {
  60. seed(seed_value);
  61. }
  62. template<class SeedSequence, typename = typename absl::enable_if_t<!std::is_same<SeedSequence, pcg_engine>::value>>
  63. explicit pcg_engine(SeedSequence&& seq)
  64. {
  65. seed(seq);
  66. }
  67. pcg_engine(const pcg_engine&) = default;
  68. pcg_engine& operator=(const pcg_engine&) = default;
  69. pcg_engine(pcg_engine&&) = default;
  70. pcg_engine& operator=(pcg_engine&&) = default;
  71. result_type operator()()
  72. {
  73. // Advance the LCG state, always using the new value to generate the output.
  74. state_ = lcg(state_);
  75. return Mix{}(state_);
  76. }
  77. void seed(uint64_t seed_value = 0)
  78. {
  79. state_type tmp = seed_value;
  80. state_ = lcg(tmp + Params::increment());
  81. }
  82. template<class SeedSequence>
  83. typename absl::enable_if_t<
  84. !std::is_convertible<SeedSequence, uint64_t>::value,
  85. void>
  86. seed(SeedSequence&& seq)
  87. {
  88. reseed(seq);
  89. }
  90. void discard(uint64_t count)
  91. {
  92. state_ = advance(state_, count);
  93. }
  94. bool operator==(const pcg_engine& other) const
  95. {
  96. return state_ == other.state_;
  97. }
  98. bool operator!=(const pcg_engine& other) const
  99. {
  100. return !(*this == other);
  101. }
  102. template<class CharT, class Traits>
  103. friend typename absl::enable_if_t<(sizeof(state_type) == 16), std::basic_ostream<CharT, Traits>&>
  104. operator<<(
  105. std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
  106. const pcg_engine& engine
  107. )
  108. {
  109. auto saver = random_internal::make_ostream_state_saver(os);
  110. random_internal::stream_u128_helper<state_type> helper;
  111. helper.write(pcg_engine::params_type::multiplier(), os);
  112. os << os.fill();
  113. helper.write(pcg_engine::params_type::increment(), os);
  114. os << os.fill();
  115. helper.write(engine.state_, os);
  116. return os;
  117. }
  118. template<class CharT, class Traits>
  119. friend typename absl::enable_if_t<(sizeof(state_type) <= 8), std::basic_ostream<CharT, Traits>&>
  120. operator<<(
  121. std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
  122. const pcg_engine& engine
  123. )
  124. {
  125. auto saver = random_internal::make_ostream_state_saver(os);
  126. os << pcg_engine::params_type::multiplier() << os.fill();
  127. os << pcg_engine::params_type::increment() << os.fill();
  128. os << engine.state_;
  129. return os;
  130. }
  131. template<class CharT, class Traits>
  132. friend typename absl::enable_if_t<(sizeof(state_type) == 16), std::basic_istream<CharT, Traits>&>
  133. operator>>(
  134. std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
  135. pcg_engine& engine
  136. )
  137. { // NOLINT(runtime/references)
  138. random_internal::stream_u128_helper<state_type> helper;
  139. auto mult = helper.read(is);
  140. auto inc = helper.read(is);
  141. auto tmp = helper.read(is);
  142. if (mult != pcg_engine::params_type::multiplier() ||
  143. inc != pcg_engine::params_type::increment())
  144. {
  145. // signal failure by setting the failbit.
  146. is.setstate(is.rdstate() | std::ios_base::failbit);
  147. }
  148. if (!is.fail())
  149. {
  150. engine.state_ = tmp;
  151. }
  152. return is;
  153. }
  154. template<class CharT, class Traits>
  155. friend typename absl::enable_if_t<(sizeof(state_type) <= 8), std::basic_istream<CharT, Traits>&>
  156. operator>>(
  157. std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
  158. pcg_engine& engine
  159. )
  160. { // NOLINT(runtime/references)
  161. state_type mult{}, inc{}, tmp{};
  162. is >> mult >> inc >> tmp;
  163. if (mult != pcg_engine::params_type::multiplier() ||
  164. inc != pcg_engine::params_type::increment())
  165. {
  166. // signal failure by setting the failbit.
  167. is.setstate(is.rdstate() | std::ios_base::failbit);
  168. }
  169. if (!is.fail())
  170. {
  171. engine.state_ = tmp;
  172. }
  173. return is;
  174. }
  175. private:
  176. state_type state_;
  177. // Returns the linear-congruential generator next state.
  178. static inline constexpr state_type lcg(state_type s)
  179. {
  180. return s * Params::multiplier() + Params::increment();
  181. }
  182. // Returns the linear-congruential arbitrary seek state.
  183. inline state_type advance(state_type s, uint64_t n) const
  184. {
  185. state_type mult = Params::multiplier();
  186. state_type inc = Params::increment();
  187. state_type m = 1;
  188. state_type i = 0;
  189. while (n > 0)
  190. {
  191. if (n & 1)
  192. {
  193. m *= mult;
  194. i = i * mult + inc;
  195. }
  196. inc = (mult + 1) * inc;
  197. mult *= mult;
  198. n >>= 1;
  199. }
  200. return m * s + i;
  201. }
  202. template<class SeedSequence>
  203. void reseed(SeedSequence& seq)
  204. {
  205. using sequence_result_type = typename SeedSequence::result_type;
  206. constexpr size_t kBufferSize =
  207. sizeof(state_type) / sizeof(sequence_result_type);
  208. sequence_result_type buffer[kBufferSize];
  209. seq.generate(std::begin(buffer), std::end(buffer));
  210. // Convert the seed output to a single state value.
  211. state_type tmp = buffer[0];
  212. for (size_t i = 1; i < kBufferSize; i++)
  213. {
  214. tmp <<= (sizeof(sequence_result_type) * 8);
  215. tmp |= buffer[i];
  216. }
  217. state_ = lcg(tmp + params_type::increment());
  218. }
  219. };
  220. // Parameterized implementation of the PCG 128-bit oneseq state.
  221. // This provides state_type, multiplier, and increment for pcg_engine.
  222. template<uint64_t kMultA, uint64_t kMultB, uint64_t kIncA, uint64_t kIncB>
  223. class pcg128_params
  224. {
  225. public:
  226. #if ABSL_HAVE_INTRINSIC_INT128
  227. using state_type = __uint128_t;
  228. static inline constexpr state_type make_u128(uint64_t a, uint64_t b)
  229. {
  230. return (static_cast<__uint128_t>(a) << 64) | b;
  231. }
  232. #else
  233. using state_type = absl::uint128;
  234. static inline constexpr state_type make_u128(uint64_t a, uint64_t b)
  235. {
  236. return absl::MakeUint128(a, b);
  237. }
  238. #endif
  239. static inline constexpr state_type multiplier()
  240. {
  241. return make_u128(kMultA, kMultB);
  242. }
  243. static inline constexpr state_type increment()
  244. {
  245. return make_u128(kIncA, kIncB);
  246. }
  247. };
  248. // Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which
  249. // accepts an input of state_type and mixes it into an output of result_type.
  250. struct pcg_xsl_rr_128_64
  251. {
  252. #if ABSL_HAVE_INTRINSIC_INT128
  253. using state_type = __uint128_t;
  254. #else
  255. using state_type = absl::uint128;
  256. #endif
  257. using result_type = uint64_t;
  258. inline uint64_t operator()(state_type state)
  259. {
  260. // This is equivalent to the xsl_rr_128_64 mixing function.
  261. #if ABSL_HAVE_INTRINSIC_INT128
  262. uint64_t rotate = static_cast<uint64_t>(state >> 122u);
  263. state ^= state >> 64;
  264. uint64_t s = static_cast<uint64_t>(state);
  265. #else
  266. uint64_t h = Uint128High64(state);
  267. uint64_t rotate = h >> 58u;
  268. uint64_t s = Uint128Low64(state) ^ h;
  269. #endif
  270. return rotr(s, static_cast<int>(rotate));
  271. }
  272. };
  273. // Parameterized implementation of the PCG 64-bit oneseq state.
  274. // This provides state_type, multiplier, and increment for pcg_engine.
  275. template<uint64_t kMult, uint64_t kInc>
  276. class pcg64_params
  277. {
  278. public:
  279. using state_type = uint64_t;
  280. static inline constexpr state_type multiplier()
  281. {
  282. return kMult;
  283. }
  284. static inline constexpr state_type increment()
  285. {
  286. return kInc;
  287. }
  288. };
  289. // Implementation of the PCG xsh_rr_64_32 64-bit mixing function, which accepts
  290. // an input of state_type and mixes it into an output of result_type.
  291. struct pcg_xsh_rr_64_32
  292. {
  293. using state_type = uint64_t;
  294. using result_type = uint32_t;
  295. inline uint32_t operator()(uint64_t state)
  296. {
  297. return rotr(static_cast<uint32_t>(((state >> 18) ^ state) >> 27), state >> 59);
  298. }
  299. };
  300. // Stable pcg_engine implementations:
  301. // This is a 64-bit generator using 128-bits of state.
  302. // The output sequence is equivalent to Melissa O'Neil's pcg64_oneseq.
  303. using pcg64_2018_engine = pcg_engine<
  304. random_internal::pcg128_params<0x2360ed051fc65da4ull, 0x4385df649fccf645ull, 0x5851f42d4c957f2d, 0x14057b7ef767814f>,
  305. random_internal::pcg_xsl_rr_128_64>;
  306. // This is a 32-bit generator using 64-bits of state.
  307. // This is equivalent to Melissa O'Neil's pcg32_oneseq.
  308. using pcg32_2018_engine = pcg_engine<
  309. random_internal::pcg64_params<0x5851f42d4c957f2dull, 0x14057b7ef767814full>,
  310. random_internal::pcg_xsh_rr_64_32>;
  311. } // namespace random_internal
  312. ABSL_NAMESPACE_END
  313. } // namespace absl
  314. #endif // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_