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:
George Lebl 2004-01-10 01:38:55 +00:00 committed by Tim Janik
parent 43da83fdae
commit 80591652ff
9 changed files with 376 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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;
}