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