diff options
author | Jeroen van Wolffelaar <jeroen@php.net> | 2001-08-22 21:53:21 +0000 |
---|---|---|
committer | Jeroen van Wolffelaar <jeroen@php.net> | 2001-08-22 21:53:21 +0000 |
commit | 8d6a9b0a178f7d84cc8557bd0604b68c7ddb2a4a (patch) | |
tree | 8bd3aea6250fdf565f09bcd27ea20d52eec0f1f9 | |
parent | 6e8ad1f0d9b3192fa4334994308ea3b8e04f7b6c (diff) | |
download | php-git-8d6a9b0a178f7d84cc8557bd0604b68c7ddb2a4a.tar.gz |
Experimental beginning of a change in the way rand-functions work.
This is only a _move around_ of code into functions.
-rw-r--r-- | ext/standard/Makefile.in | 2 | ||||
-rw-r--r-- | ext/standard/php_math.h | 6 | ||||
-rw-r--r-- | ext/standard/php_rand.h | 86 | ||||
-rw-r--r-- | ext/standard/rand.c | 410 | ||||
-rw-r--r-- | ext/standard/rand_mt.c | 220 |
5 files changed, 441 insertions, 283 deletions
diff --git a/ext/standard/Makefile.in b/ext/standard/Makefile.in index 123e4cb110..628adea8a8 100644 --- a/ext/standard/Makefile.in +++ b/ext/standard/Makefile.in @@ -5,7 +5,7 @@ LTLIBRARY_SOURCES=\ dir.c dl.c dns.c exec.c file.c filestat.c flock_compat.c \ formatted_print.c fsock.c head.c html.c image.c info.c iptc.c lcg.c \ link.c mail.c math.c md5.c metaphone.c microtime.c pack.c pageinfo.c \ - parsedate.c quot_print.c rand.c reg.c soundex.c string.c scanf.c \ + parsedate.c quot_print.c rand.c rand_mt.c reg.c soundex.c string.c scanf.c \ syslog.c type.c uniqid.c url.c url_scanner.c var.c assert.c \ strnatcmp.c levenshtein.c incomplete_class.c url_scanner_ex.c \ ftp_fopen_wrapper.c http_fopen_wrapper.c php_fopen_wrapper.c credits.c diff --git a/ext/standard/php_math.h b/ext/standard/php_math.h index beef7dd160..71e7d40972 100644 --- a/ext/standard/php_math.h +++ b/ext/standard/php_math.h @@ -34,12 +34,6 @@ PHP_FUNCTION(log); PHP_FUNCTION(log10); PHP_FUNCTION(pow); PHP_FUNCTION(sqrt); -PHP_FUNCTION(srand); -PHP_FUNCTION(rand); -PHP_FUNCTION(getrandmax); -PHP_FUNCTION(mt_srand); -PHP_FUNCTION(mt_rand); -PHP_FUNCTION(mt_getrandmax); PHP_FUNCTION(abs); PHP_FUNCTION(ceil); PHP_FUNCTION(floor); diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h index b4d7e991d0..0d20f84225 100644 --- a/ext/standard/php_rand.h +++ b/ext/standard/php_rand.h @@ -21,6 +21,41 @@ */ /* $Id$ */ +/* Layout implementation random functions + * + * The PHPAPI contains these functions: + * - long php_rand() + * - long php_rand_range(long min, long max) + * - void php_srand() + * - long php_getrandmax() + * + * Note that it is not possible to choose the algoritm. This is done to + * give the user the possibility to control all randomness by means of + * srand()/php.ini in a portable and consistent way. + * + * rand.c: (the only rand*.c file with PHP_API and PHP_FUNCTION functions) + * + * - PHP_FUNCTION([mt_]srand) + * +-> void php_srand(void) + * +-> void php_srand2(long seed, int alg) + * +-> (rand_sys.c) long php_rand_sys() + * +-> (rand_mt.c ) long php_rand_mt() + * + * - PHP_FUNCTION([mt_]rand) + * +-> long php_rand() + * +-> (rand_sys.c) long php_rand_sys() + * +-> (rand_mt.c ) long php_rand_mt() + * +-> long php_rand_range(long min, long max) + * +-> calls php_rand() + * + * - PHP_FUNCTION([mt_]getrandmax) + * +-> PHPAPI long php_randmax(void) + * +-> (rand_sys.c) long php_randmax_sys() + * +-> (rand_mt.c ) long php_randmax_mt() + * + * --Jeroen + */ + #ifndef PHP_RAND_H #define PHP_RAND_H @@ -31,31 +66,66 @@ #endif #if HAVE_LRAND48 -#define PHP_RAND_MAX 2147483647 +#define php_randmax_sys() 2147483647 #else -#define PHP_RAND_MAX RAND_MAX +#define php_randmax_sys() RAND_MAX #endif +/* + * Melo: it could be 2^^32 but we only use 2^^31 to maintain + * compatibility with the previous php_rand + */ +#define php_randmax_mt() ((long)(0x7FFFFFFF)) /* 2^^31 - 1 */ + +PHP_FUNCTION(srand); +PHP_FUNCTION(rand); +PHP_FUNCTION(getrandmax); +PHP_FUNCTION(mt_srand); +PHP_FUNCTION(mt_rand); +PHP_FUNCTION(mt_getrandmax); + +PHPAPI long php_rand(void); +PHPAPI long php_rand_range(long min, long max); +PHPAPI long php_randmax(void); +long php_rand_mt(void); +void php_srand_mt(long seed TSRMLS_DC); + /* Define rand Function wrapper */ #ifdef HAVE_RANDOM -#define php_rand() random() +#define php_rand_sys() random() #else #ifdef HAVE_LRAND48 -#define php_rand() lrand48() +#define php_rand_sys() lrand48() #else -#define php_rand() rand() +#define php_rand_sys() rand() #endif #endif /* Define srand Function wrapper */ #ifdef HAVE_SRANDOM -#define php_srand(seed) srandom((unsigned int)seed) +#define php_srand_sys(seed) srandom((unsigned int)seed) #else #ifdef HAVE_SRAND48 -#define php_srand(seed) srand48((long)seed) +#define php_srand_sys(seed) srand48((long)seed) #else -#define php_srand(seed) srand((unsigned int)seed) +#define php_srand_sys(seed) srand((unsigned int)seed) #endif #endif +/* Define random generator constants */ +#define RAND_SYS 1 +#define RAND_MT 2 + +/* BC */ +#define PHP_RAND_MAX php_randmax() + #endif /* PHP_RAND_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: fdm=marker + * vim: sw=4 ts=4 tw=78 + */ diff --git a/ext/standard/rand.c b/ext/standard/rand.c index 4ac3bc0507..5e1c39fbdc 100644 --- a/ext/standard/rand.c +++ b/ext/standard/rand.c @@ -15,8 +15,6 @@ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | | Zeev Suraski <zeev@zend.com> | | Pedro Melo <melo@ip.pt> | - | | - | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | +----------------------------------------------------------------------+ */ /* $Id$ */ @@ -27,301 +25,183 @@ #include "php_math.h" #include "php_rand.h" -#include "basic_functions.h" - -/* - This is the ``Mersenne Twister'' random number generator MT19937, which - generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) - starting from any odd seed in 0..(2^32 - 1). This version is a recode - by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by - Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in - July-August 1997). - - Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha - running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to - generate 300 million random numbers; after recoding: 24.0 sec. for the same - (i.e., 46.5% of original time), so speed is now about 12.5 million random - number generations per second on this machine. - - According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html> - (and paraphrasing a bit in places), the Mersenne Twister is ``designed - with consideration of the flaws of various existing generators,'' has - a period of 2^19937 - 1, gives a sequence that is 623-dimensionally - equidistributed, and ``has passed many stringent tests, including the - die-hard test of G. Marsaglia and the load test of P. Hellekalek and - S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 - to 5012 bytes of static data, depending on data type sizes, and the code - is quite short as well). It generates random numbers in batches of 624 - at a time, so the caching and pipelining of modern systems is exploited. - It is also divide- and mod-free. - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation (either version 2 of the License or, at your - option, any later version). This library is distributed in the hope that - it will be useful, but WITHOUT ANY WARRANTY, without even the implied - warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - the GNU Library General Public License for more details. You should have - received a copy of the GNU Library General Public License along with this - library; if not, write to the Free Software Foundation, Inc., 59 Temple - Place, Suite 330, Boston, MA 02111-1307, USA. - - The code as Shawn received it included the following notice: - - Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When - you use this, send an e-mail to <matumoto@math.keio.ac.jp> with - an appropriate reference to your work. - - It would be nice to CC: <Cokus@math.washington.edu> when you write. - - - - php_uint32 must be an unsigned integer type capable of holding at least 32 - bits; exactly 32 should be fastest, but 64 is better on an Alpha with - GCC at -O3 optimization so try your options and see what's best for you - - Melo: we should put some ifdefs here to catch those alphas... -*/ +#include "zend_execute.h" +#include "basic_functions.h" -#define N MT_N /* length of state vector */ -#define M (397) /* a period parameter */ -#define K (0x9908B0DFU) /* a magic constant */ -#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ -#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ -#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ -#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */ +/* See php_rand.h for information about layout */ -#define MT_RAND_MAX ((long)(0x7FFFFFFF)) /* (1<<31) - 1 */ +/* srand */ -/* {{{ seedMT - */ -static void seedMT(php_uint32 seed TSRMLS_DC) +/* {{{ PHPAPI void php_srand(void) */ +PHPAPI void php_srand(void) { - /* - We initialize state[0..(N-1)] via the generator - - x_new = (69069 * x_old) mod 2^32 - - from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's - _The Art of Computer Programming_, Volume 2, 3rd ed. - - Notes (SJC): I do not know what the initial state requirements - of the Mersenne Twister are, but it seems this seeding generator - could be better. It achieves the maximum period for its modulus - (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if - x_initial can be even, you have sequences like 0, 0, 0, ...; - 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, - 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. - - Even if x_initial is odd, if x_initial is 1 mod 4 then - - the lowest bit of x is always 1, - the next-to-lowest bit of x is always 0, - the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , - the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , - ... - - and if x_initial is 3 mod 4 then - - the lowest bit of x is always 1, - the next-to-lowest bit of x is always 1, - the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , - the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , - the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , - ... - - The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is - 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It - also does well in the dimension 2..5 spectral tests, but it could be - better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). - - Note that the random number user does not see the values generated - here directly since reloadMT() will always munge them first, so maybe - none of all of this matters. In fact, the seed values made here could - even be extra-special desirable if the Mersenne Twister theory says - so-- that's why the only change I made is to restrict to odd seeds. - */ - - register php_uint32 x = (seed | 1U) & 0xFFFFFFFFU, *s = BG(state); - register int j; - - for(BG(left)=0, *s++=x, j=N; --j; - *s++ = (x*=69069U) & 0xFFFFFFFFU); + /* php_srand_2( 1e6d * microtime() , protocol-specified-in-php.ini ) */ + php_error(E_ERROR,"Not yet implemented"); } /* }}} */ -static php_uint32 reloadMT(TSRMLS_D) +/* {{{ void php_srand2(long seed, int alg) */ +static inline void php_srand2(long seed, int alg) { - register php_uint32 *p0=BG(state), *p2=BG(state)+2, *pM=BG(state)+M, s0, s1; - register int j; - - if(BG(left) < -1) - seedMT(4357U TSRMLS_CC); - - BG(left)=N-1, BG(next)=BG(state)+1; - - for(s0=BG(state)[0], s1=BG(state)[1], j=N-M+1; --j; s0=s1, s1=*p2++) - *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); - - for(pM=BG(state), j=M; --j; s0=s1, s1=*p2++) - *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); - - s1=BG(state)[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9D2C5680U; - s1 ^= (s1 << 15) & 0xEFC60000U; - return(s1 ^ (s1 >> 18)); + switch (alg) { + case RAND_SYS: + php_srand_sys(seed); + return; + case RAND_MT: + php_srand_mt(seed TSRMLS_CC); + return; + default: + php_error(E_ERROR,"Bug, please report (php_srand2-%d)",alg); + return; + } } +/* }}} */ - -static inline php_uint32 randomMT(void) -{ - php_uint32 y; - TSRMLS_FETCH(); - - if(--BG(left) < 0) - return(reloadMT(TSRMLS_C)); - - y = *BG(next)++; - y ^= (y >> 11); - y ^= (y << 7) & 0x9D2C5680U; - y ^= (y << 15) & 0xEFC60000U; - return(y ^ (y >> 18)); +/* {{{ [mt_]srand common */ +#define pim_srand_common(name,type) \ +PHP_FUNCTION(name) \ +{ \ + zval **arg; \ + \ + if (ZEND_NUM_ARGS() != 1) { \ + WRONG_PARAM_COUNT; \ + } \ + zend_get_parameters_ex(1, &arg); \ + convert_to_long_ex(arg); \ + \ + php_srand2((*arg)->value.lval, type); \ } +/* }}} */ /* {{{ proto void srand(int seed) Seeds random number generator */ -PHP_FUNCTION(srand) -{ - pval **arg; - - if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { - WRONG_PARAM_COUNT; - } - convert_to_long_ex(arg); - php_srand((*arg)->value.lval); -} +pim_srand_common(srand,RAND_SYS) /* }}} */ /* {{{ proto void mt_srand(int seed) - Seeds Mersenne Twister random number generator */ -PHP_FUNCTION(mt_srand) -{ - pval **arg; - - if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { - WRONG_PARAM_COUNT; - } - convert_to_long_ex(arg); - seedMT((*arg)->value.lval TSRMLS_CC); -} + Seeds random number generator */ +pim_srand_common(mt_srand,RAND_MT) /* }}} */ -/* {{{ proto int rand([int min, int max]) - Returns a random number */ -PHP_FUNCTION(rand) -{ - pval **p_min=NULL, **p_max=NULL; - - switch (ZEND_NUM_ARGS()) { - case 0: - break; - case 2: - if (zend_get_parameters_ex(2, &p_min, &p_max)==FAILURE) { - RETURN_FALSE; - } - convert_to_long_ex(p_min); - convert_to_long_ex(p_max); - if ((*p_max)->value.lval-(*p_min)->value.lval < 0) { - php_error(E_WARNING, "rand(): Invalid range: %ld..%ld", (*p_min)->value.lval, (*p_max)->value.lval); - } else if ((*p_max)->value.lval-(*p_min)->value.lval > PHP_RAND_MAX){ - php3_error(E_WARNING, "rand(): Invalid range: %ld..%ld", (*p_min)->value.lval, (*p_max)->value.lval); - } - break; - default: - WRONG_PARAM_COUNT; - break; - } - - return_value->type = IS_LONG; +/* rand */ - return_value->value.lval = php_rand(); +/* {{{ PHPAPI long php_rand(void) */ +PHPAPI long php_rand(void) +{ + /* algorithm = BG(current_alg) */ + return php_rand_sys(); +} +/* }}} */ +/* {{{ macro: php_map_a_range */ +#define php_map_a_range(number,min,max,MAX) { \ /* * A bit of tricky math here. We want to avoid using a modulus because * that simply tosses the high-order bits and might skew the distribution * of random values over the range. Instead we map the range directly. * * We need to map the range from 0...M evenly to the range a...b + * Expressed in real numbers, this becomes: + * + * [0,M+1[ mapped to [a,b+1[ + * * Let n = the random number and n' = the mapped random number + * So the formula needs to be: + * + * n' = a + n((b+1)-a)/(m+1) * - * Then we have: n' = a + n(b-a)/M - * - * We have a problem here in that only n==M will get mapped to b which - # means the chances of getting b is much much less than getting any of - # the other values in the range. We can fix this by increasing our range - # artifically and using: - # - # n' = a + n(b-a+1)/M - * - # Now we only have a problem if n==M which would cause us to produce a - # number of b+1 which would be bad. So we bump M up by one to make sure - # this will never happen, and the final algorithm looks like this: - # - # n' = a + n(b-a+1)/(M+1) - * - * -RL - */ - if (p_min && p_max) { /* implement range */ - return_value->value.lval = (*p_min)->value.lval + - (int)((double)((*p_max)->value.lval - (*p_min)->value.lval + 1.0) * return_value->value.lval/(PHP_RAND_MAX+1.0)); - } + * This isn't perfect, because n only takes integer values. So when a..b + * spans a significant portion of 0..M, some numbers have nearly twice as + * much chance. But since twice a very small chance is still a very small + * chance, it's ignored. + * + * --Rasmus and Jeroen + */ \ + if ((max) < (min)) { \ + php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (minimum can't be larger than maximum)", \ + get_active_function_name(TSRMLS_C), (min), (max)); \ + } else if ( (max) - (min) > (MAX) ) { \ + /* TODO: this can done better, get two numbers and combine... */ \ + php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (can't give that much randomness)", \ + get_active_function_name(TSRMLS_C), (min), (max)); \ + } \ + (number) = (min) + (int) ((double)((max)-(min)+1) * (number)/(MAX+1.0)); \ } /* }}} */ -/* {{{ proto int mt_rand([int min, int max]) - Returns a random number from Mersenne Twister */ -PHP_FUNCTION(mt_rand) +/* {{{ long php_rand_range2(long min, long max, int alg) + * Temporary hack function */ +static inline long php_rand_range2(long min, long max, int alg) { - pval **p_min=NULL, **p_max=NULL; + long number; - switch (ZEND_NUM_ARGS()) { - case 0: - break; - case 2: - if (zend_get_parameters_ex(2, &p_min, &p_max)==FAILURE) { - RETURN_FALSE; - } - convert_to_long_ex(p_min); - convert_to_long_ex(p_max); - if ((*p_max)->value.lval-(*p_min)->value.lval <= 0) { - php_error(E_WARNING, "mt_rand(): Invalid range: %ld..%ld", (*p_min)->value.lval, (*p_max)->value.lval); - }else if ((*p_max)->value.lval-(*p_min)->value.lval > MT_RAND_MAX){ - php3_error(E_WARNING, "mt_rand(): Invalid range: %ld..%ld", (*p_min)->value.lval, (*p_max)->value.lval); - } - break; + switch (alg) { + case RAND_SYS: + number = php_rand_sys(); + php_map_a_range(number,min,max,php_randmax_sys()); + return number; + case RAND_MT: + number = php_rand_mt(); + php_map_a_range(number,min,max,php_randmax_mt()); + return number; default: - WRONG_PARAM_COUNT; - break; + php_error(E_ERROR,"Bug, please report (php_rand_range2-%d)",alg); + return; } - - return_value->type = IS_LONG; - /* - * Melo: hmms.. randomMT() returns 32 random bits... - * Yet, the previous php_rand only returns 31 at most. - * So I put a right shift to loose the lsb. It *seems* - * better than clearing the msb. - * Update: - * I talked with Cokus via email and it won't ruin the algorithm - */ - return_value->value.lval = (long)(randomMT() >> 1); +} +/* }}} */ - if (p_min && p_max) { /* implement range */ - return_value->value.lval = (*p_min)->value.lval + - (long)((double)((*p_max)->value.lval - (*p_min)->value.lval + 1.0) * return_value->value.lval/(MT_RAND_MAX+1.0)); - } +/* {{{ PHPAPI long php_rand_range(long min, long max) */ +PHPAPI long php_rand_range(long min, long max) +{ + /* will be php.ini & srand aware */ + return php_rand_range2(min,max,RAND_SYS); +} +/* }}} */ + +/* {{{ [mt_]rand common */ +#define PHP_FUNCTION_RAND(name,type,lowertype) \ +PHP_FUNCTION(name) \ +{ \ + zval **min, **max; \ + \ + switch (ZEND_NUM_ARGS()) { \ + case 0: \ + RETURN_LONG(php_rand_##lowertype()); \ + case 2: \ + if (zend_get_parameters_ex(2, &min, &max)==FAILURE) { \ + RETURN_FALSE; \ + } \ + convert_to_long_ex(min); \ + convert_to_long_ex(max); \ + RETURN_LONG(php_rand_range2(Z_LVAL_PP(min), \ + Z_LVAL_PP(max), type)); \ + default: \ + WRONG_PARAM_COUNT; \ + break; \ + } \ +} +/* }}} */ + +/* {{{ proto int rand([int min, int max]) + Returns a random number */ +PHP_FUNCTION_RAND(rand,RAND_SYS,sys) +/* }}} */ + +/* {{{ proto int mt_rand([int min, int max]) + Returns a random number by means of Mersenne Twister */ +PHP_FUNCTION_RAND(mt_rand,RAND_MT,mt) +/* }}} */ + +/* getrandmax */ + +/* {{{ PHPAPI long php_randmax(void) + Returns the maximum value a random number can have */ +PHPAPI long php_randmax(void) +{ + /* Get current algorithm */ + return php_randmax_sys(); } /* }}} */ @@ -333,25 +213,19 @@ PHP_FUNCTION(getrandmax) WRONG_PARAM_COUNT; } - return_value->type = IS_LONG; - return_value->value.lval = PHP_RAND_MAX; + RETURN_LONG( php_randmax_sys() ); } /* }}} */ /* {{{ proto int mt_getrandmax(void) - Returns the maximum value a random number from Mersenne Twister can have */ + Returns the maximum value a random number can have */ PHP_FUNCTION(mt_getrandmax) { if (ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; } - return_value->type = IS_LONG; - /* - * Melo: it could be 2^^32 but we only use 2^^31 to maintain - * compatibility with the previous php_rand - */ - return_value->value.lval = MT_RAND_MAX; /* 2^^31 */ + RETURN_LONG( php_randmax_mt() ); } /* }}} */ diff --git a/ext/standard/rand_mt.c b/ext/standard/rand_mt.c new file mode 100644 index 0000000000..bb0114951f --- /dev/null +++ b/ext/standard/rand_mt.c @@ -0,0 +1,220 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2001 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | + | Zeev Suraski <zeev@zend.com> | + | Pedro Melo <melo@ip.pt> | + | | + | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include <stdlib.h> + +#include "php.h" +#include "php_rand.h" + +#include "basic_functions.h" + +/* + This is the ``Mersenne Twister'' random number generator MT19937, which + generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) + starting from any odd seed in 0..(2^32 - 1). This version is a recode + by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by + Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in + July-August 1997). + + Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha + running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to + generate 300 million random numbers; after recoding: 24.0 sec. for the same + (i.e., 46.5% of original time), so speed is now about 12.5 million random + number generations per second on this machine. + + According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html> + (and paraphrasing a bit in places), the Mersenne Twister is ``designed + with consideration of the flaws of various existing generators,'' has + a period of 2^19937 - 1, gives a sequence that is 623-dimensionally + equidistributed, and ``has passed many stringent tests, including the + die-hard test of G. Marsaglia and the load test of P. Hellekalek and + S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 + to 5012 bytes of static data, depending on data type sizes, and the code + is quite short as well). It generates random numbers in batches of 624 + at a time, so the caching and pipelining of modern systems is exploited. + It is also divide- and mod-free. + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation (either version 2 of the License or, at your + option, any later version). This library is distributed in the hope that + it will be useful, but WITHOUT ANY WARRANTY, without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU Library General Public License for more details. You should have + received a copy of the GNU Library General Public License along with this + library; if not, write to the Free Software Foundation, Inc., 59 Temple + Place, Suite 330, Boston, MA 02111-1307, USA. + + The code as Shawn received it included the following notice: + + Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When + you use this, send an e-mail to <matumoto@math.keio.ac.jp> with + an appropriate reference to your work. + + It would be nice to CC: <Cokus@math.washington.edu> when you write. + + + + php_uint32 must be an unsigned integer type capable of holding at least 32 + bits; exactly 32 should be fastest, but 64 is better on an Alpha with + GCC at -O3 optimization so try your options and see what's best for you + + Melo: we should put some ifdefs here to catch those alphas... +*/ + + +#define N MT_N /* length of state vector */ +#define M (397) /* a period parameter */ +#define K (0x9908B0DFU) /* a magic constant */ +#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ +#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ +#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ +#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */ + +#define MIX_AND_RETURN(var) { \ + (var) ^= ((var) >> 11); \ + (var) ^= ((var) << 7) & 0x9D2C5680U; \ + (var) ^= ((var) << 15) & 0xEFC60000U; \ + (var) ^= ((var) >> 18); \ + /* + * Melo: hmms.. these are 32 random bits... + * Yet, the previous php_rand only returns 31 at most. + * So I put a right shift to loose the lsb. It *seems* + * better than clearing the msb. + * Update: + * I talked with Cokus via email and it won't ruin the algorithm + * + * Jeroen: moved this to end of php_rand_mt and php_rand_mt_reload + */ \ + return (long)((var) >> 1); \ +} + +/* {{{ void php_srand_mt(long seed) + */ +void php_srand_mt(long seed TSRMLS_DC) +{ + /* + We initialize state[0..(N-1)] via the generator + + x_new = (69069 * x_old) mod 2^32 + + from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's + _The Art of Computer Programming_, Volume 2, 3rd ed. + + Notes (SJC): I do not know what the initial state requirements + of the Mersenne Twister are, but it seems this seeding generator + could be better. It achieves the maximum period for its modulus + (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if + x_initial can be even, you have sequences like 0, 0, 0, ...; + 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, + 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. + + Even if x_initial is odd, if x_initial is 1 mod 4 then + + the lowest bit of x is always 1, + the next-to-lowest bit of x is always 0, + the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , + the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , + ... + + and if x_initial is 3 mod 4 then + + the lowest bit of x is always 1, + the next-to-lowest bit of x is always 1, + the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , + the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , + the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , + ... + + The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is + 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It + also does well in the dimension 2..5 spectral tests, but it could be + better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). + + Note that the random number user does not see the values generated + here directly since reloadMT() will always munge them first, so maybe + none of all of this matters. In fact, the seed values made here could + even be extra-special desirable if the Mersenne Twister theory says + so-- that's why the only change I made is to restrict to odd seeds. + */ + + register php_uint32 x = ((php_uint32)seed | 1U) & 0xFFFFFFFFU, *s = BG(state); + register int j; + + for(BG(left)=0, *s++=x, j=N; --j; + *s++ = (x*=69069U) & 0xFFFFFFFFU); +} +/* }}} */ + +/* {{{ long php_rand_mt_reload(void) [internal function] + * Generates a new batch of random numbers, and returns the first of it */ +static inline long php_rand_mt_reload(TSRMLS_D) +{ + register php_uint32 *p0=BG(state), *p2=BG(state)+2, *pM=BG(state)+M, s0, s1; + register int j; + + if(BG(left) < -1) + php_srand_mt(4357U TSRMLS_CC); + + BG(left)=N-1, BG(next)=BG(state)+1; + + for(s0=BG(state)[0], s1=BG(state)[1], j=N-M+1; --j; s0=s1, s1=*p2++) + *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + + for(pM=BG(state), j=M; --j; s0=s1, s1=*p2++) + *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + + s1=BG(state)[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + MIX_AND_RETURN(s1); +} +/*}}}*/ + +/* {{{ long php_rand_mt(void) */ +long php_rand_mt(void) +{ + php_uint32 y; + TSRMLS_FETCH(); + + if(--BG(left) < 0) + return(php_rand_mt_reload(TSRMLS_C)); + + y = *BG(next)++; + MIX_AND_RETURN(y); +} +/* }}} */ + +/* {{{ long php_randmax_mt(void) */ + +/* in php_rand.h */ + +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 tw=78 fdm=marker + * vim<600: sw=4 ts=4 tw=78 + */ |