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

486 lines
20KB

  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Casey Carter 2016
  5. //
  6. // Use, modification and distribution is subject to the
  7. // Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. // Project home: https://github.com/ericniebler/range-v3
  12. //
  13. /*
  14. * Random-Number Utilities (randutil)
  15. * Addresses common issues with C++11 random number generation.
  16. * Makes good seeding easier, and makes using RNGs easy while retaining
  17. * all the power.
  18. *
  19. * The MIT License (MIT)
  20. *
  21. * Copyright (c) 2015 Melissa E. O'Neill
  22. *
  23. * Permission is hereby granted, free of charge, to any person obtaining a copy
  24. * of this software and associated documentation files (the "Software"), to deal
  25. * in the Software without restriction, including without limitation the rights
  26. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  27. * copies of the Software, and to permit persons to whom the Software is
  28. * furnished to do so, subject to the following conditions:
  29. *
  30. * The above copyright notice and this permission notice shall be included in
  31. * all copies or substantial portions of the Software.
  32. *
  33. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  34. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  35. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  36. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  37. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  38. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  39. * SOFTWARE.
  40. */
  41. #ifndef RANGES_V3_UTILITY_RANDOM_HPP
  42. #define RANGES_V3_UTILITY_RANDOM_HPP
  43. #include <array>
  44. #include <cstddef>
  45. #include <cstdint>
  46. #include <initializer_list>
  47. #include <new>
  48. #include <random>
  49. #include <meta/meta.hpp>
  50. #include <concepts/concepts.hpp>
  51. #include <range/v3/range_fwd.hpp>
  52. #include <range/v3/algorithm/copy.hpp>
  53. #include <range/v3/algorithm/generate.hpp>
  54. #include <range/v3/functional/invoke.hpp>
  55. #include <range/v3/functional/reference_wrapper.hpp>
  56. #include <range/v3/iterator/concepts.hpp>
  57. #if !RANGES_CXX_THREAD_LOCAL
  58. #include <mutex>
  59. #endif
  60. RANGES_DIAGNOSTIC_PUSH
  61. RANGES_DIAGNOSTIC_IGNORE_CXX17_COMPAT
  62. namespace ranges
  63. {
  64. /// \addtogroup group-numerics
  65. /// @{
  66. // clang-format off
  67. CPP_def
  68. (
  69. template(typename Gen)
  70. concept uniform_random_bit_generator,
  71. requires(int)
  72. (
  73. Gen::min(),
  74. Gen::max(),
  75. concepts::requires_<same_as<invoke_result_t<Gen&>, decltype(Gen::min())>>,
  76. concepts::requires_<same_as<invoke_result_t<Gen&>, decltype(Gen::max())>>
  77. ) &&
  78. invocable<Gen &> &&
  79. unsigned_integral<invoke_result_t<Gen&>>
  80. );
  81. // clang-format on
  82. /// @}
  83. /// \cond
  84. namespace detail
  85. {
  86. namespace randutils
  87. {
  88. inline std::array<std::uint32_t, 8> get_entropy()
  89. {
  90. std::array<std::uint32_t, 8> seeds;
  91. // Hopefully high-quality entropy from random_device.
  92. #if defined(__GLIBCXX__) && defined(RANGES_WORKAROUND_VALGRIND_RDRAND)
  93. std::random_device rd{"/dev/urandom"};
  94. #else
  95. std::random_device rd;
  96. #endif
  97. std::uniform_int_distribution<std::uint32_t> dist{};
  98. ranges::generate(seeds, [&] { return dist(rd); });
  99. return seeds;
  100. }
  101. template<typename I>
  102. constexpr auto fast_exp(I x, I power, I result = I{1}) -> CPP_ret(I)( //
  103. requires unsigned_integral<I>)
  104. {
  105. return power == I{0}
  106. ? result
  107. : randutils::fast_exp(
  108. x * x, power >> 1, result * (power & I{1} ? x : 1));
  109. }
  110. //////////////////////////////////////////////////////////////////////////////
  111. //
  112. // seed_seq_fe
  113. //
  114. //////////////////////////////////////////////////////////////////////////////
  115. /*
  116. * seed_seq_fe implements a fixed-entropy seed sequence; it conforms to all
  117. * the requirements of a Seed Sequence concept.
  118. *
  119. * seed_seq_fe<N> implements a seed sequence which seeds based on a store of
  120. * N * 32 bits of entropy. Typically, it would be initialized with N or more
  121. * integers.
  122. *
  123. * seed_seq_fe128 and seed_seq_fe256 are provided as convenience typedefs for
  124. * 128- and 256-bit entropy stores respectively. These variants outperform
  125. * std::seed_seq, while being better mixing the bits it is provided as
  126. * entropy. In almost all common use cases, they serve as better drop-in
  127. * replacements for seed_seq.
  128. *
  129. * Technical details
  130. *
  131. * Assuming it constructed with M seed integers as input, it exhibits the
  132. * following properties
  133. *
  134. * * Diffusion/Avalanche: A single-bit change in any of the M inputs has a
  135. * 50% chance of flipping every bit in the bitstream produced by generate.
  136. * Initializing the N-word entropy store with M words requires O(N * M)
  137. * time precisely because of the avalanche requirements. Once constructed,
  138. * calls to generate are linear in the number of words generated.
  139. *
  140. * * Bias freedom/Bijection: If M == N, the state of the entropy store is a
  141. * bijection from the M inputs (i.e., no states occur twice, none are
  142. * omitted). If M > N the number of times each state can occur is the same
  143. * (each state occurs 2**(32*(M-N)) times, where ** is the power function).
  144. * If M < N, some states cannot occur (bias) but no state occurs more
  145. * than once (it's impossible to avoid bias if M < N; ideally N should not
  146. * be chosen so that it is more than M).
  147. *
  148. * Likewise, the generate function has similar properties (with the entropy
  149. * store as the input data). If more outputs are requested than there is
  150. * entropy, some outputs cannot occur. For example, the Mersenne Twister
  151. * will request 624 outputs, to initialize its 19937-bit state, which is
  152. * much larger than a 128-bit or 256-bit entropy pool. But in practice,
  153. * limiting the Mersenne Twister to 2**128 possible initializations gives
  154. * us enough initializations to give a unique initialization to trillions
  155. * of computers for billions of years. If you really have 624 words of
  156. * *real* high-quality entropy you want to use, you probably don't need
  157. * an entropy mixer like this class at all. But if you *really* want to,
  158. * nothing is stopping you from creating a randutils::seed_seq_fe<624>.
  159. *
  160. * * As a consequence of the above properties, if all parts of the provided
  161. * seed data are kept constant except one, and the remaining part is varied
  162. * through K different states, K different output sequences will be
  163. * produced.
  164. *
  165. * * Also, because the amount of entropy stored is fixed, this class never
  166. * performs dynamic allocation and is free of the possibility of generating
  167. * an exception.
  168. *
  169. * Ideas used to implement this code include hashing, a simple PCG generator
  170. * based on an MCG base with an XorShift output function and permutation
  171. * functions on tuples.
  172. *
  173. * More detail at
  174. * http://www.pcg-random.org/posts/developing-a-seed_seq-alternative.html
  175. */
  176. template<std::size_t count, typename IntRep = std::uint32_t>
  177. struct seed_seq_fe
  178. {
  179. public:
  180. CPP_assert(unsigned_integral<IntRep>);
  181. typedef IntRep result_type;
  182. private:
  183. static constexpr std::size_t mix_rounds = 1 + (count <= 2);
  184. static constexpr std::uint32_t INIT_A = 0x43b0d7e5;
  185. static constexpr std::uint32_t MULT_A = 0x931e8875;
  186. static constexpr std::uint32_t INIT_B = 0x8b51f9dd;
  187. static constexpr std::uint32_t MULT_B = 0x58f38ded;
  188. static constexpr std::uint32_t MIX_MULT_L = 0xca01f9dd;
  189. static constexpr std::uint32_t MIX_MULT_R = 0x4973f715;
  190. static constexpr std::uint32_t XSHIFT = sizeof(IntRep) * 8 / 2;
  191. std::array<IntRep, count> mixer_;
  192. template<typename I, typename S>
  193. auto mix_entropy(I first, S last) -> CPP_ret(void)( //
  194. requires input_iterator<I> && sentinel_for<S, I> &&
  195. convertible_to<iter_reference_t<I>, IntRep>)
  196. {
  197. auto hash_const = INIT_A;
  198. auto hash = [&](IntRep value) RANGES_INTENDED_MODULAR_ARITHMETIC {
  199. value ^= hash_const;
  200. hash_const *= MULT_A;
  201. value *= hash_const;
  202. value ^= value >> XSHIFT;
  203. return value;
  204. };
  205. auto mix = [](IntRep x, IntRep y) RANGES_INTENDED_MODULAR_ARITHMETIC {
  206. IntRep result = MIX_MULT_L * x - MIX_MULT_R * y;
  207. result ^= result >> XSHIFT;
  208. return result;
  209. };
  210. for(auto & elem : mixer_)
  211. {
  212. if(first != last)
  213. {
  214. elem = hash(static_cast<IntRep>(*first));
  215. ++first;
  216. }
  217. else
  218. elem = hash(IntRep{0});
  219. }
  220. for(auto & src : mixer_)
  221. for(auto & dest : mixer_)
  222. if(&src != &dest)
  223. dest = mix(dest, hash(src));
  224. for(; first != last; ++first)
  225. for(auto & dest : mixer_)
  226. dest = mix(dest, hash(static_cast<IntRep>(*first)));
  227. }
  228. public:
  229. seed_seq_fe(const seed_seq_fe &) = delete;
  230. void operator=(const seed_seq_fe &) = delete;
  231. template<typename T>
  232. CPP_ctor(seed_seq_fe)(std::initializer_list<T> init)( //
  233. requires convertible_to<T const &, IntRep>)
  234. {
  235. seed(init.begin(), init.end());
  236. }
  237. template<typename I, typename S>
  238. CPP_ctor(seed_seq_fe)(I first, S last)( //
  239. requires input_iterator<I> && sentinel_for<S, I> &&
  240. convertible_to<iter_reference_t<I>, IntRep>)
  241. {
  242. seed(first, last);
  243. }
  244. // generating functions
  245. template<typename I, typename S>
  246. RANGES_INTENDED_MODULAR_ARITHMETIC auto generate(I first,
  247. S const last) const
  248. -> CPP_ret(void)( //
  249. requires random_access_iterator<I> && sentinel_for<S, I>)
  250. {
  251. auto src_begin = mixer_.begin();
  252. auto src_end = mixer_.end();
  253. auto src = src_begin;
  254. auto hash_const = INIT_B;
  255. for(; first != last; ++first)
  256. {
  257. auto dataval = *src;
  258. if(++src == src_end)
  259. src = src_begin;
  260. dataval ^= hash_const;
  261. hash_const *= MULT_B;
  262. dataval *= hash_const;
  263. dataval ^= dataval >> XSHIFT;
  264. *first = dataval;
  265. }
  266. }
  267. constexpr std::size_t size() const
  268. {
  269. return count;
  270. }
  271. template<typename O>
  272. RANGES_INTENDED_MODULAR_ARITHMETIC auto param(O dest) const
  273. -> CPP_ret(void)( //
  274. requires weakly_incrementable<O> &&
  275. indirectly_copyable<decltype(mixer_.begin()), O>)
  276. {
  277. constexpr IntRep INV_A = randutils::fast_exp(MULT_A, IntRep(-1));
  278. constexpr IntRep MIX_INV_L =
  279. randutils::fast_exp(MIX_MULT_L, IntRep(-1));
  280. auto mixer_copy = mixer_;
  281. for(std::size_t round = 0; round < mix_rounds; ++round)
  282. {
  283. // Advance to the final value. We'll backtrack from that.
  284. auto hash_const =
  285. INIT_A * randutils::fast_exp(MULT_A, IntRep(count * count));
  286. for(auto src = mixer_copy.rbegin(); src != mixer_copy.rend();
  287. ++src)
  288. for(auto rdest = mixer_copy.rbegin();
  289. rdest != mixer_copy.rend();
  290. ++rdest)
  291. if(src != rdest)
  292. {
  293. IntRep revhashed = *src;
  294. auto mult_const = hash_const;
  295. hash_const *= INV_A;
  296. revhashed ^= hash_const;
  297. revhashed *= mult_const;
  298. revhashed ^= revhashed >> XSHIFT;
  299. IntRep unmixed = *rdest;
  300. unmixed ^= unmixed >> XSHIFT;
  301. unmixed += MIX_MULT_R * revhashed;
  302. unmixed *= MIX_INV_L;
  303. *rdest = unmixed;
  304. }
  305. for(auto i = mixer_copy.rbegin(); i != mixer_copy.rend(); ++i)
  306. {
  307. IntRep unhashed = *i;
  308. unhashed ^= unhashed >> XSHIFT;
  309. unhashed *= randutils::fast_exp(hash_const, IntRep(-1));
  310. hash_const *= INV_A;
  311. unhashed ^= hash_const;
  312. *i = unhashed;
  313. }
  314. }
  315. ranges::copy(mixer_copy, dest);
  316. }
  317. template<typename I, typename S>
  318. auto seed(I first, S last) -> CPP_ret(void)( //
  319. requires input_iterator<I> && sentinel_for<S, I> &&
  320. convertible_to<iter_reference_t<I>, IntRep>)
  321. {
  322. mix_entropy(first, last);
  323. // For very small sizes, we do some additional mixing. For normal
  324. // sizes, this loop never performs any iterations.
  325. for(std::size_t i = 1; i < mix_rounds; ++i)
  326. stir();
  327. }
  328. seed_seq_fe & stir()
  329. {
  330. mix_entropy(mixer_.begin(), mixer_.end());
  331. return *this;
  332. }
  333. };
  334. using seed_seq_fe128 = seed_seq_fe<4, std::uint32_t>;
  335. using seed_seq_fe256 = seed_seq_fe<8, std::uint32_t>;
  336. //////////////////////////////////////////////////////////////////////////////
  337. //
  338. // auto_seeded
  339. //
  340. //////////////////////////////////////////////////////////////////////////////
  341. /*
  342. * randutils::auto_seeded
  343. *
  344. * Extends a seed sequence class with a nondeterministic default
  345. * constructor. Uses a variety of local sources of entropy to portably
  346. * initialize any seed sequence to a good default state.
  347. *
  348. * In normal use, it's accessed via one of the following type aliases, which
  349. * use seed_seq_fe128 and seed_seq_fe256 above.
  350. *
  351. * randutils::auto_seed_128
  352. * randutils::auto_seed_256
  353. *
  354. * It's discussed in detail at
  355. * http://www.pcg-random.org/posts/simple-portable-cpp-seed-entropy.html
  356. * and its motivation (why you can't just use std::random_device) here
  357. * http://www.pcg-random.org/posts/cpps-random_device.html
  358. */
  359. template<typename SeedSeq>
  360. struct auto_seeded : public SeedSeq
  361. {
  362. auto_seeded()
  363. : auto_seeded(randutils::get_entropy())
  364. {}
  365. template<std::size_t N>
  366. auto_seeded(std::array<std::uint32_t, N> const & seeds)
  367. : SeedSeq(seeds.begin(), seeds.end())
  368. {}
  369. using SeedSeq::SeedSeq;
  370. const SeedSeq & base() const
  371. {
  372. return *this;
  373. }
  374. SeedSeq & base()
  375. {
  376. return *this;
  377. }
  378. };
  379. using auto_seed_128 = auto_seeded<seed_seq_fe128>;
  380. using auto_seed_256 = auto_seeded<seed_seq_fe256>;
  381. } // namespace randutils
  382. using default_URNG = meta::if_c<(sizeof(void *) >= sizeof(long long)),
  383. std::mt19937_64, std::mt19937>;
  384. #if !RANGES_CXX_THREAD_LOCAL
  385. template<typename URNG>
  386. class sync_URNG : private URNG
  387. {
  388. mutable std::mutex mtx_;
  389. public:
  390. using URNG::URNG;
  391. sync_URNG() = default;
  392. using typename URNG::result_type;
  393. result_type operator()()
  394. {
  395. std::lock_guard<std::mutex> guard{mtx_};
  396. return static_cast<URNG &>(*this)();
  397. }
  398. using URNG::max;
  399. using URNG::min;
  400. };
  401. using default_random_engine = sync_URNG<default_URNG>;
  402. #else
  403. using default_random_engine = default_URNG;
  404. #endif
  405. template<typename T = void>
  406. default_random_engine & get_random_engine()
  407. {
  408. using Seeder = meta::if_c<(sizeof(default_URNG) > 16),
  409. randutils::auto_seed_256,
  410. randutils::auto_seed_128>;
  411. #if RANGES_CXX_THREAD_LOCAL >= RANGES_CXX_THREAD_LOCAL_11
  412. static thread_local default_random_engine engine{Seeder{}.base()};
  413. #elif RANGES_CXX_THREAD_LOCAL
  414. static __thread bool initialized = false;
  415. static __thread meta::_t<std::aligned_storage<sizeof(default_random_engine),
  416. alignof(default_random_engine)>>
  417. storage;
  418. if(!initialized)
  419. {
  420. ::new(static_cast<void *>(&storage))
  421. default_random_engine{Seeder{}.base()};
  422. initialized = true;
  423. }
  424. auto & engine = reinterpret_cast<default_random_engine &>(storage);
  425. #else
  426. static default_random_engine engine{Seeder{}.base()};
  427. #endif // RANGES_CXX_THREAD_LOCAL
  428. return engine;
  429. }
  430. } // namespace detail
  431. /// \endcond
  432. } // namespace ranges
  433. RANGES_DIAGNOSTIC_POP
  434. #endif