Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_srand, 0, 0, 0)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_mt_srand_array, 0)
ZEND_ARG_INFO(0, seed)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_rand, 0, 0, 0)
ZEND_ARG_INFO(0, min)
ZEND_ARG_INFO(0, max)
Expand Down Expand Up @@ -2856,6 +2860,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
PHP_FALIAS(getrandmax, mt_getrandmax, arginfo_mt_getrandmax)
PHP_FE(mt_rand, arginfo_mt_rand)
PHP_FE(mt_srand, arginfo_mt_srand)
PHP_FE(mt_srand_array, arginfo_mt_srand_array)
PHP_FE(mt_getrandmax, arginfo_mt_getrandmax)

PHP_FE(random_bytes, arginfo_random_bytes)
Expand Down
84 changes: 83 additions & 1 deletion ext/standard/mt_rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ PHPAPI void php_mt_srand(uint32_t seed)
{
/* Seed the generator with a simple uint32 */
php_mt_initialize(seed, BG(state));
php_mt_reload();

/* Reload the generator when mt_rand is next called.
* (Reloading immediately would break php_mt_srand_array.) */
BG(left) = 0;

/* Seed only once */
BG(mt_rand_is_seeded) = 1;
Expand Down Expand Up @@ -209,6 +212,85 @@ PHP_FUNCTION(mt_srand)
}
/* }}} */

/* {{{ proto void mt_srand_array(array seed)
Seeds Mersenne Twister random number generator with multiple integers */
PHP_FUNCTION(mt_srand_array)
{
zval *arr = 0, *tmp;
uint32_t length = 0, seed_j;
zend_long seed_j_long;
double seed_j_double;
int i, j, k, first_loop = 1;
uint32_t *mt = BG(state);

if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arr) == FAILURE)
return;

length = zend_hash_num_elements(Z_ARRVAL_P(arr));
if (length == 0) {
php_error_docref(NULL, E_WARNING, "expects parameter 1 to be an array with at least 1 element");
return;
}

ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
if (UNEXPECTED(Z_TYPE_P(tmp) == IS_REFERENCE)) {
tmp = Z_REFVAL_P(tmp);
}
seed_j_double = 0.5;
if (Z_TYPE_P(tmp) == IS_LONG) {
seed_j_double = Z_LVAL_P(tmp);
} else if (Z_TYPE_P(tmp) == IS_DOUBLE) {
seed_j_double = Z_DVAL_P(tmp);
} else if (Z_TYPE_P(tmp) == IS_STRING) {
if (is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &seed_j_long, &seed_j_double, -1) == IS_LONG) {
seed_j_double = seed_j_long;
}
}
/* check for range */
if (seed_j_double > UINT32_MAX || seed_j_double < INT32_MIN) {
php_error_docref(NULL, E_WARNING, "expects parameter 1 to be an array of 32-bit integers");
return;
}
/* check for decimals */
/* (notice the default 0.5, which also provokes this warning) */
if (seed_j_double != (uint32_t)seed_j_double && seed_j_double != (int32_t)seed_j_double) {
php_error_docref(NULL, E_WARNING, "expects parameter 1 to be an array of integers");
return;
}
} ZEND_HASH_FOREACH_END();

php_mt_srand(19650218U);
i = 1;
k = 0;
while (k < N) {
j = 0;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
seed_j = zval_get_long(tmp);
mt[i] = ((mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525U)) + seed_j + j) & 0xffffffffU;
i++; j++; k++;
if (i == N) {
mt[0] = mt[N-1];
i = 1;
}
if (!first_loop && k == N) {
break;
}
} ZEND_HASH_FOREACH_END();
first_loop = 0;
}
for (k = 1; k < N; ++k) {
mt[i] = ((mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941U)) - i) & 0xffffffffU;
i++;
if (i == N) {
mt[0] = mt[N-1];
i = 1;
}
}

mt[0] = 0x80000000U;
}
/* }}} */

/* {{{ php_mt_rand_range
*/
PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
Expand Down
1 change: 1 addition & 0 deletions ext/standard/php_math.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ PHP_FUNCTION(pow);
PHP_FUNCTION(sqrt);
PHP_FUNCTION(rand);
PHP_FUNCTION(mt_srand);
PHP_FUNCTION(mt_srand_array);
PHP_FUNCTION(mt_rand);
PHP_FUNCTION(mt_getrandmax);
PHP_FUNCTION(abs);
Expand Down
190 changes: 190 additions & 0 deletions ext/standard/tests/math/mt_srand_array_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
--TEST--
Test mt_srand_array() - basic function mt_srand_array()
--FILE--
<?php

function generate_some() {
$t = [];
for ($i = 0; $i < 100; ++$i) {
$t[] = mt_rand();
}
return $t;
}

function test_seed($a) {
// Print mt_srand_array result
var_dump(mt_srand_array($a));
$ar = generate_some();
@mt_srand_array($a);
$br = generate_some();
// Print true if the seed produces the same sequence.
var_dump($ar === $br);
}

echo "Seed = missing. Expected: warning, NULL:\n";
var_dump(mt_srand_array());

echo "Seed = not array. Expected: warning, NULL, false:\n";
test_seed(NULL);
test_seed(true);
test_seed(false);
test_seed(1);
test_seed(1.2);
test_seed('x');

echo "Seed = empty array. Expected: warning, NULL, false:\n";
test_seed([]);

echo "Seed = integer array = correct usage. Expected: NULL, true:\n";
test_seed([1]);
test_seed([1, 2]);
test_seed([1, 2, 3]);

echo "Seed = array with a reference to integer = correct usage. Expected: NULL, true:\n";
$x = 1;
test_seed([&$x]);

echo "Seed = array with integral floats = correct usage. Expected: NULL, true:\n";
test_seed([1.0, 2.0, 3.0, 1.0e9]);

echo "Seed = array with integral strings = correct usage. Expected: NULL, true:\n";
test_seed(['1', '+2', '-3']);

echo "Seed = array with too big values. Expected: warning, NULL, false:\n";
test_seed([0x123ffffffff]);
test_seed([-0x123ffffffff]);

echo "Seed = array with non well formed numeric string. Expected: notice, NULL, true:\n";
test_seed(['123xyz']);

echo "Seed = non-integer array. Expected: warning, NULL, false:\n";
test_seed([NULL]);
test_seed([true]);
test_seed([false]);
test_seed([1.2, 24.7]);
test_seed(['']);
test_seed(['x']);
test_seed(['1.1']);
test_seed([[]]);
test_seed([[1]]);

echo "Same results from mt_rand with same seed values:\n";
mt_srand_array([1,2,3,4,5]);
$a = generate_some();
mt_srand_array([1,2,3,4,5]);
$b = generate_some();
var_dump($a == $b);

echo "Different results from mt_rand with different seed values:\n";
mt_srand_array([1,2,3,4,5]);
$a = generate_some();
mt_srand_array([1,2,7,4,5]);
$b = generate_some();
var_dump($a != $b);
?>
--EXPECTF--
Seed = missing. Expected: warning, NULL:

Warning: mt_srand_array() expects exactly 1 parameter, 0 given in %s on line %d
NULL
Seed = not array. Expected: warning, NULL, false:

Warning: mt_srand_array() expects parameter 1 to be array, null given in %s on line %d
NULL
bool(false)

Warning: mt_srand_array() expects parameter 1 to be array, boolean given in %s on line %d
NULL
bool(false)

Warning: mt_srand_array() expects parameter 1 to be array, boolean given in %s on line %d
NULL
bool(false)

Warning: mt_srand_array() expects parameter 1 to be array, integer given in %s on line %d
NULL
bool(false)

Warning: mt_srand_array() expects parameter 1 to be array, float given in %s on line %d
NULL
bool(false)

Warning: mt_srand_array() expects parameter 1 to be array, string given in %s on line %d
NULL
bool(false)
Seed = empty array. Expected: warning, NULL, false:

Warning: mt_srand_array(): expects parameter 1 to be an array with at least 1 element in %s on line %d
NULL
bool(false)
Seed = integer array = correct usage. Expected: NULL, true:
NULL
bool(true)
NULL
bool(true)
NULL
bool(true)
Seed = array with a reference to integer = correct usage. Expected: NULL, true:
NULL
bool(true)
Seed = array with integral floats = correct usage. Expected: NULL, true:
NULL
bool(true)
Seed = array with integral strings = correct usage. Expected: NULL, true:
NULL
bool(true)
Seed = array with too big values. Expected: warning, NULL, false:

Warning: mt_srand_array(): expects parameter 1 to be an array of 32-bit integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of 32-bit integers in %s on line %d
NULL
bool(false)
Seed = array with non well formed numeric string. Expected: notice, NULL, true:

Notice: A non well formed numeric value encountered in %s on line %d
NULL
bool(true)
Seed = non-integer array. Expected: warning, NULL, false:

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)

Warning: mt_srand_array(): expects parameter 1 to be an array of integers in %s on line %d
NULL
bool(false)
Same results from mt_rand with same seed values:
bool(true)
Different results from mt_rand with different seed values:
bool(true)
Loading