[gnumeric] random: cleanup.



commit f969e245e2e84933db7e6aeb29f318fd10efe5e5
Author: Morten Welinder <terra gnome org>
Date:   Mon Aug 2 14:37:22 2010 -0400

    random: cleanup.

 ChangeLog        |    1 +
 src/gnm-random.c |  168 ++++++++++++++++++++++++++++++++----------------------
 2 files changed, 101 insertions(+), 68 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 6076b49..f670d80 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
 2010-08-02  Morten Welinder  <terra gnome org>
 
 	* src/gnm-random.c: Split from src/mathfunc.c.
+	(random_01): Split into manageable pieces.
 
 2010-08-01  Jean Brefort  <jean brefort normalesup org>
 
diff --git a/src/gnm-random.c b/src/gnm-random.c
index ee73287..9ca3bee 100644
--- a/src/gnm-random.c
+++ b/src/gnm-random.c
@@ -239,88 +239,120 @@ int main(void)
 
 /* ------------------------------------------------------------------------ */
 
-/* FIXME: we need something that catches partials and EAGAIN.  */
-#define fullread read
+static void
+mt_setup_seed (const char *seed)
+{
+	int len = strlen (seed);
+	int i;
+	unsigned long *longs = g_new (unsigned long, len + 1);
+
+	/* We drop only one character into each long.  */
+	for (i = 0; i < len; i++)
+		longs[i] = (unsigned char)seed[i];
+	mt_init_by_array (longs, len);
+	g_free (longs);
+}
+
+static gnm_float
+random_01_mersenne (void)
+{
+	/*
+	 * Only 52-bit precision.  But hey, if you are using pseudo
+	 * random numbers that ought to be good enough to you.
+	 */
+	return genrand_res53 ();
+}
+
+/* ------------------------------------------------------------------------ */
 
+static gnm_float
+random_01_data (const unsigned char *data)
+{
+	unsigned ui;
+	gnm_float res = 0;
+
+	for (ui = 0; ui < sizeof (gnm_float); ui++)
+		res = (res + data[ui]) / 256;
+	return res;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static FILE *random_device_file = NULL;
 #define RANDOM_DEVICE "/dev/urandom"
 
-/*
- * Conservative random number generator.  The result is (supposedly) uniform
- * and between 0 and 1.	 (0 possible, 1 not.)  The result should have about
- * 64 bits randomness.
- */
-gnm_float
-random_01 (void)
+static gnm_float
+random_01_device (void)
 {
-	static int device_fd = -2;
-	static int seeded = -2;
-
-	while (seeded) {
-		if (seeded == -2) {
-			char const *seed = g_getenv ("GNUMERIC_PRNG_SEED");
-			if (seed) {
-				int len = strlen (seed);
-				int i;
-				unsigned long *longs = g_new (unsigned long, len + 1);
-
-				/* We drop only one character into each long.  */
-				for (i = 0; i < len; i++)
-					longs[i] = (unsigned char)seed[i];
-				mt_init_by_array (longs, len);
-				g_free (longs);
-				seeded = 1;
-
-				g_warning ("Using pseudo-random numbers.");
-			} else {
-				seeded = 0;
-				break;
-			}
+	static size_t bytes_left = 0;
+	static unsigned char data[32 * sizeof (gnm_float)];
+
+	while (bytes_left < sizeof (gnm_float)) {
+		gssize items = fread (data + bytes_left, 1, sizeof (data),
+				      random_device_file);
+		if (items <= 0) {
+			g_warning ("Reading from %s failed; reverting to pseudo-random.",
+				   RANDOM_DEVICE);
+			return random_01_mersenne ();
 		}
+		bytes_left += items;
+	}
 
-		/*
-		 * Only 52-bit precision.  But hey, if you are using pseudo
-		 * random numbers that ought to be good enough to you.
-		 */
-		return genrand_res53 ();
+	bytes_left -= sizeof (gnm_float);
+	return random_01_data (data + bytes_left);
+}
+
+/* ------------------------------------------------------------------------ */
+
+typedef enum {
+	RS_UNDETERMINED,
+	RS_MERSENNE,
+	RS_DEVICE
+} RandomSource;
+
+static RandomSource random_src = RS_UNDETERMINED;
+
+static void
+random_01_determine (void)
+{
+	char const *seed = g_getenv ("GNUMERIC_PRNG_SEED");
+	if (seed) {
+		mt_setup_seed (seed);
+		g_warning ("Using pseudo-random numbers.");
+		random_src = RS_MERSENNE;
+		return;
 	}
 
-	if (device_fd == -2) {
-		device_fd = g_open (RANDOM_DEVICE, O_RDONLY, 0);
-		/*
-		 * We could check that we really have a device, but it hard
-		 * to come up with a non-paranoid reason to.
-		 */
+	random_device_file = g_fopen (RANDOM_DEVICE, "rb");
+	if (random_device_file) {
+		random_src = RS_DEVICE;
+		return;
 	}
 
-	if (device_fd >= 0) {
-		static ssize_t bytes_left = 0;
-		static unsigned char data[32 * sizeof (gnm_float)];
-		gnm_float res = 0;
-		size_t i;
-
-		if (bytes_left < (ssize_t)sizeof (gnm_float)) {
-			ssize_t got = fullread (device_fd, &data, sizeof (data));
-			if (got < (ssize_t)sizeof (gnm_float))
-				goto failure;
-			bytes_left += got;
-		}
+	/* Fallback.  */
+	g_warning ("Using pseudo-random numbers.");
+	random_src = RS_MERSENNE;
+	return;
+}
 
-		bytes_left -= sizeof (gnm_float);
-		for (i = 0; i < sizeof (gnm_float); i++)
-			res = (res + data[bytes_left + i]) / 256;
-		return res;
-
-	failure:
-		/* It failed when it shouldn't.	 Disable.  */
-		g_warning ("Reading from %s failed; reverting to pseudo-random.",
-			   RANDOM_DEVICE);
-		close (device_fd);
-		device_fd = -1;
+gnm_float
+random_01 (void)
+{
+	if (random_src == RS_UNDETERMINED)
+		random_01_determine ();
+
+	switch (random_src) {
+	case RS_UNDETERMINED:
+	default:
+		g_assert_not_reached ();
+	case RS_MERSENNE:
+		return random_01_mersenne ();
+	case RS_DEVICE:
+		return random_01_device ();
 	}
-
-	return genrand_res53 ();
 }
 
+/* ------------------------------------------------------------------------ */
 /*
  * Generate a N(0,1) distributed number.
  */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]