xref: /PHP-7.2/ext/standard/mt_rand.c (revision 60a69dae)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Zeev Suraski <zeev@zend.com>                                |
17    |          Pedro Melo <melo@ip.pt>                                     |
18    |          Sterling Hughes <sterling@php.net>                          |
19    |                                                                      |
20    | Based on code from: Richard J. Wagner <rjwagner@writeme.com>         |
21    |                     Makoto Matsumoto <matumoto@math.keio.ac.jp>      |
22    |                     Takuji Nishimura                                 |
23    |                     Shawn Cokus <Cokus@math.washington.edu>          |
24    +----------------------------------------------------------------------+
25  */
26 /* $Id$ */
27 
28 #include "php.h"
29 #include "php_rand.h"
30 #include "php_mt_rand.h"
31 
32 /* MT RAND FUNCTIONS */
33 
34 /*
35 	The following php_mt_...() functions are based on a C++ class MTRand by
36 	Richard J. Wagner. For more information see the web page at
37 	http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h
38 
39 	Mersenne Twister random number generator -- a C++ class MTRand
40 	Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
41 	Richard J. Wagner  v1.0  15 May 2003  rjwagner@writeme.com
42 
43 	The Mersenne Twister is an algorithm for generating random numbers.  It
44 	was designed with consideration of the flaws in various other generators.
45 	The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
46 	are far greater.  The generator is also fast; it avoids multiplication and
47 	division, and it benefits from caches and pipelines.  For more information
48 	see the inventors' web page at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
49 
50 	Reference
51 	M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
52 	Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
53 	Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
54 
55 	Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
56 	Copyright (C) 2000 - 2003, Richard J. Wagner
57 	All rights reserved.
58 
59 	Redistribution and use in source and binary forms, with or without
60 	modification, are permitted provided that the following conditions
61 	are met:
62 
63 	1. Redistributions of source code must retain the above copyright
64 	   notice, this list of conditions and the following disclaimer.
65 
66 	2. Redistributions in binary form must reproduce the above copyright
67 	   notice, this list of conditions and the following disclaimer in the
68 	   documentation and/or other materials provided with the distribution.
69 
70 	3. The names of its contributors may not be used to endorse or promote
71 	   products derived from this software without specific prior written
72 	   permission.
73 
74 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
75 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
76 	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
77 	A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
78 	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
79 	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
80 	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
81 	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
82 	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
83 	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
84 	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
85 */
86 
87 #define N             MT_N                 /* length of state vector */
88 #define M             (397)                /* a period parameter */
89 #define hiBit(u)      ((u) & 0x80000000U)  /* mask all but highest   bit of u */
90 #define loBit(u)      ((u) & 0x00000001U)  /* mask all but lowest    bit of u */
91 #define loBits(u)     ((u) & 0x7FFFFFFFU)  /* mask     the highest   bit of u */
92 #define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
93 
94 #define twist(m,u,v)  (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU))
95 #define twist_php(m,u,v)  (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
96 
97 /* {{{ php_mt_initialize
98  */
php_mt_initialize(uint32_t seed,uint32_t * state)99 static inline void php_mt_initialize(uint32_t seed, uint32_t *state)
100 {
101 	/* Initialize generator state with seed
102 	   See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
103 	   In previous versions, most significant bits (MSBs) of the seed affect
104 	   only MSBs of the state array.  Modified 9 Jan 2002 by Makoto Matsumoto. */
105 
106 	register uint32_t *s = state;
107 	register uint32_t *r = state;
108 	register int i = 1;
109 
110 	*s++ = seed & 0xffffffffU;
111 	for( ; i < N; ++i ) {
112 		*s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
113 		r++;
114 	}
115 }
116 /* }}} */
117 
118 /* {{{ php_mt_reload
119  */
php_mt_reload(void)120 static inline void php_mt_reload(void)
121 {
122 	/* Generate N new values in state
123 	   Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
124 
125 	register uint32_t *state = BG(state);
126 	register uint32_t *p = state;
127 	register int i;
128 
129 	if (BG(mt_rand_mode) == MT_RAND_MT19937) {
130 		for (i = N - M; i--; ++p)
131 			*p = twist(p[M], p[0], p[1]);
132 		for (i = M; --i; ++p)
133 			*p = twist(p[M-N], p[0], p[1]);
134 		*p = twist(p[M-N], p[0], state[0]);
135 	}
136 	else {
137 		for (i = N - M; i--; ++p)
138 			*p = twist_php(p[M], p[0], p[1]);
139 		for (i = M; --i; ++p)
140 			*p = twist_php(p[M-N], p[0], p[1]);
141 		*p = twist_php(p[M-N], p[0], state[0]);
142 	}
143 	BG(left) = N;
144 	BG(next) = state;
145 }
146 /* }}} */
147 
148 /* {{{ php_mt_srand
149  */
php_mt_srand(uint32_t seed)150 PHPAPI void php_mt_srand(uint32_t seed)
151 {
152 	/* Seed the generator with a simple uint32 */
153 	php_mt_initialize(seed, BG(state));
154 	php_mt_reload();
155 
156 	/* Seed only once */
157 	BG(mt_rand_is_seeded) = 1;
158 }
159 /* }}} */
160 
161 /* {{{ php_mt_rand
162  */
php_mt_rand(void)163 PHPAPI uint32_t php_mt_rand(void)
164 {
165 	/* Pull a 32-bit integer from the generator state
166 	   Every other access function simply transforms the numbers extracted here */
167 
168 	register uint32_t s1;
169 
170 	if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
171 		php_mt_srand(GENERATE_SEED());
172 	}
173 
174 	if (BG(left) == 0) {
175 		php_mt_reload();
176 	}
177 	--BG(left);
178 
179 	s1 = *BG(next)++;
180 	s1 ^= (s1 >> 11);
181 	s1 ^= (s1 <<  7) & 0x9d2c5680U;
182 	s1 ^= (s1 << 15) & 0xefc60000U;
183 	return ( s1 ^ (s1 >> 18) );
184 }
185 /* }}} */
186 
187 /* {{{ proto void mt_srand([int seed])
188    Seeds Mersenne Twister random number generator */
PHP_FUNCTION(mt_srand)189 PHP_FUNCTION(mt_srand)
190 {
191 	zend_long seed = 0;
192 	zend_long mode = MT_RAND_MT19937;
193 
194 	ZEND_PARSE_PARAMETERS_START(0, 2)
195 		Z_PARAM_OPTIONAL
196 		Z_PARAM_LONG(seed)
197 		Z_PARAM_LONG(mode)
198 	ZEND_PARSE_PARAMETERS_END();
199 
200 	if (ZEND_NUM_ARGS() == 0)
201 		seed = GENERATE_SEED();
202 
203 	switch (mode) {
204 		case MT_RAND_PHP:
205 			BG(mt_rand_mode) = MT_RAND_PHP;
206 			break;
207 		default:
208 			BG(mt_rand_mode) = MT_RAND_MT19937;
209 	}
210 
211 	php_mt_srand(seed);
212 }
213 /* }}} */
214 
rand_range32(uint32_t umax)215 static uint32_t rand_range32(uint32_t umax) {
216 	uint32_t result, limit;
217 
218 	result = php_mt_rand();
219 
220 	/* Special case where no modulus is required */
221 	if (UNEXPECTED(umax == UINT32_MAX)) {
222 		return result;
223 	}
224 
225 	/* Increment the max so the range is inclusive of max */
226 	umax++;
227 
228 	/* Powers of two are not biased */
229 	if ((umax & (umax - 1)) == 0) {
230 		return result & (umax - 1);
231 	}
232 
233 	/* Ceiling under which UINT32_MAX % max == 0 */
234 	limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
235 
236 	/* Discard numbers over the limit to avoid modulo bias */
237 	while (UNEXPECTED(result > limit)) {
238 		result = php_mt_rand();
239 	}
240 
241 	return result % umax;
242 }
243 
244 #if ZEND_ULONG_MAX > UINT32_MAX
rand_range64(uint64_t umax)245 static uint64_t rand_range64(uint64_t umax) {
246 	uint64_t result, limit;
247 
248 	result = php_mt_rand();
249 	result = (result << 32) | php_mt_rand();
250 
251 	/* Special case where no modulus is required */
252 	if (UNEXPECTED(umax == UINT64_MAX)) {
253 		return result;
254 	}
255 
256 	/* Increment the max so the range is inclusive of max */
257 	umax++;
258 
259 	/* Powers of two are not biased */
260 	if ((umax & (umax - 1)) == 0) {
261 		return result & (umax - 1);
262 	}
263 
264 	/* Ceiling under which UINT64_MAX % max == 0 */
265 	limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
266 
267 	/* Discard numbers over the limit to avoid modulo bias */
268 	while (UNEXPECTED(result > limit)) {
269 		result = php_mt_rand();
270 		result = (result << 32) | php_mt_rand();
271 	}
272 
273 	return result % umax;
274 }
275 #endif
276 
277 /* {{{ php_mt_rand_range
278  */
php_mt_rand_range(zend_long min,zend_long max)279 PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
280 {
281 	zend_ulong umax = max - min;
282 
283 #if ZEND_ULONG_MAX > UINT32_MAX
284 	if (umax > UINT32_MAX) {
285 		return (zend_long) (rand_range64(umax) + min);
286 	}
287 #endif
288 
289 	return (zend_long) (rand_range32(umax) + min);
290 }
291 /* }}} */
292 
293 /* {{{ php_mt_rand_common
294  * rand() allows min > max, mt_rand does not */
php_mt_rand_common(zend_long min,zend_long max)295 PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
296 {
297 	int64_t n;
298 
299 	if (BG(mt_rand_mode) == MT_RAND_MT19937) {
300 		return php_mt_rand_range(min, max);
301 	}
302 
303 	/* Legacy mode deliberately not inside php_mt_rand_range()
304 	 * to prevent other functions being affected */
305 	n = (int64_t)php_mt_rand() >> 1;
306 	RAND_RANGE_BADSCALING(n, min, max, PHP_MT_RAND_MAX);
307 
308 	return n;
309 }
310 /* }}} */
311 
312 /* {{{ proto int mt_rand([int min, int max])
313    Returns a random number from Mersenne Twister */
PHP_FUNCTION(mt_rand)314 PHP_FUNCTION(mt_rand)
315 {
316 	zend_long min;
317 	zend_long max;
318 	int argc = ZEND_NUM_ARGS();
319 
320 	if (argc == 0) {
321 		// genrand_int31 in mt19937ar.c performs a right shift
322 		RETURN_LONG(php_mt_rand() >> 1);
323 	}
324 
325 	ZEND_PARSE_PARAMETERS_START(2, 2)
326 		Z_PARAM_LONG(min)
327 		Z_PARAM_LONG(max)
328 	ZEND_PARSE_PARAMETERS_END();
329 
330 	if (UNEXPECTED(max < min)) {
331 		php_error_docref(NULL, E_WARNING, "max(" ZEND_LONG_FMT ") is smaller than min(" ZEND_LONG_FMT ")", max, min);
332 		RETURN_FALSE;
333 	}
334 
335 	RETURN_LONG(php_mt_rand_common(min, max));
336 }
337 /* }}} */
338 
339 /* {{{ proto int mt_getrandmax(void)
340    Returns the maximum value a random number from Mersenne Twister can have */
PHP_FUNCTION(mt_getrandmax)341 PHP_FUNCTION(mt_getrandmax)
342 {
343 	if (zend_parse_parameters_none() == FAILURE) {
344 		return;
345 	}
346 
347 	/*
348 	 * Melo: it could be 2^^32 but we only use 2^^31 to maintain
349 	 * compatibility with the previous php_rand
350 	 */
351   	RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
352 }
353 /* }}} */
354 
PHP_MINIT_FUNCTION(mt_rand)355 PHP_MINIT_FUNCTION(mt_rand)
356 {
357 	REGISTER_LONG_CONSTANT("MT_RAND_MT19937", MT_RAND_MT19937, CONST_CS | CONST_PERSISTENT);
358 	REGISTER_LONG_CONSTANT("MT_RAND_PHP",     MT_RAND_PHP, CONST_CS | CONST_PERSISTENT);
359 
360 	return SUCCESS;
361 }
362 
363 /*
364  * Local variables:
365  * tab-width: 4
366  * c-basic-offset: 4
367  * End:
368  * vim600: noet sw=4 ts=4 fdm=marker
369  * vim<600: noet sw=4 ts=4
370  */
371