comparison common/random.h @ 0:a4671277546c tip

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:a4671277546c
1 #ifndef EFFOLKRONIUM_RANDOM_HPP
2 #define EFFOLKRONIUM_RANDOM_HPP
3
4 #include <random>
5 #include <chrono> // timed seed
6 #include <type_traits>
7 #include <cassert>
8 #include <initializer_list>
9 #include <utility> // std::forward, std::declval
10 #include <algorithm> // std::shuffle, std::next, std::distance
11 #include <iterator> // std::begin, std::end, std::iterator_traits
12 #include <limits> // std::numeric_limits
13 #include <ostream>
14 #include <istream>
15
16 namespace effolkronium {
17
18 namespace details {
19 /// Key type for getting common type numbers or objects
20 struct common{ };
21
22 /// True if type T is applicable by a std::uniform_int_distribution
23 template<typename T>
24 struct is_uniform_int {
25 static constexpr bool value =
26 std::is_same<T, short>::value
27 || std::is_same<T, int>::value
28 || std::is_same<T, long>::value
29 || std::is_same<T, long long>::value
30 || std::is_same<T, unsigned short>::value
31 || std::is_same<T, unsigned int>::value
32 || std::is_same<T, unsigned long>::value
33 || std::is_same<T, unsigned long long>::value;
34 };
35
36 /// True if type T is applicable by a std::uniform_real_distribution
37 template<typename T>
38 struct is_uniform_real {
39 static constexpr bool value =
40 std::is_same<T, float>::value
41 || std::is_same<T, double>::value
42 || std::is_same<T, long double>::value;
43 };
44
45 /// True if type T is plain byte
46 template<typename T>
47 struct is_byte {
48 static constexpr bool value =
49 std::is_same<T, signed char>::value
50 || std::is_same<T, unsigned char>::value;
51 };
52
53 /// True if type T is plain number type
54 template<typename T>
55 struct is_supported_number {
56 static constexpr bool value =
57 is_byte <T>::value
58 || is_uniform_real<T>::value
59 || is_uniform_int <T>::value;
60 };
61
62 /// True if type T is iterator
63 template<typename T>
64 struct is_iterator {
65 private:
66 static char test( ... );
67
68 template <typename U,
69 typename = typename std::iterator_traits<U>::difference_type,
70 typename = typename std::iterator_traits<U>::pointer,
71 typename = typename std::iterator_traits<U>::reference,
72 typename = typename std::iterator_traits<U>::value_type,
73 typename = typename std::iterator_traits<U>::iterator_category
74 > static long test( U&& );
75 public:
76 static constexpr bool value = std::is_same<
77 decltype( test( std::declval<T>( ) ) ), long>::value;
78 };
79
80 } // namespace details
81
82 /// Default seeder for 'random' classes
83 struct seeder_default {
84 /// return seed sequence
85 std::seed_seq& operator() ( ) {
86 // MinGW issue, std::random_device returns constant value
87 // Use std::seed_seq with additional seed from C++ chrono
88 return seed_seq;
89 }
90 private:
91 std::seed_seq seed_seq{ {
92 static_cast<std::uintmax_t>( std::random_device{ }( ) ),
93 static_cast<std::uintmax_t>( std::chrono::steady_clock::now( )
94 .time_since_epoch( ).count( ) ),
95 } };
96 };
97
98 /**
99 * \brief Base template class for random
100 * with static API and static internal member storage
101 * \note it is NOT thread safe but more efficient then
102 * basic_random_thread_local
103 * \param Engine A random engine with interface like in the std::mt19937
104 * \param Seeder A seeder type which return seed for internal engine
105 * through operator()
106 */
107 template<
108 typename Engine,
109 typename Seeder = seeder_default,
110 template<typename> class IntegerDist = std::uniform_int_distribution,
111 template<typename> class RealDist = std::uniform_real_distribution,
112 typename BoolDist = std::bernoulli_distribution
113 >
114 class basic_random_static {
115 public:
116 basic_random_static( ) = delete;
117
118 /// Type of used random number engine
119 using engine_type = Engine;
120
121 /// Type of used random number seeder
122 using seeder_type = Seeder;
123
124 /// Type of used integer distribution
125 template<typename T>
126 using integer_dist_t = IntegerDist<T>;
127
128 /// Type of used real distribution
129 template<typename T>
130 using real_dist_t = RealDist<T>;
131
132 /// Type of used bool distribution
133 using bool_dist_t = BoolDist;
134
135 /// Key type for getting common type numbers or objects
136 using common = details::common;
137
138 /**
139 * \return The minimum value
140 * potentially generated by the random-number engine
141 */
142 static constexpr typename Engine::result_type min( ) {
143 return Engine::min( );
144 }
145
146 /**
147 * \return The maximum value
148 * potentially generated by the random-number engine
149 */
150 static constexpr typename Engine::result_type max( ) {
151 return Engine::max( );
152 }
153
154 /// Advances the internal state by z times
155 static void discard( const unsigned long long z ) {
156 engine.discard( z );
157 }
158
159 /// Reseed by Seeder
160 static void reseed( ) {
161 Seeder seeder;
162 seed( seeder( ) );
163 }
164
165 /**
166 * \brief Reinitializes the internal state
167 * of the random-number engine using new seed value
168 * \param value The seed value to use
169 * in the initialization of the internal state
170 */
171 static void seed( const typename Engine::result_type value =
172 Engine::default_seed ) {
173 engine.seed( value );
174 }
175
176 /**
177 * \brief Reinitializes the internal state
178 * of the random-number engine using new seed value
179 * \param seq The seed sequence
180 * to use in the initialization of the internal state
181 */
182 template<typename Sseq>
183 static void seed( Sseq& seq ) {
184 engine.seed( seq );
185 }
186
187 /// return random number from engine in [min(), max()] range
188 static typename Engine::result_type get( ) {
189 return engine( );
190 }
191
192 /**
193 * \brief Compares internal pseudo-random number engine
194 * with 'other' pseudo-random number engine.
195 * Two engines are equal, if their internal states
196 * are equivalent, that is, if they would generate
197 * equivalent values for any number of calls of operator()
198 * \param other The engine, with which the internal engine will be compared
199 * \return true, if other and internal engine are equal
200 */
201 static bool is_equal( const Engine& other ) {
202 return engine == other;
203 }
204
205 /**
206 * \brief Serializes the internal state of the
207 * internal pseudo-random number engine as a sequence
208 * of decimal numbers separated by one or more spaces,
209 * and inserts it to the stream ost. The fill character
210 * and the formatting flags of the stream are
211 * ignored and unaffected.
212 * \param ost The output stream to insert the data to
213 */
214 template<typename CharT, typename Traits>
215 static void serialize( std::basic_ostream<CharT, Traits>& ost ) {
216 ost << engine;
217 }
218
219 /**
220 * \brief Restores the internal state of the
221 * internal pseudo-random number engine from
222 * the serialized representation, which
223 * was created by an earlier call to 'serialize'
224 * using a stream with the same imbued locale and
225 * the same CharT and Traits.
226 * If the input cannot be deserialized,
227 * internal engine is left unchanged and failbit is raised on ist
228 * \param ost The input stream to extract the data from
229 */
230 template<typename CharT, typename Traits>
231 static void deserialize( std::basic_istream<CharT, Traits>& ist ) {
232 ist >> engine;
233 }
234
235 /**
236 * \brief Generate a random integer number in a [from; to] range
237 * by std::uniform_int_distribution
238 * \param from The first limit number of a random range
239 * \param to The second limit number of a random range
240 * \return A random integer number in a [from; to] range
241 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
242 * \note Prevent implicit type conversion
243 */
244 template<typename T>
245 static typename std::enable_if<details::is_uniform_int<T>::value
246 , T>::type get( T from = std::numeric_limits<T>::min( ),
247 T to = std::numeric_limits<T>::max( ) ) {
248 if( from < to ) // Allow range from higher to lower
249 return IntegerDist<T>{ from, to }( engine );
250 return IntegerDist<T>{ to, from }( engine );
251 }
252
253 /**
254 * \brief Generate a random real number in a [from; to] range
255 * by std::uniform_real_distribution
256 * \param from The first limit number of a random range
257 * \param to The second limit number of a random range
258 * \return A random real number in a [from; to] range
259 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
260 * \note Prevent implicit type conversion
261 */
262 template<typename T>
263 static typename std::enable_if<details::is_uniform_real<T>::value
264 , T>::type get( T from = std::numeric_limits<T>::min( ),
265 T to = std::numeric_limits<T>::max( ) ) {
266 if( from < to ) // Allow range from higher to lower
267 return RealDist<T>{ from, to }( engine );
268 return RealDist<T>{ to, from }( engine );
269 }
270
271 /**
272 * \brief Generate a random byte number in a [from; to] range
273 * \param from The first limit number of a random range
274 * \param to The second limit number of a random range
275 * \return A random byte number in a [from; to] range
276 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
277 * \note Prevent implicit type conversion
278 */
279 template<typename T>
280 static typename std::enable_if<details::is_byte<T>::value
281 , T>::type get( T from = std::numeric_limits<T>::min( ),
282 T to = std::numeric_limits<T>::max( ) ) {
283 // Choose between short and unsigned short for byte conversion
284 using short_t = typename std::conditional<std::is_signed<T>::value,
285 short, unsigned short>::type;
286
287 return static_cast<T>( get<short_t>( from, to ) );
288 }
289
290 /**
291 * \brief Generate a random common_type number in a [from; to] range
292 * \param Key The Key type for this version of 'get' method
293 * Type should be '(THIS_TYPE)::common' struct
294 * \param from The first limit number of a random range
295 * \param to The second limit number of a random range
296 * \return A random common_type number in a [from; to] range
297 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
298 * \note Allow implicit type conversion
299 * \note Prevent implicit type conversion from singed to unsigned types
300 * Why? std::common_type<Unsigned, Signed> chooses unsigned value,
301 * then Signed value will be converted to Unsigned value
302 * which gives us a wrong range for random values.
303 * https://stackoverflow.com/a/5416498/5734836
304 */
305 template<
306 typename Key,
307 typename A,
308 typename B,
309 typename C = typename std::common_type<A, B>::type
310 >
311 static typename std::enable_if<
312 std::is_same<Key, common>::value
313 && details::is_supported_number<A>::value
314 && details::is_supported_number<B>::value
315 // Prevent implicit type conversion from singed to unsigned types
316 && std::is_signed<A>::value != std::is_unsigned<B>::value
317 , C>::type get( A from = std::numeric_limits<A>::min( ),
318 B to = std::numeric_limits<B>::max( ) ) {
319 return get( static_cast<C>( from ), static_cast<C>( to ) );
320 }
321
322 /**
323 * \brief Generate a bool value with specific probability
324 * by std::bernoulli_distribution
325 * \param probability The probability of generating true in [0; 1] range
326 * 0 means always false, 1 means always true
327 * \return 'true' with 'probability' probability ('false' otherwise)
328 */
329 template<typename T>
330 static typename std::enable_if<std::is_same<T, bool>::value
331 , bool>::type get( const double probability = 0.5 ) {
332 assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
333 return BoolDist{ probability }( engine );
334 }
335
336 /**
337 * \brief Return random value from initilizer_list
338 * \param init_list initilizer_list with values
339 * \return Random value from initilizer_list
340 * \note Should be 1 or more elements in initilizer_list
341 * \note Warning! Elements in initilizer_list can't be moved:
342 * https://stackoverflow.com/a/8193157/5734836
343 */
344 template<typename T>
345 static T get( std::initializer_list<T> init_list ) {
346 assert( 0u != init_list.size( ) );
347 return *get( init_list.begin( ), init_list.end( ) );
348 }
349
350 /**
351 * \brief Return random iterator from iterator range
352 * \param first, last - the range of elements
353 * \return Random iterator from [first, last) range
354 * \note If first == last, return last
355 */
356 template<typename InputIt>
357 static typename std::enable_if<details::is_iterator<InputIt>::value
358 , InputIt>::type get( InputIt first, InputIt last ) {
359 const auto size = std::distance( first, last );
360 if( 0u == size ) return last;
361 using diff_t = typename std::iterator_traits<InputIt>::difference_type;
362 return std::next( first, get<diff_t>( 0, size - 1 ) );
363 }
364
365 /**
366 * \brief Return random iterator from Container
367 * \param container The container with elements
368 * \return Random iterator from container
369 * \note If container is empty return std::end( container ) iterator
370 */
371 template<typename Container>
372 static typename std::enable_if<details::is_iterator<
373 decltype( std::begin( std::declval<Container>( ) ) )>::value
374 , decltype( std::begin( std::declval<Container>( ) ) )
375 >::type get( Container& container ) {
376 return get( std::begin( container ), std::end( container ) );
377 }
378
379 /**
380 * \brief Return value from custom Dist distribution
381 * seeded by internal random engine
382 * \param Dist The type of custom distribution with next concept:
383 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
384 * \param args The arguments which will be forwarded to Dist constructor
385 * \return Value from custom distribution
386 */
387 template<typename Dist, typename... Args>
388 static typename Dist::result_type get( Args&&... args ) {
389 return Dist{ std::forward<Args>( args )... }( engine );
390 }
391
392 /**
393 * \brief Return value from custom 'dist' distribution
394 * seeded by internal random engine
395 * \param dist The custom distribution with next concept:
396 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
397 * \param args The arguments which will be forwarded to Dist constructor
398 * \return Value from custom 'dist' distribution
399 */
400 template<typename Dist>
401 static typename Dist::result_type get( Dist& dist ) {
402 return dist( engine );
403 }
404
405 /**
406 * \brief Reorders the elements in the given range [first, last)
407 * such that each possible permutation of those elements
408 * has equal probability of appearance.
409 * \param first, last - the range of elements to shuffle randomly
410 */
411 template<typename RandomIt>
412 static void shuffle( RandomIt first, RandomIt last ) {
413 std::shuffle( first, last, engine );
414 }
415
416 /**
417 * \brief Reorders the elements in the given container
418 * such that each possible permutation of those elements
419 * has equal probability of appearance.
420 * \param container - the container with elements to shuffle randomly
421 */
422 template<typename Container>
423 static void shuffle( Container& container ) {
424 shuffle( std::begin( container ), std::end( container ) );
425 }
426
427 /// return internal engine by copy
428 static Engine get_engine( ) {
429 return engine;
430 }
431 protected:
432 /// return engine seeded by Seeder
433 static Engine make_seeded_engine( ) {
434 // Make seeder instance for seed return by reference like std::seed_seq
435 Seeder seeder;
436 return Engine{ seeder( ) };
437 }
438 protected:
439 /// The random number engine
440 static Engine engine;
441 };
442
443 /// Seed random number engine by Seeder
444 template<
445 typename Engine,
446 typename Seeder,
447 template<typename> class IntegerDist,
448 template<typename> class RealDist,
449 typename BoolDist
450 >
451 Engine basic_random_static<Engine, Seeder, IntegerDist, RealDist, BoolDist
452 // VS2017 issue, can't init by Seeder from lambda
453 >::engine( make_seeded_engine( ) );
454
455 /**
456 * \brief Base template class for random
457 * with thread_local API and thread_local internal member storage
458 * \note it IS thread safe but less efficient then
459 * basic_random_static
460 * \param Engine A random engine with interface like in the std::mt19937
461 * \param Seeder A seeder type which return seed for internal engine
462 * through operator()
463 */
464 template<
465 typename Engine,
466 typename Seeder = seeder_default,
467 template<typename> class IntegerDist = std::uniform_int_distribution,
468 template<typename> class RealDist = std::uniform_real_distribution,
469 typename BoolDist = std::bernoulli_distribution
470 >
471 class basic_random_thread_local {
472 public:
473 basic_random_thread_local( ) = delete;
474
475 /// Type of used random number engine
476 using engine_type = Engine;
477
478 /// Type of used random number seeder
479 using seeder_type = Seeder;
480
481 /// Type of used integer distribution
482 template<typename T>
483 using integer_dist_t = IntegerDist<T>;
484
485 /// Type of used real distribution
486 template<typename T>
487 using real_dist_t = RealDist<T>;
488
489 /// Type of used bool distribution
490 using bool_dist_t = BoolDist;
491
492 /// Key type for getting common type numbers or objects
493 using common = details::common;
494
495 /**
496 * \return The minimum value
497 * potentially generated by the random-number engine
498 */
499 static constexpr typename Engine::result_type min( ) {
500 return Engine::min( );
501 }
502
503 /**
504 * \return The maximum value
505 * potentially generated by the random-number engine
506 */
507 static constexpr typename Engine::result_type max( ) {
508 return Engine::max( );
509 }
510
511 /// Advances the internal state by z times
512 static void discard( const unsigned long long z ) {
513 engine.discard( z );
514 }
515
516 /// Reseed by Seeder
517 static void reseed( ) {
518 Seeder seeder;
519 seed( seeder( ) );
520 }
521
522 /**
523 * \brief Reinitializes the internal state
524 * of the random-number engine using new seed value
525 * \param value The seed value to use
526 * in the initialization of the internal state
527 */
528 static void seed( const typename Engine::result_type value =
529 Engine::default_seed ) {
530 engine.seed( value );
531 }
532
533 /**
534 * \brief Reinitializes the internal state
535 * of the random-number engine using new seed value
536 * \param seq The seed sequence
537 * to use in the initialization of the internal state
538 */
539 template<typename Sseq>
540 static void seed( Sseq& seq ) {
541 engine.seed( seq );
542 }
543
544 /// return random number from engine in [min(), max()] range
545 static typename Engine::result_type get( ) {
546 return engine( );
547 }
548
549 /**
550 * \brief Compares internal pseudo-random number engine
551 * with 'other' pseudo-random number engine.
552 * Two engines are equal, if their internal states
553 * are equivalent, that is, if they would generate
554 * equivalent values for any number of calls of operator()
555 * \param other The engine, with which the internal engine will be compared
556 * \return true, if other and internal engine are equal
557 */
558 static bool is_equal( const Engine& other ) {
559 return engine == other;
560 }
561
562 /**
563 * \brief Serializes the internal state of the
564 * internal pseudo-random number engine as a sequence
565 * of decimal numbers separated by one or more spaces,
566 * and inserts it to the stream ost. The fill character
567 * and the formatting flags of the stream are
568 * ignored and unaffected.
569 * \param ost The output stream to insert the data to
570 */
571 template<typename CharT, typename Traits>
572 static void serialize( std::basic_ostream<CharT, Traits>& ost ) {
573 ost << engine;
574 }
575
576 /**
577 * \brief Restores the internal state of the
578 * internal pseudo-random number engine from
579 * the serialized representation, which
580 * was created by an earlier call to 'serialize'
581 * using a stream with the same imbued locale and
582 * the same CharT and Traits.
583 * If the input cannot be deserialized,
584 * internal engine is left unchanged and failbit is raised on ist
585 * \param ost The input stream to extract the data from
586 */
587 template<typename CharT, typename Traits>
588 static void deserialize( std::basic_istream<CharT, Traits>& ist ) {
589 ist >> engine;
590 }
591
592 /**
593 * \brief Generate a random integer number in a [from; to] range
594 * by std::uniform_int_distribution
595 * \param from The first limit number of a random range
596 * \param to The second limit number of a random range
597 * \return A random integer number in a [from; to] range
598 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
599 * \note Prevent implicit type conversion
600 */
601 template<typename T>
602 static typename std::enable_if<details::is_uniform_int<T>::value
603 , T>::type get( T from = std::numeric_limits<T>::min( ),
604 T to = std::numeric_limits<T>::max( ) ) {
605 if( from < to ) // Allow range from higher to lower
606 return IntegerDist<T>{ from, to }( engine );
607 return IntegerDist<T>{ to, from }( engine );
608 }
609
610 /**
611 * \brief Generate a random real number in a [from; to] range
612 * by std::uniform_real_distribution
613 * \param from The first limit number of a random range
614 * \param to The second limit number of a random range
615 * \return A random real number in a [from; to] range
616 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
617 * \note Prevent implicit type conversion
618 */
619 template<typename T>
620 static typename std::enable_if<details::is_uniform_real<T>::value
621 , T>::type get( T from = std::numeric_limits<T>::min( ),
622 T to = std::numeric_limits<T>::max( ) ) {
623 if( from < to ) // Allow range from higher to lower
624 return RealDist<T>{ from, to }( engine );
625 return RealDist<T>{ to, from }( engine );
626 }
627
628 /**
629 * \brief Generate a random byte number in a [from; to] range
630 * \param from The first limit number of a random range
631 * \param to The second limit number of a random range
632 * \return A random byte number in a [from; to] range
633 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
634 * \note Prevent implicit type conversion
635 */
636 template<typename T>
637 static typename std::enable_if<details::is_byte<T>::value
638 , T>::type get( T from = std::numeric_limits<T>::min( ),
639 T to = std::numeric_limits<T>::max( ) ) {
640 // Choose between short and unsigned short for byte conversion
641 using short_t = typename std::conditional<std::is_signed<T>::value,
642 short, unsigned short>::type;
643
644 return static_cast<T>( get<short_t>( from, to ) );
645 }
646
647 /**
648 * \brief Generate a random common_type number in a [from; to] range
649 * \param Key The Key type for this version of 'get' method
650 * Type should be '(THIS_TYPE)::common' struct
651 * \param from The first limit number of a random range
652 * \param to The second limit number of a random range
653 * \return A random common_type number in a [from; to] range
654 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
655 * \note Allow implicit type conversion
656 * \note Prevent implicit type conversion from singed to unsigned types
657 * Why? std::common_type<Unsigned, Signed> chooses unsigned value,
658 * then Signed value will be converted to Unsigned value
659 * which gives us a wrong range for random values.
660 * https://stackoverflow.com/a/5416498/5734836
661 */
662 template<
663 typename Key,
664 typename A,
665 typename B,
666 typename C = typename std::common_type<A, B>::type
667 >
668 static typename std::enable_if<
669 std::is_same<Key, common>::value
670 && details::is_supported_number<A>::value
671 && details::is_supported_number<B>::value
672 // Prevent implicit type conversion from singed to unsigned types
673 && std::is_signed<A>::value != std::is_unsigned<B>::value
674 , C>::type get( A from = std::numeric_limits<A>::min( ),
675 B to = std::numeric_limits<B>::max( ) ) {
676 return get( static_cast<C>( from ), static_cast<C>( to ) );
677 }
678
679 /**
680 * \brief Generate a bool value with specific probability
681 * by std::bernoulli_distribution
682 * \param probability The probability of generating true in [0; 1] range
683 * 0 means always false, 1 means always true
684 * \return 'true' with 'probability' probability ('false' otherwise)
685 */
686 template<typename T>
687 static typename std::enable_if<std::is_same<T, bool>::value
688 , bool>::type get( const double probability = 0.5 ) {
689 assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
690 return BoolDist{ probability }( engine );
691 }
692
693 /**
694 * \brief Return random value from initilizer_list
695 * \param init_list initilizer_list with values
696 * \return Random value from initilizer_list
697 * \note Should be 1 or more elements in initilizer_list
698 * \note Warning! Elements in initilizer_list can't be moved:
699 * https://stackoverflow.com/a/8193157/5734836
700 */
701 template<typename T>
702 static T get( std::initializer_list<T> init_list ) {
703 assert( 0u != init_list.size( ) );
704 return *get( init_list.begin( ), init_list.end( ) );
705 }
706
707 /**
708 * \brief Return random iterator from iterator range
709 * \param first, last - the range of elements
710 * \return Random iterator from [first, last) range
711 * \note If first == last, return last
712 */
713 template<typename InputIt>
714 static typename std::enable_if<details::is_iterator<InputIt>::value
715 , InputIt>::type get( InputIt first, InputIt last ) {
716 const auto size = std::distance( first, last );
717 if( 0u == size ) return last;
718 using diff_t = typename std::iterator_traits<InputIt>::difference_type;
719 return std::next( first, get<diff_t>( 0, size - 1 ) );
720 }
721
722 /**
723 * \brief Return random iterator from Container
724 * \param container The container with elements
725 * \return Random iterator from container
726 * \note If container is empty return std::end( container ) iterator
727 */
728 template<typename Container>
729 static typename std::enable_if<details::is_iterator<
730 decltype( std::begin( std::declval<Container>( ) ) )>::value
731 , decltype( std::begin( std::declval<Container>( ) ) )
732 >::type get( Container& container ) {
733 return get( std::begin( container ), std::end( container ) );
734 }
735
736 /**
737 * \brief Return value from custom Dist distribution
738 * seeded by internal random engine
739 * \param Dist The type of custom distribution with next concept:
740 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
741 * \param args The arguments which will be forwarded to Dist constructor
742 * \return Value from custom distribution
743 */
744 template<typename Dist, typename... Args>
745 static typename Dist::result_type get( Args&&... args ) {
746 return Dist{ std::forward<Args>( args )... }( engine );
747 }
748
749 /**
750 * \brief Return value from custom 'dist' distribution
751 * seeded by internal random engine
752 * \param dist The custom distribution with next concept:
753 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
754 * \param args The arguments which will be forwarded to Dist constructor
755 * \return Value from custom 'dist' distribution
756 */
757 template<typename Dist>
758 static typename Dist::result_type get( Dist& dist ) {
759 return dist( engine );
760 }
761
762 /**
763 * \brief Reorders the elements in the given range [first, last)
764 * such that each possible permutation of those elements
765 * has equal probability of appearance.
766 * \param first, last - the range of elements to shuffle randomly
767 */
768 template<typename RandomIt>
769 static void shuffle( RandomIt first, RandomIt last ) {
770 std::shuffle( first, last, engine );
771 }
772
773 /**
774 * \brief Reorders the elements in the given container
775 * such that each possible permutation of those elements
776 * has equal probability of appearance.
777 * \param container - the container with elements to shuffle randomly
778 */
779 template<typename Container>
780 static void shuffle( Container& container ) {
781 shuffle( std::begin( container ), std::end( container ) );
782 }
783
784 /// return internal engine by copy
785 static Engine get_engine( ) {
786 return engine;
787 }
788 protected:
789 /// return engine seeded by Seeder
790 static Engine make_seeded_engine( ) {
791 // Make seeder instance for seed return by reference like std::seed_seq
792 Seeder seeder;
793 return Engine{ seeder( ) };
794 }
795 protected:
796 /// The random number engine
797 static thread_local Engine engine;
798 };
799
800 /// Seed random number engine by Seeder
801 template<
802 typename Engine,
803 typename Seeder,
804 template<typename> class IntegerDist,
805 template<typename> class RealDist,
806 typename BoolDist
807 >
808 thread_local Engine basic_random_thread_local<Engine, Seeder, IntegerDist, RealDist, BoolDist
809 // VS2017 issue, can't init by Seeder from lambda
810 >::engine( make_seeded_engine( ) );
811
812 /**
813 * \brief Base template class for random
814 * with local API and local internal member storage
815 * \note it IS thread safe but less efficient then
816 * basic_random_static
817 * \param Engine A random engine with interface like in the std::mt19937
818 * \param Seeder A seeder type which return seed for internal engine
819 * through operator()
820 */
821 template<
822 typename Engine,
823 typename Seeder = seeder_default,
824 template<typename> class IntegerDist = std::uniform_int_distribution,
825 template<typename> class RealDist = std::uniform_real_distribution,
826 typename BoolDist = std::bernoulli_distribution
827 >
828 class basic_random_local {
829 public:
830 /// Type of used random number engine
831 using engine_type = Engine;
832
833 /// Type of used random number seeder
834 using seeder_type = Seeder;
835
836 /// Type of used integer distribution
837 template<typename T>
838 using integer_dist_t = IntegerDist<T>;
839
840 /// Type of used real distribution
841 template<typename T>
842 using real_dist_t = RealDist<T>;
843
844 /// Type of used bool distribution
845 using bool_dist_t = BoolDist;
846
847 /// Key type for getting common type numbers or objects
848 using common = details::common;
849
850 /**
851 * \return The minimum value
852 * potentially generated by the random-number engine
853 */
854 static constexpr typename Engine::result_type min( ) {
855 return Engine::min( );
856 }
857
858 /**
859 * \return The maximum value
860 * potentially generated by the random-number engine
861 */
862 static constexpr typename Engine::result_type max( ) {
863 return Engine::max( );
864 }
865
866 /// Advances the internal state by z times
867 void discard( const unsigned long long z ) {
868 engine.discard( z );
869 }
870
871 /// Reseed by Seeder
872 void reseed( ) {
873 Seeder seeder;
874 seed( seeder( ) );
875 }
876
877 /**
878 * \brief Reinitializes the internal state
879 * of the random-number engine using new seed value
880 * \param value The seed value to use
881 * in the initialization of the internal state
882 */
883 void seed( const typename Engine::result_type value =
884 Engine::default_seed ) {
885 engine.seed( value );
886 }
887
888 /**
889 * \brief Reinitializes the internal state
890 * of the random-number engine using new seed value
891 * \param seq The seed sequence
892 * to use in the initialization of the internal state
893 */
894 template<typename Sseq>
895 void seed( Sseq& seq ) {
896 engine.seed( seq );
897 }
898
899 /// return random number from engine in [min(), max()] range
900 typename Engine::result_type get( ) {
901 return engine( );
902 }
903
904 /**
905 * \brief Compares internal pseudo-random number engine
906 * with 'other' pseudo-random number engine.
907 * Two engines are equal, if their internal states
908 * are equivalent, that is, if they would generate
909 * equivalent values for any number of calls of operator()
910 * \param other The engine, with which the internal engine will be compared
911 * \return true, if other and internal engine are equal
912 */
913 bool is_equal( const Engine& other ) {
914 return engine == other;
915 }
916
917 /**
918 * \brief Serializes the internal state of the
919 * internal pseudo-random number engine as a sequence
920 * of decimal numbers separated by one or more spaces,
921 * and inserts it to the stream ost. The fill character
922 * and the formatting flags of the stream are
923 * ignored and unaffected.
924 * \param ost The output stream to insert the data to
925 */
926 template<typename CharT, typename Traits>
927 void serialize( std::basic_ostream<CharT, Traits>& ost ) {
928 ost << engine;
929 }
930
931 /**
932 * \brief Restores the internal state of the
933 * internal pseudo-random number engine from
934 * the serialized representation, which
935 * was created by an earlier call to 'serialize'
936 * using a stream with the same imbued locale and
937 * the same CharT and Traits.
938 * If the input cannot be deserialized,
939 * internal engine is left unchanged and failbit is raised on ist
940 * \param ost The input stream to extract the data from
941 */
942 template<typename CharT, typename Traits>
943 void deserialize( std::basic_istream<CharT, Traits>& ist ) {
944 ist >> engine;
945 }
946
947 /**
948 * \brief Generate a random integer number in a [from; to] range
949 * by std::uniform_int_distribution
950 * \param from The first limit number of a random range
951 * \param to The second limit number of a random range
952 * \return A random integer number in a [from; to] range
953 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
954 * \note Prevent implicit type conversion
955 */
956 template<typename T>
957 typename std::enable_if<details::is_uniform_int<T>::value
958 , T>::type get( T from = std::numeric_limits<T>::min( ),
959 T to = std::numeric_limits<T>::max( ) ) {
960 if( from < to ) // Allow range from higher to lower
961 return IntegerDist<T>{ from, to }( engine );
962 return IntegerDist<T>{ to, from }( engine );
963 }
964
965 /**
966 * \brief Generate a random real number in a [from; to] range
967 * by std::uniform_real_distribution
968 * \param from The first limit number of a random range
969 * \param to The second limit number of a random range
970 * \return A random real number in a [from; to] range
971 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
972 * \note Prevent implicit type conversion
973 */
974 template<typename T>
975 typename std::enable_if<details::is_uniform_real<T>::value
976 , T>::type get( T from = std::numeric_limits<T>::min( ),
977 T to = std::numeric_limits<T>::max( ) ) {
978 if( from < to ) // Allow range from higher to lower
979 return RealDist<T>{ from, to }( engine );
980 return RealDist<T>{ to, from }( engine );
981 }
982
983 /**
984 * \brief Generate a random byte number in a [from; to] range
985 * \param from The first limit number of a random range
986 * \param to The second limit number of a random range
987 * \return A random byte number in a [from; to] range
988 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
989 * \note Prevent implicit type conversion
990 */
991 template<typename T>
992 typename std::enable_if<details::is_byte<T>::value
993 , T>::type get( T from = std::numeric_limits<T>::min( ),
994 T to = std::numeric_limits<T>::max( ) ) {
995 // Choose between short and unsigned short for byte conversion
996 using short_t = typename std::conditional<std::is_signed<T>::value,
997 short, unsigned short>::type;
998
999 return static_cast<T>( get<short_t>( from, to ) );
1000 }
1001
1002 /**
1003 * \brief Generate a random common_type number in a [from; to] range
1004 * \param Key The Key type for this version of 'get' method
1005 * Type should be '(THIS_TYPE)::common' struct
1006 * \param from The first limit number of a random range
1007 * \param to The second limit number of a random range
1008 * \return A random common_type number in a [from; to] range
1009 * \note Allow both: 'from' <= 'to' and 'from' >= 'to'
1010 * \note Allow implicit type conversion
1011 * \note Prevent implicit type conversion from singed to unsigned types
1012 * Why? std::common_type<Unsigned, Signed> chooses unsigned value,
1013 * then Signed value will be converted to Unsigned value
1014 * which gives us a wrong range for random values.
1015 * https://stackoverflow.com/a/5416498/5734836
1016 */
1017 template<
1018 typename Key,
1019 typename A,
1020 typename B,
1021 typename C = typename std::common_type<A, B>::type
1022 >
1023 typename std::enable_if<
1024 std::is_same<Key, common>::value
1025 && details::is_supported_number<A>::value
1026 && details::is_supported_number<B>::value
1027 // Prevent implicit type conversion from singed to unsigned types
1028 && std::is_signed<A>::value != std::is_unsigned<B>::value
1029 , C>::type get( A from = std::numeric_limits<A>::min( ),
1030 B to = std::numeric_limits<B>::max( ) ) {
1031 return get( static_cast<C>( from ), static_cast<C>( to ) );
1032 }
1033
1034 /**
1035 * \brief Generate a bool value with specific probability
1036 * by std::bernoulli_distribution
1037 * \param probability The probability of generating true in [0; 1] range
1038 * 0 means always false, 1 means always true
1039 * \return 'true' with 'probability' probability ('false' otherwise)
1040 */
1041 template<typename T>
1042 typename std::enable_if<std::is_same<T, bool>::value
1043 , bool>::type get( const double probability = 0.5 ) {
1044 assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
1045 return BoolDist{ probability }( engine );
1046 }
1047
1048 /**
1049 * \brief Return random value from initilizer_list
1050 * \param init_list initilizer_list with values
1051 * \return Random value from initilizer_list
1052 * \note Should be 1 or more elements in initilizer_list
1053 * \note Warning! Elements in initilizer_list can't be moved:
1054 * https://stackoverflow.com/a/8193157/5734836
1055 */
1056 template<typename T>
1057 T get( std::initializer_list<T> init_list ) {
1058 assert( 0u != init_list.size( ) );
1059 return *get( init_list.begin( ), init_list.end( ) );
1060 }
1061
1062 /**
1063 * \brief Return random iterator from iterator range
1064 * \param first, last - the range of elements
1065 * \return Random iterator from [first, last) range
1066 * \note If first == last, return last
1067 */
1068 template<typename InputIt>
1069 typename std::enable_if<details::is_iterator<InputIt>::value
1070 , InputIt>::type get( InputIt first, InputIt last ) {
1071 const auto size = std::distance( first, last );
1072 if( 0u == size ) return last;
1073 using diff_t = typename std::iterator_traits<InputIt>::difference_type;
1074 return std::next( first, get<diff_t>( 0, size - 1 ) );
1075 }
1076
1077 /**
1078 * \brief Return random iterator from Container
1079 * \param container The container with elements
1080 * \return Random iterator from container
1081 * \note If container is empty return std::end( container ) iterator
1082 */
1083 template<typename Container>
1084 typename std::enable_if<details::is_iterator<
1085 decltype( std::begin( std::declval<Container>( ) ) )>::value
1086 , decltype( std::begin( std::declval<Container>( ) ) )
1087 >::type get( Container& container ) {
1088 return get( std::begin( container ), std::end( container ) );
1089 }
1090
1091 /**
1092 * \brief Return value from custom Dist distribution
1093 * seeded by internal random engine
1094 * \param Dist The type of custom distribution with next concept:
1095 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
1096 * \param args The arguments which will be forwarded to Dist constructor
1097 * \return Value from custom distribution
1098 */
1099 template<typename Dist, typename... Args>
1100 typename Dist::result_type get( Args&&... args ) {
1101 return Dist{ std::forward<Args>( args )... }( engine );
1102 }
1103
1104 /**
1105 * \brief Return value from custom 'dist' distribution
1106 * seeded by internal random engine
1107 * \param dist The custom distribution with next concept:
1108 * http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
1109 * \param args The arguments which will be forwarded to Dist constructor
1110 * \return Value from custom 'dist' distribution
1111 */
1112 template<typename Dist>
1113 typename Dist::result_type get( Dist& dist ) {
1114 return dist( engine );
1115 }
1116
1117 /**
1118 * \brief Reorders the elements in the given range [first, last)
1119 * such that each possible permutation of those elements
1120 * has equal probability of appearance.
1121 * \param first, last - the range of elements to shuffle randomly
1122 */
1123 template<typename RandomIt>
1124 void shuffle( RandomIt first, RandomIt last ) {
1125 std::shuffle( first, last, engine );
1126 }
1127
1128 /**
1129 * \brief Reorders the elements in the given container
1130 * such that each possible permutation of those elements
1131 * has equal probability of appearance.
1132 * \param container - the container with elements to shuffle randomly
1133 */
1134 template<typename Container>
1135 void shuffle( Container& container ) {
1136 shuffle( std::begin( container ), std::end( container ) );
1137 }
1138
1139 /// return internal engine by copy
1140 Engine get_engine( ) const {
1141 return engine;
1142 }
1143 protected:
1144 /// return engine seeded by Seeder
1145 static Engine make_seeded_engine( ) {
1146 // Make seeder instance for seed return by reference like std::seed_seq
1147 Seeder seeder;
1148 return Engine{ seeder( ) };
1149 }
1150 protected:
1151 /// The random number engine
1152 Engine engine{ make_seeded_engine( ) };
1153 };
1154
1155 /**
1156 * \brief The basic static random alias based on a std::mt19937
1157 * \note It uses static methods API and data with static storage
1158 * \note Not thread safe but more prefomance
1159 */
1160 using random_static = basic_random_static<std::mt19937>;
1161
1162 /**
1163 * \brief The basic static random alias based on a std::mt19937
1164 * \note It uses static methods API and data with thread_local storage
1165 * \note Thread safe but less perfomance
1166 */
1167 using random_thread_local = basic_random_thread_local<std::mt19937>;
1168
1169 /**
1170 * \brief The basic static random alias based on a std::mt19937
1171 * \note It uses non static methods API and data with auto storage
1172 * \note Not thread safe. Should construct on the stack at local scope
1173 */
1174 using random_local = basic_random_local<std::mt19937>;
1175
1176 } // namespace effolkronium
1177
1178 #endif // #ifndef EFFOLKRONIUM_RANDOM_HPP