random.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // TODO: need a test file per header and include the header first, to detect missing includes
  2. #include "simple/support/random/engine/tiny.hpp"
  3. #include "simple/support/random/distribution/naive.hpp"
  4. #include "simple/support/random/distribution/diagonal.hpp"
  5. #include "simple/support/misc.hpp"
  6. #include "simple/support/algorithm.hpp"
  7. #include <iostream>
  8. #include <sstream>
  9. #include <cassert>
  10. #include <random>
  11. #include <unordered_map>
  12. using namespace simple::support;
  13. using namespace random;
  14. using namespace distribution;
  15. using namespace engine;
  16. std::random_device rd{};
  17. template <typename Range>
  18. double bit_entropy(Range& range)
  19. {
  20. const auto size = range.end() - range.begin();
  21. static std::unordered_map<std::remove_reference_t<decltype(*range.begin())>, double> frequencies;
  22. for(auto&& ch : range)
  23. frequencies[ch] += 1;
  24. for(auto&& f : frequencies)
  25. f.second /= size;
  26. double entropy = 0;
  27. for(auto&& pair : frequencies)
  28. {
  29. const auto& f = pair.second;
  30. if(f != 0.0)
  31. entropy -= f*std::log2(f);
  32. }
  33. return entropy;
  34. }
  35. struct entropy_result
  36. {
  37. double base;
  38. double byte_base;
  39. double variance;
  40. double byte_variance;
  41. };
  42. template<typename PRNG>
  43. entropy_result get_entropy(PRNG prng)
  44. {
  45. entropy_result result;
  46. std::vector<std::invoke_result_t<PRNG>> data(10000);
  47. std::generate(data.begin(), data.end(), prng);
  48. unsigned char* bytes = reinterpret_cast<unsigned char*>(data.data());
  49. const std::size_t byte_size = data.size() * sizeof(typename decltype(data)::value_type);
  50. auto byte_range = range{+bytes, bytes + byte_size};
  51. result.base = bit_entropy(data);
  52. result.byte_base = bit_entropy(byte_range);
  53. auto abs_minus = [](auto a, auto b)
  54. {
  55. return a > b ? a - b : b - a;
  56. };
  57. auto variance_range = variance(data, abs_minus);
  58. auto byte_variance_range = variance(byte_range, abs_minus);
  59. result.variance = bit_entropy(variance_range);
  60. result.byte_variance = bit_entropy(byte_variance_range);
  61. return result;
  62. }
  63. void TinyEngine()
  64. {
  65. auto seed = rd();
  66. std::cout << "Tiny engine test seed: " << std::hex << std::showbase << seed << std::endl;
  67. tiny<unsigned long long> t{seed};
  68. tiny<unsigned long long> t2{seed};
  69. assert(t == t2);
  70. auto tiny_entropy = get_entropy(t);
  71. assert( tiny_entropy.byte_base > 7.995 );
  72. assert( tiny_entropy.byte_variance > 7.7 );
  73. t.discard(1000);
  74. assert(t2 != t);
  75. std::stringstream ss;
  76. ss << t;
  77. ss >> t2;
  78. assert(t2 == t);
  79. for(int i = 0; i < 10000; ++i)
  80. assert(t() == t2());
  81. t.seed({1,2});
  82. ss.clear();
  83. ss.str("1 2");
  84. tiny<unsigned long long> t3{};
  85. ss >> t3;
  86. assert( t == t3 );
  87. // unlike standard engines, tiny accepts rvalue seed_seq
  88. tiny<unsigned> t4(std::seed_seq({1,2,3}));
  89. std::seed_seq sseq({1,2,3});
  90. tiny<unsigned> t5(sseq);
  91. assert( t4 == t5 );
  92. }
  93. void NaiveDistributions()
  94. {
  95. auto seed = rd();
  96. std::cout << "Naive distribution test seed: " << std::hex << std::showbase << seed << std::endl;
  97. tiny<unsigned long long> t{seed};
  98. naive_int<int> ni(-128,127);
  99. auto ni_entropy = get_entropy([&ni, &t](){ return ni(t); });
  100. assert( ni_entropy.base > 7.97 );
  101. naive_real<float> nr(-128.0,127.0);
  102. auto nr_entropy = get_entropy([&nr, &t](){ return (int)nr(t); });
  103. assert( nr_entropy.base > 7.95 );
  104. }
  105. void DiagonalDistribution()
  106. {
  107. auto seed = rd();
  108. std::cout << "Diagonal distribution test seed: " << std::hex << std::showbase << seed << std::endl;
  109. tiny<unsigned long long> t{seed};
  110. triangle<naive_real<double>> tri(0,1);
  111. for(int i = 0; i < 1000000; ++i)
  112. {
  113. auto a = tri(t);
  114. auto b = tri(t);
  115. assert((a + b <= 1.0));
  116. }
  117. tetrahedron<naive_real<double>> tetra(0,1);
  118. for(int i = 0; i < 1000000; ++i)
  119. {
  120. auto a = tetra(t);
  121. auto b = tetra(t);
  122. auto c = tetra(t);
  123. assert((a + b + c <= 1.0));
  124. }
  125. diagonal<naive_int<int>, 4, diagonal_side::middle> middle(0,100);
  126. for(int i = 0; i < 1000000; ++i)
  127. {
  128. auto a = middle(t);
  129. auto b = middle(t);
  130. auto c = middle(t);
  131. auto d = middle(t);
  132. assert((a + b + c + d == 100));
  133. }
  134. diagonal<naive_real<float>, 4, diagonal_side::upper> upper(0,100);
  135. for(int i = 0; i < 1000000; ++i)
  136. {
  137. auto a = upper(t);
  138. auto b = upper(t);
  139. auto c = upper(t);
  140. auto d = upper(t);
  141. assert(a <= 100);
  142. assert(b <= 100);
  143. assert(c <= 100);
  144. assert(d <= 100);
  145. assert((a + b + c + d >= 100));
  146. }
  147. diagonal<naive_int<int>, 3, diagonal_side::lower> li(0,255);
  148. auto li_entropy = get_entropy([&li, &t](){ return li(t); });
  149. assert( li_entropy.base > 7.3 );
  150. diagonal<naive_int<int>, 3, diagonal_side::upper> lu(0,255);
  151. auto lu_entropy = get_entropy([&lu, &t](){ return lu(t); });
  152. assert( lu_entropy.base > 7.9 );
  153. diagonal<naive_int<int>, 3, diagonal_side::middle> lm(0,255);
  154. auto lm_entropy = get_entropy([&lm, &t](){ return lm(t); });
  155. assert( lm_entropy.base > 7.65 );
  156. tetrahedron<std::uniform_real_distribution<double>> std_tetra(0,123);
  157. auto std_tetra2 = std_tetra;
  158. std::stringstream ss;
  159. ss << std_tetra2;
  160. ss >> std_tetra2;
  161. assert(std_tetra == std_tetra2);
  162. }
  163. void Noexcept()
  164. {
  165. auto noexcept_seeder = []() noexcept {return 0;};
  166. static_assert(noexcept(tiny<unsigned int>{noexcept_seeder}));
  167. static_assert(!noexcept(tiny<unsigned int>{std::random_device{}}));
  168. }
  169. int main()
  170. {
  171. TinyEngine();
  172. NaiveDistributions();
  173. DiagonalDistribution();
  174. Noexcept();
  175. return 0;
  176. }