diff options
author | EuAndreh <eu@euandre.org> | 2024-03-20 03:38:00 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-03-20 03:38:05 -0300 |
commit | 4ce404bc32e46b7cdcb834f0077cda2219988e44 (patch) | |
tree | b86a2735e20e876c08718986f8d32a514df4a502 /tests | |
parent | tests/js/hero.mjs: Fix function ordering (diff) | |
download | papod-4ce404bc32e46b7cdcb834f0077cda2219988e44.tar.gz papod-4ce404bc32e46b7cdcb834f0077cda2219988e44.tar.xz |
tests/rand.mjs: Add MersenneTwister random number generator
The `tests/rand.c` is also added: a simplified adaptation of the
original algorithm implementation in C. A 10k numbers test case shows
that the JavaScript version behaves the same that the C one does.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/js/rand.mjs | 96 | ||||
-rw-r--r-- | tests/rand.c | 83 | ||||
-rw-r--r-- | tests/rand.mjs | 55 |
3 files changed, 234 insertions, 0 deletions
diff --git a/tests/js/rand.mjs b/tests/js/rand.mjs new file mode 100644 index 0000000..4044bb8 --- /dev/null +++ b/tests/js/rand.mjs @@ -0,0 +1,96 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; + +import * as runner from "../runner.mjs"; +import { + makeRandom, +} from "../rand.mjs"; + + +const test_makeRandom = async t => { + t.start("makeRandom()"); + + await t.test("known expected values", () => { + const expected1 = [ + 545331265, 2211535594, 4152021490, 3857419313, + 1118735928, 3031474347, 3853601519, 3345048107, + 1618127707, 4001288224, 1444061399, 2700534436, + 1938647176, 1459718602, 3608868106, 926825740, + 528719691, 508226068, 2332279787, 357437743, + 1602075314, 1655472598, 1924131686, 4125580919, + 555943458, 4047836046, 3693150918, 1515015978, + 3523541186, 753964207, 1512058436, 682070622, + 983063469, 4097791860, 3336260823, 603291380, + 2554576084, 139062484, 590788013, 3608810491, + 3663176645, 3481732081, 1011496919, 2935401636, + 628041163, 3997974947, 2533467101, 2287863463, + 2465361808, 1196937660, 263152482, 618228754, + 2535860367, 3089904367, 1053769440, 1270818139, + 1462199892, 49766906, 4229377745, 445624458, + 3949395950, 4102223196, 162228180, 352248782, + 3700326028, 965464548, 3236553557, 1342363444, + 1740229486, 4006226087, 1475432391, 2100043423, + 734083679, 2197742340, 1695047538, 2684537025, + 2755935210, 2185177924, 1179365521, 682202996, + 1985796754, 1203188028, 3742512741, 2573038834, + 1722845493, 971634808, 2622456659, 1542773708, + 506664990, 516191900, 3015858858, 2439287065, + 1778261054, 3352336947, 1470361474, 2832338531, + 2559479753, 2841821596, 858410480, 1311022275, + ]; + + const expected2 = [ + 1867215825, 1512725508, 59490082, 898255083, + 2128587452, 3375572255, 3011978101, 664390052, + 712189293, 1309145867, 1574443536, 3005563625, + 1937221174, 2592810353, 1221596501, 3864084723, + 2447880554, 1373397507, 4101388729, 1434258649, + 2876658371, 694177935, 1850641240, 2934786942, + 3613830458, 3660625740, 1450431957, 978896710, + 108365537, 1669305275, 1505684879, 1989942961, + 4089952722, 2832268551, 27863256, 3866494241, + 3916427650, 3309597508, 24597008, 3442418170, + 1617059167, 175885832, 854293503, 3965431971, + 3276364445, 4079612430, 1622146979, 1665096344, + 2283667308, 2042586700, 2997013224, 1214521941, + 860865914, 3204144669, 3352312876, 407025370, + 1854088521, 1518454983, 2467488963, 3014922727, + 667505194, 1382726996, 3519130642, 3473985175, + 387425140, 826920070, 3222196824, 3272392876, + 2814202151, 1958017767, 2343175108, 513394477, + 983853656, 3389045986, 177790781, 1853562919, + 1417029053, 115479138, 3505632337, 339954849, + 2552460743, 1260703764, 1719642924, 3129628830, + 1828716278, 3537151540, 4169628597, 444526909, + 4191927207, 3891278677, 1954375449, 3973495864, + 2231816005, 4062939046, 2084239497, 3470238230, + 1145760705, 2114090486, 4245867725, 1687967890, + ]; + + const expected3 = JSON.parse( + fs.readFileSync("tests/bigger-rand-output.txt"), + ); + + const given1 = Array.from( + new Array(expected1.length), + makeRandom(123456), + ); + const given2 = Array.from( + new Array(expected2.length), + makeRandom(123457), + ); + const given3 = Array.from( + new Array(expected3.length), + makeRandom(2222), + ); + + assert.deepEqual(given1, expected1); + assert.deepEqual(given2, expected2); + assert.deepEqual(given3, expected3); + }); +}; + + +await runner.runTests([ + test_makeRandom, +]); diff --git a/tests/rand.c b/tests/rand.c new file mode 100644 index 0000000..925d1ba --- /dev/null +++ b/tests/rand.c @@ -0,0 +1,83 @@ +// Taken from +// http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/MT2002/CODES/mt19937ar.c + +#define _XOPEN_SOURCE 700 + +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> + + +#define N 624U +static const uint16_t M = 397; +static const uint32_t MATRIX_A = 0x9908b0dfUL; +static const uint32_t UPPER_MASK = 0x80000000UL; +static const uint32_t LOWER_MASK = 0x7fffffffUL; + +static uint32_t mt[N]; +static uint16_t mti; +static bool initialized = false; + +static void +init(uint32_t s) { + initialized = true; + mt[0] = s & 0xffffffffUL; + for (mti = 1; mti < N; mti++) { + mt[mti] = (1812433253 * (mt[mti - 1] ^ (mt[mti - 1] >> 30))) + mti; + } +} + +/// Generates a random number on the [ 0, 0xffffffff ]-interval. +static uint32_t +generate(void) { + assert(initialized == true); + static uint32_t mag01[2] = { 0, MATRIX_A }; + + if (mti >= N) { + uint16_t kk; + + for (kk = 0; kk < N - M; kk++) { + const uint32_t y0 = + (mt[kk] & UPPER_MASK) | + (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y0 >> 1) ^ mag01[y0 & 1U]; + } + + for (; kk < N - 1; kk++) { + const uint32_t y1 = + (mt[kk] & UPPER_MASK) | + (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y1 >> 1) ^ mag01[y1 & 1U]; + } + + const uint32_t y2 = + (mt[N - 1] & UPPER_MASK) | + (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y2 >> 1) ^ mag01[y2 & 1U]; + mti = 0; + } + + uint32_t y = mt[mti++]; + + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + + +int +main(void) { + init(2222); + + for (uint16_t i = 0; i < 10000; i++) { + assert( + printf("%" PRIu32 "\n", generate()) > 0 + ); + } + + return 0; +} diff --git a/tests/rand.mjs b/tests/rand.mjs new file mode 100644 index 0000000..ba4e3b1 --- /dev/null +++ b/tests/rand.mjs @@ -0,0 +1,55 @@ +const N = 624; +const M = 397; +const MATRIX_A = 0x9908b0df; +const UPPER_MASK = 0x80000000; +const LOWER_MASK = 0x7fffffff; +const mag01 = [ 0x0, MATRIX_A ]; +const INIT_MUL = 1812433253; + +/// The given function generates random numbers on the [ 0, 0xffffffff ]-interval. +export const makeRandom = (seed = (new Date()).getTime()) => { + const mt = new Array(N); + let mti = null; + + mt[0] = seed >>> 0; + for (mti = 1; mti < N; mti++) { + const s0 = mt[mti - 1] ^ (mt[mti - 1] >>> 30); + mt[mti] = + (((((s0 & 0xffff0000) >>> 16) * INIT_MUL) << 16) + + (( s0 & 0x0000ffff) * INIT_MUL) + + mti) >>> 0; + } + + return () => { + if (mti >= N) { + let kk = null; + + for (kk = 0; kk < N - M; kk++) { + const y0 = + (mt[kk] & UPPER_MASK) | + (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y0 >>> 1) ^ mag01[y0 & 0x1]; + } + + for (; kk < N - 1; kk++) { + const y1 = + (mt[kk] & UPPER_MASK) | + (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y1 >>> 1) ^ mag01[y1 & 0x1]; + } + + const y2 = + (mt[N - 1] & UPPER_MASK) | + (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y2 >>> 1) ^ mag01[y2 & 0x1]; + mti = 0; + } + + let y = mt[mti++]; + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); + return y >>> 0; + }; +}; |