[gnumeric] random: cleanup.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] random: cleanup.
- Date: Mon, 2 Aug 2010 18:37:52 +0000 (UTC)
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]