From 33052bc41cd9e635badaa3d95bf942c3e6ba2e8a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 03 Mar 2026 21:27:27 +0000 Subject: [PATCH] 🚚 Rename generator files to match generator function names (#876) Rename generator source and test files to use the actual generator name (e.g., `xoroshiro128plus`) instead of the class/module name (e.g., `XoroShiro`). - **File renames:** - `XoroShiro.ts` → `xoroshiro128plus.ts` - `XorShift.ts` → `xorshift128plus.ts` - `MersenneTwister.ts` → `mersenne.ts` - `LinearCongruential.ts` → `congruential32.ts` - Corresponding `.spec.ts` files renamed accordingly (also fixes `LinearCongruencial` typo) - **Updated references** in `package.json` exports, bench files, distribution specs, `test-bundle/run-legacy.cjs`, and `README.md` - **CHANGELOGs left untouched** per request Import paths change from: ```js import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; ``` to: ```js import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; ```
Original prompt > rename file names of generators with the name of the generator. for instance xoroshiro128plus. adapt all other files accordingly (except changelog)
--- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dubzzz <5300235+dubzzz@users.noreply.github.com> --- README.md | 12 ++++++------ package.json | 24 ++++++++++++------------ test-bundle/run-legacy.cjs | 8 ++++---- src/distribution/distribution.bench.ts | 2 +- src/distribution/uniformBigInt.noreg.spec.ts | 2 +- src/distribution/uniformBigInt.spec.ts | 2 +- src/distribution/uniformInt.noreg.spec.ts | 2 +- src/generator/LinearCongruencial.spec.ts | 139 ------------------------------------------------------------------------------------------------------------------------------------------- src/generator/LinearCongruential.ts | 52 ---------------------------------------------------- src/generator/MersenneTwister.spec.ts | 143 ----------------------------------------------------------------------------------------------------------------------------------------------- src/generator/MersenneTwister.ts | 73 ------------------------------------------------------------------------- src/generator/XorShift.spec.ts | 103 ------------------------------------------------------------------------------------------------------- src/generator/XorShift.ts | 72 ------------------------------------------------------------------------ src/generator/XoroShiro.spec.ts | 100 ---------------------------------------------------------------------------------------------------- src/generator/XoroShiro.ts | 71 ----------------------------------------------------------------------- src/generator/congruential32.spec.ts | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/congruential32.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/generator.bench.ts | 8 ++++---- src/generator/mersenne.spec.ts | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/mersenne.ts | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/xoroshiro128plus.spec.ts | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/xoroshiro128plus.ts | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/xorshift128plus.spec.ts | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator/xorshift128plus.ts | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 24 file(s) changed, 783 insertion(s)(+), 783 deletion(s)(-) diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ```javascript import { uniformInt } from 'pure-rand/distribution/uniformInt'; -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; const seed = 42; const rng = xoroshiro128plus(seed); @@ -43,7 +43,7 @@ ```javascript import { uniformIntDistribution } from 'pure-rand/distribution/UniformIntDistribution'; -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; import { purify } from 'pure-rand/utils/purify'; const uniformIntDistributionPure = purify(uniformIntDistribution); @@ -64,7 +64,7 @@ ```javascript import { uniformInt } from 'pure-rand/distribution/uniformInt'; -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; import { purify } from 'pure-rand/utils/purify'; const pureJump = purify((rng) => rng.jump()); @@ -85,7 +85,7 @@ While not recommended as non-uniform distribution implies that one or several values from the range will be more likely than others, it might be tempting for people wanting to maximize the throughput. ```javascript -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; const seed = 42; const rng = xoroshiro128plus(seed); @@ -228,7 +228,7 @@ ```js import { uniformInt } from 'pure-rand/distribution/uniformInt'; -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; function generateFloat32(rng) { const g1 = uniformInt(rng, 0, (1 << 24) - 1); @@ -249,7 +249,7 @@ ```js import { uniformInt } from 'pure-rand/distribution/uniformInt'; -import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus'; function generateFloat64(rng) { const g1 = uniformInt(rng, 0, (1 << 26) - 1); diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -13,21 +13,21 @@ "require": "./lib/distribution/uniformInt.js", "import": "./lib/esm/distribution/uniformInt.js" }, - "./generator/LinearCongruential": { - "require": "./lib/generator/LinearCongruential.js", - "import": "./lib/esm/generator/LinearCongruential.js" + "./generator/congruential32": { + "require": "./lib/generator/congruential32.js", + "import": "./lib/esm/generator/congruential32.js" }, - "./generator/MersenneTwister": { - "require": "./lib/generator/MersenneTwister.js", - "import": "./lib/esm/generator/MersenneTwister.js" + "./generator/mersenne": { + "require": "./lib/generator/mersenne.js", + "import": "./lib/esm/generator/mersenne.js" }, - "./generator/XorShift": { - "require": "./lib/generator/XorShift.js", - "import": "./lib/esm/generator/XorShift.js" + "./generator/xorshift128plus": { + "require": "./lib/generator/xorshift128plus.js", + "import": "./lib/esm/generator/xorshift128plus.js" }, - "./generator/XoroShiro": { - "require": "./lib/generator/XoroShiro.js", - "import": "./lib/esm/generator/XoroShiro.js" + "./generator/xoroshiro128plus": { + "require": "./lib/generator/xoroshiro128plus.js", + "import": "./lib/esm/generator/xoroshiro128plus.js" }, "./types/JumpableRandomGenerator": { "require": "./lib/types/JumpableRandomGenerator.js", diff --git a/test-bundle/run-legacy.cjs b/test-bundle/run-legacy.cjs --- a/test-bundle/run-legacy.cjs +++ b/test-bundle/run-legacy.cjs @@ -2,10 +2,10 @@ 'use strict'; const assert = require('assert'); -const { congruential32 } = require('pure-rand/generator/LinearCongruential'); -const { mersenne } = require('pure-rand/generator/MersenneTwister'); -const { xorshift128plus } = require('pure-rand/generator/XorShift'); -const { xoroshiro128plus } = require('pure-rand/generator/XoroShiro'); +const { congruential32 } = require('pure-rand/generator/congruential32'); +const { mersenne } = require('pure-rand/generator/mersenne'); +const { xorshift128plus } = require('pure-rand/generator/xorshift128plus'); +const { xoroshiro128plus } = require('pure-rand/generator/xoroshiro128plus'); const { uniformInt } = require('pure-rand/distribution/uniformInt'); const { uniformBigInt } = require('pure-rand/distribution/uniformBigInt'); const { generateN } = require('pure-rand/utils/generateN'); diff --git a/src/distribution/distribution.bench.ts b/src/distribution/distribution.bench.ts --- a/src/distribution/distribution.bench.ts +++ b/src/distribution/distribution.bench.ts @@ -1,5 +1,5 @@ import { describe, bench } from 'vitest'; -import { xorshift128plus } from '../generator/XorShift'; +import { xorshift128plus } from '../generator/xorshift128plus'; import type { RandomGenerator } from '../../src/types/RandomGenerator'; import { uniformInt } from './uniformInt'; import { uniformBigInt } from './uniformBigInt'; diff --git a/src/distribution/uniformBigInt.noreg.spec.ts b/src/distribution/uniformBigInt.noreg.spec.ts --- a/src/distribution/uniformBigInt.noreg.spec.ts +++ b/src/distribution/uniformBigInt.noreg.spec.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { uniformBigInt } from './uniformBigInt'; -import { mersenne } from '../generator/MersenneTwister'; +import { mersenne } from '../generator/mersenne'; import type { RandomGenerator } from '../types/RandomGenerator'; describe('uniformBigInt [non regression]', () => { diff --git a/src/distribution/uniformBigInt.spec.ts b/src/distribution/uniformBigInt.spec.ts --- a/src/distribution/uniformBigInt.spec.ts +++ b/src/distribution/uniformBigInt.spec.ts @@ -3,7 +3,7 @@ import { uniformBigInt } from './uniformBigInt'; import { uniformInt } from './uniformInt'; -import { mersenne } from '../generator/MersenneTwister'; +import { mersenne } from '../generator/mersenne'; const bigIntArbitrary = fc .tuple(fc.boolean(), fc.nat(0xffffffff), fc.nat(0xffffffff), fc.nat(0xffffffff), fc.nat(0xffffffff)) diff --git a/src/distribution/uniformInt.noreg.spec.ts b/src/distribution/uniformInt.noreg.spec.ts --- a/src/distribution/uniformInt.noreg.spec.ts +++ b/src/distribution/uniformInt.noreg.spec.ts @@ -2,7 +2,7 @@ import fc from 'fast-check'; import { uniformInt } from './uniformInt'; -import { mersenne } from '../generator/MersenneTwister'; +import { mersenne } from '../generator/mersenne'; import type { RandomGenerator } from '../types/RandomGenerator'; describe('uniformInt [non regression]', () => { diff --git a/src/generator/LinearCongruencial.spec.ts b/src/generator/LinearCongruencial.spec.ts deleted file mode 100644 --- a/src/generator/LinearCongruencial.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import * as fc from 'fast-check'; - -import { congruential32, congruential32FromState } from './LinearCongruential'; -import * as p from './RandomGenerator.properties'; - -describe('congruential32', () => { - it('Should produce the right sequence for seed=42', () => { - const g = congruential32(42); - const data = []; - for (let idx = 0; idx !== 1000; ++idx) { - const v = g.next(); - data.push(v); - } - expect(data).toEqual( - [ - 3234350541, 527020623, 250494401, 2135749886, 3453847840, 1768043920, 3865977547, 3120260103, 2378176704, - 3334114947, 2265379032, 1332836779, 2062426087, 4121359587, 3757575956, 3358259829, 3704101252, 3758918454, - 4194525353, 2542924373, 217440218, 3519482194, 1726011385, 1653505356, 1852543577, 1929301549, 2169742874, - 3308492120, 3155389883, 78406889, 3018048146, 1703776674, 1963946594, 1026705694, 1546700628, 26198758, - 3433100529, 1126200938, 15228153, 3324074273, 2746283304, 1678446390, 1885156162, 1565458049, 2985449221, - 93300426, 1035813326, 26530696, 3836336663, 778959507, 1927728407, 3293427842, 1494543140, 2781390515, - 3973999538, 2504233002, 2020420606, 3330449866, 2028210119, 2036000663, 462441890, 3300658178, 3895335118, - 4267618156, 2085001362, 57843040, 1882689931, 2582966847, 1512488819, 2017247049, 230440523, 2175087689, - 1495128042, 2066324818, 2152402783, 2853038158, 3214120370, 2159950156, 3565128665, 4213908943, 1513268225, - 2618609295, 2463426693, 1741327470, 726415645, 1192726662, 3374055210, 1981087905, 1809858617, 3470987387, - 2224202242, 658550170, 1145091476, 574674596, 849053558, 1280997493, 39522516, 1615264365, 2114931230, - 1983369379, 4087044511, 1695860230, 2671130871, 243468693, 678576763, 4071797039, 1952281574, 1206528429, - 2273207783, 3020898115, 1493897325, 3010157153, 1984823738, 941395074, 3520676523, 2831410608, 1036537019, - 3809416867, 1122322073, 268365001, 2956018304, 3015412889, 1189807491, 2049065977, 1993376138, 1528115621, - 2633511876, 3606880213, 221812641, 2211028892, 1120133829, 4128604073, 2418661485, 3355669625, 4053975863, - 1622573343, 639277654, 859977509, 1411257231, 3139406131, 660501916, 241074053, 1781298882, 4207898198, - 3421004217, 2476879300, 4060098370, 3757346784, 1152699638, 151796962, 3414653495, 2147190459, 2261518923, - 2661907581, 1819838210, 3476740941, 770453884, 253083102, 1527793390, 4156714149, 3167407473, 3759718470, - 90214712, 1799976169, 3774805204, 1097164921, 3947681602, 1044141823, 3229821168, 3883143052, 4045137170, - 971645306, 3068106441, 2693231851, 1353574928, 869593798, 3354236535, 29433773, 4283844096, 404368023, - 3996976327, 1785314074, 3192739389, 1963463822, 3407696659, 3142007421, 2882027319, 902232109, 2654923105, - 1986598, 179269166, 3659938300, 2331915963, 693044163, 3606236975, 2576316924, 289803213, 3549819872, - 1552020303, 4279196491, 3222772676, 2078609083, 2799706919, 1820778777, 2276376420, 2640727628, 2136807823, - 1391889610, 2974406251, 3616949140, 2414273182, 233843051, 2727273253, 1518664997, 1619237601, 807465050, - 4069506128, 4147221745, 1164249818, 2936140593, 4134891826, 2450190679, 866212539, 2074441459, 1270758391, - 2508785003, 1384438371, 916696185, 3324780111, 3807485060, 2139888024, 221016605, 3343090760, 523271435, - 3673738374, 3979315880, 2776041832, 3654774402, 2466592888, 514946425, 2833857550, 2889641682, 1136534740, - 1882346487, 2211740426, 246981337, 2708835420, 1145112632, 210815353, 3255094835, 2212235939, 2921887985, - 3745166566, 1253531207, 129155533, 3954878112, 1828137374, 484627605, 3710104714, 1782775288, 3738980691, - 4124351550, 2025410644, 613323661, 2457098228, 2180126420, 3169887207, 3357717695, 367309515, 2360765539, - 309526017, 796981739, 1773618647, 1490053823, 82309211, 1604077008, 3996779371, 3730931727, 1517318886, - 2523225663, 1116430855, 1297882265, 1001923402, 410388998, 3141256310, 1375700944, 1655626907, 2346934301, - 915746856, 4185388187, 635151198, 3458708498, 4102254033, 3030048998, 2522096059, 3543354289, 3088347201, - 3603303534, 2253664258, 2073741815, 2560892293, 1323242184, 2926650723, 803306297, 1218467391, 2020439219, - 2272292073, 1860923024, 3085752130, 1121688735, 3468630712, 1569261703, 2548814193, 2362714749, 193363335, - 86473479, 656659417, 2982154952, 2321737823, 1335512705, 176448524, 2808372435, 1706537109, 772728925, - 1514954944, 1772063828, 3500776453, 957725347, 2828012199, 1380516401, 4205820816, 2010351848, 3881881488, - 2603507849, 1103949271, 1548643962, 881832894, 1747992069, 157517368, 1658458574, 1897362823, 2541892705, - 732812296, 3802706216, 3927852848, 906532405, 2614232595, 3178755212, 3114253064, 1440012296, 781788752, - 3160158437, 3836755395, 1690597526, 2680465897, 129239156, 493075224, 3242194016, 2315604330, 1211208826, - 2924726429, 2870382708, 2811155783, 2668233768, 4123456225, 1316948094, 290560659, 3276037318, 1987217611, - 1194650639, 1389194238, 1915045666, 1676017969, 2578593580, 3364418320, 12588603, 4035892924, 2157310490, - 522710436, 2091086638, 2203776506, 1276732519, 3911146406, 3919927904, 453286340, 833890108, 2702090512, - 1255429612, 83177938, 165061345, 3467827234, 75735375, 2915807132, 1506427792, 3833450464, 1165599313, - 2978076019, 1739518334, 197915345, 2235491126, 4046742494, 3063289895, 3048837621, 92863013, 3565568313, - 2831665369, 1721520135, 617286103, 1017706380, 1712833667, 1376954611, 2951104388, 1325474985, 2221570239, - 3183497626, 1089516042, 2641246344, 273628188, 1084287940, 1495950943, 1378721254, 868275632, 3573434981, - 2215066937, 2774739227, 3640695773, 1393017364, 4049542650, 37677371, 2274866787, 168214984, 1421488865, - 566278001, 2574372774, 3459559380, 4156582510, 1092755607, 1176355136, 1899185956, 2378592789, 3118007576, - 3794372554, 4213141689, 1987843622, 557779722, 1352829414, 2588431714, 1035645692, 4124622471, 78773904, - 1602724796, 2255659876, 3224927307, 291110565, 3174319723, 4005091408, 2911306387, 3847589299, 1589216921, - 2599749052, 1235797044, 823567875, 1322210991, 3056734431, 3741604345, 350393316, 3259679059, 3582471061, - 1642507842, 353414201, 1033549985, 3433908492, 3985325786, 4066912837, 3940139436, 2711738285, 2025885975, - 1688191157, 2525214775, 763391053, 2284860472, 351807982, 3905889967, 2557037204, 2218913274, 3606954904, - 3516952172, 4271109804, 3097685371, 2103771498, 4246715433, 2893593645, 1668063316, 151536439, 3337618624, - 3223541320, 434800113, 1342528057, 2181498409, 2644288819, 1761549618, 1526516124, 1260259509, 75209940, - 925583431, 1450151501, 1003347078, 1955780784, 2385625800, 3594287620, 4097724241, 1874559253, 3529541139, - 1228795279, 2441320023, 2261413253, 2373269482, 1527348832, 3331254433, 2140450918, 2468478796, 2714456921, - 1841612155, 1109784552, 624286611, 1447511255, 1228350258, 1527345205, 3714046415, 4092922876, 1198916112, - 12128991, 3264791649, 3208514880, 1124736920, 3928887173, 1344579802, 2413496890, 3532080638, 1686813127, - 2487078932, 1565869739, 524579817, 3052054636, 2891285649, 2879352011, 414242923, 2068967890, 3305063188, - 3587652823, 2051727168, 3484922261, 3815846270, 354669530, 3124087110, 1782072194, 2662332394, 3249575354, - 3597569341, 23060413, 240146443, 1808696966, 379878591, 3784967217, 2497584836, 3408928511, 950692225, 79714104, - 2347027464, 1648879716, 3717351115, 415625099, 2005994740, 1405479809, 2908783413, 59256171, 3355173930, - 3359096570, 1197610659, 1371214096, 2383092848, 566509779, 3038838915, 3740809809, 3925664136, 2617952067, - 905851460, 3840671125, 428317521, 3592443066, 3647185433, 4288887289, 3953616670, 615357231, 3181117934, - 1425759426, 3220982614, 2855230127, 1766479530, 1831747076, 399901004, 2471552802, 1434151847, 713048723, - 2664984928, 3043680607, 509664879, 3994054441, 2973678934, 1831104371, 2963553975, 398645465, 1170633973, - 2066513965, 1767800480, 2941394611, 3241136355, 2099013101, 1051373356, 3833354424, 426282943, 3228685485, - 2799813886, 877602816, 240210304, 2376994639, 342614357, 563663912, 2652802316, 1771556905, 2083937099, - 242459137, 2070281244, 2861676750, 1020134729, 2767605623, 3938359631, 1612698910, 470837965, 3707723967, - 235572363, 3934289295, 4051894879, 3441732292, 1130045851, 7054525, 4081881041, 3569501906, 494314758, - 3237239856, 2076619446, 1294359427, 3690971949, 2407570540, 4256521508, 1340106474, 3912877480, 1475124286, - 2099064493, 2150561278, 57789153, 952419141, 3267030938, 2777511240, 3623834608, 4054287294, 1612064873, - 1164150479, 4126196921, 1405134740, 4198109577, 164694339, 3729502788, 1382505638, 4044019540, 2744990825, - 3623263068, 4057861211, 3644452428, 893660870, 1295902570, 4063185507, 517895066, 1965416591, 2566375314, - 1948935887, 111760835, 3570517967, 1229837838, 1988390740, 2747122092, 3682116057, 4263546827, 1762864090, - 941821904, 3245784560, 688817217, 875455051, 367013311, 4211322164, 2501498775, 2989092249, 4138450905, - 1073841104, 1005449975, 955675664, 2689038156, 2129106785, 1321533229, 1910491358, 3413203019, 3369330344, - 2029028742, 1567114464, 595455120, 2763396723, 3254604519, 2169086638, 1418403347, 2543857608, 360216876, - 1586571752, 4267226635, 1345489747, 3596094146, 2917053889, 3250338853, 3274413585, 2214620295, 3217121997, - 1052226051, 2803096381, 384227809, 2134306823, 959473399, 3708649339, 450656028, 4098400037, 3125010432, - 4106262851, 1827737097, 3301679135, 608047109, 2574062223, 2734379219, 2436649981, 2402311248, 2517925831, - 45858596, 733704928, 3827673421, 2127009772, 1319504248, 1194352979, 3438963586, 3977853443, 1313115839, - 3699589331, 1859221713, 2645044164, 406802489, 3824977819, 1160287725, 3704552835, 1141045134, 549694388, - 1402240026, 3496951646, 1224985388, 2625285437, 67982121, 2363782055, 2980556005, 285150198, 669899698, - 3272403603, 2815033033, 1581675539, 1904395666, 4118508324, 14682688, 173715006, 2621828775, 2193853779, - 1338164586, 1769742061, 2578351562, 854067585, 3792736954, 2891735270, 2024689165, 3588833568, 1865963216, - 2994521871, 1572863541, 2708559005, 2669281431, 1588384122, 3248715898, 2611223184, 1850761119, 1269326255, - 3434075773, 17673254, 3591726268, 517323323, 3301639561, 4088249037, 4257786079, 2535658874, 2751557390, - 2703727393, 2618676787, 1443127201, 1303832922, 3994038550, 3451539323, 2157076494, 2021780633, 1415260518, - 2280239530, 1992744402, 1117734927, 48835645, 3965160387, 1054673791, 2265469844, 3963024675, 1691937011, - 583910959, 2838059343, 3525835817, 2360183020, 1575842772, 1832268726, 3170442326, 2372199044, 413863110, - 1394826180, 1800168276, 895667341, 2423135369, 1245143932, 2840950358, 772335862, 3918808542, 1933875359, - 1563015244, 2541235746, 3381499405, 125782953, 3056508233, 2952473224, 4145951601, 2582705937, 3450197812, - 1292394123, 3736744772, 160350912, 2790999981, 3421194395, 956474027, 1509072849, 1500930882, 2148007820, - 1902513924, 2293102730, 509833682, 2227369781, 2329647471, 356593818, 4226078784, 2030390093, 549740954, - 1370550677, 1346393947, 1248144513, 2179322832, 1908178860, 1049390093, 367643608, 59385412, 2931004080, - 2937994435, 74506315, 1254003098, 3052302153, 823771163, 3904316064, 2846855350, 937311346, 2870708436, - 1157421959, 1328001992, 231395640, 3909870321, 4266352630, 2788053655, 3424810675, 1963437615, 3814397897, - 1673734669, 4151401028, 3379765673, 2367416980, 549499139, 4059671567, 1243931849, 2128031435, 4258607182, - 2548606395, 313731442, 200931093, 1873078793, 539686248, 3353752426, 2843138540, 1896111140, 2011416817, - 2051111181, 1892683641, 3109677612, 1854141795, 129157423, 2058977710, 4112087644, 679409340, 2321785425, - 3613600419, 2657210853, 782156775, 3265406727, 1247403317, 3015556217, 268382968, 2314427304, 3490829277, - 3942632221, 2896095652, 4248705335, 319431117, 1581168175, 3734377986, 739991954, 1452597840, 3505958581, - 2549115784, 4215439576, 3174646371, 2336959208, 2870812735, 1209575455, 4039032974, 1767176704, 336291401, - 996011350, 1372471048, 3702664001, 2010717589, 2566544503, 1966883851, 4263285829, 825244128, 988743153, - 350946420, 3978756747, 3744962799, 90256980, 978556061, 3692880960, 3442165002, 3251192638, 2292931454, - 100594764, 1754357691, 2940213637, 1822860290, 1822569888, 3692081352, 4126111909, 2548328615, 1559153086, - 2229736813, 3416000363, 1307522393, 2537287304, 1493158878, 3075976674, 3907402638, 433041229, 3014088443, - 3259785341, 1539470501, 1803261200, 518157909, 1135233158, 406515323, 3831259675, 2405854817, 1438184902, - ].map((v) => v | 0), - ); - }); - it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(congruential32))); - it('Should return the same sequence when built from state', () => - fc.assert(p.clonedFromStateSameSequences(congruential32, congruential32FromState))); - it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(congruential32))); - it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(congruential32))); - it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(congruential32))); - it('Should not impact clones when impacting itself on next', () => - fc.assert(p.noChangeOnClonedWithNext(congruential32))); -}); diff --git a/src/generator/LinearCongruential.ts b/src/generator/LinearCongruential.ts deleted file mode 100644 --- a/src/generator/LinearCongruential.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { RandomGenerator } from '../types/RandomGenerator'; - -// Inspired from java.util.Random implementation -// http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Random.java#Random.next%28int%29 -// Updated with values from: https://en.wikipedia.org/wiki/Linear_congruential_generator -const MULTIPLIER: number = 0x000343fd; -const INCREMENT: number = 0x00269ec3; -const MASK: number = 0xffffffff; -const MASK_2: number = (1 << 31) - 1; - -class LinearCongruential32 implements RandomGenerator { - constructor(private seed: number) {} - clone(): LinearCongruential32 { - return new LinearCongruential32(this.seed); - } - next(): number { - const s1 = computeNextSeed(this.seed); - const v1 = computeValueFromNextSeed(s1); - const s2 = computeNextSeed(s1); - const v2 = computeValueFromNextSeed(s2); - this.seed = computeNextSeed(s2); - const v3 = computeValueFromNextSeed(this.seed); - // value between: -0x80000000 and 0x7fffffff - // in theory it should have been: v1 & 3 instead of v1 alone - // but as binary operations truncate between -0x80000000 and 0x7fffffff in JavaScript - // we can get rid of this operation - const vnext = v3 + ((v2 + (v1 << 15)) << 15); - return vnext | 0; - } - getState(): readonly number[] { - return [this.seed]; - } -} - -function computeNextSeed(seed: number) { - return (seed * MULTIPLIER + INCREMENT) & MASK; -} -function computeValueFromNextSeed(nextseed: number) { - return (nextseed & MASK_2) >> 16; -} - -export function congruential32FromState(state: readonly number[]): RandomGenerator { - const valid = state.length === 1; - if (!valid) { - throw new Error('The state must have been produced by a congruential32 RandomGenerator'); - } - return new LinearCongruential32(state[0]); -} - -export function congruential32(seed: number): RandomGenerator { - return new LinearCongruential32(seed); -} diff --git a/src/generator/MersenneTwister.spec.ts b/src/generator/MersenneTwister.spec.ts deleted file mode 100644 --- a/src/generator/MersenneTwister.spec.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import * as fc from 'fast-check'; - -import { mersenne, mersenneFromState } from './MersenneTwister'; -import * as p from './RandomGenerator.properties'; - -describe('mersenne', () => { - it('Should produce the right sequence for seed=42', () => { - const g = mersenne(42); - const data = []; - for (let idx = 0; idx !== 1000; ++idx) { - const v = g.next(); - data.push(v); - } - // should be equivalent to the following Python code: - // from numpy.random import MT19937 - // rng = MT19937(42) - // rng._legacy_seeding(42) - // print([(rng.random_raw() & 0xffffffff) for v in range(1000)]) - expect(data).toEqual( - [ - 1608637542, 3421126067, 4083286876, 787846414, 3143890026, 3348747335, 2571218620, 2563451924, 670094950, - 1914837113, 669991378, 429389014, 249467210, 1972458954, 3720198231, 1433267572, 2581769315, 613608295, - 3041148567, 2795544706, 88409749, 242285876, 4165731073, 3100961111, 3575313899, 4031053213, 911989541, 3344769, - 780932287, 4261516219, 787716372, 2652062880, 1306710475, 2627030329, 2253811733, 30349564, 1855189739, - 99052376, 1250819632, 2253890010, 2627888186, 1717389822, 599121577, 200427519, 1254751707, 4182248123, - 1573512143, 999745294, 1958805693, 389151677, 3372305070, 2655947709, 857592370, 1642661739, 2208620086, - 4222944499, 2544401215, 2004731384, 199502978, 3693415908, 2609385266, 2921898630, 732395540, 1934879560, - 279394470, 56972561, 4075432323, 4046725720, 4147358011, 2419304461, 3472040177, 1655351289, 1308306184, - 68574553, 419498548, 991681409, 2938758483, 1035196507, 1890440558, 2934594491, 524150214, 2619915691, - 2126768636, 3578544903, 147697582, 744595490, 3905501389, 1679592528, 1111451555, 782698033, 2845511527, - 3244252547, 1338788865, 1826030589, 2233675141, 893102645, 2348102761, 2438254339, 793943861, 134489564, - 4164334270, 3617585553, 3329170137, 1931679275, 4035117217, 1697157321, 3843254205, 3979969507, 2567960845, - 3123609438, 3959419695, 1402481934, 380072391, 2450038221, 841739990, 2236966139, 194249720, 4128202429, - 1397283111, 3627245268, 1669356239, 3209715436, 1165435217, 2317960046, 3559400500, 2520077079, 1532243865, - 4145739992, 1206604539, 2607192251, 2330861947, 1185407468, 605264936, 1272485020, 3445409806, 709816108, - 320192576, 67157848, 4238647110, 1818495496, 3316766039, 1696003200, 853477355, 1260522119, 23717335, 60472382, - 3502380170, 854021618, 3035929168, 3055190407, 3131061922, 3393778082, 3312580896, 2602578298, 318019332, - 3978431977, 1539598566, 2796354553, 497653800, 3929721883, 3707000966, 3650887880, 2677045063, 1930375947, - 1421196193, 409783328, 272981039, 1592652278, 1335658902, 2872651325, 1396651735, 2860114724, 3133634658, - 2539604651, 2738288487, 1179921109, 3810549722, 2410522146, 2028147648, 1644658402, 513653348, 4173471662, - 3063363021, 3646057090, 3267546880, 3099804676, 2410667225, 1013547510, 3311278846, 1099805069, 2120835942, - 173660954, 2245120392, 3052273870, 1836274702, 476272473, 109174313, 1886935931, 463390156, 866377394, - 134987326, 3847275359, 2733361894, 2041699568, 1350148659, 2419250172, 2184294495, 2987218819, 3897968349, - 598424036, 1070701974, 2595952870, 1762581228, 2318599822, 3245067434, 872141340, 982680611, 4049525260, - 330626207, 2572107590, 1244473018, 2984078578, 692440149, 3781580571, 3993020993, 2681580201, 3470850604, - 1269737021, 2720448440, 453094388, 3742894725, 1960801051, 3451745307, 938194539, 801312299, 1788896595, - 3833511709, 3793659837, 2316457290, 1393051263, 3467929081, 524363766, 3848682843, 1530287576, 1365814502, - 3894798525, 472669408, 1168799104, 978974072, 2781807898, 1834414013, 2235000, 3513346675, 1514271692, - 3696809704, 1309025538, 29859174, 707191493, 2193642951, 2293896602, 1792766600, 2082328893, 953945764, - 2973990112, 514817842, 1157117161, 1450046123, 1048511127, 4049766350, 722804538, 1388146037, 939585183, - 2228188767, 2397029847, 3019443432, 1734463155, 1561777264, 278710077, 4173772272, 1090558393, 4133679667, - 1060324619, 1081396733, 2990604070, 2135666049, 3059178882, 1292262512, 636028516, 1223380592, 4285262775, - 158428240, 1145815738, 2618058864, 4194529281, 2158989953, 1765390555, 221099573, 141951830, 1196777444, - 1482069727, 3900972256, 2724518272, 1028910482, 2923607677, 622318721, 2280346676, 2102183595, 1923214041, - 4233336479, 2374657733, 1039619487, 2545613046, 2886800195, 347262390, 3271131338, 1587653815, 1020645498, - 1040069008, 3127665406, 3449458981, 1579616535, 2019925828, 2715732851, 4223770209, 2720989381, 1712937941, - 2301134730, 3506548222, 387791599, 3428866191, 3587596896, 647326920, 1377739899, 2182697146, 801090885, - 2988493263, 175127900, 3686622978, 2537865875, 1399982843, 2910116794, 945928099, 71244178, 3054363993, - 2199422914, 3476780542, 972791954, 1497509011, 2770996063, 413075142, 748898099, 4039516648, 2967554976, - 1707558823, 1661015679, 2223725094, 4023224657, 3597937511, 590647936, 2902066954, 1464868827, 3157729208, - 487365080, 897955761, 3971528854, 2325501342, 3768143837, 2988371241, 1107850850, 981614854, 2834609915, - 751425679, 3509942617, 4218380911, 2384569336, 2218934259, 2274831931, 1120252784, 1038747649, 4278877056, - 399873327, 4146444541, 3853512331, 2397852100, 3867266084, 3790894239, 2719150074, 810490870, 1456121864, - 1197743336, 1499843682, 3008013970, 3117955887, 3636381903, 3853059202, 3677884819, 3810007191, 1737349173, - 3349539959, 3812943520, 2757504919, 3654709875, 361378378, 4018521712, 694190023, 3373012387, 3859260837, - 2873282663, 2604592979, 2494030077, 39501026, 1598942319, 435816957, 4037842392, 2849718370, 4181854360, - 21739356, 1219431313, 690665343, 1311527789, 2356793681, 2085695169, 2971667253, 1925967010, 2800152271, - 4271162300, 963229352, 755593187, 3058786464, 77633091, 1018977056, 2121257357, 1397581076, 768037679, - 3206156179, 1573971447, 2790152063, 3196188039, 3647386758, 3096413378, 2824425872, 1323111039, 2440866848, - 2330192559, 402330059, 2185339834, 1579327346, 2733027797, 1139035510, 1075725333, 1047927533, 2533475997, - 4179048485, 4204312805, 1688341868, 2090541618, 3831310773, 3891664647, 2710719770, 1865709594, 3413688545, - 1503575316, 2158809885, 2770697824, 2477783323, 2873006958, 2115347391, 3711571424, 838562244, 988638203, - 3102908220, 2144019247, 1205908114, 2456739331, 104436258, 3300914367, 2772282416, 187276799, 760684560, - 4271561899, 4039238875, 2018396317, 4097092060, 1200702509, 3929312628, 3794577925, 1589819490, 3211427707, - 66385640, 4093412388, 3987097879, 1420561756, 1839036912, 2374107437, 4151750836, 2457977438, 4138716258, - 4210492091, 3663647712, 323609715, 1264648382, 1312958716, 1653982164, 819956639, 3655604182, 1153090720, - 1361169663, 2084261186, 727965823, 1600677905, 2391443224, 1695186921, 4020754142, 3625867829, 2989425209, - 3994391874, 2448394087, 302434989, 417369879, 897299057, 2641435935, 2882539438, 4252248917, 1540376214, - 601656257, 1091624574, 2226208922, 1268263401, 3768288641, 1385344985, 3181576976, 3645008999, 2993659808, - 586784136, 3017146154, 3044749557, 1544002735, 2374343718, 1260967388, 1273501353, 3476179702, 1802945049, - 3479410530, 1100400433, 3724047256, 2626431383, 3922338316, 350444347, 2196198905, 22268806, 2153996088, - 2696785955, 3428651692, 834400275, 2791573824, 304688903, 3014924799, 1704173549, 3417903503, 218049165, - 3822543819, 3807991666, 1451678166, 118613134, 1613116507, 2486205793, 403649345, 1883232031, 2483694294, - 2886330302, 154370901, 1409404978, 1999728280, 665898681, 2330640958, 4216974525, 1230685308, 3603191957, - 2537609529, 3695409721, 130997589, 1074821424, 160409267, 166793897, 3533042501, 1302515470, 1547007029, - 2306751466, 545720763, 1402956388, 2243017696, 3555670279, 3307097140, 1166267963, 926944235, 4145725035, - 2675294212, 1963938918, 366564547, 3616461573, 221971308, 834855903, 2282150771, 1766751581, 2322010158, - 3004382050, 2737740598, 594221991, 3118538547, 570137231, 4191252748, 4164129137, 2217493115, 3069162601, - 1387087517, 176383634, 3415298704, 1712922743, 1163215678, 1861957394, 1885367899, 3195638841, 336967606, - 1077437785, 108880612, 791707097, 4134543476, 347346749, 3590507286, 1839596670, 2989186440, 2957084555, - 1756439540, 249939584, 744293433, 3930813049, 671891968, 1899888366, 1074785057, 1029878879, 2358910589, - 403182709, 3069166127, 785403480, 2835526119, 4014136556, 1202306932, 2741351296, 4101115151, 2219193532, - 3169243110, 2822271679, 2380932542, 1871200836, 2627320597, 3135495004, 1802168566, 204939202, 1063996491, - 2431111321, 1528891023, 681381298, 3254924260, 516103253, 61819576, 1468362012, 498528205, 394274011, 197579844, - 404401198, 174928880, 1337509981, 3674175213, 4206965693, 3022187507, 753037764, 2036561099, 73706383, - 420194521, 3278625241, 2111474095, 3465664852, 2033545766, 1487365731, 743896352, 1995758845, 1863378653, - 2790756708, 1711564822, 206411513, 2645056009, 4076549877, 2727706436, 3808263267, 194579233, 1120529587, - 1608948958, 65732489, 2688047865, 4009078418, 2160953785, 2151949919, 3678595840, 2316608503, 2829067588, - 2937602028, 699798019, 2645060623, 303090455, 4053983567, 2759169782, 4055529729, 113865200, 3724591067, - 2515886975, 2733332639, 4038258138, 3440051027, 2471642775, 2908415883, 1667177137, 2462592674, 2762901883, - 551904800, 1968181152, 3484095420, 2343406258, 3524619690, 4043560548, 2688390411, 1658298178, 3523706432, - 4128282016, 2798105767, 3888451401, 887702570, 840916523, 1176654108, 297904524, 921653259, 432838238, - 1620315437, 78262145, 167345404, 405629426, 2655380209, 2933491746, 1445489366, 305752912, 2816307289, - 1369989895, 1655265884, 3628711833, 2927508425, 99952217, 1462971389, 3498115489, 1119674482, 1210557032, - 2130464646, 507514051, 2975941433, 2992463348, 1496094321, 2701288940, 4022873162, 3768713628, 168303991, - 3157106083, 1795064536, 3450924308, 4155726844, 1211329272, 2353521315, 762097033, 1818793828, 3223865800, - 2441776023, 3465328831, 2473577186, 4254187184, 3142403170, 1772179439, 548423203, 1597805521, 1073812464, - 3334668262, 2493417567, 1463740055, 3724237462, 3997572285, 2413199071, 3686854692, 1024765707, 1842515312, - 2919911094, 3224966668, 3177883926, 3240736984, 1023216482, 442913640, 1622333213, 3876435216, 2294919024, - 2170042429, 2132714067, 3549607791, 1673396952, 1374602581, 1278333324, 3846243002, 429431826, 1671608496, - 229717603, 46547339, 4116904380, 3888585960, 3638452114, 392073291, 1524306184, 1371441606, 4109428507, - 4080485069, 2906704606, 4082826623, 2072411699, 2462896978, 2117529081, 2713720174, 357703823, 1926058868, - 393866308, 1259330658, 2587464075, 1411603480, 2378136499, 2888444794, 913659348, 3231423971, 4063874612, - 3399806102, 3355640975, 3391384120, 487326758, 391727212, 3998310068, 2123519017, 4184364217, 247212966, - 4277492109, 2360208606, 239964793, 1896359044, 3165543586, 3812660438, 2344690396, 1507168490, 3031523146, - 502798993, 4160328430, 614144587, 2955066376, 3270663241, 3594732473, 2655226347, 3723172752, 434318588, - 3601247461, 361235974, 1830048941, 3010639500, 955958439, 312514729, 1703605640, 3529862065, 3830668058, - 3033287289, 629662913, 349390340, 2204719132, 364375204, 1001706917, 4237584722, 2496687751, 1607480815, - 3707151724, 1591895872, 3781117269, 3490947571, 1016555152, 4068401662, 3898514758, 4234842328, 2542142764, - 3235734659, 1504175907, 1616022597, 3041615099, 358632862, 2068743954, 3337820604, 1623444331, 2398327989, - 3028314278, 1822019674, 1068262089, 3892762457, 1418423826, 477589542, 1865956477, 2115808706, 1089556652, - 48763532, 1740329713, 2012882139, 2454457981, 241820729, 3182458515, 510319067, 3295076807, 504771388, - 3533857662, 2788337000, 3196364710, 3204238336, 2925042050, 2505549772, 1020082523, 4132499645, 1718944248, - 1610056894, 2051769851, 1227124080, 356015512, 3730604855, 2269332384, 960336805, 1874048116, 4137009285, - 3445033017, 52203051, 4200051372, 4165597855, 2388023468, 185370428, 1385927470, 3827430548, 186404943, - 2266458997, 3971312852, 4264751306, 3947571963, 316953828, 1086584483, 2378786018, 2986768018, 4163122707, - 323988920, 2246688145, 713889838, 2703246568, 931188016, 2988217869, 1264841870, 1952239006, 4277063168, - 2695341428, 2993270325, 2509610869, 1650134274, 3870444175, 3165823446, 195190712, 3930987466, 1206727691, - 4117595465, 4081986271, 248523535, 3823653831, 1694458213, 1957030859, 458513815, 2663449243, 1441649012, - 1191343111, 728769657, 807974235, 2778192547, 1991569497, 1667600007, 1517636281, 985242939, 2506783921, - 1142194715, 333867739, 1547670372, 4184993832, 1116476131, 4235742911, 1946654618, - ].map((v) => v | 0), - ); - }); - it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(mersenne))); - it('Should return the same sequence when built from state', () => - fc.assert(p.clonedFromStateSameSequences(mersenne, mersenneFromState))); - it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(mersenne))); - it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(mersenne))); - it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(mersenne))); - it('Should not impact clones when impacting itself on next', () => fc.assert(p.noChangeOnClonedWithNext(mersenne))); -}); diff --git a/src/generator/MersenneTwister.ts b/src/generator/MersenneTwister.ts deleted file mode 100644 --- a/src/generator/MersenneTwister.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { RandomGenerator } from '../types/RandomGenerator'; - -const N = 624; -const M = 397; -const R = 31; -const A = 0x9908b0df; -const F = 1812433253; -const U = 11; -const S = 7; -const B = 0x9d2c5680; -const T = 15; -const C = 0xefc60000; -const L = 18; -const MASK_LOWER = 2 ** R - 1; -const MASK_UPPER = 2 ** R; - -class MersenneTwister implements RandomGenerator { - constructor( - private states: number[], // states: between -0x80000000 and 0x7fffffff - private index: number, - ) {} - clone(): MersenneTwister { - return new MersenneTwister(this.states, this.index); - } - next(): number { - let y = this.states[this.index]; - y ^= this.states[this.index] >>> U; - y ^= (y << S) & B; - y ^= (y << T) & C; - y ^= y >>> L; - if (++this.index >= N) { - this.states = twist(this.states); - this.index = 0; - } - return y; - } - getState(): readonly number[] { - return [this.index, ...this.states]; - } -} - -function twist(prev: number[]): number[] { - const mt = prev.slice(); - for (let idx = 0; idx !== N - M; ++idx) { - const y = (mt[idx] & MASK_UPPER) + (mt[idx + 1] & MASK_LOWER); - mt[idx] = mt[idx + M] ^ (y >>> 1) ^ (-(y & 1) & A); - } - for (let idx = N - M; idx !== N - 1; ++idx) { - const y = (mt[idx] & MASK_UPPER) + (mt[idx + 1] & MASK_LOWER); - mt[idx] = mt[idx + M - N] ^ (y >>> 1) ^ (-(y & 1) & A); - } - const y = (mt[N - 1] & MASK_UPPER) + (mt[0] & MASK_LOWER); - mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ (-(y & 1) & A); - return mt; -} - -export function mersenneFromState(state: readonly number[]): RandomGenerator { - const valid = state.length === N + 1 && state[0] >= 0 && state[0] < N; - if (!valid) { - throw new Error('The state must have been produced by a mersenne RandomGenerator'); - } - return new MersenneTwister(state.slice(1), state[0]); -} - -export function mersenne(seed: number): RandomGenerator { - const out = Array(N); - out[0] = seed; - for (let idx = 1; idx !== N; ++idx) { - const xored = out[idx - 1] ^ (out[idx - 1] >>> 30); - out[idx] = (Math.imul(F, xored) + idx) | 0; - } - return new MersenneTwister(twist(out), 0); -} diff --git a/src/generator/XorShift.spec.ts b/src/generator/XorShift.spec.ts deleted file mode 100644 --- a/src/generator/XorShift.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import * as fc from 'fast-check'; - -import { xorshift128plus, xorshift128plusFromState } from './XorShift'; -import * as p from './RandomGenerator.properties'; - -describe('xorshift128plus', () => { - it('Should produce the right sequence for seed=42', () => { - const g = xorshift128plus(42); - let data = []; - for (let idx = 0; idx !== 100; ++idx) { - const v = g.next(); - data.push(v); - } - // should be equivalent to the following C code: - // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; - // uint64_t next() { - // uint64_t s1 = s[0]; - // const uint64_t s0 = s[1]; - // const uint64_t result = s0 + s1; - // s[0] = s0; - // s1 ^= s1 << 23; // a - // s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c - // return result & 0xffffffff; - // } - // int main() { - // for (int i = 0 ; i != 100 ; ++i) { std::cout << next() << ","; } - // } - expect(data).toEqual( - [ - 4294967253, 1166015114, 1692303336, 3482095935, 4288634584, 1325520545, 1367235622, 1759582253, 2328126844, - 649610899, 3328014937, 278910909, 2928761053, 1702820659, 3325106640, 2884641937, 2678880596, 999204680, - 3611521009, 2947011440, 3416747393, 1634581895, 1067867852, 88558932, 2888797634, 2105694663, 1296024496, - 2583386047, 2401573111, 2171058030, 657541993, 915947238, 696903927, 2687397535, 161811119, 793638981, - 3330697646, 532898537, 3343714389, 1441469376, 1718504920, 1003802668, 3343696598, 1851708816, 2581827611, - 1621906647, 3349035115, 264489114, 3672600296, 2277593912, 1157240243, 3243862613, 1246478095, 3690736557, - 1366822450, 1444073535, 841971999, 1179174583, 3726899152, 817046495, 509174047, 1199791094, 2405463672, - 827001813, 1820926848, 400677546, 1599444384, 3885120670, 2210955775, 2746964964, 211002306, 1381674843, - 2689051285, 1702045018, 882144076, 553855887, 2369937669, 3656191263, 1560721536, 3818918581, 86283002, - 3023862018, 1296400131, 625483410, 2364517346, 3034167893, 1805836022, 2782947729, 1110539129, 3221939945, - 2436039688, 1150739462, 3430900671, 439413983, 4238985145, 3053101980, 1358457066, 1504768706, 2433376141, - 4069088729, - ].map((v) => v | 0), - ); - }); - it('Should produce the right sequence after jump for seed=42', () => { - const g = xorshift128plus(42); - g.jump(); - let data = []; - for (let idx = 0; idx !== 100; ++idx) { - const v = g.next(); - data.push(v); - } - // should be equivalent to the following C++ code (+previous): - // void jump() { - // static const uint64_t JUMP[] = { 0x8a5cd789635d2dff, 0x121fd2155c472f96 }; - // uint64_t s0 = 0; - // uint64_t s1 = 0; - // for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) { - // for(int b = 0; b < 64; b++) { - // if (JUMP[i] & UINT64_C(1) << b) { - // s0 ^= s[0]; - // s1 ^= s[1]; - // } - // next(); - // } - // } - // s[0] = s0; - // s[1] = s1; - // } - // int main() { - // jump(); - // for (int i = 0 ; i != 100 ; ++i) { std::cout << next() << ","; } - // } - expect(data).toEqual( - [ - 2971276074, 3466165198, 456875496, 2879848137, 4162428146, 2513269982, 2277233661, 2163024882, 3082356668, - 1459960119, 3225207140, 418458707, 465389025, 33345291, 9975393, 1398264340, 2941704490, 4219353700, 1050887263, - 3537623901, 1011298813, 2886999094, 2095512742, 719748796, 2031575611, 246165700, 306697934, 932458853, - 3811330946, 2780216938, 3008525324, 1217535119, 3060075487, 3829564179, 1862997734, 3188200581, 3652713690, - 1950714292, 2865049298, 1937705104, 1297917374, 1333060788, 4089226157, 205959794, 2227024661, 3714058862, - 3728103989, 3728972300, 659396325, 3185943613, 1039549819, 2822001969, 406983436, 2343603502, 299506842, - 383551218, 698423599, 3611096673, 3762219019, 2293131106, 373955997, 2445208504, 3057049169, 1255899189, - 4215756297, 957357335, 3456668141, 1989928862, 3510600746, 3806106322, 2542824253, 3920739152, 2851853721, - 4208037803, 1276020689, 3735104409, 3925674077, 3708482212, 4262192769, 2607567703, 531897672, 3317376658, - 1903132291, 353686572, 509303103, 3991810724, 1786729004, 1709580489, 550755974, 1081340778, 1040041085, - 2723398036, 3276389190, 506083384, 2810025290, 3110824839, 3094567277, 2333614204, 3869414189, 678984428, - ].map((v) => v | 0), - ); - }); - it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(xorshift128plus))); - it('Should return the same sequence when built from state', () => - fc.assert(p.clonedFromStateSameSequences(xorshift128plus, xorshift128plusFromState))); - it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xorshift128plus))); - it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xorshift128plus))); - it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xorshift128plus))); - it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xorshift128plus))); - it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xorshift128plus))); - it('Should not impact clones when impacting itself on next', () => - fc.assert(p.noChangeOnClonedWithNext(xorshift128plus))); - it('Should not impact clones when impacting itself on jump', () => - fc.assert(p.noChangeOnClonedWithJump(xorshift128plus))); -}); diff --git a/src/generator/XorShift.ts b/src/generator/XorShift.ts deleted file mode 100644 --- a/src/generator/XorShift.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { JumpableRandomGenerator } from '../types/JumpableRandomGenerator'; - -// XorShift128+ with a=23, b=18, c=5 -// - http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf -// - http://vigna.di.unimi.it/xorshift/xorshift128plus.c -// - https://docs.rs/crate/xorshift/0.1.3/source/src/xorshift128.rs -// -// NOTE: Math.random() of V8 uses XorShift128+ with a=23, b=17, c=26, -// See https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/base/utils/random-number-generator.h#L119-L128 -class XorShift128Plus implements JumpableRandomGenerator { - constructor( - private s01: number, - private s00: number, - private s11: number, - private s10: number, - ) {} - clone(): XorShift128Plus { - return new XorShift128Plus(this.s01, this.s00, this.s11, this.s10); - } - next(): number { - const a0 = this.s00 ^ (this.s00 << 23); - const a1 = this.s01 ^ ((this.s01 << 23) | (this.s00 >>> 9)); - const b0 = a0 ^ this.s10 ^ ((a0 >>> 18) | (a1 << 14)) ^ ((this.s10 >>> 5) | (this.s11 << 27)); - const b1 = a1 ^ this.s11 ^ (a1 >>> 18) ^ (this.s11 >>> 5); - const out = (this.s00 + this.s10) | 0; - this.s01 = this.s11; - this.s00 = this.s10; - this.s11 = b1; - this.s10 = b0; - return out; - } - jump() { - // equivalent to 2^64 calls to next() - // can be used to generate 2^64 non-overlapping subsequences - let ns01 = 0; - let ns00 = 0; - let ns11 = 0; - let ns10 = 0; - const jump = [0x635d2dff, 0x8a5cd789, 0x5c472f96, 0x121fd215]; - for (let i = 0; i !== 4; ++i) { - for (let mask = 1; mask; mask <<= 1) { - // Because: (1 << 31) << 1 === 0 - if (jump[i] & mask) { - ns01 ^= this.s01; - ns00 ^= this.s00; - ns11 ^= this.s11; - ns10 ^= this.s10; - } - this.next(); - } - } - this.s01 = ns01; - this.s00 = ns00; - this.s11 = ns11; - this.s10 = ns10; - } - getState(): readonly number[] { - return [this.s01, this.s00, this.s11, this.s10]; - } -} - -export function xorshift128plusFromState(state: readonly number[]): JumpableRandomGenerator { - const valid = state.length === 4; - if (!valid) { - throw new Error('The state must have been produced by a xorshift128plus RandomGenerator'); - } - return new XorShift128Plus(state[0], state[1], state[2], state[3]); -} - -export function xorshift128plus(seed: number): JumpableRandomGenerator { - return new XorShift128Plus(-1, ~seed, seed | 0, 0); -} diff --git a/src/generator/XoroShiro.spec.ts b/src/generator/XoroShiro.spec.ts deleted file mode 100644 --- a/src/generator/XoroShiro.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import * as fc from 'fast-check'; - -import { xoroshiro128plus, xoroshiro128plusFromState } from './XoroShiro'; -import * as p from './RandomGenerator.properties'; - -describe('xoroshiro128plus', () => { - it('Should produce the right sequence for seed=42', () => { - const g = xoroshiro128plus(42); - let data = []; - for (let idx = 0; idx !== 100; ++idx) { - const v = g.next(); - data.push(v); - } - // should be equivalent to the following C code: - // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; - // uint64_t rotl(const uint64_t x, int k) { - // return (x << k) | (x >> (64 - k)); - // } - // uint64_t next() { - // const uint64_t s0 = s[0]; - // uint64_t s1 = s[1]; - // const uint64_t result = s0 + s1; - // s1 ^= s0; - // s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b - // s[1] = rotl(s1, 37); // c - // return result & 0xffffffff; - // } - expect(data).toEqual( - [ - 4294967253, 3587504873, 4286635183, 3511956468, 673719186, 1055838436, 982607204, 1805613139, 3223288787, - 1244866785, 2728956151, 371855737, 3026236645, 761656985, 3623017146, 1674769232, 1260144694, 1416578544, - 2676463084, 2327532132, 471399469, 3030140883, 3568270373, 1826979091, 469148973, 3655950307, 3683414099, - 145605805, 1297033582, 3082414981, 1426789818, 3231579470, 3488546250, 1264086088, 2948764953, 372475665, - 3412248164, 1493309586, 3927896870, 3919452640, 2709861855, 2298347239, 572622743, 2011037876, 3360650359, - 1693810876, 1171187038, 3872489959, 3989285417, 2747878152, 773928046, 4189989944, 1112534369, 4090243208, - 3154249958, 1333914584, 3040415146, 4032858677, 453868310, 825945095, 4289451331, 91466297, 1431128327, - 3208131715, 1831493458, 1461061492, 236677200, 651954392, 3509171451, 2033752905, 2253549766, 1751887713, - 4106536982, 3543831362, 2833653165, 2379144789, 2545941655, 3165371118, 300732224, 2117517824, 2796938915, - 2864151717, 1141572753, 4186463190, 1556859054, 1314617775, 4077757361, 2161308990, 3777135249, 1363575427, - 198627145, 3707137083, 4244826523, 3176117579, 881773079, 2488531002, 1345130922, 1379428837, 1687164873, - 325336063, - ].map((v) => v | 0), - ); - }); - it('Should produce the right sequence after jump for seed=42', () => { - const g = xoroshiro128plus(42); - g.jump(); - let data = []; - for (let idx = 0; idx !== 100; ++idx) { - const v = g.next(); - data.push(v); - } - // should be equivalent to the following C code (+previous): - // void jump() { - // static const uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc }; - // uint64_t s0 = 0; - // uint64_t s1 = 0; - // for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) { - // for(int b = 0; b < 64; b++) { - // if (JUMP[i] & UINT64_C(1) << b) { - // s0 ^= s[0]; - // s1 ^= s[1]; - // } - // next(); - // } - // } - // s[0] = s0; - // s[1] = s1; - // } - expect(data).toEqual( - [ - 1900530380, 2341274553, 4162717490, 2793985206, 3278912033, 1720265279, 1825471876, 3286742441, 1587050712, - 3950106747, 540536355, 991034460, 2981829782, 4159175603, 2930607761, 2509744087, 137421383, 4073225526, - 1650357769, 3278907225, 1023629082, 1415062380, 1032291476, 630729539, 1062523753, 2745179945, 1492748476, - 2700841735, 540597325, 3257104696, 2538947884, 3763735773, 3752663556, 75786448, 959579757, 794851477, - 2028874214, 802763541, 501515614, 3011491499, 3209732194, 2106362488, 573325014, 692069843, 2018337928, - 1079162215, 4086381254, 4010906511, 2073612605, 1843940750, 2647345033, 3519462589, 834294388, 1754260385, - 3973457473, 4129446855, 2052775225, 2106507442, 4178200040, 1507482091, 831962939, 4036397176, 3450323052, - 810857617, 1009339640, 544755776, 2015019841, 1590786875, 2300047967, 1153713139, 3461511701, 2255374235, - 3041282447, 3874500660, 2365439220, 3476174909, 2804287165, 3529576764, 2482037719, 3758708190, 2041488362, - 3953105597, 2604691846, 4241700961, 3231746381, 2481435019, 2100261339, 1442114730, 756514823, 316144959, - 3160143158, 2910044562, 4048037725, 1229183044, 2685549593, 173816268, 3565373219, 4220080560, 3252249431, - 2240144151, - ].map((v) => v | 0), - ); - }); - it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(xoroshiro128plus))); - it('Should return the same sequence when built from state', () => - fc.assert(p.clonedFromStateSameSequences(xoroshiro128plus, xoroshiro128plusFromState))); - it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xoroshiro128plus))); - it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xoroshiro128plus))); - it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xoroshiro128plus))); - it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xoroshiro128plus))); - it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xoroshiro128plus))); - it('Should not impact clones when impacting itself on next', () => - fc.assert(p.noChangeOnClonedWithNext(xoroshiro128plus))); - it('Should not impact clones when impacting itself on jump', () => - fc.assert(p.noChangeOnClonedWithJump(xoroshiro128plus))); -}); diff --git a/src/generator/XoroShiro.ts b/src/generator/XoroShiro.ts deleted file mode 100644 --- a/src/generator/XoroShiro.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { JumpableRandomGenerator } from '../types/JumpableRandomGenerator'; - -// XoroShiro128+ with a=24, b=16, c=37, -// - https://en.wikipedia.org/wiki/Xoroshiro128%2B -// - http://prng.di.unimi.it/xoroshiro128plus.c -class XoroShiro128Plus implements JumpableRandomGenerator { - constructor( - private s01: number, - private s00: number, - private s11: number, - private s10: number, - ) {} - clone(): XoroShiro128Plus { - return new XoroShiro128Plus(this.s01, this.s00, this.s11, this.s10); - } - next(): number { - const out = (this.s00 + this.s10) | 0; - // a = s0[n] ^ s1[n] - const a0 = this.s10 ^ this.s00; - const a1 = this.s11 ^ this.s01; - const s00 = this.s00; - const s01 = this.s01; - // s0[n+1] = rotl(s0[n], 24) ^ a ^ (a << 16) - this.s00 = (s00 << 24) ^ (s01 >>> 8) ^ a0 ^ (a0 << 16); - this.s01 = (s01 << 24) ^ (s00 >>> 8) ^ a1 ^ ((a1 << 16) | (a0 >>> 16)); - // s1[n+1] = rotl(a, 37) - this.s10 = (a1 << 5) ^ (a0 >>> 27); - this.s11 = (a0 << 5) ^ (a1 >>> 27); - return out; - } - jump(): void { - // equivalent to 2^64 calls to next() - // can be used to generate 2^64 non-overlapping subsequences - let ns01 = 0; - let ns00 = 0; - let ns11 = 0; - let ns10 = 0; - const jump = [0xd8f554a5, 0xdf900294, 0x4b3201fc, 0x170865df]; - for (let i = 0; i !== 4; ++i) { - for (let mask = 1; mask; mask <<= 1) { - // Because: (1 << 31) << 1 === 0 - if (jump[i] & mask) { - ns01 ^= this.s01; - ns00 ^= this.s00; - ns11 ^= this.s11; - ns10 ^= this.s10; - } - this.next(); - } - } - this.s01 = ns01; - this.s00 = ns00; - this.s11 = ns11; - this.s10 = ns10; - } - getState(): readonly number[] { - return [this.s01, this.s00, this.s11, this.s10]; - } -} - -export function xoroshiro128plusFromState(state: readonly number[]): JumpableRandomGenerator { - const valid = state.length === 4; - if (!valid) { - throw new Error('The state must have been produced by a xoroshiro128plus RandomGenerator'); - } - return new XoroShiro128Plus(state[0], state[1], state[2], state[3]); -} - -export function xoroshiro128plus(seed: number): JumpableRandomGenerator { - return new XoroShiro128Plus(-1, ~seed, seed | 0, 0); -} diff --git a/src/generator/congruential32.spec.ts b/src/generator/congruential32.spec.ts new file mode 100644 --- /dev/null +++ b/src/generator/congruential32.spec.ts @@ -0,0 +1,139 @@ +import { describe, it, expect } from 'vitest'; +import * as fc from 'fast-check'; + +import { congruential32, congruential32FromState } from './congruential32'; +import * as p from './RandomGenerator.properties'; + +describe('congruential32', () => { + it('Should produce the right sequence for seed=42', () => { + const g = congruential32(42); + const data = []; + for (let idx = 0; idx !== 1000; ++idx) { + const v = g.next(); + data.push(v); + } + expect(data).toEqual( + [ + 3234350541, 527020623, 250494401, 2135749886, 3453847840, 1768043920, 3865977547, 3120260103, 2378176704, + 3334114947, 2265379032, 1332836779, 2062426087, 4121359587, 3757575956, 3358259829, 3704101252, 3758918454, + 4194525353, 2542924373, 217440218, 3519482194, 1726011385, 1653505356, 1852543577, 1929301549, 2169742874, + 3308492120, 3155389883, 78406889, 3018048146, 1703776674, 1963946594, 1026705694, 1546700628, 26198758, + 3433100529, 1126200938, 15228153, 3324074273, 2746283304, 1678446390, 1885156162, 1565458049, 2985449221, + 93300426, 1035813326, 26530696, 3836336663, 778959507, 1927728407, 3293427842, 1494543140, 2781390515, + 3973999538, 2504233002, 2020420606, 3330449866, 2028210119, 2036000663, 462441890, 3300658178, 3895335118, + 4267618156, 2085001362, 57843040, 1882689931, 2582966847, 1512488819, 2017247049, 230440523, 2175087689, + 1495128042, 2066324818, 2152402783, 2853038158, 3214120370, 2159950156, 3565128665, 4213908943, 1513268225, + 2618609295, 2463426693, 1741327470, 726415645, 1192726662, 3374055210, 1981087905, 1809858617, 3470987387, + 2224202242, 658550170, 1145091476, 574674596, 849053558, 1280997493, 39522516, 1615264365, 2114931230, + 1983369379, 4087044511, 1695860230, 2671130871, 243468693, 678576763, 4071797039, 1952281574, 1206528429, + 2273207783, 3020898115, 1493897325, 3010157153, 1984823738, 941395074, 3520676523, 2831410608, 1036537019, + 3809416867, 1122322073, 268365001, 2956018304, 3015412889, 1189807491, 2049065977, 1993376138, 1528115621, + 2633511876, 3606880213, 221812641, 2211028892, 1120133829, 4128604073, 2418661485, 3355669625, 4053975863, + 1622573343, 639277654, 859977509, 1411257231, 3139406131, 660501916, 241074053, 1781298882, 4207898198, + 3421004217, 2476879300, 4060098370, 3757346784, 1152699638, 151796962, 3414653495, 2147190459, 2261518923, + 2661907581, 1819838210, 3476740941, 770453884, 253083102, 1527793390, 4156714149, 3167407473, 3759718470, + 90214712, 1799976169, 3774805204, 1097164921, 3947681602, 1044141823, 3229821168, 3883143052, 4045137170, + 971645306, 3068106441, 2693231851, 1353574928, 869593798, 3354236535, 29433773, 4283844096, 404368023, + 3996976327, 1785314074, 3192739389, 1963463822, 3407696659, 3142007421, 2882027319, 902232109, 2654923105, + 1986598, 179269166, 3659938300, 2331915963, 693044163, 3606236975, 2576316924, 289803213, 3549819872, + 1552020303, 4279196491, 3222772676, 2078609083, 2799706919, 1820778777, 2276376420, 2640727628, 2136807823, + 1391889610, 2974406251, 3616949140, 2414273182, 233843051, 2727273253, 1518664997, 1619237601, 807465050, + 4069506128, 4147221745, 1164249818, 2936140593, 4134891826, 2450190679, 866212539, 2074441459, 1270758391, + 2508785003, 1384438371, 916696185, 3324780111, 3807485060, 2139888024, 221016605, 3343090760, 523271435, + 3673738374, 3979315880, 2776041832, 3654774402, 2466592888, 514946425, 2833857550, 2889641682, 1136534740, + 1882346487, 2211740426, 246981337, 2708835420, 1145112632, 210815353, 3255094835, 2212235939, 2921887985, + 3745166566, 1253531207, 129155533, 3954878112, 1828137374, 484627605, 3710104714, 1782775288, 3738980691, + 4124351550, 2025410644, 613323661, 2457098228, 2180126420, 3169887207, 3357717695, 367309515, 2360765539, + 309526017, 796981739, 1773618647, 1490053823, 82309211, 1604077008, 3996779371, 3730931727, 1517318886, + 2523225663, 1116430855, 1297882265, 1001923402, 410388998, 3141256310, 1375700944, 1655626907, 2346934301, + 915746856, 4185388187, 635151198, 3458708498, 4102254033, 3030048998, 2522096059, 3543354289, 3088347201, + 3603303534, 2253664258, 2073741815, 2560892293, 1323242184, 2926650723, 803306297, 1218467391, 2020439219, + 2272292073, 1860923024, 3085752130, 1121688735, 3468630712, 1569261703, 2548814193, 2362714749, 193363335, + 86473479, 656659417, 2982154952, 2321737823, 1335512705, 176448524, 2808372435, 1706537109, 772728925, + 1514954944, 1772063828, 3500776453, 957725347, 2828012199, 1380516401, 4205820816, 2010351848, 3881881488, + 2603507849, 1103949271, 1548643962, 881832894, 1747992069, 157517368, 1658458574, 1897362823, 2541892705, + 732812296, 3802706216, 3927852848, 906532405, 2614232595, 3178755212, 3114253064, 1440012296, 781788752, + 3160158437, 3836755395, 1690597526, 2680465897, 129239156, 493075224, 3242194016, 2315604330, 1211208826, + 2924726429, 2870382708, 2811155783, 2668233768, 4123456225, 1316948094, 290560659, 3276037318, 1987217611, + 1194650639, 1389194238, 1915045666, 1676017969, 2578593580, 3364418320, 12588603, 4035892924, 2157310490, + 522710436, 2091086638, 2203776506, 1276732519, 3911146406, 3919927904, 453286340, 833890108, 2702090512, + 1255429612, 83177938, 165061345, 3467827234, 75735375, 2915807132, 1506427792, 3833450464, 1165599313, + 2978076019, 1739518334, 197915345, 2235491126, 4046742494, 3063289895, 3048837621, 92863013, 3565568313, + 2831665369, 1721520135, 617286103, 1017706380, 1712833667, 1376954611, 2951104388, 1325474985, 2221570239, + 3183497626, 1089516042, 2641246344, 273628188, 1084287940, 1495950943, 1378721254, 868275632, 3573434981, + 2215066937, 2774739227, 3640695773, 1393017364, 4049542650, 37677371, 2274866787, 168214984, 1421488865, + 566278001, 2574372774, 3459559380, 4156582510, 1092755607, 1176355136, 1899185956, 2378592789, 3118007576, + 3794372554, 4213141689, 1987843622, 557779722, 1352829414, 2588431714, 1035645692, 4124622471, 78773904, + 1602724796, 2255659876, 3224927307, 291110565, 3174319723, 4005091408, 2911306387, 3847589299, 1589216921, + 2599749052, 1235797044, 823567875, 1322210991, 3056734431, 3741604345, 350393316, 3259679059, 3582471061, + 1642507842, 353414201, 1033549985, 3433908492, 3985325786, 4066912837, 3940139436, 2711738285, 2025885975, + 1688191157, 2525214775, 763391053, 2284860472, 351807982, 3905889967, 2557037204, 2218913274, 3606954904, + 3516952172, 4271109804, 3097685371, 2103771498, 4246715433, 2893593645, 1668063316, 151536439, 3337618624, + 3223541320, 434800113, 1342528057, 2181498409, 2644288819, 1761549618, 1526516124, 1260259509, 75209940, + 925583431, 1450151501, 1003347078, 1955780784, 2385625800, 3594287620, 4097724241, 1874559253, 3529541139, + 1228795279, 2441320023, 2261413253, 2373269482, 1527348832, 3331254433, 2140450918, 2468478796, 2714456921, + 1841612155, 1109784552, 624286611, 1447511255, 1228350258, 1527345205, 3714046415, 4092922876, 1198916112, + 12128991, 3264791649, 3208514880, 1124736920, 3928887173, 1344579802, 2413496890, 3532080638, 1686813127, + 2487078932, 1565869739, 524579817, 3052054636, 2891285649, 2879352011, 414242923, 2068967890, 3305063188, + 3587652823, 2051727168, 3484922261, 3815846270, 354669530, 3124087110, 1782072194, 2662332394, 3249575354, + 3597569341, 23060413, 240146443, 1808696966, 379878591, 3784967217, 2497584836, 3408928511, 950692225, 79714104, + 2347027464, 1648879716, 3717351115, 415625099, 2005994740, 1405479809, 2908783413, 59256171, 3355173930, + 3359096570, 1197610659, 1371214096, 2383092848, 566509779, 3038838915, 3740809809, 3925664136, 2617952067, + 905851460, 3840671125, 428317521, 3592443066, 3647185433, 4288887289, 3953616670, 615357231, 3181117934, + 1425759426, 3220982614, 2855230127, 1766479530, 1831747076, 399901004, 2471552802, 1434151847, 713048723, + 2664984928, 3043680607, 509664879, 3994054441, 2973678934, 1831104371, 2963553975, 398645465, 1170633973, + 2066513965, 1767800480, 2941394611, 3241136355, 2099013101, 1051373356, 3833354424, 426282943, 3228685485, + 2799813886, 877602816, 240210304, 2376994639, 342614357, 563663912, 2652802316, 1771556905, 2083937099, + 242459137, 2070281244, 2861676750, 1020134729, 2767605623, 3938359631, 1612698910, 470837965, 3707723967, + 235572363, 3934289295, 4051894879, 3441732292, 1130045851, 7054525, 4081881041, 3569501906, 494314758, + 3237239856, 2076619446, 1294359427, 3690971949, 2407570540, 4256521508, 1340106474, 3912877480, 1475124286, + 2099064493, 2150561278, 57789153, 952419141, 3267030938, 2777511240, 3623834608, 4054287294, 1612064873, + 1164150479, 4126196921, 1405134740, 4198109577, 164694339, 3729502788, 1382505638, 4044019540, 2744990825, + 3623263068, 4057861211, 3644452428, 893660870, 1295902570, 4063185507, 517895066, 1965416591, 2566375314, + 1948935887, 111760835, 3570517967, 1229837838, 1988390740, 2747122092, 3682116057, 4263546827, 1762864090, + 941821904, 3245784560, 688817217, 875455051, 367013311, 4211322164, 2501498775, 2989092249, 4138450905, + 1073841104, 1005449975, 955675664, 2689038156, 2129106785, 1321533229, 1910491358, 3413203019, 3369330344, + 2029028742, 1567114464, 595455120, 2763396723, 3254604519, 2169086638, 1418403347, 2543857608, 360216876, + 1586571752, 4267226635, 1345489747, 3596094146, 2917053889, 3250338853, 3274413585, 2214620295, 3217121997, + 1052226051, 2803096381, 384227809, 2134306823, 959473399, 3708649339, 450656028, 4098400037, 3125010432, + 4106262851, 1827737097, 3301679135, 608047109, 2574062223, 2734379219, 2436649981, 2402311248, 2517925831, + 45858596, 733704928, 3827673421, 2127009772, 1319504248, 1194352979, 3438963586, 3977853443, 1313115839, + 3699589331, 1859221713, 2645044164, 406802489, 3824977819, 1160287725, 3704552835, 1141045134, 549694388, + 1402240026, 3496951646, 1224985388, 2625285437, 67982121, 2363782055, 2980556005, 285150198, 669899698, + 3272403603, 2815033033, 1581675539, 1904395666, 4118508324, 14682688, 173715006, 2621828775, 2193853779, + 1338164586, 1769742061, 2578351562, 854067585, 3792736954, 2891735270, 2024689165, 3588833568, 1865963216, + 2994521871, 1572863541, 2708559005, 2669281431, 1588384122, 3248715898, 2611223184, 1850761119, 1269326255, + 3434075773, 17673254, 3591726268, 517323323, 3301639561, 4088249037, 4257786079, 2535658874, 2751557390, + 2703727393, 2618676787, 1443127201, 1303832922, 3994038550, 3451539323, 2157076494, 2021780633, 1415260518, + 2280239530, 1992744402, 1117734927, 48835645, 3965160387, 1054673791, 2265469844, 3963024675, 1691937011, + 583910959, 2838059343, 3525835817, 2360183020, 1575842772, 1832268726, 3170442326, 2372199044, 413863110, + 1394826180, 1800168276, 895667341, 2423135369, 1245143932, 2840950358, 772335862, 3918808542, 1933875359, + 1563015244, 2541235746, 3381499405, 125782953, 3056508233, 2952473224, 4145951601, 2582705937, 3450197812, + 1292394123, 3736744772, 160350912, 2790999981, 3421194395, 956474027, 1509072849, 1500930882, 2148007820, + 1902513924, 2293102730, 509833682, 2227369781, 2329647471, 356593818, 4226078784, 2030390093, 549740954, + 1370550677, 1346393947, 1248144513, 2179322832, 1908178860, 1049390093, 367643608, 59385412, 2931004080, + 2937994435, 74506315, 1254003098, 3052302153, 823771163, 3904316064, 2846855350, 937311346, 2870708436, + 1157421959, 1328001992, 231395640, 3909870321, 4266352630, 2788053655, 3424810675, 1963437615, 3814397897, + 1673734669, 4151401028, 3379765673, 2367416980, 549499139, 4059671567, 1243931849, 2128031435, 4258607182, + 2548606395, 313731442, 200931093, 1873078793, 539686248, 3353752426, 2843138540, 1896111140, 2011416817, + 2051111181, 1892683641, 3109677612, 1854141795, 129157423, 2058977710, 4112087644, 679409340, 2321785425, + 3613600419, 2657210853, 782156775, 3265406727, 1247403317, 3015556217, 268382968, 2314427304, 3490829277, + 3942632221, 2896095652, 4248705335, 319431117, 1581168175, 3734377986, 739991954, 1452597840, 3505958581, + 2549115784, 4215439576, 3174646371, 2336959208, 2870812735, 1209575455, 4039032974, 1767176704, 336291401, + 996011350, 1372471048, 3702664001, 2010717589, 2566544503, 1966883851, 4263285829, 825244128, 988743153, + 350946420, 3978756747, 3744962799, 90256980, 978556061, 3692880960, 3442165002, 3251192638, 2292931454, + 100594764, 1754357691, 2940213637, 1822860290, 1822569888, 3692081352, 4126111909, 2548328615, 1559153086, + 2229736813, 3416000363, 1307522393, 2537287304, 1493158878, 3075976674, 3907402638, 433041229, 3014088443, + 3259785341, 1539470501, 1803261200, 518157909, 1135233158, 406515323, 3831259675, 2405854817, 1438184902, + ].map((v) => v | 0), + ); + }); + it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(congruential32))); + it('Should return the same sequence when built from state', () => + fc.assert(p.clonedFromStateSameSequences(congruential32, congruential32FromState))); + it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(congruential32))); + it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(congruential32))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(congruential32))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(congruential32))); +}); diff --git a/src/generator/congruential32.ts b/src/generator/congruential32.ts new file mode 100644 --- /dev/null +++ b/src/generator/congruential32.ts @@ -0,0 +1,52 @@ +import type { RandomGenerator } from '../types/RandomGenerator'; + +// Inspired from java.util.Random implementation +// http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Random.java#Random.next%28int%29 +// Updated with values from: https://en.wikipedia.org/wiki/Linear_congruential_generator +const MULTIPLIER: number = 0x000343fd; +const INCREMENT: number = 0x00269ec3; +const MASK: number = 0xffffffff; +const MASK_2: number = (1 << 31) - 1; + +class LinearCongruential32 implements RandomGenerator { + constructor(private seed: number) {} + clone(): LinearCongruential32 { + return new LinearCongruential32(this.seed); + } + next(): number { + const s1 = computeNextSeed(this.seed); + const v1 = computeValueFromNextSeed(s1); + const s2 = computeNextSeed(s1); + const v2 = computeValueFromNextSeed(s2); + this.seed = computeNextSeed(s2); + const v3 = computeValueFromNextSeed(this.seed); + // value between: -0x80000000 and 0x7fffffff + // in theory it should have been: v1 & 3 instead of v1 alone + // but as binary operations truncate between -0x80000000 and 0x7fffffff in JavaScript + // we can get rid of this operation + const vnext = v3 + ((v2 + (v1 << 15)) << 15); + return vnext | 0; + } + getState(): readonly number[] { + return [this.seed]; + } +} + +function computeNextSeed(seed: number) { + return (seed * MULTIPLIER + INCREMENT) & MASK; +} +function computeValueFromNextSeed(nextseed: number) { + return (nextseed & MASK_2) >> 16; +} + +export function congruential32FromState(state: readonly number[]): RandomGenerator { + const valid = state.length === 1; + if (!valid) { + throw new Error('The state must have been produced by a congruential32 RandomGenerator'); + } + return new LinearCongruential32(state[0]); +} + +export function congruential32(seed: number): RandomGenerator { + return new LinearCongruential32(seed); +} diff --git a/src/generator/generator.bench.ts b/src/generator/generator.bench.ts --- a/src/generator/generator.bench.ts +++ b/src/generator/generator.bench.ts @@ -1,8 +1,8 @@ import { describe, bench } from 'vitest'; -import { xoroshiro128plus } from './XoroShiro'; -import { congruential32 } from './LinearCongruential'; -import { mersenne } from './MersenneTwister'; -import { xorshift128plus } from './XorShift'; +import { xoroshiro128plus } from './xoroshiro128plus'; +import { congruential32 } from './congruential32'; +import { mersenne } from './mersenne'; +import { xorshift128plus } from './xorshift128plus'; import type { JumpableRandomGenerator } from '../types/JumpableRandomGenerator'; const numInts = 5_000; diff --git a/src/generator/mersenne.spec.ts b/src/generator/mersenne.spec.ts new file mode 100644 --- /dev/null +++ b/src/generator/mersenne.spec.ts @@ -0,0 +1,143 @@ +import { describe, it, expect } from 'vitest'; +import * as fc from 'fast-check'; + +import { mersenne, mersenneFromState } from './mersenne'; +import * as p from './RandomGenerator.properties'; + +describe('mersenne', () => { + it('Should produce the right sequence for seed=42', () => { + const g = mersenne(42); + const data = []; + for (let idx = 0; idx !== 1000; ++idx) { + const v = g.next(); + data.push(v); + } + // should be equivalent to the following Python code: + // from numpy.random import MT19937 + // rng = MT19937(42) + // rng._legacy_seeding(42) + // print([(rng.random_raw() & 0xffffffff) for v in range(1000)]) + expect(data).toEqual( + [ + 1608637542, 3421126067, 4083286876, 787846414, 3143890026, 3348747335, 2571218620, 2563451924, 670094950, + 1914837113, 669991378, 429389014, 249467210, 1972458954, 3720198231, 1433267572, 2581769315, 613608295, + 3041148567, 2795544706, 88409749, 242285876, 4165731073, 3100961111, 3575313899, 4031053213, 911989541, 3344769, + 780932287, 4261516219, 787716372, 2652062880, 1306710475, 2627030329, 2253811733, 30349564, 1855189739, + 99052376, 1250819632, 2253890010, 2627888186, 1717389822, 599121577, 200427519, 1254751707, 4182248123, + 1573512143, 999745294, 1958805693, 389151677, 3372305070, 2655947709, 857592370, 1642661739, 2208620086, + 4222944499, 2544401215, 2004731384, 199502978, 3693415908, 2609385266, 2921898630, 732395540, 1934879560, + 279394470, 56972561, 4075432323, 4046725720, 4147358011, 2419304461, 3472040177, 1655351289, 1308306184, + 68574553, 419498548, 991681409, 2938758483, 1035196507, 1890440558, 2934594491, 524150214, 2619915691, + 2126768636, 3578544903, 147697582, 744595490, 3905501389, 1679592528, 1111451555, 782698033, 2845511527, + 3244252547, 1338788865, 1826030589, 2233675141, 893102645, 2348102761, 2438254339, 793943861, 134489564, + 4164334270, 3617585553, 3329170137, 1931679275, 4035117217, 1697157321, 3843254205, 3979969507, 2567960845, + 3123609438, 3959419695, 1402481934, 380072391, 2450038221, 841739990, 2236966139, 194249720, 4128202429, + 1397283111, 3627245268, 1669356239, 3209715436, 1165435217, 2317960046, 3559400500, 2520077079, 1532243865, + 4145739992, 1206604539, 2607192251, 2330861947, 1185407468, 605264936, 1272485020, 3445409806, 709816108, + 320192576, 67157848, 4238647110, 1818495496, 3316766039, 1696003200, 853477355, 1260522119, 23717335, 60472382, + 3502380170, 854021618, 3035929168, 3055190407, 3131061922, 3393778082, 3312580896, 2602578298, 318019332, + 3978431977, 1539598566, 2796354553, 497653800, 3929721883, 3707000966, 3650887880, 2677045063, 1930375947, + 1421196193, 409783328, 272981039, 1592652278, 1335658902, 2872651325, 1396651735, 2860114724, 3133634658, + 2539604651, 2738288487, 1179921109, 3810549722, 2410522146, 2028147648, 1644658402, 513653348, 4173471662, + 3063363021, 3646057090, 3267546880, 3099804676, 2410667225, 1013547510, 3311278846, 1099805069, 2120835942, + 173660954, 2245120392, 3052273870, 1836274702, 476272473, 109174313, 1886935931, 463390156, 866377394, + 134987326, 3847275359, 2733361894, 2041699568, 1350148659, 2419250172, 2184294495, 2987218819, 3897968349, + 598424036, 1070701974, 2595952870, 1762581228, 2318599822, 3245067434, 872141340, 982680611, 4049525260, + 330626207, 2572107590, 1244473018, 2984078578, 692440149, 3781580571, 3993020993, 2681580201, 3470850604, + 1269737021, 2720448440, 453094388, 3742894725, 1960801051, 3451745307, 938194539, 801312299, 1788896595, + 3833511709, 3793659837, 2316457290, 1393051263, 3467929081, 524363766, 3848682843, 1530287576, 1365814502, + 3894798525, 472669408, 1168799104, 978974072, 2781807898, 1834414013, 2235000, 3513346675, 1514271692, + 3696809704, 1309025538, 29859174, 707191493, 2193642951, 2293896602, 1792766600, 2082328893, 953945764, + 2973990112, 514817842, 1157117161, 1450046123, 1048511127, 4049766350, 722804538, 1388146037, 939585183, + 2228188767, 2397029847, 3019443432, 1734463155, 1561777264, 278710077, 4173772272, 1090558393, 4133679667, + 1060324619, 1081396733, 2990604070, 2135666049, 3059178882, 1292262512, 636028516, 1223380592, 4285262775, + 158428240, 1145815738, 2618058864, 4194529281, 2158989953, 1765390555, 221099573, 141951830, 1196777444, + 1482069727, 3900972256, 2724518272, 1028910482, 2923607677, 622318721, 2280346676, 2102183595, 1923214041, + 4233336479, 2374657733, 1039619487, 2545613046, 2886800195, 347262390, 3271131338, 1587653815, 1020645498, + 1040069008, 3127665406, 3449458981, 1579616535, 2019925828, 2715732851, 4223770209, 2720989381, 1712937941, + 2301134730, 3506548222, 387791599, 3428866191, 3587596896, 647326920, 1377739899, 2182697146, 801090885, + 2988493263, 175127900, 3686622978, 2537865875, 1399982843, 2910116794, 945928099, 71244178, 3054363993, + 2199422914, 3476780542, 972791954, 1497509011, 2770996063, 413075142, 748898099, 4039516648, 2967554976, + 1707558823, 1661015679, 2223725094, 4023224657, 3597937511, 590647936, 2902066954, 1464868827, 3157729208, + 487365080, 897955761, 3971528854, 2325501342, 3768143837, 2988371241, 1107850850, 981614854, 2834609915, + 751425679, 3509942617, 4218380911, 2384569336, 2218934259, 2274831931, 1120252784, 1038747649, 4278877056, + 399873327, 4146444541, 3853512331, 2397852100, 3867266084, 3790894239, 2719150074, 810490870, 1456121864, + 1197743336, 1499843682, 3008013970, 3117955887, 3636381903, 3853059202, 3677884819, 3810007191, 1737349173, + 3349539959, 3812943520, 2757504919, 3654709875, 361378378, 4018521712, 694190023, 3373012387, 3859260837, + 2873282663, 2604592979, 2494030077, 39501026, 1598942319, 435816957, 4037842392, 2849718370, 4181854360, + 21739356, 1219431313, 690665343, 1311527789, 2356793681, 2085695169, 2971667253, 1925967010, 2800152271, + 4271162300, 963229352, 755593187, 3058786464, 77633091, 1018977056, 2121257357, 1397581076, 768037679, + 3206156179, 1573971447, 2790152063, 3196188039, 3647386758, 3096413378, 2824425872, 1323111039, 2440866848, + 2330192559, 402330059, 2185339834, 1579327346, 2733027797, 1139035510, 1075725333, 1047927533, 2533475997, + 4179048485, 4204312805, 1688341868, 2090541618, 3831310773, 3891664647, 2710719770, 1865709594, 3413688545, + 1503575316, 2158809885, 2770697824, 2477783323, 2873006958, 2115347391, 3711571424, 838562244, 988638203, + 3102908220, 2144019247, 1205908114, 2456739331, 104436258, 3300914367, 2772282416, 187276799, 760684560, + 4271561899, 4039238875, 2018396317, 4097092060, 1200702509, 3929312628, 3794577925, 1589819490, 3211427707, + 66385640, 4093412388, 3987097879, 1420561756, 1839036912, 2374107437, 4151750836, 2457977438, 4138716258, + 4210492091, 3663647712, 323609715, 1264648382, 1312958716, 1653982164, 819956639, 3655604182, 1153090720, + 1361169663, 2084261186, 727965823, 1600677905, 2391443224, 1695186921, 4020754142, 3625867829, 2989425209, + 3994391874, 2448394087, 302434989, 417369879, 897299057, 2641435935, 2882539438, 4252248917, 1540376214, + 601656257, 1091624574, 2226208922, 1268263401, 3768288641, 1385344985, 3181576976, 3645008999, 2993659808, + 586784136, 3017146154, 3044749557, 1544002735, 2374343718, 1260967388, 1273501353, 3476179702, 1802945049, + 3479410530, 1100400433, 3724047256, 2626431383, 3922338316, 350444347, 2196198905, 22268806, 2153996088, + 2696785955, 3428651692, 834400275, 2791573824, 304688903, 3014924799, 1704173549, 3417903503, 218049165, + 3822543819, 3807991666, 1451678166, 118613134, 1613116507, 2486205793, 403649345, 1883232031, 2483694294, + 2886330302, 154370901, 1409404978, 1999728280, 665898681, 2330640958, 4216974525, 1230685308, 3603191957, + 2537609529, 3695409721, 130997589, 1074821424, 160409267, 166793897, 3533042501, 1302515470, 1547007029, + 2306751466, 545720763, 1402956388, 2243017696, 3555670279, 3307097140, 1166267963, 926944235, 4145725035, + 2675294212, 1963938918, 366564547, 3616461573, 221971308, 834855903, 2282150771, 1766751581, 2322010158, + 3004382050, 2737740598, 594221991, 3118538547, 570137231, 4191252748, 4164129137, 2217493115, 3069162601, + 1387087517, 176383634, 3415298704, 1712922743, 1163215678, 1861957394, 1885367899, 3195638841, 336967606, + 1077437785, 108880612, 791707097, 4134543476, 347346749, 3590507286, 1839596670, 2989186440, 2957084555, + 1756439540, 249939584, 744293433, 3930813049, 671891968, 1899888366, 1074785057, 1029878879, 2358910589, + 403182709, 3069166127, 785403480, 2835526119, 4014136556, 1202306932, 2741351296, 4101115151, 2219193532, + 3169243110, 2822271679, 2380932542, 1871200836, 2627320597, 3135495004, 1802168566, 204939202, 1063996491, + 2431111321, 1528891023, 681381298, 3254924260, 516103253, 61819576, 1468362012, 498528205, 394274011, 197579844, + 404401198, 174928880, 1337509981, 3674175213, 4206965693, 3022187507, 753037764, 2036561099, 73706383, + 420194521, 3278625241, 2111474095, 3465664852, 2033545766, 1487365731, 743896352, 1995758845, 1863378653, + 2790756708, 1711564822, 206411513, 2645056009, 4076549877, 2727706436, 3808263267, 194579233, 1120529587, + 1608948958, 65732489, 2688047865, 4009078418, 2160953785, 2151949919, 3678595840, 2316608503, 2829067588, + 2937602028, 699798019, 2645060623, 303090455, 4053983567, 2759169782, 4055529729, 113865200, 3724591067, + 2515886975, 2733332639, 4038258138, 3440051027, 2471642775, 2908415883, 1667177137, 2462592674, 2762901883, + 551904800, 1968181152, 3484095420, 2343406258, 3524619690, 4043560548, 2688390411, 1658298178, 3523706432, + 4128282016, 2798105767, 3888451401, 887702570, 840916523, 1176654108, 297904524, 921653259, 432838238, + 1620315437, 78262145, 167345404, 405629426, 2655380209, 2933491746, 1445489366, 305752912, 2816307289, + 1369989895, 1655265884, 3628711833, 2927508425, 99952217, 1462971389, 3498115489, 1119674482, 1210557032, + 2130464646, 507514051, 2975941433, 2992463348, 1496094321, 2701288940, 4022873162, 3768713628, 168303991, + 3157106083, 1795064536, 3450924308, 4155726844, 1211329272, 2353521315, 762097033, 1818793828, 3223865800, + 2441776023, 3465328831, 2473577186, 4254187184, 3142403170, 1772179439, 548423203, 1597805521, 1073812464, + 3334668262, 2493417567, 1463740055, 3724237462, 3997572285, 2413199071, 3686854692, 1024765707, 1842515312, + 2919911094, 3224966668, 3177883926, 3240736984, 1023216482, 442913640, 1622333213, 3876435216, 2294919024, + 2170042429, 2132714067, 3549607791, 1673396952, 1374602581, 1278333324, 3846243002, 429431826, 1671608496, + 229717603, 46547339, 4116904380, 3888585960, 3638452114, 392073291, 1524306184, 1371441606, 4109428507, + 4080485069, 2906704606, 4082826623, 2072411699, 2462896978, 2117529081, 2713720174, 357703823, 1926058868, + 393866308, 1259330658, 2587464075, 1411603480, 2378136499, 2888444794, 913659348, 3231423971, 4063874612, + 3399806102, 3355640975, 3391384120, 487326758, 391727212, 3998310068, 2123519017, 4184364217, 247212966, + 4277492109, 2360208606, 239964793, 1896359044, 3165543586, 3812660438, 2344690396, 1507168490, 3031523146, + 502798993, 4160328430, 614144587, 2955066376, 3270663241, 3594732473, 2655226347, 3723172752, 434318588, + 3601247461, 361235974, 1830048941, 3010639500, 955958439, 312514729, 1703605640, 3529862065, 3830668058, + 3033287289, 629662913, 349390340, 2204719132, 364375204, 1001706917, 4237584722, 2496687751, 1607480815, + 3707151724, 1591895872, 3781117269, 3490947571, 1016555152, 4068401662, 3898514758, 4234842328, 2542142764, + 3235734659, 1504175907, 1616022597, 3041615099, 358632862, 2068743954, 3337820604, 1623444331, 2398327989, + 3028314278, 1822019674, 1068262089, 3892762457, 1418423826, 477589542, 1865956477, 2115808706, 1089556652, + 48763532, 1740329713, 2012882139, 2454457981, 241820729, 3182458515, 510319067, 3295076807, 504771388, + 3533857662, 2788337000, 3196364710, 3204238336, 2925042050, 2505549772, 1020082523, 4132499645, 1718944248, + 1610056894, 2051769851, 1227124080, 356015512, 3730604855, 2269332384, 960336805, 1874048116, 4137009285, + 3445033017, 52203051, 4200051372, 4165597855, 2388023468, 185370428, 1385927470, 3827430548, 186404943, + 2266458997, 3971312852, 4264751306, 3947571963, 316953828, 1086584483, 2378786018, 2986768018, 4163122707, + 323988920, 2246688145, 713889838, 2703246568, 931188016, 2988217869, 1264841870, 1952239006, 4277063168, + 2695341428, 2993270325, 2509610869, 1650134274, 3870444175, 3165823446, 195190712, 3930987466, 1206727691, + 4117595465, 4081986271, 248523535, 3823653831, 1694458213, 1957030859, 458513815, 2663449243, 1441649012, + 1191343111, 728769657, 807974235, 2778192547, 1991569497, 1667600007, 1517636281, 985242939, 2506783921, + 1142194715, 333867739, 1547670372, 4184993832, 1116476131, 4235742911, 1946654618, + ].map((v) => v | 0), + ); + }); + it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(mersenne))); + it('Should return the same sequence when built from state', () => + fc.assert(p.clonedFromStateSameSequences(mersenne, mersenneFromState))); + it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(mersenne))); + it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(mersenne))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(mersenne))); + it('Should not impact clones when impacting itself on next', () => fc.assert(p.noChangeOnClonedWithNext(mersenne))); +}); diff --git a/src/generator/mersenne.ts b/src/generator/mersenne.ts new file mode 100644 --- /dev/null +++ b/src/generator/mersenne.ts @@ -0,0 +1,73 @@ +import type { RandomGenerator } from '../types/RandomGenerator'; + +const N = 624; +const M = 397; +const R = 31; +const A = 0x9908b0df; +const F = 1812433253; +const U = 11; +const S = 7; +const B = 0x9d2c5680; +const T = 15; +const C = 0xefc60000; +const L = 18; +const MASK_LOWER = 2 ** R - 1; +const MASK_UPPER = 2 ** R; + +class MersenneTwister implements RandomGenerator { + constructor( + private states: number[], // states: between -0x80000000 and 0x7fffffff + private index: number, + ) {} + clone(): MersenneTwister { + return new MersenneTwister(this.states, this.index); + } + next(): number { + let y = this.states[this.index]; + y ^= this.states[this.index] >>> U; + y ^= (y << S) & B; + y ^= (y << T) & C; + y ^= y >>> L; + if (++this.index >= N) { + this.states = twist(this.states); + this.index = 0; + } + return y; + } + getState(): readonly number[] { + return [this.index, ...this.states]; + } +} + +function twist(prev: number[]): number[] { + const mt = prev.slice(); + for (let idx = 0; idx !== N - M; ++idx) { + const y = (mt[idx] & MASK_UPPER) + (mt[idx + 1] & MASK_LOWER); + mt[idx] = mt[idx + M] ^ (y >>> 1) ^ (-(y & 1) & A); + } + for (let idx = N - M; idx !== N - 1; ++idx) { + const y = (mt[idx] & MASK_UPPER) + (mt[idx + 1] & MASK_LOWER); + mt[idx] = mt[idx + M - N] ^ (y >>> 1) ^ (-(y & 1) & A); + } + const y = (mt[N - 1] & MASK_UPPER) + (mt[0] & MASK_LOWER); + mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ (-(y & 1) & A); + return mt; +} + +export function mersenneFromState(state: readonly number[]): RandomGenerator { + const valid = state.length === N + 1 && state[0] >= 0 && state[0] < N; + if (!valid) { + throw new Error('The state must have been produced by a mersenne RandomGenerator'); + } + return new MersenneTwister(state.slice(1), state[0]); +} + +export function mersenne(seed: number): RandomGenerator { + const out = Array(N); + out[0] = seed; + for (let idx = 1; idx !== N; ++idx) { + const xored = out[idx - 1] ^ (out[idx - 1] >>> 30); + out[idx] = (Math.imul(F, xored) + idx) | 0; + } + return new MersenneTwister(twist(out), 0); +} diff --git a/src/generator/xoroshiro128plus.spec.ts b/src/generator/xoroshiro128plus.spec.ts new file mode 100644 --- /dev/null +++ b/src/generator/xoroshiro128plus.spec.ts @@ -0,0 +1,100 @@ +import { describe, it, expect } from 'vitest'; +import * as fc from 'fast-check'; + +import { xoroshiro128plus, xoroshiro128plusFromState } from './xoroshiro128plus'; +import * as p from './RandomGenerator.properties'; + +describe('xoroshiro128plus', () => { + it('Should produce the right sequence for seed=42', () => { + const g = xoroshiro128plus(42); + let data = []; + for (let idx = 0; idx !== 100; ++idx) { + const v = g.next(); + data.push(v); + } + // should be equivalent to the following C code: + // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; + // uint64_t rotl(const uint64_t x, int k) { + // return (x << k) | (x >> (64 - k)); + // } + // uint64_t next() { + // const uint64_t s0 = s[0]; + // uint64_t s1 = s[1]; + // const uint64_t result = s0 + s1; + // s1 ^= s0; + // s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b + // s[1] = rotl(s1, 37); // c + // return result & 0xffffffff; + // } + expect(data).toEqual( + [ + 4294967253, 3587504873, 4286635183, 3511956468, 673719186, 1055838436, 982607204, 1805613139, 3223288787, + 1244866785, 2728956151, 371855737, 3026236645, 761656985, 3623017146, 1674769232, 1260144694, 1416578544, + 2676463084, 2327532132, 471399469, 3030140883, 3568270373, 1826979091, 469148973, 3655950307, 3683414099, + 145605805, 1297033582, 3082414981, 1426789818, 3231579470, 3488546250, 1264086088, 2948764953, 372475665, + 3412248164, 1493309586, 3927896870, 3919452640, 2709861855, 2298347239, 572622743, 2011037876, 3360650359, + 1693810876, 1171187038, 3872489959, 3989285417, 2747878152, 773928046, 4189989944, 1112534369, 4090243208, + 3154249958, 1333914584, 3040415146, 4032858677, 453868310, 825945095, 4289451331, 91466297, 1431128327, + 3208131715, 1831493458, 1461061492, 236677200, 651954392, 3509171451, 2033752905, 2253549766, 1751887713, + 4106536982, 3543831362, 2833653165, 2379144789, 2545941655, 3165371118, 300732224, 2117517824, 2796938915, + 2864151717, 1141572753, 4186463190, 1556859054, 1314617775, 4077757361, 2161308990, 3777135249, 1363575427, + 198627145, 3707137083, 4244826523, 3176117579, 881773079, 2488531002, 1345130922, 1379428837, 1687164873, + 325336063, + ].map((v) => v | 0), + ); + }); + it('Should produce the right sequence after jump for seed=42', () => { + const g = xoroshiro128plus(42); + g.jump(); + let data = []; + for (let idx = 0; idx !== 100; ++idx) { + const v = g.next(); + data.push(v); + } + // should be equivalent to the following C code (+previous): + // void jump() { + // static const uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc }; + // uint64_t s0 = 0; + // uint64_t s1 = 0; + // for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) { + // for(int b = 0; b < 64; b++) { + // if (JUMP[i] & UINT64_C(1) << b) { + // s0 ^= s[0]; + // s1 ^= s[1]; + // } + // next(); + // } + // } + // s[0] = s0; + // s[1] = s1; + // } + expect(data).toEqual( + [ + 1900530380, 2341274553, 4162717490, 2793985206, 3278912033, 1720265279, 1825471876, 3286742441, 1587050712, + 3950106747, 540536355, 991034460, 2981829782, 4159175603, 2930607761, 2509744087, 137421383, 4073225526, + 1650357769, 3278907225, 1023629082, 1415062380, 1032291476, 630729539, 1062523753, 2745179945, 1492748476, + 2700841735, 540597325, 3257104696, 2538947884, 3763735773, 3752663556, 75786448, 959579757, 794851477, + 2028874214, 802763541, 501515614, 3011491499, 3209732194, 2106362488, 573325014, 692069843, 2018337928, + 1079162215, 4086381254, 4010906511, 2073612605, 1843940750, 2647345033, 3519462589, 834294388, 1754260385, + 3973457473, 4129446855, 2052775225, 2106507442, 4178200040, 1507482091, 831962939, 4036397176, 3450323052, + 810857617, 1009339640, 544755776, 2015019841, 1590786875, 2300047967, 1153713139, 3461511701, 2255374235, + 3041282447, 3874500660, 2365439220, 3476174909, 2804287165, 3529576764, 2482037719, 3758708190, 2041488362, + 3953105597, 2604691846, 4241700961, 3231746381, 2481435019, 2100261339, 1442114730, 756514823, 316144959, + 3160143158, 2910044562, 4048037725, 1229183044, 2685549593, 173816268, 3565373219, 4220080560, 3252249431, + 2240144151, + ].map((v) => v | 0), + ); + }); + it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(xoroshiro128plus))); + it('Should return the same sequence when built from state', () => + fc.assert(p.clonedFromStateSameSequences(xoroshiro128plus, xoroshiro128plusFromState))); + it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xoroshiro128plus))); + it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xoroshiro128plus))); + it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xoroshiro128plus))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xoroshiro128plus))); + it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xoroshiro128plus))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(xoroshiro128plus))); + it('Should not impact clones when impacting itself on jump', () => + fc.assert(p.noChangeOnClonedWithJump(xoroshiro128plus))); +}); diff --git a/src/generator/xoroshiro128plus.ts b/src/generator/xoroshiro128plus.ts new file mode 100644 --- /dev/null +++ b/src/generator/xoroshiro128plus.ts @@ -0,0 +1,71 @@ +import type { JumpableRandomGenerator } from '../types/JumpableRandomGenerator'; + +// XoroShiro128+ with a=24, b=16, c=37, +// - https://en.wikipedia.org/wiki/Xoroshiro128%2B +// - http://prng.di.unimi.it/xoroshiro128plus.c +class XoroShiro128Plus implements JumpableRandomGenerator { + constructor( + private s01: number, + private s00: number, + private s11: number, + private s10: number, + ) {} + clone(): XoroShiro128Plus { + return new XoroShiro128Plus(this.s01, this.s00, this.s11, this.s10); + } + next(): number { + const out = (this.s00 + this.s10) | 0; + // a = s0[n] ^ s1[n] + const a0 = this.s10 ^ this.s00; + const a1 = this.s11 ^ this.s01; + const s00 = this.s00; + const s01 = this.s01; + // s0[n+1] = rotl(s0[n], 24) ^ a ^ (a << 16) + this.s00 = (s00 << 24) ^ (s01 >>> 8) ^ a0 ^ (a0 << 16); + this.s01 = (s01 << 24) ^ (s00 >>> 8) ^ a1 ^ ((a1 << 16) | (a0 >>> 16)); + // s1[n+1] = rotl(a, 37) + this.s10 = (a1 << 5) ^ (a0 >>> 27); + this.s11 = (a0 << 5) ^ (a1 >>> 27); + return out; + } + jump(): void { + // equivalent to 2^64 calls to next() + // can be used to generate 2^64 non-overlapping subsequences + let ns01 = 0; + let ns00 = 0; + let ns11 = 0; + let ns10 = 0; + const jump = [0xd8f554a5, 0xdf900294, 0x4b3201fc, 0x170865df]; + for (let i = 0; i !== 4; ++i) { + for (let mask = 1; mask; mask <<= 1) { + // Because: (1 << 31) << 1 === 0 + if (jump[i] & mask) { + ns01 ^= this.s01; + ns00 ^= this.s00; + ns11 ^= this.s11; + ns10 ^= this.s10; + } + this.next(); + } + } + this.s01 = ns01; + this.s00 = ns00; + this.s11 = ns11; + this.s10 = ns10; + } + getState(): readonly number[] { + return [this.s01, this.s00, this.s11, this.s10]; + } +} + +export function xoroshiro128plusFromState(state: readonly number[]): JumpableRandomGenerator { + const valid = state.length === 4; + if (!valid) { + throw new Error('The state must have been produced by a xoroshiro128plus RandomGenerator'); + } + return new XoroShiro128Plus(state[0], state[1], state[2], state[3]); +} + +export function xoroshiro128plus(seed: number): JumpableRandomGenerator { + return new XoroShiro128Plus(-1, ~seed, seed | 0, 0); +} diff --git a/src/generator/xorshift128plus.spec.ts b/src/generator/xorshift128plus.spec.ts new file mode 100644 --- /dev/null +++ b/src/generator/xorshift128plus.spec.ts @@ -0,0 +1,103 @@ +import { describe, it, expect } from 'vitest'; +import * as fc from 'fast-check'; + +import { xorshift128plus, xorshift128plusFromState } from './xorshift128plus'; +import * as p from './RandomGenerator.properties'; + +describe('xorshift128plus', () => { + it('Should produce the right sequence for seed=42', () => { + const g = xorshift128plus(42); + let data = []; + for (let idx = 0; idx !== 100; ++idx) { + const v = g.next(); + data.push(v); + } + // should be equivalent to the following C code: + // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; + // uint64_t next() { + // uint64_t s1 = s[0]; + // const uint64_t s0 = s[1]; + // const uint64_t result = s0 + s1; + // s[0] = s0; + // s1 ^= s1 << 23; // a + // s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c + // return result & 0xffffffff; + // } + // int main() { + // for (int i = 0 ; i != 100 ; ++i) { std::cout << next() << ","; } + // } + expect(data).toEqual( + [ + 4294967253, 1166015114, 1692303336, 3482095935, 4288634584, 1325520545, 1367235622, 1759582253, 2328126844, + 649610899, 3328014937, 278910909, 2928761053, 1702820659, 3325106640, 2884641937, 2678880596, 999204680, + 3611521009, 2947011440, 3416747393, 1634581895, 1067867852, 88558932, 2888797634, 2105694663, 1296024496, + 2583386047, 2401573111, 2171058030, 657541993, 915947238, 696903927, 2687397535, 161811119, 793638981, + 3330697646, 532898537, 3343714389, 1441469376, 1718504920, 1003802668, 3343696598, 1851708816, 2581827611, + 1621906647, 3349035115, 264489114, 3672600296, 2277593912, 1157240243, 3243862613, 1246478095, 3690736557, + 1366822450, 1444073535, 841971999, 1179174583, 3726899152, 817046495, 509174047, 1199791094, 2405463672, + 827001813, 1820926848, 400677546, 1599444384, 3885120670, 2210955775, 2746964964, 211002306, 1381674843, + 2689051285, 1702045018, 882144076, 553855887, 2369937669, 3656191263, 1560721536, 3818918581, 86283002, + 3023862018, 1296400131, 625483410, 2364517346, 3034167893, 1805836022, 2782947729, 1110539129, 3221939945, + 2436039688, 1150739462, 3430900671, 439413983, 4238985145, 3053101980, 1358457066, 1504768706, 2433376141, + 4069088729, + ].map((v) => v | 0), + ); + }); + it('Should produce the right sequence after jump for seed=42', () => { + const g = xorshift128plus(42); + g.jump(); + let data = []; + for (let idx = 0; idx !== 100; ++idx) { + const v = g.next(); + data.push(v); + } + // should be equivalent to the following C++ code (+previous): + // void jump() { + // static const uint64_t JUMP[] = { 0x8a5cd789635d2dff, 0x121fd2155c472f96 }; + // uint64_t s0 = 0; + // uint64_t s1 = 0; + // for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) { + // for(int b = 0; b < 64; b++) { + // if (JUMP[i] & UINT64_C(1) << b) { + // s0 ^= s[0]; + // s1 ^= s[1]; + // } + // next(); + // } + // } + // s[0] = s0; + // s[1] = s1; + // } + // int main() { + // jump(); + // for (int i = 0 ; i != 100 ; ++i) { std::cout << next() << ","; } + // } + expect(data).toEqual( + [ + 2971276074, 3466165198, 456875496, 2879848137, 4162428146, 2513269982, 2277233661, 2163024882, 3082356668, + 1459960119, 3225207140, 418458707, 465389025, 33345291, 9975393, 1398264340, 2941704490, 4219353700, 1050887263, + 3537623901, 1011298813, 2886999094, 2095512742, 719748796, 2031575611, 246165700, 306697934, 932458853, + 3811330946, 2780216938, 3008525324, 1217535119, 3060075487, 3829564179, 1862997734, 3188200581, 3652713690, + 1950714292, 2865049298, 1937705104, 1297917374, 1333060788, 4089226157, 205959794, 2227024661, 3714058862, + 3728103989, 3728972300, 659396325, 3185943613, 1039549819, 2822001969, 406983436, 2343603502, 299506842, + 383551218, 698423599, 3611096673, 3762219019, 2293131106, 373955997, 2445208504, 3057049169, 1255899189, + 4215756297, 957357335, 3456668141, 1989928862, 3510600746, 3806106322, 2542824253, 3920739152, 2851853721, + 4208037803, 1276020689, 3735104409, 3925674077, 3708482212, 4262192769, 2607567703, 531897672, 3317376658, + 1903132291, 353686572, 509303103, 3991810724, 1786729004, 1709580489, 550755974, 1081340778, 1040041085, + 2723398036, 3276389190, 506083384, 2810025290, 3110824839, 3094567277, 2333614204, 3869414189, 678984428, + ].map((v) => v | 0), + ); + }); + it('Should return the same sequence given same seeds', () => fc.assert(p.sameSeedSameSequences(xorshift128plus))); + it('Should return the same sequence when built from state', () => + fc.assert(p.clonedFromStateSameSequences(xorshift128plus, xorshift128plusFromState))); + it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xorshift128plus))); + it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xorshift128plus))); + it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xorshift128plus))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xorshift128plus))); + it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xorshift128plus))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(xorshift128plus))); + it('Should not impact clones when impacting itself on jump', () => + fc.assert(p.noChangeOnClonedWithJump(xorshift128plus))); +}); diff --git a/src/generator/xorshift128plus.ts b/src/generator/xorshift128plus.ts new file mode 100644 --- /dev/null +++ b/src/generator/xorshift128plus.ts @@ -0,0 +1,72 @@ +import type { JumpableRandomGenerator } from '../types/JumpableRandomGenerator'; + +// XorShift128+ with a=23, b=18, c=5 +// - http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf +// - http://vigna.di.unimi.it/xorshift/xorshift128plus.c +// - https://docs.rs/crate/xorshift/0.1.3/source/src/xorshift128.rs +// +// NOTE: Math.random() of V8 uses XorShift128+ with a=23, b=17, c=26, +// See https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/base/utils/random-number-generator.h#L119-L128 +class XorShift128Plus implements JumpableRandomGenerator { + constructor( + private s01: number, + private s00: number, + private s11: number, + private s10: number, + ) {} + clone(): XorShift128Plus { + return new XorShift128Plus(this.s01, this.s00, this.s11, this.s10); + } + next(): number { + const a0 = this.s00 ^ (this.s00 << 23); + const a1 = this.s01 ^ ((this.s01 << 23) | (this.s00 >>> 9)); + const b0 = a0 ^ this.s10 ^ ((a0 >>> 18) | (a1 << 14)) ^ ((this.s10 >>> 5) | (this.s11 << 27)); + const b1 = a1 ^ this.s11 ^ (a1 >>> 18) ^ (this.s11 >>> 5); + const out = (this.s00 + this.s10) | 0; + this.s01 = this.s11; + this.s00 = this.s10; + this.s11 = b1; + this.s10 = b0; + return out; + } + jump() { + // equivalent to 2^64 calls to next() + // can be used to generate 2^64 non-overlapping subsequences + let ns01 = 0; + let ns00 = 0; + let ns11 = 0; + let ns10 = 0; + const jump = [0x635d2dff, 0x8a5cd789, 0x5c472f96, 0x121fd215]; + for (let i = 0; i !== 4; ++i) { + for (let mask = 1; mask; mask <<= 1) { + // Because: (1 << 31) << 1 === 0 + if (jump[i] & mask) { + ns01 ^= this.s01; + ns00 ^= this.s00; + ns11 ^= this.s11; + ns10 ^= this.s10; + } + this.next(); + } + } + this.s01 = ns01; + this.s00 = ns00; + this.s11 = ns11; + this.s10 = ns10; + } + getState(): readonly number[] { + return [this.s01, this.s00, this.s11, this.s10]; + } +} + +export function xorshift128plusFromState(state: readonly number[]): JumpableRandomGenerator { + const valid = state.length === 4; + if (!valid) { + throw new Error('The state must have been produced by a xorshift128plus RandomGenerator'); + } + return new XorShift128Plus(state[0], state[1], state[2], state[3]); +} + +export function xorshift128plus(seed: number): JumpableRandomGenerator { + return new XorShift128Plus(-1, ~seed, seed | 0, 0); +} -- tangled.sh