diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/README.md b/lib/node_modules/@stdlib/random/base/xorshift128/README.md new file mode 100644 index 000000000000..d025d2ec2297 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/README.md @@ -0,0 +1,267 @@ + + +# Xorshift128+ + +> A 128-bit xorshift pseudorandom number generator ([PRNG][xorshift]). + +
+ +## Usage + +```javascript +var factory = require( '@stdlib/random/base/xorshift128' ); +``` + +#### factory() + +Returns a xorshift128+ pseudorandom number generator. + +```javascript +var rng = factory(); + +var r = rng(); +// returns +``` + +#### factory.normalized() + +Returns a pseudorandom number on the interval `[0,1)`. + +```javascript +var rng = factory(); + +var r = rng.normalized(); +// returns +``` + +#### factory( \[options] ) + +Returns a xorshift128+ pseudorandom number generator ([PRNG][xorshift]). + +```javascript +var rng = factory(); +``` + +The function accepts the following `options`: + +- **seed**: pseudorandom number generator seed. Must be a non-negative integer or BigInt. Default: a random seed. +- **state**: a state array (Array) containing pseudorandom number generator state. If provided, the function ignores the `seed` option. +- **copy**: `boolean` indicating whether to copy a provided pseudorandom number generator state. Setting this option to `false` allows sharing state between two or more pseudorandom number generators. Setting this option to `true` ensures that a returned generator has exclusive control over its internal state. Default: `true`. + +By default, a random seed is used to seed the returned generator. To seed the generator, provide either a non-negative integer: + +```javascript +var BigInt = require( '@stdlib/bigint/ctor' ); + +var rng = factory({ + 'seed': BigInt( 1234 ) +}); + +var r = rng(); +// returns +``` + +or, for additional control, a BigInt value: + +```javascript +var BigInt = require( '@stdlib/bigint/ctor' ); + +var rng = factory({ + 'seed': BigInt( 1234 ) +}); + +var r = rng(); +// returns +``` + +To return a generator having a specific initial state, set the generator `state` option: + +```javascript +var rng1 = factory(); + +var r; +var i; +for ( i = 0; i < 1000; i++ ) { + r = rng1(); +} + +var state = rng1.state; + +var rng2 = factory({ + 'state': state +}); + +var bool = ( rng2() === rng1() ); +// returns true +``` + +#### factory.NAME + +The generator name. + +```javascript +var rng = factory(); + +var str = rng.NAME; +// returns 'xorshift128+' +``` + +#### rng.seed + +The value used to seed the generator. + +```javascript +var BigInt = require( '@stdlib/bigint/ctor' ); + +var rng = factory({ + 'seed': BigInt( 1234 ) +}); + +var seed = rng.seed; +// returns +``` + +#### rng.state + +Writable property for getting and setting the generator state. + +```javascript +var rng = factory(); + +var r = rng(); +// returns + +var state = rng.state; +// returns + +r = rng(); +// returns + +rng.state = state; + +r = rng(); +// returns +``` + +#### rng.toJSON() + +Serializes the pseudorandom number generator as a JSON object. + +```javascript +var rng = factory(); + +var o = rng.toJSON(); +// returns { 'type': 'PRNG', 'name': 'xorshift128+', 'state': [...], 'params': [] } +``` + +
+ + + +
+ +## Notes + +- The generator has a period of approximately `2^128` (see [References](#references)). +- Xorshift128+ is a fast, simple pseudorandom number generator with good statistical properties for most applications. +- The generator produces 64-bit pseudorandom integers using BigInt arithmetic. +- The "randomness quality" of the generator's output is suitable for general-purpose use, Monte Carlo simulations, and parallel computations (with different seeds). +- For cryptographic applications, use a cryptographically secure pseudorandom number generator (CSPRNG). +- If PRNG state is "shared" (meaning a state array was provided during PRNG creation and **not** copied) and one sets the generator state to a state array having a different length, the PRNG does **not** update the existing shared state and, instead, points to the newly provided state array. In order to synchronize PRNG output according to the new shared state array, the state array for **each** relevant PRNG must be **explicitly** set. +- If PRNG state is "shared" and one sets the generator state to a state array of the same length, the PRNG state is updated (along with the state of all other PRNGs sharing the PRNG's state array). + +
+ + + +
+ +## Examples + + + +```javascript +var factory = require( '@stdlib/random/base/xorshift128' ); +var BigInt = require( '@stdlib/bigint/ctor' ); + +var rng = factory({ + 'seed': BigInt( 1234 ) +}); + +var i; +for ( i = 0; i < 10; i++ ) { + console.log( rng() ); +} +``` + +
+ + + +* * * + +
+ +## References + +- Vigna, S. (2014). "An experimental exploration of Marsaglia's xorshift generators, scrambled." _ACM Transactions on Mathematical Software (TOMS)_, 42(4), 30. doi:[10.1145/2714064.2714122][vigna:2014]. +- Vigna, S. (2018). "Further scramblings of Marsaglia's xorshift generators." _Journal of Computational and Applied Mathematics_, 341, 273–282. doi:[10.1016/j.cam.2018.03.024][vigna:2018]. + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/benchmark/benchmark.js b/lib/node_modules/@stdlib/random/base/xorshift128/benchmark/benchmark.js new file mode 100644 index 000000000000..7ec953220740 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/benchmark/benchmark.js @@ -0,0 +1,357 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/* eslint-disable node/no-unpublished-require */ + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var isBigInt = require( '@stdlib/assert/is-bigint' ); +var BigInt = require( '@stdlib/bigint/ctor' ); +var pkg = require( './../package.json' ).name; +var xorshift128plus = require( './../lib' ); + + +// MAIN // + +bench( pkg, function benchmark( b ) { + var rng; + var z; + var i; + + rng = xorshift128plus(); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = rng(); + if ( !isBigInt( z ) ) { + b.fail( 'should return a BigInt' ); + } + } + b.toc(); + + if ( !isBigInt( z ) ) { + b.fail( 'should return a BigInt' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':normalized', function benchmark( b ) { + var rng; + var z; + var i; + + rng = xorshift128plus(); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = rng.normalized(); + if ( typeof z !== 'number' ) { + b.fail( 'should return a number' ); + } + } + b.toc(); + + if ( typeof z !== 'number' ) { + b.fail( 'should return a number' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':factory', function benchmark( b ) { + var z; + var i; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = xorshift128plus({ + 'seed': BigInt(12345) + }); + if ( typeof z !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( typeof z !== 'function' ) { + b.fail( 'should return a function' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':factory:seed', function benchmark( b ) { + var z; + var i; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = xorshift128plus({ + 'seed': BigInt( i ) + }); + if ( typeof z !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( typeof z !== 'function' ) { + b.fail( 'should return a function' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':state', function benchmark( b ) { + var state; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + state = rng.state; + if ( state.length !== 6 ) { + b.fail( 'invalid state length' ); + } + } + b.toc(); + + if ( state.length !== 6 ) { + b.fail( 'invalid state length' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':state:set', function benchmark( b ) { + var state; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + state = rng.state; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng.state = state; + } + b.toc(); + + if ( state.length !== 6 ) { + b.fail( 'invalid state length' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':seed', function benchmark( b ) { + var seed; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + seed = rng.seed; + if ( !isBigInt( seed ) ) { + b.fail( 'should return a BigInt' ); + } + } + b.toc(); + + if ( !isBigInt( seed ) ) { + b.fail( 'should return a BigInt' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':copy', function benchmark( b ) { + var rng2; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng2 = rng.copy(); + if ( typeof rng2 !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( typeof rng2 !== 'function' ) { + b.fail( 'should return a function' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':toJSON', function benchmark( b ) { + var json; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + json = rng.toJSON(); + if ( typeof json !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + + if ( typeof json !== 'object' ) { + b.fail( 'should return an object' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':reproducible', function benchmark( b ) { + var rng1; + var rng2; + var v1; + var v2; + var i; + + rng1 = xorshift128plus({ + 'seed': BigInt(54321) + }); + rng2 = xorshift128plus({ + 'seed': BigInt(54321) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v1 = rng1(); + v2 = rng2(); + if ( v1 !== v2 ) { + b.fail( 'sequences should be identical' ); + } + } + b.toc(); + + if ( v1 !== v2 ) { + b.fail( 'sequences should be identical' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':batch:next', function benchmark( b ) { + var values; + var rng; + var i; + var j; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + values = []; + values.length = 100; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + for ( j = 0; j < 100; j++ ) { + values[ j ] = rng(); + } + } + b.toc(); + + if ( values.length !== 100 ) { + b.fail( 'invalid values array length' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':batch:normalized', function benchmark( b ) { + var values; + var rng; + var i; + var j; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + values = []; + values.length = 100; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + for ( j = 0; j < 100; j++ ) { + values[ j ] = rng.normalized(); + } + } + b.toc(); + + if ( values.length !== 100 ) { + b.fail( 'invalid values array length' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':mixed', function benchmark( b ) { + var rng; + var v1; + var v2; + var i; + + rng = xorshift128plus({ + 'seed': BigInt(12345) + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v1 = rng(); + v2 = rng.normalized(); + } + b.toc(); + + if ( !isBigInt( v1 ) ) { + b.fail( 'should return a BigInt' ); + } + if ( typeof v2 !== 'number' ) { + b.fail( 'should return a number' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/examples/basic.js b/lib/node_modules/@stdlib/random/base/xorshift128/examples/basic.js new file mode 100644 index 000000000000..fb360d6350d6 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/examples/basic.js @@ -0,0 +1,140 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var BigInt = require( '@stdlib/bigint/ctor' ); +var xorshift128plus = require( './../lib/' ); +var i; + +// EXAMPLE 1: Create a generator with a seed +console.log( '\n=== Example 1: Basic Generator ===' ); + +var rng = xorshift128plus({ + 'seed': BigInt(12345) +}); + +console.log( 'Generated 64-bit integers:' ); +console.log( ' %s', rng() ); +console.log( ' %s', rng() ); +console.log( ' %s', rng() ); + +// EXAMPLE 2: Generate normalized values +console.log( '\n=== Example 2: Normalized Values ===' ); + +var normalizedRng = xorshift128plus({ + 'seed': BigInt(54321) +}); + +console.log( 'Normalized values in [0, 1):' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %d', normalizedRng.normalized() ); +} + +// EXAMPLE 3: Reproducibility with same seed +console.log( '\n=== Example 3: Reproducibility ===' ); + +var rng1 = xorshift128plus({ + 'seed': BigInt(99999) +}); +var rng2 = xorshift128plus({ + 'seed': BigInt(99999) +}); + +console.log( 'Both generators with same seed (99999n):' ); +for ( i = 0; i < 3; i++ ) { + console.log( ' rng1: %s, rng2: %s', rng1().toString(), rng2().toString() ); +} + +// EXAMPLE 4: Seeding with different values +console.log( '\n=== Example 4: Different Seeds ===' ); + +var rngA = xorshift128plus({ + 'seed': BigInt(111) +}); +var rngB = xorshift128plus({ + 'seed': BigInt(222) +}); + +console.log( 'Different seeds produce different sequences:' ); +console.log( ' seed 111n: %s', rngA().toString() ); +console.log( ' seed 222n: %s', rngB().toString() ); + +// EXAMPLE 5: Using without explicit seed (random) +console.log( '\n=== Example 5: Random Seed ===' ); + +var randomRng = xorshift128plus(); + +console.log( 'Generator with random seed:' ); +for ( i = 0; i < 3; i++ ) { + console.log( ' %s', randomRng().toString() ); +} + +// EXAMPLE 6: Accessing generator properties +console.log( '\n=== Example 6: Generator Properties ===' ); + +var propRng = xorshift128plus({ + 'seed': BigInt(777) +}); + +console.log( 'Generator properties:' ); +console.log( ' NAME: %s', propRng.NAME ); +console.log( ' seed (BigInt): %s', propRng.seed ); +console.log( ' state (array length): %d', propRng.state.length ); + +// EXAMPLE 7: State management +console.log( '\n=== Example 7: State Management ===' ); + +var stateRng = xorshift128plus({ + 'seed': BigInt(555) +}); + +console.log( 'Initial values:' ); +console.log( ' %s', stateRng() ); +console.log( ' %s', stateRng() ); + +var savedState = stateRng.state; +console.log( 'State saved' ); + +console.log( 'Continue generating:' ); +console.log( ' %s', stateRng() ); +console.log( ' %s', stateRng() ); + +stateRng.state = savedState; +console.log( 'State restored, same values:' ); +console.log( ' %s (should be same as value 3)', stateRng() ); +console.log( ' %s (should be same as value 4)', stateRng() ); + +// EXAMPLE 8: Copying a generator +console.log( '\n=== Example 8: Generator Copy ===' ); + +var original = xorshift128plus({ + 'seed': BigInt(333) +}); +var duplicate = original.copy(); + +console.log( 'Original: %s', original() ); +console.log( 'Copy: %s', duplicate() ); + +console.log( 'Both continue with same sequence (initially):' ); +console.log( 'Original: %s', original() ); +console.log( 'Copy: %s', duplicate() ); + +console.log( 'But now they are independent:' ); +console.log( 'Original: %s', original() ); +console.log( 'Copy: %s', duplicate() ); diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/examples/index.js b/lib/node_modules/@stdlib/random/base/xorshift128/examples/index.js new file mode 100644 index 000000000000..d79af0a718b5 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/examples/index.js @@ -0,0 +1,177 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var BigInt = require( '@stdlib/bigint/ctor' ); +var xorshift128plus = require( './../lib' ); +var match; +var valA; +var valB; + +// Generate pseudorandom 64-bit integers... +console.log( '\n=== Pseudorandom 64-bit Integers ===' ); +console.log( 'seed: %s', BigInt(12345) ); + +var rng = xorshift128plus({ + 'seed': BigInt(12345) +}); + +var i; +for ( i = 0; i < 10; i++ ) { + console.log( rng().toString() ); +} + +// Generate normalized random numbers in [0, 1)... +console.log( '\n=== Normalized Random Numbers [0, 1) ===' ); + +rng = xorshift128plus({ + 'seed': BigInt(12345) +}); + +for ( i = 0; i < 10; i++ ) { + console.log( rng.normalized() ); +} + +// Create a new pseudorandom number generator with a custom seed... +console.log( '\n=== Custom Seed ===' ); + +var seed = BigInt(54321); +var rand = xorshift128plus({ + 'seed': seed +}); + +console.log( 'seed: %s', seed ); +for ( i = 0; i < 10; i++ ) { + console.log( rand().toString() ); +} + +// Create another pseudorandom number generator using a previous seed... +console.log( '\n=== Using Previous Seed ===' ); + +rand = xorshift128plus({ + 'seed': BigInt(99999) +}); +var previousSeed = rand.seed; + +// Generate some values +for ( i = 0; i < 5; i++ ) { + rand(); +} + +// Create a new generator with the previous seed +rand = xorshift128plus({ + 'seed': previousSeed +}); + +console.log( 'seed: %s', previousSeed ); +for ( i = 0; i < 10; i++ ) { + console.log( rand().toString() ); +} + +// Demonstrate state management... +console.log( '\n=== State Management ===' ); + +var rng1 = xorshift128plus({ + 'seed': BigInt(777) +}); +console.log( 'Generated values: ' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', rng1().toString() ); +} + +// Save state +var state = rng1.state; +console.log( 'State saved' ); + +// Generate more values +console.log( 'Generated more values:' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', rng1().toString() ); +} + +// Restore state +rng1.state = state; +console.log( 'State restored' ); + +// Generate the same values as before +console.log( 'Same values regenerated:' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', rng1().toString() ); +} + +// Demonstrate generator copying... +console.log( '\n=== Generator Copying ===' ); + +var original = xorshift128plus({ + 'seed': BigInt(555) +}); +console.log( 'Original generator:' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', original().toString() ); +} + +var copy = original.copy(); +console.log( 'Copied generator (should produce same sequence):' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', copy().toString() ); +} + +// Now they diverge +console.log( 'Original continues:' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', original().toString() ); +} + +console.log( 'Copy continues (different):' ); +for ( i = 0; i < 5; i++ ) { + console.log( ' %s', copy().toString() ); +} + +// Demonstrate reproducibility... +console.log( '\n=== Reproducibility ===' ); + +var rngA = xorshift128plus({ + 'seed': BigInt(888) +}); +var rngB = xorshift128plus({ + 'seed': BigInt(888) +}); + +console.log( 'Both generators with same seed produce identical sequences:' ); +for ( i = 0; i < 10; i++ ) { + valA = rngA(); + valB = rngB(); + match = ( valA === valB ) ? '✓' : '✗'; + console.log( ' %s %s === %s', match, valA.toString(), valB.toString() ); +} + +// Demonstrate JSON serialization... +console.log( '\n=== JSON Serialization ===' ); + +var jsonRng = xorshift128plus({ + 'seed': BigInt(333) +}); +var json = jsonRng.toJSON(); + +console.log( 'Serialized state:' ); +console.log( ' type: %s', json.type ); +console.log( ' name: %s', json.name ); +console.log( ' state length: %d', json.state.length ); +console.log( 'Full JSON object:' ); +console.log( json ); diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/lib/factory.js b/lib/node_modules/@stdlib/random/base/xorshift128/lib/factory.js new file mode 100644 index 000000000000..32f4152a5ced --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/factory.js @@ -0,0 +1,438 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* Xorshift128+ pseudorandom number generator (PRNG). +* +* ## Algorithm +* +* The Xorshift128+ algorithm generates 64-bit pseudorandom numbers by +* maintaining two 64-bit state values and performing XOR and shift operations. +* +* ## Notes +* +* - Uses JavaScript BigInt for 64-bit arithmetic. +* - State consists of two 64-bit values: s0 and s1. +* - Period: approximately 2^128. +* +* @example +* var xorshift128plus = require( '@stdlib/random/base/xorshift128plus' ); +* +* var rng = xorshift128plus({ +* 'seed': BigInt(12345) +* }); +* +* var v = rng(); +* // returns +*/ + +'use strict'; + +// MODULES // + +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); +var setReadWriteAccessor = require( '@stdlib/utils/define-nonenumerable-read-write-accessor' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var isObject = require( '@stdlib/assert/is-plain-object' ); +var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ); +var isBigInt = require( '@stdlib/assert/is-bigint' ); +var BigInt = require( '@stdlib/bigint/ctor' ); +var Number = require( '@stdlib/number/ctor' ); +var randu = require( '@stdlib/random/base/randu' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var format = require( '@stdlib/string/format' ); + + +// VARIABLES // + +/* eslint-disable no-magic-numbers */ + +/** +* State array schema version. +* +* @private +* @type {bigint} +*/ +var STATE_ARRAY_VERSION = BigInt( 1 ); + +/** +* Number of state elements (2 × 64-bit values). +* +* @private +* @type {bigint} +*/ +var NUM_STATE_ELEMENTS = BigInt( 2 ); + +/** +* Total state array length:. +* [version | state_length | state[0] | state[1] | seed_length | seed] +* +* @private +* @type {number} +*/ +var STATE_ARRAY_SIZE = 6; + +/** +* State array indices. +* +* @private +* @type {number} +*/ +var VERSION_INDEX = 0; +var STATE_LENGTH_INDEX = 1; +var STATE_OFFSET = 2; +var SEED_LENGTH_INDEX = 4; +var SEED_OFFSET = 5; + +/** +* Maximum value for a 64-bit unsigned integer. +* +* @private +* @type {bigint} +*/ +var UINT64_MAX_BIGINT = BigInt( '0xffffffffffffffff' ); + +/** +* 2^64 as BigInt for normalization. +* +* @private +* @type {bigint} +*/ +var TWO_64 = BigInt( '0x10000000000000000' ); + +/** +* Normalization constant for converting uint64 to [0,1). +* +* @private +* @type {number} +*/ +var NORMALIZATION_CONSTANT = 1.0 / Number( TWO_64 ); + +/* eslint-enable no-magic-numbers */ + + +// FUNCTIONS // + +/** +* Initializes the internal PRNG state from a seed. +* +* @private +* @param {bigint} seed - seed value +* @returns {Array} state array with two 64-bit values +*/ +function initializeState( seed ) { + var MULTIPLIER_1 = BigInt( '0x9e3779b97f4a7c15' ); + var MULTIPLIER_2 = BigInt( '0xbf58476d1ce4e5b9' ); + var state = [ BigInt( 0 ), BigInt( 0 ) ]; + + state[ 0 ] = (seed ^ MULTIPLIER_1) & UINT64_MAX_BIGINT; + state[ 1 ] = (seed ^ MULTIPLIER_2) & UINT64_MAX_BIGINT; + + if ( state[ 0 ] === BigInt( 0 ) && state[ 1 ] === BigInt( 0 ) ) { + state[ 0 ] = BigInt( 1 ); + } + + return state; +} + +/** +* Verifies state array integrity. +* +* @private +* @param {Array} stateArray - state array +* @returns {(Error|null)} error or null +*/ +function verifyState( stateArray ) { + if ( !Array.isArray( stateArray ) ) { + return new TypeError( 'invalid state array. State must be an Array.' ); + } + + if ( stateArray.length !== STATE_ARRAY_SIZE ) { + return new RangeError(format('invalid state array. Expected length %u. Got %u.', STATE_ARRAY_SIZE, stateArray.length)); + } + + if ( stateArray[ VERSION_INDEX ] !== STATE_ARRAY_VERSION ) { + return new RangeError(format('invalid state array. Unsupported version %u.', stateArray[ VERSION_INDEX ])); + } + + if ( stateArray[ STATE_LENGTH_INDEX ] !== NUM_STATE_ELEMENTS ) { + return new RangeError(format('invalid state array. Expected state length %u. Got %u.', NUM_STATE_ELEMENTS, stateArray[ STATE_LENGTH_INDEX ])); + } + + if ( typeof stateArray[ STATE_OFFSET ] !== 'bigint' ) { + return new TypeError( 'invalid state array. State[0] must be a BigInt.' ); + } + + if ( typeof stateArray[ STATE_OFFSET + 1 ] !== 'bigint' ) { + return new TypeError( 'invalid state array. State[1] must be a BigInt.' ); + } + + return null; +} + + +// MAIN // + +/** +* Returns an Xorshift128+ pseudorandom number generator. +* +* @param {Object} [options] - configuration object +* @param {(number|bigint)} [options.seed] - seed value +* @param {Array} [options.state] - generator state +* @param {boolean} [options.copy=true] - copy state +* @throws {TypeError} options must be an object +* @throws {TypeError} invalid option value +* @throws {RangeError} invalid state array +* @returns {Function} PRNG function +*/ +function factory( options ) { + var STATE; + var state; + var seed; + var opts; + var err; + var tmp; + var i; + + opts = {}; + if ( arguments.length ) { + if ( !isObject( options ) ) { + throw new TypeError(format('invalid argument. Options must be an object. Received: %s', typeof options)); + } + + if ( hasOwnProp( options, 'copy' ) ) { + opts.copy = options.copy; + if ( !isBoolean( options.copy ) ) { + throw new TypeError(format('invalid option. `copy` must be a boolean. Received: %s', typeof options.copy)); + } + } + + if ( hasOwnProp( options, 'state' ) ) { + STATE = options.state; + opts.state = true; + + if ( !Array.isArray( STATE ) ) { + throw new TypeError('invalid option. `state` must be an Array.'); + } + + err = verifyState( STATE ); + if ( err ) { + throw err; + } + + if ( opts.copy !== false ) { + tmp = []; + tmp.length = STATE_ARRAY_SIZE; + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + tmp[ i ] = STATE[ i ]; + } + STATE = tmp; + } + } + + if ( !opts.state ) { + if ( hasOwnProp( options, 'seed' ) ) { + seed = options.seed; + opts.seed = true; + + if ( typeof seed === 'number' ) { + if ( !isNonNegativeInteger( seed ) ) { + throw new TypeError('invalid option. `seed` must be a non-negative integer or BigInt.'); + } + seed = BigInt( seed ); + } else if ( isBigInt( seed ) ) { + if ( seed < BigInt( 0 ) ) { + throw new RangeError('invalid option. `seed` must be a non-negative BigInt.'); + } + } else { + throw new TypeError('invalid option. `seed` must be a non-negative integer or BigInt.'); + } + + if ( seed === BigInt( 0 ) ) { + seed = BigInt( 1 ); + } + } else { + seed = BigInt( floor( randu() * 0x100000000 ) ) * BigInt( '0x100000000' ); + seed |= BigInt( floor( randu() * 0x100000000 ) ); + if ( seed === BigInt( 0 ) ) { + seed = BigInt( 1 ); + } + } + } + } else { + seed = BigInt( floor( randu() * 0x100000000 ) ) * BigInt( '0x100000000' ); + seed |= BigInt( floor( randu() * 0x100000000 ) ); + if ( seed === BigInt( 0 ) ) { + seed = BigInt( 1 ); + } + } + + if ( STATE === void 0 ) { + state = initializeState( seed ); + STATE = []; + STATE.length = STATE_ARRAY_SIZE; + STATE[ VERSION_INDEX ] = STATE_ARRAY_VERSION; + STATE[ STATE_LENGTH_INDEX ] = NUM_STATE_ELEMENTS; + STATE[ STATE_OFFSET ] = state[ 0 ]; + STATE[ STATE_OFFSET + 1 ] = state[ 1 ]; + STATE[ SEED_LENGTH_INDEX ] = BigInt( 1 ); + STATE[ SEED_OFFSET ] = seed; + } + + /** + * Generates a pseudorandom 64-bit unsigned integer. + * + * @private + * @returns {bigint} pseudorandom 64-bit integer + */ + function next() { + var s1 = STATE[ STATE_OFFSET ]; + var s0 = STATE[ STATE_OFFSET + 1 ]; + + STATE[ STATE_OFFSET ] = s0; + + s1 ^= (s1 << BigInt( 23 )); + s1 ^= (s1 >> BigInt( 18 )); + s1 ^= s0; + s0 ^= (s0 >> BigInt( 5 )); + + STATE[ STATE_OFFSET + 1 ] = (s1 ^ s0) & UINT64_MAX_BIGINT; + + return ((s1 ^ s0) + s0) & UINT64_MAX_BIGINT; + } + + /** + * Generates a pseudorandom number on [0, 1). + * + * @private + * @returns {number} pseudorandom number in [0, 1) + */ + function norm() { + return Number( next() ) * NORMALIZATION_CONSTANT; + } + + /** + * Returns the current state. + * + * @private + * @returns {Array} state array + */ + function getState() { + var out = []; + var i; + out.length = STATE_ARRAY_SIZE; + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + out[ i ] = STATE[ i ]; + } + return out; + } + + /** + * Sets the state. + * + * @private + * @param {Array} s - new state + * @throws {TypeError} invalid state type + * @throws {RangeError} invalid state + */ + function setState( s ) { + var e; + var i; + if ( !Array.isArray( s ) ) { + throw new TypeError('invalid argument. `state` must be an Array.'); + } + + e = verifyState( s ); + if ( e ) { + throw e; + } + + if ( opts.copy === false ) { + if ( opts.state && s.length === STATE.length ) { + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + STATE[ i ] = s[ i ]; + } + } else { + STATE = s; + opts.state = true; + } + } else { + if ( s.length !== STATE.length ) { + STATE = []; + STATE.length = STATE_ARRAY_SIZE; + } + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + STATE[ i ] = s[ i ]; + } + } + } + + /** + * Returns a copy of the PRNG. + * + * @private + * @returns {Function} copy + */ + function copy() { + return factory({ + 'state': getState(), + 'copy': true + }); + } + + /** + * Serializes the state. + * + * @private + * @returns {Object} JSON object + */ + function toJSON() { + return { + 'type': 'PRNG', + 'name': 'xorshift128+', + 'state': Array.from( STATE ), + 'params': [] + }; + } + + /** + * Returns the seed. + * + * @private + * @returns {bigint} seed + */ + function getSeed() { + return STATE[ SEED_OFFSET ]; + } + + setReadOnly( next, 'NAME', 'xorshift128+' ); + setReadOnly( next, 'normalized', norm ); + setReadOnly( next, 'copy', copy ); + setReadOnly( next, 'toJSON', toJSON ); + setReadOnlyAccessor( next, 'seed', getSeed ); + setReadWriteAccessor( next, 'state', getState, setState ); + + return next; +} + + +// EXPORTS // + +module.exports = factory; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/lib/index.js b/lib/node_modules/@stdlib/random/base/xorshift128/lib/index.js new file mode 100644 index 000000000000..c86ce9dd3ebd --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/index.js @@ -0,0 +1,54 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Xorshift128+ pseudorandom number generator. +* +* @module @stdlib/random/base/xorshift128 +* +* @example +* var xorshift128 = require( '@stdlib/random/base/xorshift128' ); +* +* // Create a PRNG with a seed +* var rng = xorshift128( { seed: 12345n } ); +* // returns +* +* var v = rng(); +* // returns +* +* @example +* var xorshift128 = require( '@stdlib/random/base/xorshift128' ); +* +* // Without arguments, creates with random seed +* var rng = xorshift128(); +* // returns +* +* var v = rng(); +* // returns +*/ + +// MODULES // + +var factory = require( './factory.js' ); + + +// EXPORTS // + +module.exports = factory; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/lib/main.js b/lib/node_modules/@stdlib/random/base/xorshift128/lib/main.js new file mode 100644 index 000000000000..49ca6703919e --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/main.js @@ -0,0 +1,62 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Default Xorshift128+ PRNG instance. +* +* Note: This file is provided for backward compatibility with stdlib patterns. +* Most users should use the factory directly via index.js. +*/ + +// MODULES // + +var factory = require( './factory.js' ); +var randi32 = require( './rand_int64.js' ); + + +// MAIN // + +/** +* Default Xorshift128+ PRNG instance. +* +* This is a pre-created PRNG instance with a random seed. +* It can be used directly for generating random numbers. +* +* @name xorshift128plus +* @type {Function} +* @returns {bigint} pseudorandom 64-bit unsigned integer +* +* @example +* var xorshift128plus = require( '@stdlib/random/base/xorshift128/lib/main.js' ); +* +* var v = xorshift128plus(); +* // returns +* +* var v = xorshift128plus.normalized(); +* // returns +*/ +var xorshift128plus = factory({ + 'seed': randi32() +}); + + +// EXPORTS // + +module.exports = xorshift128plus; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int64.js b/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int64.js new file mode 100644 index 000000000000..3ab43f901970 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int64.js @@ -0,0 +1,71 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Generates random 32-bit integers for seed generation. +* +* This module generates random 32-bit unsigned integer seed values +* using Math.random(). These are used to seed the Xorshift128+ PRNG +* when no explicit seed is provided. +* +* Two 32-bit values are combined to create a 64-bit seed: +* seed = (high << 32) | low +* +* This provides better entropy than using a single 32-bit value. +*/ + +// MODULES // + +var randu = require( '@stdlib/random/base/randu' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var BigInt = require( '@stdlib/bigint/ctor' ); + +/** +* Generates a random 32-bit unsigned integer. +* +* @private +* @returns {bigint} random 32-bit value as BigInt +* +* @example +* var seed = randi32(); +* // throws +*/ +function randi32() { + var seed; + var high; + var low; + + // Generate two 32-bit values and combine them into a 64-bit value + high = floor( randu() * 0x100000000 ); + low = floor( randu() * 0x100000000 ); + seed = (BigInt( high ) << BigInt(32)) | BigInt( low ); + + // Ensure non-zero seed + if ( seed === BigInt(0) ) { + seed = BigInt(1); + } + + return seed; +} + + +// EXPORTS // + +module.exports = randi32; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/package.json b/lib/node_modules/@stdlib/random/base/xorshift128/package.json new file mode 100644 index 000000000000..a163ae58b0cb --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/package.json @@ -0,0 +1,82 @@ +{ + "name": "@stdlib/random/base/xorshift128plus", + "version": "0.1.0", + "description": "A 128-bit xorshift pseudorandom number generator (PRNG) with a period of approximately 2^128.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib/index.js", + "directories": { + "benchmark": "./benchmark", + "doc": "./docs", + "example": "./examples", + "include": "./include", + "lib": "./lib", + "src": "./src", + "test": "./test" + }, + "types": "./docs/types", + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=12.0.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "stdmath", + "mathematics", + "math", + "statistics", + "stats", + "prng", + "pseudorandom", + "random", + "rand", + "randint", + "randu", + "uniform", + "generator", + "xorshift", + "xorshift128", + "xorshift128plus", + "xor", + "shift", + "mersenne", + "twister", + "seed", + "seedable", + "bigint", + "64-bit", + "period", + "equidistribution" + ] +} diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/test/test.js b/lib/node_modules/@stdlib/random/base/xorshift128/test/test.js new file mode 100644 index 000000000000..a192ab8f5fc7 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/test/test.js @@ -0,0 +1,519 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var isBigInt = require( '@stdlib/assert/is-bigint' ); +var BigInt = require( '@stdlib/bigint/ctor' ); +var xorshift128plus = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof xorshift128plus, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'calling the function without arguments returns a PRNG function', function test( t ) { + var rng = xorshift128plus(); + t.strictEqual( typeof rng, 'function', 'returns a function' ); + t.end(); +}); + +tape( 'calling with empty options object returns a PRNG function', function test( t ) { + var rng = xorshift128plus( {} ); + t.strictEqual( typeof rng, 'function', 'returns a function' ); + t.end(); +}); + +tape( 'calling with a number seed returns a PRNG function', function test( t ) { + var rng = xorshift128plus({ + 'seed': 12345 + }); + t.strictEqual( typeof rng, 'function', 'returns a function' ); + t.end(); +}); + +tape( 'calling with a BigInt seed returns a PRNG function', function test( t ) { + var rng = xorshift128plus({ + 'seed': BigInt( 12345 ) + }); + t.strictEqual( typeof rng, 'function', 'returns a function' ); + t.end(); +}); + +tape( 'seeded generators with same seed produce identical sequences', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': BigInt( 54321 ) + }); + var rng2 = xorshift128plus({ + 'seed': BigInt( 54321 ) + }); + var i; + + for ( i = 0; i < 100; i++ ) { + t.strictEqual( rng1().toString(), rng2().toString(), 'iteration '+i+': same output' ); + } + t.end(); +}); + +tape( 'different seeds produce different sequences', function test( t ) { + var different = 0; + var rng1 = xorshift128plus({ + 'seed': BigInt( 111 ) + }); + var rng2 = xorshift128plus({ + 'seed': BigInt( 222 ) + }); + var i; + + for ( i = 0; i < 100; i++ ) { + if ( rng1().toString() !== rng2().toString() ) { + different += 1; + } + } + t.strictEqual( different > 95, true, 'most values are different' ); + t.end(); +}); + +tape( 'zero seed is converted to 1n', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': BigInt( 0 ) + }); + var rng2 = xorshift128plus({ + 'seed': BigInt( 1 ) + }); + var i; + + for ( i = 0; i < 50; i++ ) { + t.strictEqual( rng1().toString(), rng2().toString(), 'iteration '+i+': same output' ); + } + t.end(); +}); + +tape( 'accepts number seed and converts to BigInt', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': 12345 + }); + var rng2 = xorshift128plus({ + 'seed': BigInt( 12345 ) + }); + var i; + + for ( i = 0; i < 50; i++ ) { + t.strictEqual( rng1().toString(), rng2().toString(), 'iteration '+i+': same output' ); + } + t.end(); +}); + +tape( 'the PRNG returns BigInt values', function test( t ) { + var rng = xorshift128plus({ + 'seed': BigInt( 42 ) + }); + var v; + var i; + + for ( i = 0; i < 100; i++ ) { + v = rng(); + t.strictEqual( isBigInt( v ), true, 'iteration '+i+': returns BigInt' ); + } + t.end(); +}); + +tape( 'the PRNG returns values in [0, 2^64)', function test( t ) { + var rng = xorshift128plus({ + 'seed': BigInt( 42 ) + }); + var max = BigInt( '0xffffffffffffffff' ); + var v; + var i; + + for ( i = 0; i < 100; i++ ) { + v = rng(); + t.strictEqual( v >= BigInt( 0 ) && v <= max, true, 'iteration '+i+': value in valid range' ); + } + t.end(); +}); + +tape( 'the normalized method returns numbers in [0, 1)', function test( t ) { + var rng = xorshift128plus({ + 'seed': BigInt( 42 ) + }); + var v; + var i; + + for ( i = 0; i < 1000; i++ ) { + v = rng.normalized(); + t.strictEqual( typeof v, 'number', 'iteration '+i+': returns number' ); + t.strictEqual( v >= 0.0 && v < 1.0, true, 'iteration '+i+': in [0, 1)' ); + } + t.end(); +}); + +tape( 'normalized sequences are deterministic', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': BigInt( 99999 ) + }); + var rng2 = xorshift128plus({ + 'seed': BigInt( 99999 ) + }); + var i; + + for ( i = 0; i < 100; i++ ) { + t.strictEqual( rng1.normalized(), rng2.normalized(), 'iteration '+i+': same normalized output' ); + } + t.end(); +}); + +tape( 'the PRNG has a seed property', function test( t ) { + var seed; + var rng = xorshift128plus({ + 'seed': BigInt( 12345 ) + }); + seed = rng.seed; + + t.strictEqual( isBigInt( seed ), true, 'seed is BigInt' ); + t.strictEqual( seed, BigInt( 12345 ), 'seed value is correct' ); + t.end(); +}); + +tape( 'the PRNG has a state property', function test( t ) { + var state; + var rng = xorshift128plus({ + 'seed': BigInt( 12345 ) + }); + state = rng.state; + + t.strictEqual( Array.isArray( state ), true, 'state is Array' ); + t.strictEqual( state.length, 6, 'state has correct length' ); + t.strictEqual( state[ 0 ], BigInt( 1 ), 'version is 1n' ); + t.strictEqual( state[ 1 ], BigInt( 2 ), 'state length is 2n' ); + t.strictEqual( isBigInt( state[ 2 ] ), true, 'state[0] is BigInt' ); + t.strictEqual( isBigInt( state[ 3 ] ), true, 'state[1] is BigInt' ); + t.strictEqual( state[ 4 ], BigInt( 1 ), 'seed length is 1n' ); + t.strictEqual( state[ 5 ], BigInt( 12345 ), 'seed is stored' ); + t.end(); +}); + +tape( 'calling .copy() returns an independent PRNG with same state', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': BigInt( 54321 ) + }); + var rng2; + var v1; + var v2; + var i; + + // Generate some values to advance state + for ( i = 0; i < 50; i++ ) { + rng1(); + } + + // Copy the PRNG + rng2 = rng1.copy(); + + // Both should generate identical sequences from this point + for ( i = 0; i < 100; i++ ) { + v1 = rng1(); + v2 = rng2(); + t.strictEqual( v1.toString(), v2.toString(), 'iteration '+i+': identical output' ); + } + t.end(); +}); + +tape( 'setting state via .state property restores sequence', function test( t ) { + var state; + var rng = xorshift128plus({ + 'seed': BigInt( 54321 ) + }); + var arr; + var i; + + // Generate some values + for ( i = 0; i < 100; i++ ) { + rng(); + } + + // Capture state + state = rng.state; + + // Generate more values + arr = []; + for ( i = 0; i < 100; i++ ) { + arr.push( rng() ); + } + + // Restore state + rng.state = state; + + // Verify sequence is replayed + for ( i = 0; i < 100; i++ ) { + t.strictEqual( rng().toString(), arr[ i ].toString(), 'iteration '+i+': restored sequence' ); + } + t.end(); +}); + +tape( 'copy option=true makes independent copy', function test( t ) { + var state = [ + BigInt( 1 ), + BigInt( 2 ), + BigInt( 12345 ), + BigInt( 67890 ), + BigInt( 1 ), + BigInt( 12345 ) + ]; + var rng1 = xorshift128plus({ + 'state': state, + 'copy': true + }); + var rng2 = xorshift128plus({ + 'state': state, + 'copy': true + }); + var v1; + var v2; + + state[ 2 ] = BigInt( 99999 ); // Modify original state array + + v1 = rng1(); + v2 = rng2(); + + t.strictEqual( v1.toString(), v2.toString(), 'copies are independent of original' ); + t.end(); +}); + +tape( 'the PRNG has a toJSON method', function test( t ) { + var json; + var rng = xorshift128plus({ + 'seed': BigInt( 12345 ) + }); + json = rng.toJSON(); + + t.strictEqual( typeof json, 'object', 'returns object' ); + t.strictEqual( json.type, 'PRNG', 'type is PRNG' ); + t.strictEqual( json.name, 'xorshift128+', 'name is correct' ); + t.strictEqual( Array.isArray( json.state ), true, 'state is array' ); + t.strictEqual( json.state.length, 6, 'state array has correct length' ); + t.end(); +}); + +tape( 'the PRNG has a NAME property', function test( t ) { + var rng = xorshift128plus(); + t.strictEqual( rng.NAME, 'xorshift128+', 'NAME is correct' ); + t.end(); +}); + +tape( 'throws error if options is not an object', function test( t ) { + var values; + var i; + + values = [ + 'string', + 123, + true, + null, + undefined, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws(badValue( values[ i ] ), TypeError, 'throws TypeError for '+typeof values[ i ]); + } + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus( value ); + }; + } +}); + +tape( 'throws error if seed is not a number or BigInt', function test( t ) { + var values; + var i; + + values = [ + 'string', + true, + null, + undefined, + {}, + [] + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws(badValue( values[ i ] ), TypeError, 'throws TypeError for '+typeof values[ i ]); + } + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus({ + 'seed': value + }); + }; + } +}); + +tape( 'throws error if copy option is not a boolean', function test( t ) { + var values = [ 'string', 123, null, undefined, {}, [] ]; + var i; + + for ( i = 0; i < values.length; i++ ) { + t.throws(badValue( values[ i ] ), TypeError, 'throws TypeError for '+typeof values[ i ]); + } + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus({ + 'copy': value + }); + }; + } +}); + +tape( 'throws error if state is not an Array', function test( t ) { + var values = [ 'string', 123, true, null, undefined, {}, function noop() {} ]; + var i; + + for ( i = 0; i < values.length; i++ ) { + t.throws(badValue( values[ i ] ), TypeError, 'throws TypeError for '+typeof values[ i ]); + } + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus({ + 'state': value + }); + }; + } +}); + +tape( 'throws error if state has incorrect length', function test( t ) { + var values = [ + [], + [ BigInt( 1 ) ], + [ BigInt( 1 ), BigInt( 2 ) ] + ]; + var i; + + for ( i = 0; i < values.length; i++ ) { + t.throws(badValue( values[ i ] ), RangeError, 'throws RangeError for length '+values[ i ].length); + } + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus({ + 'state': value + }); + }; + } +}); + +tape( 'throws error if state has incorrect version', function test( t ) { + var state = [ + BigInt( 0 ), + BigInt( 2 ), + BigInt( 12345 ), + BigInt( 67890 ), + BigInt( 1 ), + BigInt( 12345 ) + ]; + + t.throws(function testFn() { + xorshift128plus({ + 'state': state + }); + }, RangeError, 'throws RangeError for invalid version'); + t.end(); +}); + +tape( 'throws error if state has incorrect state length', function test( t ) { + var state = [ + BigInt( 1 ), + BigInt( 1 ), + BigInt( 12345 ), + BigInt( 67890 ), + BigInt( 1 ), + BigInt( 12345 ) + ]; + + t.throws(function testFn() { + xorshift128plus({ + 'state': state + }); + }, RangeError, 'throws RangeError for invalid state length'); + t.end(); +}); + +tape( 'the PRNG has good period (state doesn\'t cycle quickly)', function test( t ) { + var firstVal; + var found; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': BigInt( 42 ) + }); + firstVal = rng(); + found = false; + + // Generate 10000 values and check we don't see the initial state repeated + for ( i = 1; i < 10000; i++ ) { + if ( rng().toString() === firstVal.toString() ) { + found = true; + break; + } + } + + t.strictEqual( found, false, 'no premature cycling detected in 10000 iterations' ); + t.end(); +}); + +tape( 'normalized output is reasonably uniform', function test( t ) { + var ratio; + var high = 0; + var rng = xorshift128plus({ + 'seed': BigInt( 42 ) + }); + var low = 0; + var v; + var i; + + for ( i = 0; i < 10000; i++ ) { + v = rng.normalized(); + if ( v < 0.5 ) { + low += 1; + } else { + high += 1; + } + } + + ratio = low / high; + t.strictEqual( ratio > 0.9 && ratio < 1.1, true, 'distribution is approximately uniform' ); + t.end(); +});