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..0f2475d609ac --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/README.md @@ -0,0 +1,271 @@ + + +# 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. Default: a random seed. +- **state**: a state array (Uint32Array) 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 a non-negative integer: + +```javascript +var rng = factory({ + 'seed': 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 rng = factory({ + 'seed': 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.copy() + +Returns a copy of the pseudorandom number generator. + +```javascript +var rng = factory({ + 'seed': 1234 +}); + +var rng2 = rng.copy(); + +var v1 = rng(); +// returns + +var v2 = rng2(); +// returns + +var bool = ( v1 === v2 ); +// returns true +``` + +#### 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 32-bit pseudorandom integers using bitwise operations on 32-bit pairs. +- Entropy mixing: the output is computed by XORing the upper and lower 32 bits of the 64-bit result to preserve entropy from the full state. +- The "randomness quality" of the generator's output is suitable for general-purpose use, Monte Carlo simulations, parallel computations (with different seeds), and statistical sampling. +- 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 rng = factory({ + 'seed': 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..6b6bc27862b9 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/benchmark/benchmark.js @@ -0,0 +1,344 @@ +/** +* @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 bench = require( '@stdlib/bench' ); +var isnan = require( '@stdlib/math/base/assert/is-nan' ); +var Uint32Array = require( '@stdlib/array/uint32' ); +var pkg = require( './../package.json' ).name; +var xorshift128plus = require( './../lib' ); + + +// MAIN // + +bench( pkg+':factory', function benchmark( b ) { + var rng; + var i; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng = xorshift128plus(); + if ( typeof rng !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( isnan( rng() ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':factory:seed=', function benchmark( b ) { + var opts; + var rng; + var i; + + opts = { + 'seed': 1 + }; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + opts.seed = i + 1; + rng = xorshift128plus( opts ); + if ( typeof rng !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( isnan( rng() ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':factory:state=', function benchmark( b ) { + var state; + var opts; + var rng; + var i; + + opts = {}; + state = new Uint32Array( 4 ); + for ( i = 0; i < state.length; i++ ) { + state[ i ] = 123 + i; + } + opts.state = state; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + opts.state[ 0 ] = i + 1; + rng = xorshift128plus( opts ); + if ( typeof rng !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + + if ( isnan( rng() ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':generation', function benchmark( b ) { + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 12345 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng(); + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':generation:normalized', function benchmark( b ) { + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 54321 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng.normalized(); + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':state:get', function benchmark( b ) { + var state; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 11111 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + state = rng.state; + if ( typeof state !== 'object' ) { + b.fail( 'should return object' ); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':state:set', function benchmark( b ) { + var state; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 22222 + }); + state = new Uint32Array( 4 ); + state[ 0 ] = 1000; + state[ 1 ] = 2000; + state[ 2 ] = 3000; + state[ 3 ] = 4000; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + rng.state = state; + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':copy', function benchmark( b ) { + var copy; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 33333 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + copy = rng.copy(); + if ( typeof copy !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':toJSON', function benchmark( b ) { + var json; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 44444 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + json = rng.toJSON(); + if ( typeof json !== 'object' ) { + b.fail( 'should return object' ); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':generation:sequential', function benchmark( b ) { + var rng; + var i; + var j; + + rng = xorshift128plus({ + 'seed': 55555 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + for ( j = 0; j < 100; j++ ) { + rng(); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':multiple:instances', function benchmark( b ) { + var rngs; + var i; + var j; + + rngs = []; + for ( i = 0; i < 100; i++ ) { + rngs.push( xorshift128plus({ + 'seed': i + 1 + }) ); + } + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + for ( j = 0; j < rngs.length; j++ ) { + rngs[ j ](); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':mixed:operations', function benchmark( b ) { + var state; + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 66666 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + // Generate regular value + rng(); + + // Generate normalized value + rng.normalized(); + + // Access state + state = rng.state; + if ( typeof state !== 'object') { + b.fail( 'should return object' ); + } + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':state:cycle', function benchmark( b ) { + var state; + var rng1; + var rng2; + var i; + + rng1 = xorshift128plus({ + 'seed': 77777 + }); + rng2 = xorshift128plus({ + 'seed': 88888 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + // Save state from rng1 + state = rng1.state; + + // Restore state to rng2 + rng2.state = state; + + // Generate from restored PRNG + rng2(); + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+':entropy:mixing', function benchmark( b ) { + var rng; + var i; + + rng = xorshift128plus({ + 'seed': 99999 + }); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + // Core generation with entropy mixing (XOR of upper/lower 32 bits) + rng(); + } + b.toc(); + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/docs/repl.txt b/lib/node_modules/@stdlib/random/base/xorshift128/docs/repl.txt new file mode 100644 index 000000000000..22fcb83270c1 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/docs/repl.txt @@ -0,0 +1,176 @@ + +{{alias}}() + Returns a xorshift128+ pseudorandom number generator. + + This pseudorandom number generator (PRNG) is a 128-bit xorshift PRNG + with a period of approximately 2^128. + + The generator produces 32-bit pseudorandom unsigned integers using + bitwise operations on 32-bit pairs. Entropy is preserved via XOR mixing + of the upper and lower 32 bits of the 64-bit internal result. + + The generator is suitable for general-purpose use, Monte Carlo + simulations, and statistical sampling. For cryptographic applications, + use a cryptographically secure pseudorandom number generator (CSPRNG). + + Returns + ------- + rng: Function + Pseudorandom number generator (PRNG). + + Examples + -------- + > var rng = {{alias}}(); + > var v = rng() + + + +{{alias}}([options]) + Returns a xorshift128+ pseudorandom number generator with specified options. + + Parameters + ---------- + options: Object (optional) + Options. + + options.seed: integer (optional) + Pseudorandom number generator seed. The seed must be a non-negative + integer. + + options.state: Uint32Array (optional) + Pseudorandom number generator state. If provided, the `seed` option + is ignored. The state array must have length 4. + + options.copy: boolean (optional) + 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. + + Returns + ------- + rng: Function + Pseudorandom number generator (PRNG). + + Examples + -------- + // Basic usage: + > var rng = {{alias}}(); + > var v = rng() + + > v = rng() + + // Provide a seed: + > rng = {{alias}}( { 'seed': 1234 } ); + > v = rng() + + + +rng() + Returns a pseudorandom 32-bit unsigned integer. + + Returns + ------- + r: number + Pseudorandom number (32-bit unsigned integer). + + Examples + -------- + > var rng = {{alias}}(); + > var v = rng() + + + +rng.normalized() + Returns a pseudorandom number on the interval `[0,1)`. + + Returns + ------- + r: number + Pseudorandom number on [0,1). + + Examples + -------- + > var rng = {{alias}}(); + > var v = rng.normalized() + + + +rng.copy() + Returns a copy of the pseudorandom number generator. + + Returns + ------- + copy: Function + Copy of the pseudorandom number generator. + + Examples + -------- + > var rng = {{alias}}(); + > var copy = rng.copy(); + > var v1 = rng(); + > var v2 = copy(); + + +rng.NAME + Generator name. + + Examples + -------- + > var rng = {{alias}}(); + > var str = rng.NAME + 'xorshift128+' + + +rng.seed + Pseudorandom number generator seed. + + Examples + -------- + > var rng = {{alias}}( { 'seed': 1234 } ); + > var seed = rng.seed + 233179498 + + +rng.state + Generator state. + + Examples + -------- + > var rng = {{alias}}(); + > var v = rng(); + > v = rng(); + > v = rng(); + + // Get the current state: + > var state = rng.state + + + > v = rng(); + > v = rng(); + + // Set the state: + > rng.state = state; + + // Replay the last two pseudorandom numbers: + > v = rng(); + > v = rng(); + + +rng.toJSON() + Serializes the pseudorandom number generator as a JSON object. + + Returns + ------- + out: Object + JSON representation. + + Examples + -------- + > var rng = {{alias}}(); + > var o = rng.toJSON() + { 'type': 'PRNG', 'name': 'xorshift128+', 'state': [...], 'params': [] } + + See Also + -------- \ No newline at end of file diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/index.d.ts b/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/index.d.ts new file mode 100644 index 000000000000..63570a6b83fb --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/index.d.ts @@ -0,0 +1,168 @@ +/* +* @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. +*/ + +// TypeScript Version: 4.1 + +/// + +/** +* Interface defining `factory` options. +*/ +interface Options { + /** + * Pseudorandom number generator seed. + */ + seed?: number; + + /** + * Pseudorandom number generator state. + */ + state?: Uint32Array; + + /** + * Specifies whether to copy a provided pseudorandom number generator state. + */ + copy?: boolean; +} + +/** +* Interface for PRNG properties and methods. +*/ +interface PRNG { + /** + * Generator name. + */ + readonly NAME: string; + + /** + * PRNG seed. + */ + readonly seed: number; + + /** + * PRNG state. + */ + state: Uint32Array; + + /** + * Serializes the pseudorandom number generator as a JSON object. + * + * @returns JSON representation + */ + toJSON(): { + type: string; + name: string; + state: Uint32Array; + params: Array; + }; +} + +/** +* Interface for generating pseudorandom 32-bit unsigned integers. +*/ +interface NullaryFunction extends PRNG { + /** + * Returns a pseudorandom 32-bit unsigned integer. + * + * @returns pseudorandom number + */ + (): number; + + /** + * Returns a pseudorandom number on the interval `[0,1)`. + * + * @returns pseudorandom number + */ + normalized(): number; + + /** + * Returns a copy of the PRNG. + * + * @returns copy of the PRNG + */ + copy(): NullaryFunction; +} + +/** +* Interface for the xorshift128+ PRNG factory. +*/ +/** + * Returns a xorshift128+ pseudorandom number generator. + * + * @param options - function options + * @param options.seed - pseudorandom number generator seed + * @param options.state - pseudorandom number generator state + * @param options.copy - boolean indicating whether to copy a provided pseudorandom number generator state (default: true) + * @throws must provide valid options + * @returns pseudorandom number generator + * + * @example + * var rng = factory(); + * var v = rng(); + * // returns + * + * @example + * var rng = factory({ + * 'seed': 12345 + * }); + * var v = rng(); + * // returns + */ +type Factory = ( options?: Options ) => NullaryFunction; + +/** +* Returns a xorshift128+ pseudorandom number generator. +* +* ## Notes +* +* - This pseudorandom number generator (PRNG) is a 128-bit xorshift PRNG with a period of approximately 2^128. +* - The generator produces 32-bit pseudorandom unsigned integers using bitwise operations on 32-bit pairs. +* - Entropy is preserved via XOR mixing of upper and lower 32 bits of the 64-bit internal result. +* - The generator is suitable for general-purpose use, Monte Carlo simulations, and statistical sampling. +* - For cryptographic applications, use a cryptographically secure pseudorandom number generator (CSPRNG). +* +* @param options - function options +* @param options.seed - pseudorandom number generator seed +* @param options.state - pseudorandom number generator state +* @param options.copy - boolean indicating whether to copy a provided pseudorandom number generator state (default: true) +* @throws must provide valid options +* @returns pseudorandom number generator +* +* @example +* var rng = factory(); +* var v = rng(); +* // returns +* +* @example +* var rng = factory(); +* var v = rng.normalized(); +* // returns +* +* @example +* var rng = factory({ +* 'seed': 12345 +* }); +* var v = rng(); +* // returns +*/ +declare var factory: Factory; + + +// EXPORTS // + +export = factory; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/test.ts b/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/test.ts new file mode 100644 index 000000000000..877df1249a94 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/docs/types/test.ts @@ -0,0 +1,203 @@ +/* +* @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. +*/ + +import factory = require( './index' ); + +/** +* TESTS +*/ + +// The factory function returns a function... +{ + const rng = factory(); + rng(); // $ExpectType number + rng.normalized(); // $ExpectType number + rng.copy(); // $ExpectType NullaryFunction +} + +// The factory function accepts a seed option... +{ + const rng = factory( { + 'seed': 1234 + } ); + rng(); // $ExpectType number +} + +// The factory function accepts a state option... +{ + const state = new Uint32Array( 4 ); + const rng = factory( { + 'state': state + } ); + rng(); // $ExpectType number +} + +// The factory function accepts both seed and state options... +{ + const state = new Uint32Array( 4 ); + const rng = factory( { + 'seed': 1234, + 'state': state + } ); + rng(); // $ExpectType number +} + +// The factory function accepts a copy option... +{ + const rng = factory( { + 'copy': true + } ); + rng(); // $ExpectType number +} + +// The factory function returns an object with NAME property... +{ + const rng = factory(); + String( rng.NAME ); // $ExpectType string +} + +// The factory function returns an object with seed property... +{ + const rng = factory(); + Number( rng.seed ); // $ExpectType number +} + +// The factory function returns an object with state property (setter)... +{ + const rng = factory(); + const state = new Uint32Array( 4 ); + rng.state = state; +} + +// The factory function returns an object with toJSON method... +{ + const rng = factory(); + rng.toJSON(); // $ExpectType { type: string; name: string; state: Uint32Array; params: unknown[]; } +} + +// The factory function returns an object with copy method... +{ + const rng = factory(); + rng.copy(); // $ExpectType NullaryFunction +} + +// The factory function returns an object with normalized method... +{ + const rng = factory(); + rng.normalized(); // $ExpectType number +} + +// Test that factory can be called with options parameter... +{ + const rng = factory( { + 'seed': 12345, + 'copy': false + } ); + rng(); // $ExpectType number + rng.normalized(); // $ExpectType number + rng.copy(); // $ExpectType NullaryFunction +} + +// Test that state array must be Uint32Array... +{ + const rng1 = factory(); + const rng2 = factory( { + 'state': rng1.state + } ); + rng2(); // $ExpectType number +} + +// Test that seed must be a number... +{ + const rng = factory( { + 'seed': 999 + } ); + rng(); // $ExpectType number +} + +// Test getting and setting state... +{ + const rng1 = factory( { + 'seed': 42 + } ); + + rng1(); + rng1(); + + const rng2 = factory( { + 'state': rng1.state + } ); + + rng2(); // $ExpectType number +} + +// Test copy functionality... +{ + const rng1 = factory( { + 'seed': 100 + } ); + + const rng2 = rng1.copy(); + + rng1(); // $ExpectType number + rng2(); // $ExpectType number +} + +// Test normalized output... +{ + const rng = factory( { + 'seed': 555 + } ); + + rng.normalized(); // $ExpectType number +} + +// Test toJSON serialization... +{ + const rng = factory( { + 'seed': 777 + } ); + + rng.toJSON(); // $ExpectType { type: string; name: string; state: Uint32Array; params: unknown[]; } +} + +// Test NAME constant... +{ + const rng = factory(); + String( rng.NAME ); // $ExpectType string +} + +// Test seed property... +{ + const rng = factory( { + 'seed': 1234 + } ); + + Number( rng.seed ); // $ExpectType number +} + +// Test consecutive generation... +{ + const rng = factory( { + 'seed': 2000 + } ); + + rng(); // $ExpectType number + rng(); // $ExpectType number + rng(); // $ExpectType number +} 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..986583488fbd --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/examples/index.js @@ -0,0 +1,52 @@ +/** +* @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 xorshift128plus = require( './../lib' ); + +var rng = xorshift128plus(); +console.log( '\nDefault Generator:' ); +var i; +for ( i = 0; i < 10; i++ ) { + console.log( rng() ); +} + +// Create a new pseudorandom number generator... +var rand1 = xorshift128plus({ + 'seed': 1234 +}); +var rand2 = xorshift128plus({ + 'seed': 1234 +}); +console.log( '\nSame seed (reproducible):' ); +for ( i = 0; i < 5; i++ ) { + console.log( rand1(), rand2() ); +} + +// Create another pseudorandom number generator using a previous seed... +var rand3 = xorshift128plus({ + 'seed': 111 +}); +var rand4 = xorshift128plus({ + 'seed': 999 +}); +console.log( '\nDifferent seeds (independent);' ); +for ( i = 0; i < 5; i++ ) { + console.log( rand3(), rand4() ); +} 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..a1b400130918 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/factory.js @@ -0,0 +1,460 @@ +/** +* @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 32-bit pseudorandom numbers using +* 64-bit state values (stored as 32-bit pairs). Entropy from both halves +* of the 64-bit output is mixed via XOR to preserve quality. +* +* ## Notes +* +* - Uses Uint32Array for state storage (@stdlib compatible). +* - Returns 32-bit unsigned integers via entropy mixing. +* - Entropy mixing: XOR(upper_32bits, lower_32bits). +* - Period: approximately 2^128. +* +* @example +* var factory = require( '@stdlib/random/base/xorshift128plus' ); +* +* var rng = factory({ +* 'seed': 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 Uint32Array = require( '@stdlib/array/uint32' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var format = require( '@stdlib/string/format' ); +var randu = require( '@stdlib/random/base/randu' ); + + +// VARIABLES // + +/** +* State array length (4 × 32-bit values = 2 × 64-bit values). +* +* @private +* @type {number} +*/ +var STATE_ARRAY_SIZE = 4; + +/** +* State array indices for first 64-bit value (s0). +* +* @private +* @type {number} +*/ +var S0_HI_INDEX = 0; +var S0_LO_INDEX = 1; + +/** +* State array indices for second 64-bit value (s1). +* +* @private +* @type {number} +*/ +var S1_HI_INDEX = 2; +var S1_LO_INDEX = 3; + +/** +* Normalization constant for converting to [0,1). +* +* @private +* @type {number} +*/ +var NORMALIZATION_CONSTANT = 1.0 / 0x100000000; + + +// FUNCTIONS // + +/** +* Performs left shift on 64-bit value stored as two 32-bit parts. +* +* @private +* @param {number} hi - high 32 bits +* @param {number} lo - low 32 bits +* @param {number} shift - shift amount (0-63) +* @returns {Array} [hi_shifted, lo_shifted] +*/ +function shift64Left( hi, lo, shift ) { + var newHi; + var newLo; + + if ( shift === 0 ) { + return [ hi, lo ]; + } + if ( shift >= 64 ) { + return [ 0, 0 ]; + } + if ( shift >= 32 ) { + return [ (lo << (shift - 32)) >>> 0, 0 ]; + } + newHi = ((hi << shift) | (lo >>> (32 - shift))) >>> 0; + newLo = (lo << shift) >>> 0; + return [ newHi, newLo ]; +} + +/** +* Performs right shift on 64-bit value stored as two 32-bit parts. +* +* @private +* @param {number} hi - high 32 bits +* @param {number} lo - low 32 bits +* @param {number} shift - shift amount (0-63) +* @returns {Array} [hi_shifted, lo_shifted] +*/ +function shift64Right( hi, lo, shift ) { + var newHi; + var newLo; + + if ( shift === 0 ) { + return [ hi, lo ]; + } + if ( shift >= 64 ) { + return [ 0, 0 ]; + } + if ( shift >= 32 ) { + return [ 0, (hi >>> (shift - 32)) >>> 0 ]; + } + newHi = (hi >>> shift) >>> 0; + newLo = (((lo >>> shift) | (hi << (32 - shift))) >>> 0); + + return [ newHi, newLo ]; +} + +/** +* Adds two 64-bit values with carry handling. +* +* @private +* @param {number} hi1 - first value high 32 bits +* @param {number} lo1 - first value low 32 bits +* @param {number} hi2 - second value high 32 bits +* @param {number} lo2 - second value low 32 bits +* @returns {Array} [hi_result, lo_result] +*/ +function add64( hi1, lo1, hi2, lo2 ) { + var carry; + var lo; + var hi; + lo = (lo1 + lo2) >>> 0; + carry = (lo < lo1) ? 1 : 0; + hi = (hi1 + hi2 + carry) >>> 0; + return [ hi, lo ]; +} + +/** +* Initializes state from seed. +* +* @private +* @param {Uint32Array} state - state array to initialize +* @param {number} seed - seed value +* @returns {Uint32Array} initialized state +*/ +function initState( state, seed ) { + // Use seed to initialize s0 and s1 + // Based on SplitMix64 constants for good distribution + var s0 = seed >>> 0; + var s1 = ((seed + 0x9e3779b97f4a7c15) >>> 0) ^ s0; + + // Create initial 64-bit values + + // s0_initial = seed * 2^32 + s0 + + // s1_initial = seed * 2^32 + s1 + state[ S0_HI_INDEX ] = (s0 * 1103515245) >>> 0; + state[ S0_LO_INDEX ] = (s1 * 1103515245) >>> 0; + state[ S1_HI_INDEX ] = (s0 + 12345) >>> 0; + state[ S1_LO_INDEX ] = (s1 + 12345) >>> 0; + + return state; +} + + +// MAIN // + +/** +* Returns an Xorshift128+ pseudorandom number generator. +* +* @param {Object} [options] - configuration object +* @param {number} [options.seed] - seed value +* @param {Uint32Array} [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 +* +* @example +* var rng = factory({ +* 'seed': 1234 +* }); +* +* var v = rng(); +* // throws 96092074 +*/ +function factory( options ) { + var STATE; + var opts; + var seed; + 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 ( !(STATE instanceof Uint32Array) ) { + throw new TypeError('invalid option. `state` must be a Uint32Array.'); + } + + if ( STATE.length !== STATE_ARRAY_SIZE ) { + throw new RangeError(format('invalid option. `state` must have length %u. Received: %u.', STATE_ARRAY_SIZE, STATE.length)); + } + + if ( opts.copy !== false ) { + tmp = new Uint32Array( 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 ( !isNonNegativeInteger( seed ) ) { + throw new TypeError('invalid option. `seed` must be a non-negative integer.'); + } + } else { + seed = floor( randu() * 0x100000000 ); + } + } + } else { + seed = floor( randu() * 0x100000000 ); + } + + if ( STATE === void 0 ) { + STATE = new Uint32Array( STATE_ARRAY_SIZE ); + STATE = initState( STATE, seed ); + } + + /** + * Generates next pseudorandom 32-bit unsigned integer. + * + * Entropy mixing approach: + * 1. Compute 64-bit output using 32-bit state pairs + * 2. Mix upper and lower 32 bits via XOR + * 3. Return 32-bit mixed result + * + * @private + * @returns {number} pseudorandom 32-bit unsigned integer + */ + function next() { + var result; + var sumHi; + var sumLo; + var s0Hi; + var s0Lo; + var s1Hi; + var s1Lo; + var t; + + // Load state + s0Hi = STATE[ S0_HI_INDEX ]; + s0Lo = STATE[ S0_LO_INDEX ]; + s1Hi = STATE[ S1_HI_INDEX ]; + s1Lo = STATE[ S1_LO_INDEX ]; + + // s1 ^= s1 << 23 + t = shift64Left( s1Hi, s1Lo, 23 ); + s1Hi ^= t[ 0 ]; + s1Lo ^= t[ 1 ]; + + // s1 ^= s1 >> 17 + t = shift64Right( s1Hi, s1Lo, 17 ); + s1Hi ^= t[ 0 ]; + s1Lo ^= t[ 1 ]; + + // s1 ^= s0 + s1Hi ^= s0Hi; + s1Lo ^= s0Lo; + + // s1 ^= s0 >> 26 + t = shift64Right( s0Hi, s0Lo, 26 ); + s1Hi ^= t[ 0 ]; + s1Lo ^= t[ 1 ]; + + // Update state + STATE[ S0_HI_INDEX ] = s1Hi; + STATE[ S0_LO_INDEX ] = s1Lo; + STATE[ S1_HI_INDEX ] = s0Hi; + STATE[ S1_LO_INDEX ] = s0Lo; + + // Result = s1 + s0 + result = add64( s1Hi, s1Lo, s0Hi, s0Lo ); + sumHi = result[ 0 ]; + sumLo = result[ 1 ]; + + // Entropy mixing + return (sumHi ^ sumLo) >>> 0; + } + /** + * Generates pseudorandom number on [0, 1). + * + * @private + * @returns {number} pseudorandom number + */ + function norm() { + return next() * NORMALIZATION_CONSTANT; + } + + /** + * Returns current state. + * + * @private + * @returns {Uint32Array} state + */ + function getState() { + var out = new Uint32Array( STATE_ARRAY_SIZE ); + var i; + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + out[ i ] = STATE[ i ]; + } + return out; + } + + /** + * Sets state. + * + * @private + * @param {Uint32Array} s - new state + * @throws {TypeError} state must be Uint32Array + * @throws {RangeError} state must have correct length + */ + function setState( s ) { + var i; + + if ( !(s instanceof Uint32Array) ) { + throw new TypeError('invalid argument. `state` must be a Uint32Array.'); + } + + if ( s.length !== STATE_ARRAY_SIZE ) { + throw new RangeError(format('invalid argument. `state` must have length %u. Received: %u.', STATE_ARRAY_SIZE, s.length)); + } + + if ( opts.copy === false ) { + if ( opts.state ) { + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + STATE[ i ] = s[ i ]; + } + } else { + STATE = s; + opts.state = true; + } + } else { + for ( i = 0; i < STATE_ARRAY_SIZE; i++ ) { + STATE[ i ] = s[ i ]; + } + } + } + + /** + * Returns copy of PRNG. + * + * @private + * @returns {Function} copy + */ + function copy() { + return factory({ + 'state': getState(), + 'copy': true + }); + } + + /** + * Serializes state as JSON. + * + * @private + * @returns {Object} JSON representation + */ + function toJSON() { + return { + 'type': 'PRNG', + 'name': 'xorshift128+', + 'state': getState(), + 'params': [] + }; + } + + /** + * Returns seed. + * + * @private + * @returns {number} seed + */ + function getSeed() { + return STATE[ S0_HI_INDEX ]; + } + + 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..ca61fbd7e40d --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/index.js @@ -0,0 +1,68 @@ +/** +* @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 (PRNG). +* +* ## Algorithm +* +* The Xorshift128+ algorithm generates 32-bit pseudorandom numbers using +* 64-bit state values (stored as 32-bit pairs). Entropy from both halves +* of the 64-bit output is mixed via XOR to preserve quality. +* +* ## Notes +* +* - Uses Uint32Array for state storage (@stdlib compatible). +* - Returns 32-bit unsigned integers via entropy mixing. +* - Entropy mixing: XOR(upper_32bits, lower_32bits). +* - Period: approximately 2^128. +* +* @example +* var xorshift128plus = require( '@stdlib/random/base/xorshift128' ); +* +* // Create PRNG with fixed seed for reproducibility +* var rng = xorshift128plus({ +* 'seed': 1234 +* }); +* +* var v = rng(); +* // returns +* +* @example +* var xorshift128plus = require( '@stdlib/random/base/xorshift128' ); +* +* // Create PRNG with random seed +* var rng = xorshift128plus(); +* +* var v = rng(); +* // returns +* +* var n = rng.normalized(); +* // 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..1aea8abfa640 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/main.js @@ -0,0 +1,59 @@ +/** +* @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 (default instance). +* +* @module @stdlib/random/base/xorshift128 +* @type {Function} +* @returns {number} pseudorandom 32-bit unsigned integer +* +* @example +* var rng = require( '@stdlib/random/base/xorshift128' ); +* +* var v = rng(); +* // returns +* +* var n = rng.normalized(); +* // throws +*/ + +// MODULES // + +var factory = require( './factory.js' ); +var randi32 = require( './rand_int32.js' ); + + +// MAIN // + +/** +* Default Xorshift128+ PRNG instance. +* +* @type {Function} +* @returns {number} pseudorandom 32-bit unsigned integer +*/ +var xorshift128plus = factory({ + 'seed': randi32() +}); + + +// EXPORTS // + +module.exports = xorshift128plus; diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int32.js b/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int32.js new file mode 100644 index 000000000000..4f15feaa6d52 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/lib/rand_int32.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 unsigned integer seeds. +* +* This module generates random 32-bit unsigned integer seed values. +* These are used to seed the Xorshift128+ PRNG when no explicit seed +* is provided by the user. +* +* @private +* @returns {number} random 32-bit unsigned integer +* +* @example +* var seed = randi32(); +* // returns +*/ + +// MODULES // + +var randu = require( '@stdlib/random/base/randu' ); +var floor = require( '@stdlib/math/base/special/floor' ); + + +// MAIN // + +/** +* Generates a random 32-bit unsigned integer seed. +* +* @private +* @returns {number} random 32-bit unsigned integer +* +* @example +* var seed = randi32(); +* // returns +*/ +function randi32() { + /* eslint-disable no-magic-numbers */ + var seed = floor( randu() * 0x100000000 ); + + /* eslint-enable no-magic-numbers */ + + // Ensure non-zero seed + if ( seed === 0 ) { + seed = 1; + } + + return seed >>> 0; // Ensure unsigned 32-bit +} + + +// 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..a5083f4c036d --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/package.json @@ -0,0 +1,79 @@ +{ + "name": "@stdlib/random/base/xorshift128", + "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", + "lib": "./lib", + "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", + "64-bit", + "period", + "equidistribution" + ] +} diff --git a/lib/node_modules/@stdlib/random/base/xorshift128/test/test.factory.js b/lib/node_modules/@stdlib/random/base/xorshift128/test/test.factory.js new file mode 100644 index 000000000000..ac9d3bba527e --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/test/test.factory.js @@ -0,0 +1,423 @@ +/** +* @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 isFunction = require( '@stdlib/assert/is-function' ); +var isUint32Array = require( '@stdlib/assert/is-uint32array' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Uint32Array = require( '@stdlib/array/uint32' ); +var factory = require( './../lib/factory.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.ok( isFunction( factory ), 'main export is a function' ); + t.end(); +}); + +tape( 'factory returns a function', function test( t ) { + var rng = factory(); + t.ok( isFunction( rng ), 'returns a function' ); + t.end(); +}); + +tape( 'factory accepts seed option', function test( t ) { + var rng; + + rng = factory({ + 'seed': 1234 + }); + t.ok( isFunction( rng ), 'returns function with seed option' ); + + rng = factory({ + 'seed': 0 + }); + t.ok( isFunction( rng ), 'returns function with seed 0' ); + + rng = factory({ + 'seed': 0xffffffff + }); + t.ok( isFunction( rng ), 'returns function with max seed' ); + + t.end(); +}); + +tape( 'factory accepts state option', function test( t ) { + var state; + var rng1 = factory({ + 'seed': 1234 + }); + var rng2; + state = rng1.state; + + rng2 = factory({ + 'state': state + }); + t.ok( isFunction( rng2 ), 'returns function with state option' ); + t.end(); +}); + +tape( 'factory accepts copy option', function test( t ) { + var state; + var rng1 = factory({ + 'seed': 5678 + }); + var rng2; + var rng3; + state = rng1.state; + + rng2 = factory({ + 'state': state, + 'copy': true + }); + t.ok( isFunction( rng2 ), 'returns function with copy=true' ); + + rng3 = factory({ + 'state': state, + 'copy': false + }); + t.ok( isFunction( rng3 ), 'returns function with copy=false' ); + + t.end(); +}); + +tape( 'factory with copy=false does not copy state', function test( t ) { + var state = new Uint32Array( 4 ); + var rng = factory({ + 'state': state, + 'copy': false + }); + + // When copy=false, modifications to original state should eventually affect RNG + state[ 0 ] = 9999; + + t.ok( isFunction( rng ), 'returns function with copy=false' ); + t.end(); +}); + +tape( 'factory with copy=true copies state', function test( t ) { + var original = new Uint32Array( 4 ); + var state1; + var state2; + var rng = factory({ + 'state': original, + 'copy': true + }); + state1 = rng.state; + state2 = rng.state; + + t.strictEqual( state1[ 0 ], state2[ 0 ], 'state not affected by modifying original' ); + t.end(); +}); + +tape( 'factory returns PRNG with correct NAME', function test( t ) { + var rng = factory({ + 'seed': 1234 + }); + t.strictEqual( rng.NAME, 'xorshift128+', 'NAME is "xorshift128+"' ); + t.end(); +}); + +tape( 'factory returns PRNG with normalized method', function test( t ) { + var rng = factory({ + 'seed': 2345 + }); + t.ok( isFunction( rng.normalized ), 'has normalized method' ); + t.end(); +}); + +tape( 'factory returns PRNG with copy method', function test( t ) { + var rng = factory({ + 'seed': 3456 + }); + t.ok( isFunction( rng.copy ), 'has copy method' ); + t.end(); +}); + +tape( 'factory returns PRNG with toJSON method', function test( t ) { + var rng = factory({ + 'seed': 4567 + }); + t.ok( isFunction( rng.toJSON ), 'has toJSON method' ); + t.end(); +}); + +tape( 'factory returns PRNG with seed getter', function test( t ) { + var rng = factory({ + 'seed': 5678 + }); + t.ok( typeof rng.seed === 'number', 'seed is accessible' ); + t.end(); +}); + +tape( 'factory returns PRNG with state getter/setter', function test( t ) { + var state; + var rng = factory({ + 'seed': 6789 + }); + + t.ok( isUint32Array( rng.state ), 'state getter works' ); + + state = new Uint32Array( 4 ); + state[ 0 ] = 1000; + state[ 1 ] = 2000; + state[ 2 ] = 3000; + state[ 3 ] = 4000; + + rng.state = state; + t.ok( isUint32Array( rng.state ), 'state setter works' ); + t.end(); +}); + +tape( 'factory throws TypeError when options is not an object', function test( t ) { + var values; + var i; + + values = [ 'invalid', 123, [] ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws on invalid options '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + factory( value ); + }; + } +}); + +tape( 'factory throws TypeError when seed is invalid', function test( t ) { + var values; + var i; + + values = [ -1, 3.14, 'invalid', {} ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badSeed( values[i] ), TypeError, 'throws on invalid seed '+values[i] ); + } + t.end(); + + function badSeed( value ) { + return function badSeed() { + factory({ + 'seed': value + }); + }; + } +}); + +tape( 'factory throws TypeError when state is not Uint32Array', function test( t ) { + var values; + var i; + + values = [ [], new Float64Array( 4 ), {} ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badState( values[i] ), TypeError, 'throws on invalid state '+values[i] ); + } + t.end(); + + function badState( value ) { + return function badState() { + factory({ + 'state': value + }); + }; + } +}); + +tape( 'factory throws RangeError when state has wrong length', function test( t ) { + var values; + var i; + + values = [ + new Uint32Array( 0 ), + new Uint32Array( 2 ), + new Uint32Array( 8 ) + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badRange( values[i] ), RangeError, 'throws on wrong state length' ); + } + t.end(); + + function badRange( value ) { + return function badRange() { + factory({ + 'state': value + }); + }; + } +}); + +tape( 'factory throws TypeError when copy is not boolean', function test( t ) { + var values; + var i; + + values = [ 'true', 1, null ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badCopy( values[i] ), TypeError, 'throws on invalid copy '+values[i] ); + } + t.end(); + + function badCopy( value ) { + return function badCopy() { + factory({ + 'seed': 1234, + 'copy': value + }); + }; + } +}); + +tape( 'factory without options uses random seed', function test( t ) { + var rng1 = factory(); + var rng2 = factory(); + var v1 = rng1(); + var v2 = rng2(); + + // Very unlikely to be equal if truly random + t.notEqual( v1, v2, 'different random seeds produce different outputs' ); + t.end(); +}); + +tape( 'factory with same seed produces deterministic sequence', function test( t ) { + var rng1 = factory({ + 'seed': 12345 + }); + var rng2 = factory({ + 'seed': 12345 + }); + var i; + + for ( i = 0; i < 100; i++ ) { + t.strictEqual( rng1(), rng2(), 'same seed at iteration ' + i ); + } + t.end(); +}); + +tape( 'factory with state produces deterministic continuation', function test( t ) { + var state; + var rng1 = factory({ + 'seed': 54321 + }); + var rng2; + var i; + + // Generate and skip values + for ( i = 0; i < 50; i++ ) { + rng1(); + } + + // Save state + state = rng1.state; + + // Create second PRNG with same state + rng2 = factory({ + 'state': state + }); + + // Verify same continuation + for ( i = 0; i < 100; i++ ) { + t.strictEqual( rng1(), rng2(), 'same state continuation at iteration ' + i ); + } + t.end(); +}); + +tape( 'factory creates independent PRNGs', function test( t ) { + var rng1 = factory({ + 'seed': 1111 + }); + var rng3 = factory({ + 'seed': 2222 + }); + var v1 = rng1(); + var v3 = rng3(); + + t.notEqual( v1, v3, 'different seeds produce different output' ); + t.end(); +}); + +tape( 'factory output is 32-bit unsigned integer', function test( t ) { + var rng = factory({ + 'seed': 99999 + }); + var v; + var i; + + for ( i = 0; i < 100; i++ ) { + v = rng(); + t.ok( v >= 0, 'output >= 0' ); + t.ok( v <= 0xffffffff, 'output <= 2^32-1' ); + t.ok( Number.isInteger( v ), 'output is integer' ); + } + t.end(); +}); + +tape( 'factory normalized output is in [0,1)', function test( t ) { + var rng = factory({ + 'seed': 88888 + }); + var v; + var i; + + for ( i = 0; i < 100; i++ ) { + v = rng.normalized(); + t.ok( v >= 0, 'normalized output >= 0' ); + t.ok( v < 1, 'normalized output < 1' ); + } + t.end(); +}); + +tape( 'factory state length is always 4', function test( t ) { + var rng1 = factory(); + var rng2 = factory({ + 'seed': 1234 + }); + var rng3 = factory({ + 'state': new Uint32Array( 4 ) + }); + + t.strictEqual( rng1.state.length, 4, 'random seed state length is 4' ); + t.strictEqual( rng2.state.length, 4, 'seeded PRNG state length is 4' ); + t.strictEqual( rng3.state.length, 4, 'state-initialized PRNG state length is 4' ); + t.end(); +}); + +tape( 'factory state is Uint32Array', function test( t ) { + var rng1 = factory(); + var rng2 = factory({ + 'seed': 1234 + }); + var rng3 = factory({ + 'state': new Uint32Array( 4 ) + }); + + t.ok( isUint32Array( rng1.state ), 'random seed state is Uint32Array' ); + t.ok( isUint32Array( rng2.state ), 'seeded PRNG state is Uint32Array' ); + t.ok( isUint32Array( rng3.state ), 'state-initialized PRNG state is Uint32Array' ); + t.end(); +}); 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..ad4bc072ad44 --- /dev/null +++ b/lib/node_modules/@stdlib/random/base/xorshift128/test/test.js @@ -0,0 +1,446 @@ +/** +* @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 isFunction = require( '@stdlib/assert/is-function' ); +var isUint32Array = require( '@stdlib/assert/is-uint32array' ); +var Uint32Array = require( '@stdlib/array/uint32' ); +var xorshift128plus = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.ok( isFunction( xorshift128plus ), 'main export is a function' ); + t.end(); +}); + +tape( 'when called with no arguments, returns a pseudorandom number generator function', function test( t ) { + var rng = xorshift128plus(); + t.ok( isFunction( rng ), 'returns a function' ); + t.end(); +}); + +tape( 'when called with a seed option, returns a PRNG with deterministic output', function test( t ) { + var rng1; + var rng2; + var v1; + var v2; + var i; + + rng1 = xorshift128plus({ + 'seed': 1234 + }); + rng2 = xorshift128plus({ + 'seed': 1234 + }); + + for ( i = 0; i < 100; i++ ) { + v1 = rng1(); + v2 = rng2(); + t.strictEqual( v1, v2, 'same seed produces same output at iteration ' + i ); + } + t.end(); +}); + +tape( 'when called with different seeds, produces different outputs', function test( t ) { + var rng1 = xorshift128plus({ + 'seed': 111 + }); + var rng2 = xorshift128plus({ + 'seed': 222 + }); + var v1 = rng1(); + var v2 = rng2(); + + t.notEqual( v1, v2, 'different seeds produce different outputs' ); + t.end(); +}); + +tape( 'generated output is a 32-bit unsigned integer', function test( t ) { + var rng = xorshift128plus({ + 'seed': 12345 + }); + var v; + var i; + + for ( i = 0; i < 100; i++ ) { + v = rng(); + t.ok( v >= 0 && v <= 0xffffffff, 'output in range [0, 2^32)' ); + t.ok( Number.isInteger( v ), 'output is integer' ); + } + t.end(); +}); + +tape( 'generated output is unsigned 32-bit', function test( t ) { + var rng = xorshift128plus({ + 'seed': 9999 + }); + var v; + + v = rng(); + t.ok( v >= 0, 'output >= 0' ); + t.ok( v < 0x100000000, 'output < 2^32' ); + t.end(); +}); + +tape( 'PRNG has a normalized method', function test( t ) { + var rng = xorshift128plus({ + 'seed': 5555 + }); + t.ok( isFunction( rng.normalized ), 'has normalized method' ); + t.end(); +}); + +tape( 'normalized method returns value in [0,1)', function test( t ) { + var rng = xorshift128plus({ + 'seed': 5555 + }); + var v; + var i; + + for ( i = 0; i < 1000; i++ ) { + v = rng.normalized(); + t.ok( v >= 0 && v < 1, 'normalized value in [0,1)' ); + } + t.end(); +}); + +tape( 'PRNG has a seed property', function test( t ) { + var rng = xorshift128plus({ + 'seed': 3333 + }); + t.ok( typeof rng.seed === 'number', 'seed property is a number' ); + t.end(); +}); + +tape( 'PRNG has a state property (getter)', function test( t ) { + var state; + var rng = xorshift128plus({ + 'seed': 4444 + }); + state = rng.state; + + t.ok( isUint32Array( state ), 'state is a Uint32Array' ); + t.strictEqual( state.length, 4, 'state has length 4' ); + t.end(); +}); + +tape( 'PRNG state property can be set', function test( t ) { + var state; + var rng1 = xorshift128plus({ + 'seed': 7777 + }); + var rng2 = xorshift128plus({ + 'seed': 1111 + }); + var v1; + var v2; + + // Save state BEFORE generating value + state = rng1.state; + + // Generate value + v1 = rng1(); + + // Restore state in rng2 + rng2.state = state; + + // Should match + v2 = rng2(); + t.strictEqual( v1, v2, 'setting state produces same sequence' ); + + t.end(); +}); + +tape( 'PRNG has a copy method', function test( t ) { + var copy; + var rng = xorshift128plus({ + 'seed': 2222 + }); + + t.ok( isFunction( rng.copy ), 'has copy method' ); + + copy = rng.copy(); + t.ok( isFunction( copy ), 'copy returns a function' ); + t.end(); +}); + +tape( 'copy method creates an independent PRNG at same state', function test( t ) { + var copy; + var rng = xorshift128plus({ + 'seed': 6666 + }); + var v1; + var v2; + var i; + + // Generate some values + for ( i = 0; i < 10; i++ ) { + rng(); + } + + // Create copy at current state + copy = rng.copy(); + + // Both should produce same sequence + for ( i = 0; i < 50; i++ ) { + v1 = rng(); + v2 = copy(); + t.strictEqual( v1, v2, 'copy at state ' + i + ' produces same output' ); + } + t.end(); +}); + +tape( 'PRNG has a toJSON method', function test( t ) { + var json; + var rng = xorshift128plus({ + 'seed': 8888 + }); + + t.ok( isFunction( rng.toJSON ), 'has toJSON method' ); + + json = rng.toJSON(); + t.ok( typeof json === 'object', 'toJSON returns object' ); + t.strictEqual( json.type, 'PRNG', 'JSON object has type property' ); + t.strictEqual( json.name, 'xorshift128+', 'JSON object has correct name' ); + t.ok( isUint32Array( json.state ), 'JSON state is Uint32Array' ); + t.end(); +}); + +tape( 'NAME property is set correctly', function test( t ) { + var rng = xorshift128plus({ + 'seed': 1111 + }); + t.strictEqual( rng.NAME, 'xorshift128+', 'NAME property is correct' ); + t.end(); +}); + +tape( 'when called with invalid options, throws error', function test( t ) { + var values; + var i; + + // Non-object options + values = [ 'invalid' ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws on non-object options' ); + } + + // Invalid seeds + values = [ -1, 3.14 ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badSeed( values[i] ), TypeError, 'throws on invalid seed '+values[i] ); + } + + // Invalid state types + values = [ [] ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badState( values[i] ), TypeError, 'throws on non-Uint32Array state' ); + } + + // Invalid state length + values = [ new Uint32Array( 2 ) ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badState( values[i] ), RangeError, 'throws on wrong state length' ); + } + + t.end(); + + function badValue( value ) { + return function badValue() { + xorshift128plus( value ); + }; + } + + function badSeed( value ) { + return function badSeed() { + xorshift128plus({ + 'seed': value + }); + }; + } + + function badState( value ) { + return function badState() { + xorshift128plus({ + 'state': value + }); + }; + } +}); + +tape( 'generated sequence has good variety', function test( t ) { + var values = []; + var unique; + var rng = xorshift128plus({ + 'seed': 9876 + }); + var i; + + for ( i = 0; i < 1000; i++ ) { + values.push( rng() ); + } + + unique = new Set( values ); + t.ok( unique.size > 990, 'output values are mostly unique' ); + t.end(); +}); + +tape( 'entropy mixing distributes bits well', function test( t ) { + var bitCounts = []; + var count; + var ratio; + var rng = xorshift128plus({ + 'seed': 5432 + }); + var v; + var i; + var b; + + // Initialize bit counters + for ( b = 0; b < 32; b++ ) { + bitCounts[ b ] = 0; + } + + // Generate values and count bits + for ( i = 0; i < 10000; i++ ) { + v = rng(); + for ( b = 0; b < 32; b++ ) { + if ( v & (1 << b) ) { + bitCounts[ b ] += 1; + } + } + } + + // Check that bits are distributed ~50/50 + count = 0; + for ( b = 0; b < 32; b++ ) { + ratio = bitCounts[ b ] / 10000; + if ( ratio > 0.40 && ratio < 0.60 ) { + count += 1; + } + } + + t.ok( count >= 25, 'most bits (~' + count + '/32) have good distribution' ); + t.end(); +}); + +tape( 'state property returns independent copy', function test( t ) { + var state2; + var state1; + var rng = xorshift128plus({ + 'seed': 1357 + }); + state1 = rng.state; + state2 = rng.state; + + t.notEqual( state1, state2, 'state getter returns new array each time' ); + + // Modifying returned state should not affect PRNG + state1[ 0 ] = 0xffffffff; + state2 = rng.state; + t.notEqual( state2[ 0 ], 0xffffffff, 'modifying returned state does not affect PRNG' ); + t.end(); +}); + +tape( 'setting invalid state throws error', function test( t ) { + var values; + var rng = xorshift128plus({ + 'seed': 2468 + }); + var i; + + // Non-Uint32Array state + values = [ [] ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws on non-Uint32Array state' ); + } + + // Wrong state length + values = [ new Uint32Array( 2 ) ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badRange( values[i] ), RangeError, 'throws on wrong state length' ); + } + + t.end(); + + function badValue( value ) { + return function badValue() { + rng.state = value; + }; + } + + function badRange( value ) { + return function badRange() { + rng.state = value; + }; + } +}); + +tape( 'normalized output matches formula', function test( t ) { + var expected; + var vNorm; + var rng2 = xorshift128plus({ + 'seed': 4680 + }); + var rng = xorshift128plus({ + 'seed': 4680 + }); + expected = (rng2() / 0x100000000); + vNorm = rng.normalized(); + + t.strictEqual( vNorm, expected, 'normalized output matches formula' ); + t.end(); +}); + +tape( 'PRNG state can be restored and continued', function test( t ) { + var state; + var rng1 = xorshift128plus({ + 'seed': 5791 + }); + var rng2 = xorshift128plus({ + 'seed': 9999 + }); + var v1; + var v2; + var i; + + // Generate some values from rng1 + for ( i = 0; i < 50; i++ ) { + rng1(); + } + + // Save state + state = rng1.state; + + // Set rng2 to this state + rng2.state = state; + + // Continue and verify same sequence + for ( i = 0; i < 100; i++ ) { + v1 = rng1(); + v2 = rng2(); + t.strictEqual( v1, v2, 'restored state produces same continuation at ' + i ); + } + t.end(); +});