1 /**
2     Random Number Generation
3 
4     This module contains the base interface for random number generators,
5     as well as a Mersenne Twister random number generator. 
6 
7     Copyright:
8         Copyright © 2023-2025, Kitsunebi Games
9         Copyright © 2023-2025, Inochi2D Project
10     
11     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12     Authors:   Luna Nielsen
13 */
14 module nulib.random;
15 import numem;
16 
17 /**
18     Base class of all random number generators
19 */
20 abstract
21 class RandomBase {
22 @nogc nothrow:
23 
24     /**
25         Resets the random number generator to its initial state.
26     */
27     abstract void reset();
28 
29     /**
30         Gets the next value in the random stream
31     */
32     abstract size_t next();
33 
34     /**
35         Gets the next value in the random stream
36     */
37     abstract double nextUniform();
38 
39     /**
40         Gets the next bytes in the random stream
41     */
42     abstract void next(ref ubyte[] destination);
43 }
44 
45 /**
46     A psuedo-random number generator
47 */
48 class Random : RandomBase {
49 @nogc nothrow:
50 private:
51 
52     enum mtWordSize = (size_t.sizeof*8);
53     static if (mtWordSize == 32) {
54         enum mtRecurrence = 624;
55         enum mtMiddleWord = 397;
56         enum mtSeperationPoint = 31;
57         enum mtMagicNumber = 1_812_433_253;
58         enum mtTwistCoefficient = 0x9908B0DF;
59 
60         enum mtU = 11;
61         enum mtD = 0xFFFFFFFF;
62         enum mtS = 7;
63         enum mtB = 0x9D2C5680;
64         enum mtT = 15;
65         enum mtC = 0xEFC60000;
66         enum mtL = 18;
67 
68     } else {
69         enum mtRecurrence = 312;
70         enum mtMiddleWord = 156;
71         enum mtSeperationPoint = 31;
72         enum mtMagicNumber = 6_364_136_223_846_793_005;
73         enum mtTwistCoefficient = 0xB5026F5AA96619E9;
74 
75         enum mtU = 29;
76         enum mtD = 0x5555555555555555;
77         enum mtS = 17;
78         enum mtB = 0x71D67FFFEDA60000;
79         enum mtT = 37;
80         enum mtC = 0xFFF7EEE000000000;
81         enum mtL = 43;
82     }
83 
84     enum mtUpperMask = (size_t.max << mtSeperationPoint);
85     enum mtLowerMask = (size_t.max >> (mtWordSize-mtSeperationPoint));
86 
87     
88     size_t[mtRecurrence] state;
89     size_t idx;
90     size_t seed;
91 
92     /// Resets the mersenne twister algorithm.
93     void initialize() {
94         state[0] = seed;
95 
96         // NOTE: Local seed iteration.
97         //       This is to allow calling reset()
98         size_t nseed = seed;
99         foreach(i; 1..state.length) {
100             nseed = mtMagicNumber * (nseed ^ (nseed >> (mtWordSize-2))) + i;
101             state[i] = nseed;
102         }
103 
104         idx = 0;
105     }
106 
107 public:
108 
109     /**
110         Constructs a random number generator with a set seed.
111     */
112     this(size_t seed) {
113         this.seed = seed;
114         this.initialize();
115     }
116 
117     /**
118         Constructs a random number generator with a set seed.
119     */
120     override
121     void reset() {
122         this.initialize();
123     }
124 
125     /**
126         Gets the next value in the random stream
127     */
128     override
129     size_t next() {
130         
131         // Get current index and at the opposite end of the circular buffer.
132         ptrdiff_t k = idx;
133         ptrdiff_t j = k - (mtRecurrence-1);
134         if (j < 0) 
135             j += mtRecurrence;
136 
137         size_t x = (state[k] & mtUpperMask) | (state[j] & mtLowerMask);
138         
139         
140         size_t xA = x >> 1;
141         if (x & 1) xA ^= mtTwistCoefficient;
142 
143         // Point to state recurrange - magic number
144         // modulo if need be.
145         j = k - (mtRecurrence-mtMiddleWord);
146         if (j < 0)
147             j += mtRecurrence;
148 
149         // Compute and set next state value
150         x = state[j] ^ xA;
151         state[k++] = x;
152 
153         // Wrap around
154         if (k >= mtRecurrence)
155             k = 0;
156         idx = k;
157 
158         // Tempering algorithm
159         size_t  y = x ^ (x >> mtU);
160                 y = y ^ ((y << mtS) & mtB);
161                 y = y ^ ((y << mtT) & mtC);
162 
163         return y ^ (y >> 1);
164     }
165 
166     /**
167         Gets the next value in the random stream
168     */
169     override
170     double nextUniform() {
171         size_t nrandom = this.next();
172         return cast(double)nrandom/cast(double)size_t.max;
173     }
174 
175     /**
176         Gets the next bytes in the random stream
177     */
178     override
179     void next(ref ubyte[] destination) {
180 
181         // State of random number generator
182         union tmp {
183             ubyte[size_t.sizeof] buffer;
184             size_t nrandom;
185         }
186         tmp _tmp;
187 
188         // Algorithm for filling buffer
189         size_t i = 0;
190         while (i < destination.length) {
191             _tmp.nrandom = next();
192 
193             // Figre out how many bytes to copy over
194             size_t count = size_t.sizeof;
195             if (i+size_t.sizeof > destination.length)
196                 count = (i+size_t.sizeof) - destination.length;
197 
198             // Write to destination
199             destination[i..i+count] = _tmp.buffer[0..count]; 
200             i += count;
201         }
202     }
203 }
204 
205 @("Random")
206 unittest {
207     // Mersenne Twister is deterministic, so this should always give the same result
208     // (on 64-bit systems)
209     static if (size_t.sizeof == 8) {
210         const ubyte[128] verification = [
211             155, 16, 179, 137, 163, 208, 254, 167, 182, 56, 19, 183, 127, 46, 
212             4, 165, 24, 95, 218, 132, 62, 197, 42, 151, 145, 200, 45, 143, 88, 
213             166, 19, 52, 93, 142, 195, 160, 49, 12, 35, 123, 216, 164, 13, 106, 
214             204, 45, 231, 157, 109, 165, 89, 86, 236, 142, 4, 245, 84, 188, 235, 
215             162, 184, 89, 247, 17, 72, 92, 160, 219, 113, 83, 43, 44, 180, 191, 
216             109, 53, 245, 47, 125, 196, 234, 11, 19, 92, 185, 161, 167, 76, 114, 
217             113, 255, 64, 83, 191, 254, 194, 169, 61, 243, 22, 54, 232, 196, 110, 
218             82, 208, 110, 80, 6, 188, 68, 101, 60, 116, 47, 210, 79, 186, 138, 122, 
219             205, 191, 62, 59, 194, 137, 117, 5
220         ];
221 
222         ubyte[128] buffer;
223         ubyte[] dest = buffer[0..$];
224 
225         Random random = nogc_new!Random(42);
226         random.next(dest);
227         
228         assert(dest == verification[0..$]);
229     }
230 
231     // Test skipped on 32 bit.
232 }
233 
234 @("Random (seed reset)")
235 unittest {
236 
237     ubyte[128] buffer1;
238     ubyte[] dest1 = buffer1[0..$];
239 
240     ubyte[128] buffer2;
241     ubyte[] dest2 = buffer2[0..$];
242 
243     Random random = nogc_new!Random(42);
244 
245     // These should not match
246     random.next(dest1);
247     random.next(dest2);
248     assert(dest1 != dest2, "Randomness is diminished?");
249 
250 
251     // These should not match
252     random.reset();
253     random.next(dest1);
254     random.reset();
255     random.next(dest2);
256 
257     assert(dest1 == dest2, "Randomness is not deterministic?");
258 }
259 
260 // TODO: optional crypto rng?