seed-random
Advanced tools
Comparing version
@@ -5,3 +5,3 @@ { | ||
"description": "Generate random numbers with a seed, useful for reproducible tests", | ||
"version": "1.0.1", | ||
"version": "2.2.0", | ||
"keywords": [], | ||
@@ -8,0 +8,0 @@ "dependencies": {}, |
170
index.js
@@ -0,9 +1,20 @@ | ||
'use strict'; | ||
var width = 256;// each RC4 output is 0 <= x < 256 | ||
var chunks = 6;// at least six RC4 outputs for each double | ||
var significance = 52;// there are 52 significant digits in a double | ||
var digits = 52;// there are 52 significant digits in a double | ||
var pool = [];// pool: entropy pool starts empty | ||
var GLOBAL = typeof global === 'undefined' ? window : global; | ||
var overflow, startdenom; //numbers | ||
// | ||
// The following constants are related to IEEE 754 limits. | ||
// | ||
var startdenom = Math.pow(width, chunks), | ||
significance = Math.pow(2, digits), | ||
overflow = significance * 2, | ||
mask = width - 1; | ||
var oldRandom = Math.random; | ||
// | ||
@@ -13,18 +24,23 @@ // seedrandom() | ||
// | ||
module.exports = function seedrandom(seed, overRideGlobal) { | ||
if (!seed) { | ||
if (overRideGlobal) { | ||
Math.random = oldRandom; | ||
} | ||
return oldRandom; | ||
module.exports = function(seed, options) { | ||
if (options && options.global === true) { | ||
options.global = false; | ||
Math.random = module.exports(seed, options); | ||
options.global = true; | ||
return Math.random; | ||
} | ||
var use_entropy = (options && options.entropy) || false; | ||
var key = []; | ||
var arc4; | ||
// Flatten the seed string or build one from local entropy if needed. | ||
seed = mixkey(flatten(seed, 3), key); | ||
var shortseed = mixkey(flatten( | ||
use_entropy ? [seed, tostring(pool)] : | ||
0 in arguments ? seed : autoseed(), 3), key); | ||
// Use the seed to initialize an ARC4 generator. | ||
arc4 = new ARC4(key); | ||
var arc4 = new ARC4(key); | ||
// Mix the randomness into accumulated entropy. | ||
mixkey(tostring(arc4.S), pool); | ||
// Override Math.random | ||
@@ -35,6 +51,6 @@ | ||
function random() { // Closure to return a random double: | ||
var n = arc4.g(chunks); // Start with a numerator n < 2 ^ 48 | ||
var d = startdenom; // and denominator d = 2 ^ 48. | ||
var x = 0; // and no 'extra last byte'. | ||
return function() { // Closure to return a random double: | ||
var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 | ||
d = startdenom, // and denominator d = 2 ^ 48. | ||
x = 0; // and no 'extra last byte'. | ||
while (n < significance) { // Fill up all significant digits by | ||
@@ -51,10 +67,7 @@ n = (n + x) * width; // shifting numerator and | ||
return (n + x) / d; // Form the number within [0, 1). | ||
} | ||
random.seed = seed; | ||
if (overRideGlobal) { | ||
Math['random'] = random; | ||
} | ||
}; | ||
}; | ||
// Return the seed that was used | ||
return random; | ||
module.exports.resetGlobal = function () { | ||
Math.random = oldRandom; | ||
}; | ||
@@ -74,6 +87,4 @@ | ||
function ARC4(key) { | ||
var t, u, me = this, keylen = key.length; | ||
var i = 0, j = me.i = me.j = me.m = 0; | ||
me.S = []; | ||
me.c = []; | ||
var t, keylen = key.length, | ||
me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; | ||
@@ -84,33 +95,24 @@ // The empty key [] is treated as [0]. | ||
// Set up S using the standard key scheduling algorithm. | ||
while (i < width) { me.S[i] = i++; } | ||
while (i < width) { | ||
s[i] = i++; | ||
} | ||
for (i = 0; i < width; i++) { | ||
t = me.S[i]; | ||
j = lowbits(j + t + key[i % keylen]); | ||
u = me.S[j]; | ||
me.S[i] = u; | ||
me.S[j] = t; | ||
s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))]; | ||
s[j] = t; | ||
} | ||
// The "g" method returns the next (count) outputs as one number. | ||
me.g = function getnext(count) { | ||
var s = me.S; | ||
var i = lowbits(me.i + 1); var t = s[i]; | ||
var j = lowbits(me.j + t); var u = s[j]; | ||
s[i] = u; | ||
s[j] = t; | ||
var r = s[lowbits(t + u)]; | ||
while (--count) { | ||
i = lowbits(i + 1); t = s[i]; | ||
j = lowbits(j + t); u = s[j]; | ||
s[i] = u; | ||
s[j] = t; | ||
r = r * width + s[lowbits(t + u)]; | ||
(me.g = function(count) { | ||
// Using instance members instead of closure state nearly doubles speed. | ||
var t, r = 0, | ||
i = me.i, j = me.j, s = me.S; | ||
while (count--) { | ||
t = s[i = mask & (i + 1)]; | ||
r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))]; | ||
} | ||
me.i = i; | ||
me.j = j; | ||
me.i = i; me.j = j; | ||
return r; | ||
}; | ||
// For robust unpredictability discard an initial batch of values. | ||
// See http://www.rsa.com/rsalabs/node.asp?id=2009 | ||
me.g(width); | ||
// For robust unpredictability discard an initial batch of values. | ||
// See http://www.rsa.com/rsalabs/node.asp?id=2009 | ||
})(width); | ||
} | ||
@@ -122,16 +124,10 @@ | ||
// | ||
/** @param {Object=} result | ||
* @param {string=} prop | ||
* @param {string=} typ */ | ||
function flatten(obj, depth, result, prop, typ) { | ||
result = []; | ||
typ = typeof(obj); | ||
if (depth && typ == 'object') { | ||
function flatten(obj, depth) { | ||
var result = [], typ = (typeof obj)[0], prop; | ||
if (depth && typ == 'o') { | ||
for (prop in obj) { | ||
if (prop.indexOf('S') < 5) { // Avoid FF3 bug (local/sessionStorage) | ||
try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} | ||
} | ||
try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} | ||
} | ||
} | ||
return (result.length ? result : obj + (typ != 'string' ? '\0' : '')); | ||
return (result.length ? result : typ == 's' ? obj : obj + '\0'); | ||
} | ||
@@ -144,27 +140,41 @@ | ||
// | ||
/** @param {number=} smear | ||
* @param {number=} j */ | ||
function mixkey(seed, key, smear, j) { | ||
seed += ''; // Ensure the seed is a string | ||
smear = 0; | ||
for (j = 0; j < seed.length; j++) { | ||
key[lowbits(j)] = | ||
lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j)); | ||
function mixkey(seed, key) { | ||
var stringseed = seed + '', smear, j = 0; | ||
while (j < stringseed.length) { | ||
key[mask & j] = | ||
mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); | ||
} | ||
seed = ''; | ||
for (j in key) { seed += String.fromCharCode(key[j]); } | ||
return seed; | ||
return tostring(key); | ||
} | ||
// | ||
// lowbits() | ||
// A quick "n mod width" for width a power of 2. | ||
// autoseed() | ||
// Returns an object for autoseeding, using window.crypto if available. | ||
// | ||
function lowbits(n) { return n & (width - 1); } | ||
/** @param {Uint8Array=} seed */ | ||
function autoseed(seed) { | ||
try { | ||
GLOBAL.crypto.getRandomValues(seed = new Uint8Array(width)); | ||
return tostring(seed); | ||
} catch (e) { | ||
return [+new Date, GLOBAL, GLOBAL.navigator && GLOBAL.navigator.plugins, | ||
GLOBAL.screen, tostring(pool)]; | ||
} | ||
} | ||
// | ||
// The following constants are related to IEEE 754 limits. | ||
// tostring() | ||
// Converts an array of charcodes to a string | ||
// | ||
startdenom = Math.pow(width, chunks); | ||
significance = Math.pow(2, significance); | ||
overflow = significance * 2; | ||
function tostring(a) { | ||
return String.fromCharCode.apply(0, a); | ||
} | ||
// | ||
// When seedrandom.js is loaded, we immediately mix a few bits | ||
// from the built-in RNG into the entropy pool. Because we do | ||
// not want to intefere with determinstic PRNG state later, | ||
// seedrandom will not call Math.random on its own again after | ||
// initialization. | ||
// | ||
mixkey(Math.random(), pool); |
{ | ||
"name": "seed-random", | ||
"version": "1.0.1", | ||
"version": "2.2.0", | ||
"description": "Generate random numbers with a seed, useful for reproducible tests", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "node test/index.js" | ||
}, | ||
@@ -13,4 +13,4 @@ "repository": { | ||
}, | ||
"author": "", | ||
"author": "ForbesLindesay", | ||
"license": "MIT" | ||
} |
@@ -1,10 +0,12 @@ | ||
# seed-random | ||
Generate random numbers with a seed, useful for reproducible tests | ||
Generate random numbers with a seed, useful for reproducible tests | ||
[](http://travis-ci.org/ForbesLindesay/seed-random) | ||
[](https://gemnasium.com/ForbesLindesay/seed-random) | ||
[](http://badge.fury.io/js/seed-random) | ||
## Installation | ||
$ component install ForbesLindesay/seed-random | ||
$ npm install seed-random | ||
@@ -14,24 +16,30 @@ | ||
```javascript | ||
var seed = require('seed-random'); | ||
```javascript | ||
var assert = require('assert'); | ||
var seed = require('../'); | ||
var trueRandomA = seed(); | ||
var trueRandomB = seed(); | ||
trueRandomA() != trueRandomB(); //almost always true | ||
var trueRandomA = seed(); | ||
var trueRandomB = seed(); | ||
assert(trueRandomA() != trueRandomB()); | ||
var fakeRandomA = seed('foo'); | ||
var fakeRandomB = seed('foo'); | ||
fakeRandomA() == fakeRandomB(); //always true | ||
var fakeRandomA = seed('foo'); | ||
var fakeRandomB = seed('foo'); | ||
assert(fakeRandomA() == fakeRandomB()); | ||
seed('foo', true);//over-ride global Math.random | ||
var numA = Math.random(); | ||
seed('foo', true); | ||
var numB = Math.random(); | ||
numA == numB;//always true | ||
var fakeRandomC = seed('foo', {entropy: true}); | ||
var fakeRandomD = seed('foo', {entropy: true}); | ||
assert(fakeRandomC() != fakeRandomD()); | ||
seed(undefined, true);//reset to default Math.random | ||
``` | ||
seed('foo', {global: true});//over-ride global Math.random | ||
var numA = Math.random(); | ||
seed('foo', {global: true}); | ||
var numB = Math.random(); | ||
assert(numA == numB);//always true | ||
seed.resetGlobal();//reset to default Math.random | ||
``` | ||
## License | ||
MIT | ||
MIT |
Sorry, the diff of this file is not supported yet
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
10855
69.77%9
50%182
15.19%2
-33.33%1
-50%45
21.62%1
Infinity%