tiny_mt.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include "common/alignment.h"
  7. #include "common/common_types.h"
  8. namespace Common {
  9. // Implementation of TinyMT (mersenne twister RNG).
  10. // Like Nintendo, we will use the sample parameters.
  11. class TinyMT {
  12. public:
  13. static constexpr std::size_t NumStateWords = 4;
  14. struct State {
  15. std::array<u32, NumStateWords> data{};
  16. };
  17. private:
  18. static constexpr u32 ParamMat1 = 0x8F7011EE;
  19. static constexpr u32 ParamMat2 = 0xFC78FF1F;
  20. static constexpr u32 ParamTmat = 0x3793FDFF;
  21. static constexpr u32 ParamMult = 0x6C078965;
  22. static constexpr u32 ParamPlus = 0x0019660D;
  23. static constexpr u32 ParamXor = 0x5D588B65;
  24. static constexpr u32 TopBitmask = 0x7FFFFFFF;
  25. static constexpr int MinimumInitIterations = 8;
  26. static constexpr int NumDiscardedInitOutputs = 8;
  27. static constexpr u32 XorByShifted27(u32 value) {
  28. return value ^ (value >> 27);
  29. }
  30. static constexpr u32 XorByShifted30(u32 value) {
  31. return value ^ (value >> 30);
  32. }
  33. private:
  34. State state{};
  35. private:
  36. // Internal API.
  37. void FinalizeInitialization() {
  38. const u32 state0 = this->state.data[0] & TopBitmask;
  39. const u32 state1 = this->state.data[1];
  40. const u32 state2 = this->state.data[2];
  41. const u32 state3 = this->state.data[3];
  42. if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
  43. this->state.data[0] = 'T';
  44. this->state.data[1] = 'I';
  45. this->state.data[2] = 'N';
  46. this->state.data[3] = 'Y';
  47. }
  48. for (int i = 0; i < NumDiscardedInitOutputs; i++) {
  49. this->GenerateRandomU32();
  50. }
  51. }
  52. u32 GenerateRandomU24() {
  53. return (this->GenerateRandomU32() >> 8);
  54. }
  55. static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
  56. u32& state0 = state->data[(index + 0) % NumStateWords];
  57. u32& state1 = state->data[(index + 1) % NumStateWords];
  58. u32& state2 = state->data[(index + 2) % NumStateWords];
  59. u32& state3 = state->data[(index + 3) % NumStateWords];
  60. const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
  61. const u32 y = x + index + value;
  62. state0 = y;
  63. state1 += x;
  64. state2 += y;
  65. }
  66. static void GenerateInitialValueXor(TinyMT::State* state, int index) {
  67. u32& state0 = state->data[(index + 0) % NumStateWords];
  68. u32& state1 = state->data[(index + 1) % NumStateWords];
  69. u32& state2 = state->data[(index + 2) % NumStateWords];
  70. u32& state3 = state->data[(index + 3) % NumStateWords];
  71. const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
  72. const u32 y = x - index;
  73. state0 = y;
  74. state1 ^= x;
  75. state2 ^= y;
  76. }
  77. public:
  78. constexpr TinyMT() = default;
  79. // Public API.
  80. // Initialization.
  81. void Initialize(u32 seed) {
  82. this->state.data[0] = seed;
  83. this->state.data[1] = ParamMat1;
  84. this->state.data[2] = ParamMat2;
  85. this->state.data[3] = ParamTmat;
  86. for (int i = 1; i < MinimumInitIterations; i++) {
  87. const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
  88. this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
  89. }
  90. this->FinalizeInitialization();
  91. }
  92. void Initialize(const u32* seed, int seed_count) {
  93. this->state.data[0] = 0;
  94. this->state.data[1] = ParamMat1;
  95. this->state.data[2] = ParamMat2;
  96. this->state.data[3] = ParamTmat;
  97. {
  98. const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
  99. GenerateInitialValuePlus(&this->state, 0, seed_count);
  100. for (int i = 0; i < num_init_iterations; i++) {
  101. GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
  102. (i < seed_count) ? seed[i] : 0);
  103. }
  104. for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
  105. GenerateInitialValueXor(&this->state,
  106. (i + 1 + num_init_iterations) % NumStateWords);
  107. }
  108. }
  109. this->FinalizeInitialization();
  110. }
  111. // State management.
  112. void GetState(TinyMT::State& out) const {
  113. out.data = this->state.data;
  114. }
  115. void SetState(const TinyMT::State& state_) {
  116. this->state.data = state_.data;
  117. }
  118. // Random generation.
  119. void GenerateRandomBytes(void* dst, std::size_t size) {
  120. const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
  121. const uintptr_t end = start + size;
  122. const uintptr_t aligned_start = Common::AlignUp(start, 4);
  123. const uintptr_t aligned_end = Common::AlignDown(end, 4);
  124. // Make sure we're aligned.
  125. if (start < aligned_start) {
  126. const u32 rnd = this->GenerateRandomU32();
  127. std::memcpy(dst, &rnd, aligned_start - start);
  128. }
  129. // Write as many aligned u32s as we can.
  130. {
  131. u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
  132. u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
  133. while (cur_dst < end_dst) {
  134. *(cur_dst++) = this->GenerateRandomU32();
  135. }
  136. }
  137. // Handle any leftover unaligned data.
  138. if (aligned_end < end) {
  139. const u32 rnd = this->GenerateRandomU32();
  140. std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
  141. }
  142. }
  143. u32 GenerateRandomU32() {
  144. // Advance state.
  145. const u32 x0 =
  146. (this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
  147. const u32 y0 = this->state.data[3];
  148. const u32 x1 = x0 ^ (x0 << 1);
  149. const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
  150. const u32 state0 = this->state.data[1];
  151. u32 state1 = this->state.data[2];
  152. u32 state2 = x1 ^ (y1 << 10);
  153. const u32 state3 = y1;
  154. if ((y1 & 1) != 0) {
  155. state1 ^= ParamMat1;
  156. state2 ^= ParamMat2;
  157. }
  158. this->state.data[0] = state0;
  159. this->state.data[1] = state1;
  160. this->state.data[2] = state2;
  161. this->state.data[3] = state3;
  162. // Temper.
  163. const u32 t1 = state0 + (state2 >> 8);
  164. u32 t0 = state3 ^ t1;
  165. if ((t1 & 1) != 0) {
  166. t0 ^= ParamTmat;
  167. }
  168. return t0;
  169. }
  170. u64 GenerateRandomU64() {
  171. const u32 lo = this->GenerateRandomU32();
  172. const u32 hi = this->GenerateRandomU32();
  173. return (u64{hi} << 32) | u64{lo};
  174. }
  175. float GenerateRandomF32() {
  176. // Floats have 24 bits of mantissa.
  177. constexpr u32 MantissaBits = 24;
  178. return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
  179. }
  180. double GenerateRandomF64() {
  181. // Doubles have 53 bits of mantissa.
  182. // The smart way to generate 53 bits of random would be to use 32 bits
  183. // from the first rnd32() call, and then 21 from the second.
  184. // Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
  185. // call, and (32 - 6) bits from the second. We'll do what they do, but
  186. // There's not a clear reason why.
  187. constexpr u32 MantissaBits = 53;
  188. constexpr u32 Shift1st = (64 - MantissaBits) / 2;
  189. constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
  190. const u32 first = (this->GenerateRandomU32() >> Shift1st);
  191. const u32 second = (this->GenerateRandomU32() >> Shift2nd);
  192. return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
  193. (1.0 / (u64{1} << MantissaBits));
  194. }
  195. };
  196. } // namespace Common