diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-06-28 17:29:32 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-06-28 17:29:32 +0000 |
commit | 23ce26f64c34e30951ad9ade2151552ed77e7357 (patch) | |
tree | d73b31a0c2f7c7be4a69f8a8e84e00dd39c432b5 /lib/util/rand.c | |
parent | 1b6e51a266008348ad93ed8b6ac9ec91b5024fea (diff) | |
download | netpbm-mirror-23ce26f64c34e30951ad9ade2151552ed77e7357.tar.gz netpbm-mirror-23ce26f64c34e30951ad9ade2151552ed77e7357.tar.xz netpbm-mirror-23ce26f64c34e30951ad9ade2151552ed77e7357.zip |
promote Advanced to Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@4558 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'lib/util/rand.c')
-rw-r--r-- | lib/util/rand.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/lib/util/rand.c b/lib/util/rand.c new file mode 100644 index 00000000..6a0a2cdb --- /dev/null +++ b/lib/util/rand.c @@ -0,0 +1,294 @@ +/* + +Pseudo-random number generator for Netpbm + +The interface provided herein should be flexible enough for anybody +who wishes to use some other random number generator. + +--- + +If you desire to implement a different generator, or writing an original +one, first take a look at the random number generator section of the +GNU Scientific Library package (GSL). + +GNU Scientific Library +https://www.gnu.org/software/gsl/ + +GSL Random Number Generators +https://wnww.gnu.org/software/gsl/doc/html/rng.html + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <strings.h> +#include <time.h> +#include <float.h> +#include <math.h> + +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/pm.h" +#include "netpbm/rand.h" + +/*----------------------------------------------------------------------------- + Use +------------------------------------------------------------------------------- + Typical usage: + + #include "rand.h" + + ... + + myfunction( ... , unsigned int const seed , ... ) { + + struct randSt; + + ... + + pm_randinit(&randSt); + pm_srand(&randSt, seed); // pm_srand2() is often more useful + + ... + + pm_rand(&randSt); + + ... + + pm_randterm(&randSt); + + } +-----------------------------------------------------------------------------*/ + + + +/*----------------------------------------------------------------------------- + Design note +------------------------------------------------------------------------------- + + Netpbm code contains multiple random number generators. Stock Netpbm always + uses an internal pseudo-random number generator that implements the Mersenne + Twister method and does not rely on any randomness facility of the operating + system, but it is easy to compile an alternative version that uses others. + + The Mersenne Twister method was new to Netpbm in Netpbm 10.94 (March 2021). + Before that, Netpbm used standard OS-provided facilities. + + Programs that use random numbers have existed in Netpbm since PBMPlus days. + The system rand() function was used in instances randomness was required; + exceptions were rare and all of them appear to be errors on the part of the + original author. + + Although the rand() function is available in every system on which Netpbm + runs, differences exist in the underlying algorithm, so that Netpbm programs + produce different output on different systems even when the user specifies + the same random number seed. + + This was not considered a problem in the early days. Deterministic + operation was not a feature users requested and it was impossible regardless + of the random number generation method on most programs because they did not + allow a user to specify a seed for the generator. + + This state of affairs changed as Netpbm got firmly established as a + base-level system package. Security became critical for many users. A + crucial component of quality control is automated regression tests (="make + check"). Unpredictable behavior gets in the way of testing. One by one + programs were given the -randomseed (or -seed) option to ensure reproducible + results. Often this was done as new test cases were written. However, + inconsistent output caused by system-level differences in rand() + implementation remained a major obstacle. + + In 2020 the decision was made to replace all calls to rand() in the Netpbm + source code with an internal random number generator. We decided to use the + Mersenne Twister, which is concise, enjoys a fine reputation and is + available under liberal conditions (see below.) +-----------------------------------------------------------------------------*/ + + +void +pm_srand(struct pm_randSt * const randStP, + unsigned int const seed) { +/*---------------------------------------------------------------------------- + Initialize (or "seed") the random number generation sequence with value + 'seed'. +-----------------------------------------------------------------------------*/ + pm_randinit(randStP); + + randStP->vtable.srand(randStP, seed); + + randStP->seed = seed; +} + + + +void +pm_srand2(struct pm_randSt * const randStP, + bool const seedValid, + unsigned int const seed) { +/*---------------------------------------------------------------------------- + Seed the random number generator. If 'seedValid' is true, use 'seed".. + Otherwise, use pm_randseed(). + + For historical reasons pm_randseed() is defined in libpm.c rather than + this source file. +-----------------------------------------------------------------------------*/ + pm_srand(randStP, seedValid ? seed : pm_randseed() ); + +} + + + +unsigned long int +pm_rand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + An integer random number in the interval [0, randStP->max]. +-----------------------------------------------------------------------------*/ + return randStP->vtable.rand(randStP); +} + + + +double +pm_drand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + A floating point random number in the interval [0, 1). + + Although the return value is declared as double, the actual value will have + no more precision than a single call to pm_rand() provides. This is 32 bits + for Mersenne Twister. +-----------------------------------------------------------------------------*/ + return (double) pm_rand(randStP) / randStP->max; +} + + + +void +pm_gaussrand2(struct pm_randSt * const randStP, + double * const r1P, + double * const r2P) { +/*---------------------------------------------------------------------------- + Generate two Gaussian (or normally) distributed random numbers *r1P and + *r2P. + + Mean = 0, Standard deviation = 1. + + This is called the Box-Muller method. + + For details of this algorithm and other methods for producing + Gaussian random numbers see: + + http://www.doc.ic.ac.uk/~wl/papers/07/csur07dt.pdf +-----------------------------------------------------------------------------*/ + double u1, u2; + + u1 = pm_drand(randStP); + u2 = pm_drand(randStP); + + if (u1 < DBL_EPSILON) + u1 = DBL_EPSILON; + + *r1P = sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2); + *r2P = sqrt(-2.0 * log(u1)) * sin(2.0 * M_PI * u2); +} + + + +double +pm_gaussrand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + A Gaussian (or normally) distributed random number. + + Mean = 0, Standard deviation = 1. + + If a randStP->gaussCache has a value, return that value. Otherwise call + pm_gaussrand2; return one generated value, remember the other. +-----------------------------------------------------------------------------*/ + double retval; + + if (!randStP->gaussCacheValid) { + pm_gaussrand2(randStP, &retval, &randStP->gaussCache); + randStP->gaussCacheValid = true; + } else { + retval = randStP->gaussCache; + randStP->gaussCacheValid = false; + } + + return retval; +} + + + +uint32_t +pm_rand32(struct pm_randSt * const randStP) { +/*----------------------------------------------------------------------------- + Generate a 32-bit random number. + + This is a provision for users who select a non-default random number + generator which returns less than 32 bits per call. Many system generators + are known to return 31 bits (max = 2147483647 or 0x7FFFFFFF). + + This does not work with generators that return less than 11 bits per call. + The least we know of is the archaic RANDU, which generates 15 bits (max = + 32767 or 0x7FFF). +-----------------------------------------------------------------------------*/ + unsigned int const randMax = randStP->max; + + uint32_t retval; + + if (randMax >= 0xFFFFFFFF) + retval = pm_rand(randStP); + else { + uint32_t scale; + + retval = 0; /* Initial value */ + + for (scale = 0xFFFFFFFF; scale > 0; scale /= (randMax +1)) + retval *= (randMax + 1) + pm_rand(randStP); + } + + return retval;; +} + + + +void +pm_randinit(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + Initialize the random number generator. +-----------------------------------------------------------------------------*/ + switch (PM_RANDOM_NUMBER_GENERATOR) { + case PM_RAND_SYS_RAND: + randStP->vtable = pm_randsysrand_vtable; + break; + case PM_RAND_SYS_RANDOM: + randStP->vtable = pm_randsysrandom_vtable; + break; + case PM_RAND_MERSENNETWISTER: + randStP->vtable = pm_randmersenne_vtable; + break; + default: + pm_error("INTERNAL ERROR: Invalid value of " + "PM_RANDOM_NUMBER_GENERATOR (random number generator " + "engine type): %u", PM_RANDOM_NUMBER_GENERATOR); + } + + randStP->vtable.init(randStP); + + randStP->gaussCacheValid = false; +} + + + +void +pm_randterm(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + Tear down the random number generator. +-----------------------------------------------------------------------------*/ + if (randStP->stateP) + free(randStP->stateP); +} + + + + |