Generator.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * Copyright (c) 2023, Martin Janiczek <martin@janiczek.cz>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <LibTest/Macros.h>
  8. #include <LibTest/Randomized/RandomRun.h>
  9. #include <AK/Function.h>
  10. #include <AK/Random.h>
  11. #include <AK/String.h>
  12. #include <AK/StringView.h>
  13. #include <AK/Tuple.h>
  14. namespace Test {
  15. namespace Randomized {
  16. // Returns a random double value in range 0..1.
  17. inline double get_random_probability()
  18. {
  19. static constexpr u32 max_u32 = NumericLimits<u32>::max();
  20. u32 random_u32 = AK::get_random_uniform(max_u32);
  21. return static_cast<double>(random_u32) / static_cast<double>(max_u32);
  22. }
  23. // Generators take random bits from the RandomnessSource and return a value
  24. // back.
  25. //
  26. // Example:
  27. // - Gen::u32(5,10) --> 9, 7, 5, 10, 8, ...
  28. namespace Gen {
  29. // An unsigned integer generator.
  30. //
  31. // The minimum value will always be 0.
  32. // The maximum value is given by user in the argument.
  33. //
  34. // Gen::unsigned_int(10) -> value 5, RandomRun [5]
  35. // -> value 8, RandomRun [8]
  36. // etc.
  37. //
  38. // Shrinks towards 0.
  39. inline u32 unsigned_int(u32 max)
  40. {
  41. if (max == 0)
  42. return 0;
  43. u32 random = Test::randomness_source().draw_value(max, [&]() {
  44. return AK::get_random_uniform(max + 1);
  45. });
  46. return random;
  47. }
  48. // An unsigned integer generator in a particular range.
  49. //
  50. // Gen::unsigned_int(3,10) -> value 3, RandomRun [0]
  51. // -> value 8, RandomRun [5]
  52. // -> value 10, RandomRun [7]
  53. // etc.
  54. //
  55. // In case `min == max`, the RandomRun footprint will be smaller: no randomness
  56. // is needed.
  57. //
  58. // Gen::unsigned_int(3,3) -> value 3, RandomRun [] (always)
  59. //
  60. // Shrinks towards the minimum.
  61. inline u32 unsigned_int(u32 min, u32 max)
  62. {
  63. VERIFY(max >= min);
  64. return unsigned_int(max - min) + min;
  65. }
  66. // Randomly (uniformly) selects a value out of the given arguments.
  67. //
  68. // Gen::one_of(20,5,10) --> value 20, RandomRun [0]
  69. // --> value 5, RandomRun [1]
  70. // --> value 10, RandomRun [2]
  71. //
  72. // Shrinks towards the earlier arguments (above, towards 20).
  73. template<typename... Ts>
  74. requires(sizeof...(Ts) > 0)
  75. CommonType<Ts...> one_of(Ts... choices)
  76. {
  77. Vector<CommonType<Ts...>> choices_vec { choices... };
  78. constexpr size_t count = sizeof...(choices);
  79. size_t i = unsigned_int(count - 1);
  80. return choices_vec[i];
  81. }
  82. template<typename T>
  83. struct Choice {
  84. i32 weight;
  85. T value;
  86. };
  87. // Workaround for clang bug fixed in clang 17
  88. template<typename T>
  89. Choice(i32, T) -> Choice<T>;
  90. // Randomly (uniformly) selects a value out of the given weighted arguments.
  91. //
  92. // Gen::frequency(
  93. // Gen::Choice {5,999},
  94. // Gen::Choice {1,111},
  95. // )
  96. // --> value 999 (5 out of 6 times), RandomRun [0]
  97. // --> value 111 (1 out of 6 times), RandomRun [1]
  98. //
  99. // Shrinks towards the earlier arguments (above, towards 'x').
  100. template<typename... Ts>
  101. requires(sizeof...(Ts) > 0)
  102. CommonType<Ts...> frequency(Choice<Ts>... choices)
  103. {
  104. Vector<Choice<CommonType<Ts...>>> choices_vec { choices... };
  105. u32 sum = 0;
  106. for (auto const& choice : choices_vec) {
  107. VERIFY(choice.weight > 0);
  108. sum += static_cast<u32>(choice.weight);
  109. }
  110. u32 target = unsigned_int(sum);
  111. size_t i = 0;
  112. for (auto const& choice : choices_vec) {
  113. u32 weight = static_cast<u32>(choice.weight);
  114. if (weight >= target) {
  115. return choice.value;
  116. }
  117. target -= weight;
  118. ++i;
  119. }
  120. return choices_vec[i - 1].value;
  121. }
  122. // An unsigned integer generator in the full u32 range.
  123. //
  124. // 8/17 (47%) of the time it will bias towards 8bit numbers,
  125. // 4/17 (23%) towards 4bit numbers,
  126. // 2/17 (12%) towards 16bit numbers,
  127. // 1/17 (6%) towards 32bit numbers,
  128. // 2/17 (12%) towards edge cases like 0 and NumericLimits::max() of various unsigned int types.
  129. //
  130. // Gen::unsigned_int() -> value 3, RandomRun [0,3]
  131. // -> value 8, RandomRun [1,8]
  132. // -> value 100, RandomRun [2,100]
  133. // -> value 5, RandomRun [3,5]
  134. // -> value 255, RandomRun [4,1]
  135. // -> value 65535, RandomRun [4,2]
  136. // etc.
  137. //
  138. // Shrinks towards 0.
  139. inline u32 unsigned_int()
  140. {
  141. u32 bits = frequency(
  142. // weight, bits
  143. Choice { 4, 4 },
  144. Choice { 8, 8 },
  145. Choice { 2, 16 },
  146. Choice { 1, 32 },
  147. Choice { 2, 0 });
  148. // The special cases go last as they can be the most extreme (large) values.
  149. if (bits == 0) {
  150. // Special cases, eg. max integers for u8, u16, u32.
  151. return one_of(
  152. 0U,
  153. NumericLimits<u8>::max(),
  154. NumericLimits<u16>::max(),
  155. NumericLimits<u32>::max());
  156. }
  157. u32 max = ((u64)1 << bits) - 1;
  158. return unsigned_int(max);
  159. }
  160. // A generator returning `true` with the given `probability` (0..1).
  161. //
  162. // If probability <= 0, doesn't use any randomness and returns false.
  163. // If probability >= 1, doesn't use any randomness and returns true.
  164. //
  165. // In general case:
  166. // Gen::weighted_boolean(0.75)
  167. // -> value false, RandomRun [0]
  168. // -> value true, RandomRun [1]
  169. //
  170. // Shrinks towards false.
  171. inline bool weighted_boolean(double probability)
  172. {
  173. if (probability <= 0)
  174. return false;
  175. if (probability >= 1)
  176. return true;
  177. u32 random_int = Test::randomness_source().draw_value(1, [&]() {
  178. double drawn_probability = get_random_probability();
  179. return drawn_probability <= probability ? 1 : 0;
  180. });
  181. bool random_bool = random_int == 1;
  182. return random_bool;
  183. }
  184. // A (fair) boolean generator.
  185. //
  186. // Gen::boolean()
  187. // -> value false, RandomRun [0]
  188. // -> value true, RandomRun [1]
  189. //
  190. // Shrinks towards false.
  191. inline bool boolean()
  192. {
  193. return weighted_boolean(0.5);
  194. }
  195. // A vector generator of a random length between the given limits.
  196. //
  197. // Gen::vector(2,3,[]() { return Gen::unsigned_int(5); })
  198. // -> value [1,5], RandomRun [1,1,1,5,0]
  199. // -> value [1,5,0], RandomRun [1,1,1,5,1,0,0]
  200. // etc.
  201. //
  202. // In case `min == max`, the RandomRun footprint will be smaller, as there will
  203. // be no randomness involved in figuring out the length:
  204. //
  205. // Gen::vector(3,3,[]() { return Gen::unsigned_int(5); })
  206. // -> value [1,3], RandomRun [1,3]
  207. // -> value [5,2], RandomRun [5,2]
  208. // etc.
  209. //
  210. // Shrinks towards shorter vectors, with simpler elements inside.
  211. template<typename Fn>
  212. inline Vector<InvokeResult<Fn>> vector(size_t min, size_t max, Fn item_gen)
  213. {
  214. VERIFY(max >= min);
  215. size_t size = 0;
  216. Vector<InvokeResult<Fn>> acc;
  217. // Special case: no randomness for the boolean
  218. if (min == max) {
  219. while (size < min) {
  220. acc.append(item_gen());
  221. ++size;
  222. }
  223. return acc;
  224. }
  225. // General case: before each item we "flip a coin" to decide whether to
  226. // generate another one.
  227. //
  228. // This algorithm is used instead of the more intuitive "generate length,
  229. // then generate that many items" algorithm, because it produces RandomRun
  230. // patterns that shrink more easily.
  231. //
  232. // See the Hypothesis paper [1], section 3.3, around the paragraph starting
  233. // with "More commonly".
  234. //
  235. // [1]: https://drops.dagstuhl.de/opus/volltexte/2020/13170/pdf/LIPIcs-ECOOP-2020-13.pdf
  236. while (size < min) {
  237. acc.append(item_gen());
  238. ++size;
  239. }
  240. double average = static_cast<double>(min + max) / 2.0;
  241. VERIFY(average > 0);
  242. // A geometric distribution: https://en.wikipedia.org/wiki/Geometric_distribution#Moments_and_cumulants
  243. // The below derives from the E(X) = 1/p formula.
  244. //
  245. // We need to flip the `p` to `1-p` as our success ("another item!") is
  246. // a "failure" in the geometric distribution's interpretation ("we fail X
  247. // times before succeeding the first time").
  248. //
  249. // That gives us `1 - 1/p`. Then, E(X) also contains the final success, so we
  250. // need to say `1 + average` instead of `average`, as it will mean "our X
  251. // items + the final failure that stops the process".
  252. double probability = 1.0 - 1.0 / (1.0 + average);
  253. while (size < max) {
  254. if (weighted_boolean(probability)) {
  255. acc.append(item_gen());
  256. ++size;
  257. } else {
  258. break;
  259. }
  260. }
  261. return acc;
  262. }
  263. // A vector generator of a given length.
  264. //
  265. // Gen::vector_of_length(3,[]() { return Gen::unsigned_int(5); })
  266. // -> value [1,5,0], RandomRun [1,1,1,5,1,0,0]
  267. // -> value [2,9,3], RandomRun [1,2,1,9,1,3,0]
  268. // etc.
  269. //
  270. // Shrinks towards shorter vectors, with simpler elements inside.
  271. template<typename Fn>
  272. inline Vector<InvokeResult<Fn>> vector(size_t length, Fn item_gen)
  273. {
  274. return vector(length, length, item_gen);
  275. }
  276. // A vector generator of a random length between 0 and 32 elements.
  277. //
  278. // If you need a different length, use vector(max,item_gen) or
  279. // vector(min,max,item_gen).
  280. //
  281. // Gen::vector([]() { return Gen::unsigned_int(5); })
  282. // -> value [], RandomRun [0]
  283. // -> value [1], RandomRun [1,1,0]
  284. // -> value [1,5], RandomRun [1,1,1,5,0]
  285. // -> value [1,5,0], RandomRun [1,1,1,5,1,0,0]
  286. // -> value [1,5,0,2], RandomRun [1,1,1,5,1,0,1,2,0]
  287. // etc.
  288. //
  289. // Shrinks towards shorter vectors, with simpler elements inside.
  290. template<typename Fn>
  291. inline Vector<InvokeResult<Fn>> vector(Fn item_gen)
  292. {
  293. return vector(0, 32, item_gen);
  294. }
  295. } // namespace Gen
  296. } // namespace Randomized
  297. } // namespace Test