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