diff --git a/ChangeLog b/ChangeLog index a262ee405..8027e9e6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index a262ee405..8027e9e6f 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index a262ee405..8027e9e6f 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index a262ee405..8027e9e6f 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index a262ee405..8027e9e6f 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index a262ee405..8027e9e6f 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,33 @@ +Fri Dec 19 11:49:21 2003 George Lebl + + * 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 * glib/gutils.h: Check defined (__OPTIMIZE__) not diff --git a/glib/grand.c b/glib/grand.c index ef28b829e..98f6b9b41 100644 --- a/glib/grand.c +++ b/glib/grand.c @@ -39,8 +39,11 @@ #include "config.h" #include +#include #include #include +#include +#include #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. diff --git a/glib/grand.h b/glib/grand.h index 051d92334..cb6887bcf 100644 --- a/glib/grand.h +++ b/glib/grand.h @@ -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) diff --git a/tests/rand-test.c b/tests/rand-test.c index c8efa21c5..141883eb8 100644 --- a/tests/rand-test.c +++ b/tests/rand-test.c @@ -3,7 +3,11 @@ #include -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; }