Add the init_by_array functionality from the reference implementation of
authorGeorge Lebl <jirka@5z.com>
Sat, 10 Jan 2004 01:38:55 +0000 (01:38 +0000)
committerTim Janik <timj@src.gnome.org>
Sat, 10 Jan 2004 01:38:55 +0000 (01:38 +0000)
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.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
glib/grand.c
glib/grand.h
tests/rand-test.c

index a262ee4..8027e9e 100644 (file)
--- a/ChangeLog
+++ b/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
index a262ee4..8027e9e 100644 (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
index a262ee4..8027e9e 100644 (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
index a262ee4..8027e9e 100644 (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
index a262ee4..8027e9e 100644 (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
index a262ee4..8027e9e 100644 (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
index ef28b82..98f6b9b 100644 (file)
 #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"
@@ -121,6 +124,23 @@ g_rand_new_with_seed (guint32 seed)
 }
 
 /**
+ * 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:
  * 
  * Creates a new random number generator initialized with a seed taken
@@ -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);
 }
 
 /**
@@ -177,6 +223,29 @@ g_rand_free (GRand* 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.
  * @seed: a value to reinitialize the random number generator.
@@ -220,6 +289,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.
  *
index 051d923..cb6887b 100644 (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)
 
index c8efa21..141883e 100644 (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;
 }