]> git.draconx.ca Git - cdecl99.git/blobdiff - t/rng.c
tests: Use 32-bit arithmetic for seed expansion.
[cdecl99.git] / t / rng.c
diff --git a/t/rng.c b/t/rng.c
index a827b2e05f610255de5368784750b6b5a737b787..63e08188291b1972eec472819e3d46d655883414 100644 (file)
--- a/t/rng.c
+++ b/t/rng.c
@@ -15,9 +15,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  *
- * The RNG implementation is adapted from xoshiro256+ and splitmix64
- * by David Blackman and Sebastiano Vigna, originally distributed under
- * the Creative Commons Zero public domain dedication.
+ * The RNG implementation is adapted from xoshiro256+ by David Blackman
+ * and Sebastiano Vigna, originally distributed under the Creative Commons
+ * Zero public domain dedication.
  */
 
 #include <config.h>
 #include <string.h>
 #include <errno.h>
 #include <limits.h>
+#include <inttypes.h>
 
 #if !TEST_RNG_NO_EXTERNAL_API
 #  include "test.h"
 #endif
 
 #define B64(x) ((x) & 0xffffffffffffffffull)
+#define B32(x) ((x) & 0xffffffffu)
 
 struct test_rng {
        unsigned long long state[4];
@@ -60,38 +62,51 @@ static unsigned long long xoshiro256p(unsigned long long *s)
        return ret;
 }
 
-static unsigned long long splitmix64(unsigned long long *state)
+/*
+ * 32-bit random number generator for seed expansion.
+ *
+ * The output filter is from the public domain MurmurHash3 by Austin Appleby.
+ *
+ * The state is incremented by the fractional part of the golden ratio,
+ * i.e., floor( (sqrt(5)-1)/2 * 2**32 )
+ */
+static uint_least32_t splitmix32(uint_least32_t *state)
 {
-       unsigned long long z;
+       uint_least32_t z;
 
-       z = B64(*state += 0x9e3779b97f4a7c15ull);
-       z = B64((z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ull);
-       z = B64((z ^ (z >> 27)) * 0x94d049bb133111ebull);
+       z = B32(*state += 0x9e3779b9);
+       z = B32((z ^ (z >> 16)) * 0x85ebca6b);
+       z = B32((z ^ (z >> 13)) * 0xc2b2ae35);
 
-       return z ^ (z >> 31);
+       return z ^ (z >> 16);
+}
+
+static uint_least64_t seed64(uint_least32_t *state)
+{
+       uint_least64_t x;
+
+       x = splitmix32(state);
+       return (x << 32) | splitmix32(state);
 }
 
 #if !TEST_RNG_NO_EXTERNAL_API
 struct test_rng *test_rng_alloc(const char *seed_str)
 {
-       test_uintmax limit, seed_val;
-       unsigned long long seed;
+       test_uintmax seed_val;
        struct test_rng *rng;
+       uint_least32_t seed;
 
-       limit  = 0xffffffff;
-       limit |= (limit << 16 << 16);
-
-       if (!test_strtoumax(&seed_val, seed_str, limit)) {
+       if (!test_strtoumax(&seed_val, seed_str, 0xffffffff)) {
                print_error("%s: invalid seed", seed_str);
                return NULL;
        }
        seed = seed_val;
 
        rng = malloc_nofail(sizeof *rng);
-       rng->state[0] = splitmix64(&seed);
-       rng->state[1] = splitmix64(&seed);
-       rng->state[2] = splitmix64(&seed);
-       rng->state[3] = splitmix64(&seed);
+       rng->state[0] = seed64(&seed);
+       rng->state[1] = seed64(&seed);
+       rng->state[2] = seed64(&seed);
+       rng->state[3] = seed64(&seed);
 
        return rng;
 }