1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// random.h
6//
7
8//
9// Defines a random number generator, initially from the System.Random code in the BCL. If you notice any problems,
10// please compare to the implementation in src\mscorlib\src\system\random.cs.
11//
12// Main advantages over rand() are:
13//
14// 1) It generates better random numbers
15// 2) It can have multiple instantiations with different seeds
16// 3) It behaves the same regardless of whether we build with VC++ or GCC
17//
18// If you are working in the VM, we have a convenience method: code:GetRandomInt. This usess a thread-local
19// Random instance if a Thread object is available, and otherwise falls back to a global instance
20// with a spin-lock.
21//
22
23#ifndef _CLRRANDOM_H_
24#define _CLRRANDOM_H_
25
26#include <math.h>
27
28//
29// Forbid the use of srand()/rand(), as these are globally shared facilities and our use of them would
30// interfere with native user code in the same process. This override is not compatible with stl headers.
31//
32#if !defined(DO_NOT_DISABLE_RAND) && !defined(USE_STL)
33
34#ifdef srand
35#undef srand
36#endif
37#define srand Do_not_use_srand
38
39#ifdef rand
40#undef rand
41#endif
42#define rand Do_not_use_rand
43
44#endif //!DO_NOT_DISABLE_RAND && !USE_STL
45
46
47class CLRRandom
48{
49private:
50 //
51 // Private Constants
52 //
53 static const int MBIG = INT_MAX;
54 static const int MSEED = 161803398;
55 static const int MZ = 0;
56
57
58 //
59 // Member Variables
60 //
61 int inext;
62 int inextp;
63 int SeedArray[56];
64
65 bool initialized;
66
67public:
68 //
69 // Constructors
70 //
71
72 CLRRandom()
73 {
74 LIMITED_METHOD_CONTRACT;
75 initialized = false;
76 }
77
78 void Init()
79 {
80 LIMITED_METHOD_CONTRACT;
81 LARGE_INTEGER time;
82 if (!QueryPerformanceCounter(&time))
83 time.QuadPart = GetTickCount();
84 Init((int)time.u.LowPart ^ GetCurrentThreadId() ^ GetCurrentProcessId());
85 }
86
87 void Init(int Seed)
88 {
89 LIMITED_METHOD_CONTRACT;
90
91 int ii;
92 int mj, mk;
93
94 //Initialize our Seed array.
95 mj = MSEED - abs(Seed);
96 SeedArray[55]=mj;
97 mk=1;
98 for (int i=1; i<55; i++) { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
99 ii = (21*i)%55;
100 SeedArray[ii]=mk;
101 mk = mj - mk;
102 if (mk<0) mk+=MBIG;
103 mj=SeedArray[ii];
104 }
105 for (int k=1; k<5; k++) {
106 for (int i=1; i<56; i++) {
107 SeedArray[i] -= SeedArray[1+(i+30)%55];
108 if (SeedArray[i]<0) SeedArray[i]+=MBIG;
109 }
110 }
111 inext=0;
112 inextp = 21;
113 Seed = 1;
114
115 initialized = true;
116 }
117
118 bool IsInitialized()
119 {
120 LIMITED_METHOD_CONTRACT;
121 return initialized;
122 }
123
124private:
125 //
126 // Package Private Methods
127 //
128
129 /*====================================Sample====================================
130 **Action: Return a new random number [0..1) and reSeed the Seed array.
131 **Returns: A double [0..1)
132 **Arguments: None
133 **Exceptions: None
134 ==============================================================================*/
135 double Sample()
136 {
137 LIMITED_METHOD_CONTRACT;
138
139 //Including this division at the end gives us significantly improved
140 //random number distribution.
141 return (InternalSample()*(1.0/MBIG));
142 }
143
144 int InternalSample()
145 {
146 LIMITED_METHOD_CONTRACT;
147
148 int retVal;
149 int locINext = inext;
150 int locINextp = inextp;
151
152 if (++locINext >=56) locINext=1;
153 if (++locINextp>= 56) locINextp = 1;
154
155 retVal = SeedArray[locINext]-SeedArray[locINextp];
156
157 if (retVal == MBIG) retVal--;
158 if (retVal<0) retVal+=MBIG;
159
160 SeedArray[locINext]=retVal;
161
162 inext = locINext;
163 inextp = locINextp;
164
165 return retVal;
166 }
167
168 double GetSampleForLargeRange()
169 {
170 LIMITED_METHOD_CONTRACT;
171
172 // The distribution of double value returned by Sample
173 // is not distributed well enough for a large range.
174 // If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
175 // We will end up getting even numbers only.
176
177 int result = InternalSample();
178 // Note we can't use addition here. The distribution will be bad if we do that.
179 bool negative = (InternalSample()%2 == 0) ? true : false; // decide the sign based on second sample
180 if( negative) {
181 result = -result;
182 }
183 double d = result;
184 d += (INT_MAX - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
185 d /= 2*(unsigned int)INT_MAX - 1 ;
186 return d;
187 }
188
189public:
190 //
191 // Public Instance Methods
192 //
193
194
195 /*=====================================Next=====================================
196 **Returns: An int [0..Int32.MaxValue)
197 **Arguments: None
198 **Exceptions: None.
199 ==============================================================================*/
200 int Next()
201 {
202 LIMITED_METHOD_CONTRACT;
203 _ASSERTE(initialized);
204 return InternalSample();
205 }
206
207
208 /*=====================================Next=====================================
209 **Returns: An int [minvalue..maxvalue)
210 **Arguments: minValue -- the least legal value for the Random number.
211 ** maxValue -- One greater than the greatest legal return value.
212 **Exceptions: None.
213 ==============================================================================*/
214 int Next(int minValue, int maxValue)
215 {
216 LIMITED_METHOD_CONTRACT;
217 _ASSERTE(initialized);
218 _ASSERTE(minValue < maxValue);
219
220 LONGLONG range = (LONGLONG)maxValue-minValue;
221 double result;
222
223 if( range <= (LONGLONG)INT_MAX)
224 result = (Sample() * range) + minValue;
225 else
226 result = (GetSampleForLargeRange() * range) + minValue;
227
228 _ASSERTE(result >= minValue && result < maxValue);
229 return (int)result;
230 }
231
232
233 /*=====================================Next=====================================
234 **Returns: An int [0..maxValue)
235 **Arguments: maxValue -- One more than the greatest legal return value.
236 **Exceptions: None.
237 ==============================================================================*/
238 int Next(int maxValue)
239 {
240 LIMITED_METHOD_CONTRACT;
241 _ASSERTE(initialized);
242 double result = Sample()*maxValue;
243 _ASSERTE(result >= 0 && result < maxValue);
244 return (int)result;
245 }
246
247
248 /*=====================================Next=====================================
249 **Returns: A double [0..1)
250 **Arguments: None
251 **Exceptions: None
252 ==============================================================================*/
253 double NextDouble()
254 {
255 LIMITED_METHOD_CONTRACT;
256 _ASSERTE(initialized);
257 double result = Sample();
258 _ASSERTE(result >= 0 && result < 1);
259 return result;
260 }
261
262
263 /*==================================NextBytes===================================
264 **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
265 **Returns:Void
266 **Arguments: buffer -- the array to be filled.
267 **Exceptions: None
268 ==============================================================================*/
269 void NextBytes(__out_ecount(length) BYTE buffer[], int length)
270 {
271 LIMITED_METHOD_CONTRACT;
272 _ASSERTE(initialized);
273 for (int i=0; i<length; i++) {
274 buffer[i]=(BYTE)(InternalSample()%(256));
275 }
276 }
277};
278
279#endif //_CLRRANDOM_H_
280