C++ Library Extensions 2022.12.09
To help learn modern C++ programming
cpg_chrono_random.hpp
Go to the documentation of this file.
1
12#ifndef _CPG_CHRONO_RANDOM_HPP
13#define _CPG_CHRONO_RANDOM_HPP
14
15#ifndef NOMINMAX
16#define NOMINMAX
17#endif
18
19#ifdef _MSVC_LANG
20 #if _MSVC_LANG < 201703L
21 #error This libary requires C++17 Standard (Visual Studio 2017).
22 #endif
23#else
24
25 #if __cplusplus < 201703
26 #error This library requires C++17 Standard (GNU g++ version 8.0 or clang++ version 8.0 above)
27 #endif // end of __cplusplus
28
29#endif // end of _MSVC_LANG
30
31#include <chrono>
32#include <ratio>
33#include <random>
34#include <iostream>
35#include <sstream>
36#include <vector>
37#include <numeric>
38#include <algorithm>
39#include <execution>
40
41#include "cpg_types.hpp"
42#include "cpg_conversion.hpp"
43
44namespace cpg
45{
46 namespace cgt = cpg::types;
47
52 namespace chrono_random
53 {
54 using nano_t = std::nano;
55 using micro_t = std::micro;
56 using milli_t = std::milli;
57 using second_t = std::ratio<1>;
58 using minute_t = std::ratio<60>;
59 using hour_t = std::ratio<3600>;
60
61 template<typename TimeUnit>
62 using duration_t = std::chrono::duration<double, TimeUnit>;
63
70
71 using high_resolution_clock_t = std::chrono::high_resolution_clock;
72 using time_point_t = std::chrono::time_point<high_resolution_clock_t>;
74
75 inline unsigned int seed()
76 { return high_resolution_clock_t::now().time_since_epoch().count(); }
77
78 // used to initialize random engine
79 unsigned int get_current_tick()
80 {
81 using std::chrono::duration;
82 using std::chrono::duration_cast;
83
84 auto current =
85 duration_cast<std::chrono::milliseconds>(now().time_since_epoch());
86
87 return current.count();
88 }
89
90 // to convert time (in TimeUnit) to type double
91 template<typename TimeUnit>
93 const time_point_t& start_time,
94 const time_point_t& end_time)
95 {
96 using std::chrono::duration;
97 using std::chrono::duration_cast;
98
99 if constexpr(std::is_same_v<TimeUnit, second_t>)
100 {
101 auto period_of_time =
102 duration_cast<std::chrono::duration<double, second_t>>(end_time - start_time);
103 return period_of_time.count();
104 }
105 else
106 {
107 auto period_of_time =
108 duration_cast<std::chrono::duration<double, TimeUnit>>(end_time - start_time);
109
110 return period_of_time.count();
111 }
112 }
113
114 using random_engine_t = std::default_random_engine;
115
116 template<std::integral IntegralType>
118 std::uniform_int_distribution<IntegralType>;
119
120 template<std::floating_point RealType>
122 std::uniform_real_distribution<RealType>;
123
125 {
126 private:
127 mutable high_resolution_clock_t m_clock;
128 mutable time_point_t m_start_time;
129
130 public:
131 stop_watch(): m_start_time{now()}
132 { }
133
134 void reset() const { this->m_start_time = now(); }
135
136 template<typename TimeUnit = milli_t>
137 auto elapsed(bool bReset = true) const
138 {
139 auto rlt = time_difference_in_unit<TimeUnit>(this->m_start_time, now());
140 if(bReset) reset();
141 return rlt;
142 }
143
144 template<typename TimeUnit = milli_t>
145 std::string elapsed_time(bool bReset = true, TimeUnit dummy_time = TimeUnit{}) const
146 {
147 std::ostringstream os;
148
149 os << time_difference_in_unit<TimeUnit>(this->m_start_time, now());
150
151 if constexpr(std::is_same_v<TimeUnit, nano_t>)
152 os << " nano-seconds";
153 else if constexpr(std::is_same_v<TimeUnit, micro_t>)
154 os << " micro-seconds";
155 else if constexpr(std::is_same_v<TimeUnit, milli_t>)
156 os << " milli-seconds";
157 else if constexpr(std::is_same_v<TimeUnit, second_t>)
158 os << " seconds";
159 else if constexpr(std::is_same_v<TimeUnit, minute_t>)
160 os << " minutes";
161 else if constexpr(std::is_same_v<TimeUnit, hour_t>)
162 os << " hours";
163
164 if(bReset) reset();
165 return os.str();
166 }
167 };
168
169 template<std::integral Type>
171 {
172 private:
173
174 std::random_device m_rd;
175 std::mt19937 m_randomizer;
176 typename std::vector<Type>::iterator m_next;
177 std::vector<Type> m_array;
178
179 public:
180 // [start, end)
181 fair_dice(Type start, Type end, Type instance = 1):
182 m_array((end-start)*instance), m_rd{}, m_randomizer{ m_rd() }
183 {
184 auto diff = end - start;
185
186 for(size_t i = 0; i < m_array.size(); ++i )
187 m_array[i] = (i % diff) + start;
188
189 shuffle();
190 }
191
192 typename std::vector<Type>::iterator begin() { return this->m_array.begin(); };
193 typename std::vector<Type>::iterator end() { return this->m_array.end(); };
194 typename std::vector<Type>::iterator next() { return this->m_next; };
195
196 size_t size() { return m_array.size(); }
197
198 void shuffle()
199 {
200 std::shuffle(m_array.begin(), m_array.end(), m_randomizer);
201 this->m_next = m_array.begin();
202 }
203
205 {
206 if(this->m_next == m_array.end())
207 shuffle();
208
209 Type v = *this->m_next;
210 ++this->m_next;
211 return v;
212 }
213
214 const std::vector<Type>& array()
215 {
216 return this->m_array;
217 }
218 };
219
220 template<typename Type,
221 typename DistributionType, typename EngineType>
223 {
224 public:
225 using engine_type = EngineType;
226 using distribution_type = DistributionType;
227 private:
228 mutable Type m_range_start;
229 mutable Type m_range_end;
230 mutable engine_type m_engine;
231 mutable distribution_type m_distribution;
232
233 public:
234 random_t(Type range_start = Type{},
235 Type range_end = Type{100},
236 unsigned int seed = 0):
237 m_range_start{range_start},
238 m_range_end{range_end},
239 m_engine{seed == 0 ? get_current_tick(): seed},
240 m_distribution{range_start, range_end} {}
241
242 random_t(const random_t&) = default;
243 random_t& operator=(const random_t&) = default;
244
245 Type operator()() const
246 {
247 return m_distribution(m_engine);
248 }
249
250 template<typename SizeType>
251 auto clone(SizeType size) const;
252
253 template<typename SizeType>
254 auto clone_pair(SizeType size) const;
255 };
256
257 template<std::integral IntegralType>
258 using random_uniform_integer_t = random_t<IntegralType,
260
261 template<std::floating_point RealType>
264
265 template<typename ValueType, typename RangeType1, typename RangeType2>
266 auto random_generator(RangeType1 range_start, RangeType2 range_end)
267 {
268 if constexpr(std::is_integral_v<ValueType>)
269 {
271 random_generator{(ValueType)range_start, (ValueType)range_end};
272
273 return random_generator;
274 }
275 else if constexpr(std::floating_point<ValueType>)
276 {
278 random_generator{(ValueType)range_start, (ValueType)range_end};
279
280 return random_generator;
281 }
282 }
283
284 template<typename Type,
285 typename DistributionType, typename EngineType>
286 template<typename SizeType>
288 {
289 using random_type =
291
292 std::vector<random_type> randoms;
293 randoms.reserve((size_t)size);
294
295 auto generator =
296 random_generator<unsigned>((unsigned)1,
297 std::numeric_limits<unsigned>::max());
298
299 std::set<unsigned> seeds;
300 while(seeds.size() != (size_t)size)
301 {
302 seeds.insert(generator());
303 }
304
305 for(auto seed: seeds)
306 randoms.emplace_back(random_type{this->m_range_start,
307 this->m_range_end, seed});
308
309 return randoms;
310 }
311
312 template<typename Type,
313 typename DistributionType, typename EngineType>
314 template<typename SizeType>
316 {
317 using random_type =
319 using random_pair = std::pair<size_t, random_type>;
320
321 std::vector<random_pair> randoms;
322 randoms.reserve((size_t)size);
323
324 auto generator =
325 random_generator<unsigned>((unsigned)1,
326 std::numeric_limits<unsigned>::max());
327
328 std::set<unsigned> seeds;
329 while(seeds.size() != (size_t)size)
330 {
331 seeds.insert(generator());
332 }
333
334 size_t index{};
335
336 for(auto seed: seeds)
337 randoms.emplace_back(index++, random_type{this->m_range_start,
338 this->m_range_end, seed});
339
340 return randoms;
341 }
342
343 template<typename Type, std::size_t N, typename RandomGeneratorType>
344 void random_fill( Type(&container)[N], RandomGeneratorType const& random_generator)
345 {
346 for(auto& e: container)
347 e = (Type)random_generator();
348 }
349
350 template<typename Type, std::size_t M, std::size_t N, typename RandomGeneratorType>
351 void random_fill( Type(&container)[M][N], RandomGeneratorType const& random_generator)
352 {
353 using array_t = Type[M * N];
354 auto& array = (array_t&)container[0][0];
355
357 }
358
359 template<typename Type, std::size_t L, std::size_t M, std::size_t N, typename RandomGeneratorType>
360 void random_fill( Type(&container)[L][M][N], RandomGeneratorType const& random_generator)
361 {
362 using array_t = Type[L * M * N];
363 auto& array = (array_t&)container[0][0];
364
366 }
367
368 template<template<typename, typename...> class ContainerType,
369 typename Type, typename... Types, typename RType,
370 template<typename, typename...> class RandomGeneratorType, typename... RTypes>
371 void random_fill(ContainerType<Type, Types...>& container,
372 const RandomGeneratorType<RType, RTypes...>& random_generator)
373 {
374 if(container.empty())
375 return;
376
377 for(auto& e: container)
378 e = (Type)random_generator();
379 }
380
381 template<template<typename, typename...> class ContainerType,
382 typename Type, typename... Types, typename RandomGeneratorType>
383 void random_fill(ContainerType<Type, Types...>& container,
384 RandomGeneratorType&& random_generator)
385 {
386 if(container.empty())
387 return;
388
389 for(auto& e: container)
390 e = (Type) random_generator();
391 }
392
393 template<template<typename, typename...> class ContainerType,
394 typename Type, typename... Types, typename RangeType>
395 void random_fill(ContainerType<Type, Types...>& container,
396 RangeType range_start, RangeType range_end)
397 {
398 random_fill(container, random_generator<RangeType>(range_start, range_end));
399 }
400
402 template<template<typename, std::size_t> class ContainerType,
403 typename Type, std::size_t N, typename RType,
404 template<typename, typename...> class RandomGeneratorType, typename... RTypes>
405 void random_fill(ContainerType<Type, N>& container,
406 const RandomGeneratorType<RType, RTypes...>& random_generator)
407 {
408 if(container.empty())
409 return;
410
411 for(auto& e: container)
412 e = (Type)random_generator();
413 }
414
415 template<template<typename, std::size_t> class ContainerType,
416 typename Type, std::size_t N, typename RandomGeneratorType>
417 void random_fill(ContainerType<Type, N>& container,
418 RandomGeneratorType&& random_generator)
419 {
420 if(container.empty())
421 return;
422
423 for(auto& e: container)
424 e = (Type)random_generator();
425 }
426
427 template<template<typename, std::size_t> class ContainerType,
428 typename Type, std::size_t N, typename RangeType>
429 void random_fill(ContainerType<Type, N>& container,
430 RangeType range_start, RangeType range_end)
431 {
432 random_fill(container, random_generator<RangeType>(range_start, range_end));
433 }
434
436
437 template<typename Type, std::size_t N,
438 template<typename, typename...> class RandomGeneratorType, typename RType, typename... RTypes>
439 void random_parallel_fill( Type( &container )[N],
440 const RandomGeneratorType<RType, RTypes...>& random_generator)
441 {
442 size_t container_size = N;
443 size_t generator_size = std::thread::hardware_concurrency();
444 size_t span = container_size / generator_size;
445
446 if(span < 100)
447 {
448 random_fill(container, random_generator);
449 return;
450 }
451
452 if(container_size % generator_size ) ++generator_size;
453
454 auto generator_pairs =
455 random_generator.clone_pair(generator_size);
456
457 std::for_each(std::execution::par,
458 generator_pairs.begin(), generator_pairs.end(),
459 [&](auto& pair)
460 {
461 auto index_start = pair.first * span;
462 auto index_end = index_start + span;
463
464 if(index_end > container_size) index_end = container_size;
465
466 for(size_t i = index_start; i < index_end; ++i)
467 container[i] = (Type)pair.second();
468 });
469 }
470
471 template<typename Type, std::size_t M, std::size_t N,
472 template<typename, typename...> class RandomGeneratorType, typename RType, typename... RTypes>
473 void random_parallel_fill( Type( &container )[M][N],
474 const RandomGeneratorType<RType, RTypes...>& random_generator)
475 {
476 using array_t = Type[M * N];
477 auto& array = (array_t&)container[0][0];
478
480 }
481
482 template<typename Type, std::size_t L, std::size_t M, std::size_t N,
483 template<typename, typename...> class RandomGeneratorType, typename RType, typename... RTypes>
484 void random_parallel_fill( Type( &container )[L][M][N],
485 const RandomGeneratorType<RType, RTypes...>& random_generator)
486 {
487 using array_t = Type[L * M * N];
488 auto& array = (array_t&)container[0][0][0];
489
491 }
492
493 template<template<typename, typename...> class ContainerType,
494 typename Type, typename... Types, typename RType,
495 template<typename, typename...> class RandomGeneratorType, typename... RTypes>
496 void random_parallel_fill(ContainerType<Type, Types...>& container,
497 const RandomGeneratorType<RType, RTypes...>& random_generator)
498 {
499 if(container.empty()) return;
500
501 size_t container_size = container.size();
502 size_t generator_size = std::thread::hardware_concurrency();
503 size_t span = container_size / generator_size;
504
505 if(span < 100)
506 {
507 random_fill(container, random_generator);
508 return;
509 }
510
511 if(container_size % generator_size ) ++generator_size;
512
513 auto generator_pairs = random_generator.clone_pair(generator_size);
514
515 std::for_each(std::execution::par,
516 generator_pairs.begin(), generator_pairs.end(),
517 [&](auto& pair)
518 {
519 auto index_start = pair.first * span;
520 auto index_end = index_start + span;
521
522 if(index_end > container_size) index_end = container_size;
523
524 for(size_t i = index_start; i < index_end; ++i)
525 container[i] = (RType)pair.second();
526 });
527 }
528
529 template<template<typename, typename...> class ContainerType,
530 typename Type, typename... Types,
531 typename RandomGeneratorType>
532 void random_parallel_fill(ContainerType<Type, Types...>& container,
533 RandomGeneratorType&& random_generator)
534 {
535 if(container.empty()) return;
536
537 size_t container_size = container.size();
538 size_t generator_size = std::thread::hardware_concurrency();
539 size_t span = container_size / generator_size;
540
541 if(span < 100)
542 {
543 random_fill(container, random_generator);
544 return;
545 }
546
547 if(container_size % generator_size ) ++generator_size;
548
549 auto generator_pairs = random_generator.clone_pair(generator_size);
550
551 std::for_each(std::execution::par,
552 generator_pairs.begin(), generator_pairs.end(),
553 [&](auto& pair)
554 {
555 auto index_start = pair.first * span;
556 auto index_end = index_start + span;
557
558 if(index_end > container_size) index_end = container_size;
559
560 for(size_t i = index_start; i < index_end; ++i)
561 container[i] = (Type)pair.second();
562 });
563 }
564
565 template<template<typename, typename...> class ContainerType,
566 typename Type, typename... Types, typename RangeType>
567 void random_parallel_fill(ContainerType<Type, Types...>& container,
568 RangeType range_start, RangeType range_end)
569 {
570 random_parallel_fill(container,
571 random_generator<RangeType>(range_start, range_end));
572 }
573
575 template<template<typename, std::size_t> class ContainerType,
576 typename Type, std::size_t N, typename RType,
577 template<typename, typename...> class RandomGeneratorType, typename... RTypes>
578 void random_parallel_fill(ContainerType<Type, N>& container,
579 const RandomGeneratorType<RType, RTypes...>& random_generator)
580 {
581 if(container.empty()) return;
582
583 size_t container_size = container.size();
584 size_t generator_size = std::thread::hardware_concurrency();
585 size_t span = container_size / generator_size;
586
587 if(span < 100)
588 {
589 random_fill(container, random_generator);
590 return;
591 }
592
593 if(container_size % generator_size ) ++generator_size;
594
595 auto generator_pairs = random_generator.clone_pair(generator_size);
596
597 std::for_each(std::execution::par,
598 generator_pairs.begin(), generator_pairs.end(),
599 [&](auto& pair)
600 {
601 auto index_start = pair.first * span;
602 auto index_end = index_start + span;
603
604 if(index_end > container_size) index_end = container_size;
605
606 for(size_t i = index_start; i < index_end; ++i)
607 container[i] = (Type)pair.second();
608 });
609 }
610
611 template<template<typename, std::size_t> class ContainerType,
612 typename Type, std::size_t N, typename RandomGeneratorType>
613 void random_parallel_fill(ContainerType<Type, N>& container,
614 RandomGeneratorType&& random_generator)
615 {
616 if(container.empty()) return;
617
618 size_t container_size = container.size();
619 size_t generator_size = std::thread::hardware_concurrency();
620 size_t span = container_size / generator_size;
621
622 if(span < 100)
623 {
624 random_fill(container, random_generator);
625 return;
626 }
627
628 if(container_size % generator_size ) ++generator_size;
629
630 auto generator_pairs = random_generator.clone_pair(generator_size);
631
632 std::for_each(std::execution::par,
633 generator_pairs.begin(), generator_pairs.end(),
634 [&](auto& pair)
635 {
636 auto index_start = pair.first * span;
637 auto index_end = index_start + span;
638
639 if(index_end > container_size) index_end = container_size;
640
641 for(size_t i = index_start; i < index_end; ++i)
642 container[i] = (Type)pair.second();
643 });
644 }
645
646 template<template<typename, std::size_t> class ContainerType,
647 typename Type, std::size_t N, typename RangeType>
648 void random_parallel_fill(ContainerType<Type, N>& container,
649 RangeType range_start, RangeType range_end)
650 {
651 random_parallel_fill(container,
652 random_generator<RangeType>(range_start, range_end));
653 }
654
655
672 template<template<typename, typename...> class ContainerType,
673 typename Type, typename... Types, typename SizeType,
674 template<typename, typename...> class RandomGeneratorType, typename... RTypes>
675 void random_fill(ContainerType<Type, Types...>& container,
676 const RandomGeneratorType<Type, RTypes...>& random_generator, SizeType size)
677 {
678 if constexpr (cgt::resize_available_c<ContainerType<Type, Types...>>)
679 {
680 container.resize((size_t)size);
681
682 for(auto& e: container)
683 e = random_generator();
684 }
685 else
686 {
687 container.clear();
688
689 while(container.size() != size)
690 {
691 container.push_back(random_generator());
692 }
693 }
694 }
695
696 template<typename CharType, size_t Size>
698 {
699 public:
700
701 const CharType* m_alphabet;
705
706 public:
707 template<typename Type, size_t ArraySize>
708 random_words(size_t minimum, size_t maximum, size_t max_words,
709 const Type(&alphabet)[ArraySize]):
710 m_alphabet{alphabet},
711 m_character_selector{ random_generator<size_t>(size_t{}, ArraySize-2)},
712 m_length_selector{ random_generator<size_t>(minimum, maximum) },
713 m_wordcount_selector{ random_generator<size_t>(1, max_words) } { }
714
715 std::basic_string<char> operator()()
716 {
717 // Windows codepage character - char
718 if constexpr(std::is_same_v<CharType, char>)
719 {
720 std::basic_ostringstream<CharType> stream;
721
722 auto word_count = m_wordcount_selector();
723
724 for(size_t j = 0; j < word_count; ++j)
725 {
726 auto length = this->m_length_selector();
727
728 std::basic_string<CharType> text(length, ' ');
729
730 for(size_t i=0; i < length; ++i)
731 text[i] = m_alphabet[m_character_selector()];
732
733 stream << text;
734
735 if(j + 1 != (size_t)word_count) stream <<' ';
736 }
737 return stream.str();
738 }
739 else
740 {
741 // utf16 - wchar_t == CharType
742 std::basic_ostringstream<CharType> stream;
743
744 auto word_count = m_wordcount_selector();
745
746 for(size_t j = 0; j < word_count; ++j)
747 {
748 auto length = this->m_length_selector();
749
750 std::basic_string<CharType> text(length, L' ');
751
752 for(size_t i=0; i < length; ++i)
753 text[i] = m_alphabet[m_character_selector()];
754
755 stream << text;
756
757 if(j + 1 != (size_t)word_count) stream << L' ';
758 }
759
760 // UTF16 - utf8 std::string
761 // conversion::wstring_to_string(stream.str());
762
763 // UTF16 - Windows codepage std::string
764 return conversion::wstring_to_string(stream.str(), CP_ACP);
765 }
766 }
767 };
768
769 template<typename Type, size_t ArraySize>
770 random_words(int, int, int, const Type(&)[ArraySize]) -> random_words<Type, ArraySize>;
771
772
773 } // end of namespace chrono_random
774
775} // end of namespace tpf
776
777#endif // end of file _CPG_CHRONO_RANDOM_HPP
tpf::sstream stream
std::vector< Type >::iterator begin()
const std::vector< Type > & array()
std::vector< Type >::iterator next()
std::vector< Type >::iterator end()
fair_dice(Type start, Type end, Type instance=1)
auto clone(SizeType size) const
random_t(const random_t &)=default
auto clone_pair(SizeType size) const
random_t(Type range_start=Type{}, Type range_end=Type{100}, unsigned int seed=0)
random_t & operator=(const random_t &)=default
std::basic_string< char > operator()()
random_uniform_integer_t< size_t > m_wordcount_selector
random_uniform_integer_t< size_t > m_length_selector
random_words(size_t minimum, size_t maximum, size_t max_words, const Type(&alphabet)[ArraySize])
random_uniform_integer_t< size_t > m_character_selector
auto elapsed(bool bReset=true) const
std::string elapsed_time(bool bReset=true, TimeUnit dummy_time=TimeUnit{}) const
std::string str() const
Definition: tpf_output.hpp:951
std::chrono::high_resolution_clock high_resolution_clock_t
auto random_generator(RangeType1 range_start, RangeType2 range_end)
duration_t< minute_t > minutes_t
duration_t< milli_t > milliseconds_t
random_words(int, int, int, const Type(&)[ArraySize]) -> random_words< Type, ArraySize >
duration_t< hour_t > hours_t
std::ratio< 3600 > hour_t
void random_parallel_fill(Type(&container)[N], const RandomGeneratorType< RType, RTypes... > &random_generator)
duration_t< micro_t > microseconds_t
std::chrono::duration< double, TimeUnit > duration_t
std::ratio< 60 > minute_t
double time_difference_in_unit(const time_point_t &start_time, const time_point_t &end_time)
std::default_random_engine random_engine_t
std::uniform_real_distribution< RealType > random_uniform_real_distribution
std::chrono::time_point< high_resolution_clock_t > time_point_t
unsigned int get_current_tick()
duration_t< nano_t > nanoseconds_t
std::uniform_int_distribution< IntegralType > random_uniform_integral_distribution
void random_fill(Type(&container)[N], RandomGeneratorType const &random_generator)
std::ratio< 1 > second_t
duration_t< second_t > seconds_t
std::string wstring_to_string(const std::wstring &wstr, unsigned int codepage=CP_UTF8)
Converts from std::wstring to std::string with codepage.
Includes subnamespace conversion.
auto minimum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:196
auto maximum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:181