about summary refs log tree commit diff
path: root/generator/pgmnoise.c
diff options
context:
space:
mode:
Diffstat (limited to 'generator/pgmnoise.c')
-rw-r--r--generator/pgmnoise.c145
1 files changed, 98 insertions, 47 deletions
diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c
index 40d0e189..ea35956b 100644
--- a/generator/pgmnoise.c
+++ b/generator/pgmnoise.c
@@ -3,15 +3,21 @@
    Frank Neumann, October 1993
 *********************************************************************/
 
+#include <assert.h>
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "rand.h"
 #include "shhopt.h"
 #include "pgm.h"
-#include <assert.h>
+
+/* constants */
+static unsigned long int const ceil31bits = 0x7fffffffUL;
+static unsigned long int const ceil32bits = 0xffffffffUL;
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -20,13 +26,15 @@ struct cmdlineInfo {
     unsigned int maxval;
     unsigned int randomseed;
     unsigned int randomseedSpec;
+    unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, const char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc,
+                 const char **        const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -45,6 +53,8 @@ parseCommandLine(int argc, const char ** const argv,
             &cmdlineP->randomseedSpec,      0);
     OPTENT3(0,   "maxval",       OPT_UINT,    &cmdlineP->maxval,
             &maxvalSpec,                    0);
+    OPTENT3(0,   "verbose",      OPT_FLAG,    NULL,
+            &cmdlineP->verbose,             0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -56,7 +66,7 @@ parseCommandLine(int argc, const char ** const argv,
 
     if (maxvalSpec) {
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
-            pm_error("Maxval too large: %u.  Maximu is %u",
+            pm_error("Maxval too large: %u.  Maximum is %u",
                      cmdlineP->maxval, PGM_OVERALLMAXVAL);
         else if (cmdlineP->maxval == 0)
             pm_error("Maxval must not be zero");
@@ -68,16 +78,24 @@ parseCommandLine(int argc, const char ** const argv,
                  "Arguments are width and height of image, in pixels",
                  argc-1);
     else {
-        int const width  = atoi(argv[1]);
-        int const height = atoi(argv[2]);
-
-        if (width <= 0)
-            pm_error("Width must be positive, not %d", width);
+        const char * error; /* error message of pm_string_to_uint */
+        unsigned int width, height;
+
+        pm_string_to_uint(argv[1], &width, &error);
+        if (error)
+            pm_error("Width argument is not an unsigned integer.  %s",
+                     error);
+        else if (width == 0)
+            pm_error("Width argument is zero; must be positive");
         else
             cmdlineP->width = width;
 
-        if (height <= 0)
-            pm_error("Height must be positive, not %d", width);
+        pm_string_to_uint(argv[2], &height, &error);
+        if (error)
+            pm_error("Height argument is not an unsigned integer.  %s ",
+                     error);
+        else if (height == 0)
+            pm_error("Height argument is zero; must be positive");
         else
             cmdlineP->height = height;
     }
@@ -86,38 +104,43 @@ parseCommandLine(int argc, const char ** const argv,
 
 
 static unsigned int
-randPool(unsigned int const digits) {
+randPool(unsigned int       const nDigits,
+         struct pm_randSt * const randStP) {
 /*----------------------------------------------------------------------------
-  Draw 'digits' bits from pool of random bits.  If the number of random bits
-  in pool is insufficient, call rand() and add 31 bits to it.
+  Draw 'nDigits' bits from pool of random bits.  If the number of random bits
+  in pool is insufficient, call pm_rand() and add N bits to it.
+
+  N is 31 or 32.  In raw mode we use N = 32 regardless of the actual number of
+  available bits.  If there are only 31 available, we use zero for the MSB.
 
-  'digits' must be at most 16.
+  'nDigits' must be at most 16.
 
-  We assume that each call to rand() generates 31 bits, or RAND_MAX ==
-  2147483647.
+  We assume that each call to pm_rand() generates 31 or 32 bits, or
+  randStP->max == 2147483647 or 4294967295.
 
-  The underlying logic is flexible and endian-free.  The above conditions
-  can be relaxed.
+  The underlying logic is flexible and endian-free.  The above conditions can
+  be relaxed.
 -----------------------------------------------------------------------------*/
     static unsigned long int hold=0;  /* entropy pool */
     static unsigned int len=0;        /* number of valid bits in pool */
 
-    unsigned int const mask = (1 << digits) - 1;
-
+    unsigned int const mask = (1 << nDigits) - 1;
+    unsigned int const randbits = (randStP->max == ceil31bits) ? 31 : 32;
     unsigned int retval;
 
-    assert(RAND_MAX == 2147483647 && digits <= 16);
+    assert(randStP->max == ceil31bits || randStP->max == ceil32bits);
+    assert(nDigits <= 16);
 
     retval = hold;  /* initial value */
 
-    if (len > digits) { /* Enough bits in hold to satisfy request */
-        hold >>= digits;
-        len   -= digits;
-    } else {              /* Load another 31 bits into hold */
-        hold    = rand();
+    if (len > nDigits) { /* Enough bits in hold to satisfy request */
+        hold >>= nDigits;
+        len   -= nDigits;
+    } else {            /* Load another 31 or 32 bits into hold */
+        hold    = pm_rand(randStP);
         retval |= (hold << len);
-        hold >>=  (digits - len);
-        len = 31 - digits + len;
+        hold >>=  (nDigits - len);
+        len = randbits - nDigits + len;
     }
     return (retval & mask);
 }
@@ -125,36 +148,61 @@ randPool(unsigned int const digits) {
 
 
 static void
-pgmnoise(FILE *       const ofP,
-         unsigned int const cols,
-         unsigned int const rows,
-         gray         const maxval) {
+reportVerbose(struct pm_randSt * const randStP,
+              gray               const maxval,
+              bool               const usingPool)  {
+
+    pm_message("random seed: %u", randStP->seed);
+    pm_message("random max: %u maxval: %u", randStP->max, maxval);
+    pm_message("method: %s", usingPool ? "pool" : "modulo");
+}
+
 
-    bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1)));
+
+static void
+pgmnoise(FILE *             const ofP,
+         unsigned int       const cols,
+         unsigned int       const rows,
+         gray               const maxval,
+         bool               const verbose,
+         struct pm_randSt * const randStP) {
+
+    bool const usingPool =
+        (randStP->max==ceil31bits || randStP->max==ceil32bits) &&
+        !(maxval & (maxval+1));
     unsigned int const bitLen = pm_maxvaltobits(maxval);
 
     unsigned int row;
     gray * destrow;
 
     /* If maxval is 2^n-1, we draw exactly n bits from the pool.
-       Otherwise call rand() and determine gray value by modulo.
+       Otherwise call pm_rand() and determine gray value by modulo.
 
-       In the latter case, there is a miniscule skew toward 0 (=black)
+       In the latter case, there is a minuscule skew toward 0 (=black)
        because smaller numbers are produced more frequently by modulo.
        Thus we employ the pool method only when it is certain that no
-       skew will ensue.
+       skew will result.
 
        To illustrate the point, consider converting the outcome of one
        roll of a fair, six-sided die to 5 values (0 to 4) by N % 5.  The
-       probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6.
+       probability for values 1, 2, 3, 4 is 1/6, but 0 alone is 2/6.
        Average is 10/6 or 1.6667, compared to 2.0 from an ideal
        generator which produces exactly 5 values.  With two dice
        average improves to 70/36 or 1.9444.
 
        The more (distinct) dice we roll, or the more binary digits we
        draw, the smaller the skew.
+
+       The pool method is economical.  But there is an additional merit:
+       No bits are lost this way.  This gives us a means to check the
+       integrity of the random number generator.
+
+       - Akira Urushibata, March 2021
     */
 
+    if (verbose)
+        reportVerbose(randStP, maxval, usingPool);
+
     destrow = pgm_allocrow(cols);
 
     pgm_writepgminit(ofP, cols, rows, maxval, 0);
@@ -163,12 +211,11 @@ pgmnoise(FILE *       const ofP,
         if (usingPool) {
             unsigned int col;
             for (col = 0; col < cols; ++col)
-                destrow[col] = randPool(bitLen);
-        }
-        else {
+                destrow[col] = randPool(bitLen, randStP);
+        } else {
             unsigned int col;
             for (col = 0; col < cols; ++col)
-                destrow[col] = rand() % (maxval + 1);
+                destrow[col] = pm_rand(randStP) % (maxval + 1);
         }
         pgm_writepgmrow(ofP, destrow, cols, maxval, 0);
     }
@@ -182,18 +229,22 @@ int
 main(int          argc,
      const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
+    struct pm_randSt randSt;
 
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+    pm_randinit(&randSt);
+    pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed);
 
-    pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval);
+    pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval,
+             cmdline.verbose, &randSt);
+
+    pm_randterm(&randSt);
 
     return 0;
 }
 
 
-