mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-03 14:42:10 +01:00
Add the init_by_array functionality from the reference implementation of
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com> * glib/grand.c glib/grand.h (g_rand_new) (g_rand_new_with_seed) (g_rand_new_with_seed_array) (g_rand_set_seed_array): Add the init_by_array functionality from the reference implementation of the mersenne twister (mt19937ar.c) and change the naming to fit with the rest of the grand API. New functions are g_rand_new_with_seed_array, g_rand_set_seed_array. This is only reliable/tested for the 2.2 version of the seeding as that's what the reference implementation uses. Also modify g_rand_new to get 4 longs from /dev/urandom since that will always be available anyway and we get more entropy and if /dev/urandom is unavailable use also 4 longs for seeding using secs, usecs, getpid and getppid. For version 2.0 use only a simple seed again but be more careful about seeding with secs/usecs in this case. * glib/grand.c glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the current state of the random number generator. * glib/grand.c (g_rand_new): Add testing for EINTR when reading from /dev/urandom * tests/rand-test.c: add testing of the array seeding stuff against the reference implementation, plus add statistical sanity check to see that the values outputted are truly kind of random. And check that g_rand_copy truly copies the state by checking a few terms.
This commit is contained in:
parent
43da83fdae
commit
80591652ff
30
ChangeLog
30
ChangeLog
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
@ -1,3 +1,33 @@
|
||||
Fri Dec 19 11:49:21 2003 George Lebl <jirka@5z.com>
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_new) (g_rand_new_with_seed)
|
||||
(g_rand_new_with_seed_array) (g_rand_set_seed_array): Add
|
||||
the init_by_array functionality from the reference implementation
|
||||
of the mersenne twister (mt19937ar.c) and change the naming
|
||||
to fit with the rest of the grand API. New functions are
|
||||
g_rand_new_with_seed_array, g_rand_set_seed_array. This is only
|
||||
reliable/tested for the 2.2 version of the seeding as that's what
|
||||
the reference implementation uses. Also modify g_rand_new to
|
||||
get 4 longs from /dev/urandom since that will always be available
|
||||
anyway and we get more entropy and if /dev/urandom is unavailable
|
||||
use also 4 longs for seeding using secs, usecs, getpid and getppid.
|
||||
For version 2.0 use only a simple seed again but be more careful
|
||||
about seeding with secs/usecs in this case.
|
||||
|
||||
* glib/grand.c
|
||||
glib/grand.h (g_rand_copy): Add g_rand_copy function to copy the
|
||||
current state of the random number generator.
|
||||
|
||||
* glib/grand.c (g_rand_new): Add testing for EINTR when reading
|
||||
from /dev/urandom
|
||||
|
||||
* tests/rand-test.c: add testing of the array seeding stuff against
|
||||
the reference implementation, plus add statistical sanity check
|
||||
to see that the values outputted are truly kind of random. And
|
||||
check that g_rand_copy truly copies the state by checking a few
|
||||
terms.
|
||||
|
||||
Tue Jan 6 15:38:30 2004 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* glib/gutils.h: Check defined (__OPTIMIZE__) not
|
||||
|
137
glib/grand.c
137
glib/grand.c
@ -39,8 +39,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "glib.h"
|
||||
#include "gthreadinit.h"
|
||||
@ -120,6 +123,23 @@ g_rand_new_with_seed (guint32 seed)
|
||||
return rand;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_new_with_seed_array:
|
||||
* @seed: an array of seeds to initialize the random number generator.
|
||||
* @seed_length: an array of seeds to initialize the random number generator.
|
||||
*
|
||||
* Creates a new random number generator initialized with @seed.
|
||||
*
|
||||
* Return value: the new #GRand.
|
||||
**/
|
||||
GRand*
|
||||
g_rand_new_with_seed_array (const guint32 *seed, guint seed_length)
|
||||
{
|
||||
GRand *rand = g_new0 (GRand, 1);
|
||||
g_rand_set_seed_array (rand, seed, seed_length);
|
||||
return rand;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_new:
|
||||
*
|
||||
@ -132,19 +152,42 @@ g_rand_new_with_seed (guint32 seed)
|
||||
GRand*
|
||||
g_rand_new (void)
|
||||
{
|
||||
guint32 seed;
|
||||
guint32 seed[4];
|
||||
GTimeVal now;
|
||||
#ifdef G_OS_UNIX
|
||||
static gboolean dev_urandom_exists = TRUE;
|
||||
|
||||
if (dev_urandom_exists)
|
||||
{
|
||||
FILE* dev_urandom = fopen("/dev/urandom", "rb");
|
||||
FILE* dev_urandom;
|
||||
|
||||
do
|
||||
{
|
||||
errno = 0;
|
||||
dev_urandom = fopen("/dev/urandom", "rb");
|
||||
}
|
||||
while G_UNLIKELY (errno == EINTR);
|
||||
|
||||
if (dev_urandom)
|
||||
{
|
||||
if (fread (&seed, sizeof (seed), 1, dev_urandom) != 1)
|
||||
int r;
|
||||
|
||||
do
|
||||
{
|
||||
errno = 0;
|
||||
r = fread (seed, sizeof (seed), 1, dev_urandom);
|
||||
}
|
||||
while G_UNLIKELY (errno == EINTR);
|
||||
|
||||
if (r != 1)
|
||||
dev_urandom_exists = FALSE;
|
||||
fclose (dev_urandom);
|
||||
|
||||
do
|
||||
{
|
||||
errno = 0;
|
||||
fclose (dev_urandom);
|
||||
}
|
||||
while G_UNLIKELY (errno == EINTR);
|
||||
}
|
||||
else
|
||||
dev_urandom_exists = FALSE;
|
||||
@ -156,10 +199,13 @@ g_rand_new (void)
|
||||
if (!dev_urandom_exists)
|
||||
{
|
||||
g_get_current_time (&now);
|
||||
seed = now.tv_sec ^ now.tv_usec;
|
||||
seed[0] = now.tv_sec;
|
||||
seed[1] = now.tv_usec;
|
||||
seed[2] = getpid ();
|
||||
seed[3] = getppid ();
|
||||
}
|
||||
|
||||
return g_rand_new_with_seed (seed);
|
||||
return g_rand_new_with_seed_array (seed, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,6 +222,29 @@ g_rand_free (GRand* rand)
|
||||
g_free (rand);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_copy:
|
||||
* @rand_: a #GRand.
|
||||
*
|
||||
* Copies a #GRand into a new one with the same exact state as before.
|
||||
* This way you can take a snapshot of the random number generator for
|
||||
* replaying later.
|
||||
*
|
||||
* Return value: the new #GRand.
|
||||
**/
|
||||
GRand *
|
||||
g_rand_copy (GRand* rand)
|
||||
{
|
||||
GRand* new_rand;
|
||||
|
||||
g_return_val_if_fail (rand != NULL, NULL);
|
||||
|
||||
new_rand = g_new0 (GRand, 1);
|
||||
memcpy (new_rand, rand, sizeof (GRand));
|
||||
|
||||
return new_rand;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_set_seed:
|
||||
* @rand_: a #GRand.
|
||||
@ -219,6 +288,62 @@ g_rand_set_seed (GRand* rand, guint32 seed)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_set_seed_array:
|
||||
* @rand_: a #GRand.
|
||||
* @seed: array to initialize with
|
||||
* @seed_length: length of array
|
||||
*
|
||||
* Initializes the random number generator by an array of
|
||||
* longs. Array can be of arbitrary size, though only the
|
||||
* first 624 values are taken. This function is useful
|
||||
* if you have many low entropy seeds, or if you require more then
|
||||
* 32bits of actual entropy for your application.
|
||||
**/
|
||||
void
|
||||
g_rand_set_seed_array (GRand* rand, const guint32 *seed, guint seed_length)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
g_return_if_fail (rand != NULL);
|
||||
g_return_if_fail (seed_length >= 1);
|
||||
|
||||
g_rand_set_seed (rand, 19650218UL);
|
||||
|
||||
i=1; j=0;
|
||||
k = (N>seed_length ? N : seed_length);
|
||||
for (; k; k--)
|
||||
{
|
||||
rand->mt[i] = (rand->mt[i] ^
|
||||
((rand->mt[i-1] ^ (rand->mt[i-1] >> 30)) * 1664525UL))
|
||||
+ seed[j] + j; /* non linear */
|
||||
rand->mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++; j++;
|
||||
if (i>=N)
|
||||
{
|
||||
rand->mt[0] = rand->mt[N-1];
|
||||
i=1;
|
||||
}
|
||||
if (j>=seed_length)
|
||||
j=0;
|
||||
}
|
||||
for (k=N-1; k; k--)
|
||||
{
|
||||
rand->mt[i] = (rand->mt[i] ^
|
||||
((rand->mt[i-1] ^ (rand->mt[i-1] >> 30)) * 1566083941UL))
|
||||
- i; /* non linear */
|
||||
rand->mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++;
|
||||
if (i>=N)
|
||||
{
|
||||
rand->mt[0] = rand->mt[N-1];
|
||||
i=1;
|
||||
}
|
||||
}
|
||||
|
||||
rand->mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
|
||||
}
|
||||
|
||||
/**
|
||||
* g_rand_int:
|
||||
* @rand_: a #GRand.
|
||||
|
@ -43,10 +43,16 @@ typedef struct _GRand GRand;
|
||||
*/
|
||||
|
||||
GRand* g_rand_new_with_seed (guint32 seed);
|
||||
GRand* g_rand_new_with_seed_array (const guint32 *seed,
|
||||
guint seed_length);
|
||||
GRand* g_rand_new (void);
|
||||
void g_rand_free (GRand *rand_);
|
||||
GRand* g_rand_copy (GRand *rand_);
|
||||
void g_rand_set_seed (GRand *rand_,
|
||||
guint32 seed);
|
||||
void g_rand_set_seed_array (GRand *rand_,
|
||||
const guint32 *seed,
|
||||
guint seed_length);
|
||||
|
||||
#define g_rand_boolean(rand_) ((g_rand_int (rand_) & (1 << 15)) != 0)
|
||||
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
const gint32 first_numbers[] =
|
||||
/* Outputs tested against the reference implementation mt19937ar.c from
|
||||
http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html */
|
||||
|
||||
/* Tests for a simple seed, first number is the seed */
|
||||
const guint32 first_numbers[] =
|
||||
{
|
||||
0x7a7a7a7a,
|
||||
0xfdcc2d54,
|
||||
@ -28,17 +32,56 @@ const gint32 first_numbers[] =
|
||||
0x1696330c,
|
||||
};
|
||||
|
||||
/* array seed */
|
||||
const guint32 seed_array[] =
|
||||
{
|
||||
0x6553375f,
|
||||
0xd6b8d43b,
|
||||
0xa1e7667f,
|
||||
0x2b10117c
|
||||
};
|
||||
|
||||
/* tests for the array seed */
|
||||
const guint32 array_outputs[] =
|
||||
{
|
||||
0xc22b7dc3,
|
||||
0xfdecb8ae,
|
||||
0xb4af0738,
|
||||
0x516bc6e1,
|
||||
0x7e372e91,
|
||||
0x2d38ff80,
|
||||
0x6096494a,
|
||||
0xd162d5a8,
|
||||
0x3c0aaa0d,
|
||||
0x10e736ae
|
||||
};
|
||||
|
||||
const gint length = sizeof (first_numbers) / sizeof (first_numbers[0]);
|
||||
const gint seed_length = sizeof (seed_array) / sizeof (seed_array[0]);
|
||||
const gint array_length = sizeof (array_outputs) / sizeof (array_outputs[0]);
|
||||
|
||||
int main()
|
||||
{
|
||||
guint n;
|
||||
guint ones;
|
||||
double proportion;
|
||||
|
||||
GRand* rand = g_rand_new_with_seed (first_numbers[0]);
|
||||
GRand* copy;
|
||||
|
||||
for (n = 1; n < length; n++)
|
||||
g_assert (first_numbers[n] == g_rand_int (rand));
|
||||
|
||||
g_rand_set_seed (rand, 2);
|
||||
g_rand_set_seed_array (rand, seed_array, seed_length);
|
||||
|
||||
for (n = 0; n < array_length; n++)
|
||||
g_assert (array_outputs[n] == g_rand_int (rand));
|
||||
|
||||
copy = g_rand_copy (rand);
|
||||
for (n = 0; n < 100; n++)
|
||||
g_assert (g_rand_int (copy) == g_rand_int (rand));
|
||||
|
||||
for (n = 1; n < 100000; n++)
|
||||
{
|
||||
gint32 i;
|
||||
@ -70,7 +113,22 @@ int main()
|
||||
g_assert (b == TRUE || b == FALSE);
|
||||
}
|
||||
|
||||
/* Statistical sanity check, count the number of ones
|
||||
* when getting random numbers in range [0,3) and see
|
||||
* that it must be semi-close to 0.25 with a VERY large
|
||||
* probability */
|
||||
ones = 0;
|
||||
for (n = 1; n < 100000; n++)
|
||||
{
|
||||
if (g_random_int_range (0, 4) == 1)
|
||||
ones ++;
|
||||
}
|
||||
proportion = (double)ones / (double)100000;
|
||||
/* 0.025 is overkill, but should suffice to test for some unreasonability */
|
||||
g_assert (ABS (proportion - 0.25) < 0.025);
|
||||
|
||||
g_rand_free (rand);
|
||||
g_rand_free (copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user