├── .eslintrc ├── .gitignore ├── .npmignore ├── .tonic.example.js ├── .travis.yml ├── LICENSE-GRC ├── LICENSE-MIT ├── README.md ├── gulpfile.js ├── index.js ├── package.json └── test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | // adapted from: https://github.com/Khan/style-guides/pull/25 2 | { 3 | "plugins": [ 4 | ], 5 | "ecmaFeatures": { 6 | "modules": true 7 | }, 8 | "env": { 9 | "browser": true, 10 | "node": true, 11 | "mocha": true 12 | }, 13 | "rules": { 14 | "arrow-parens": [2, "always"], 15 | "arrow-spacing": 2, 16 | "brace-style": 2, 17 | "camelcase": [2, { 18 | "properties": "always" 19 | }], 20 | "comma-dangle": [1, "always-multiline"], 21 | "comma-dangle": [2, "never"], 22 | "comma-spacing": [2, { 23 | "before": false, 24 | "after": true 25 | }], 26 | "eol-last": [0], 27 | "guard-for-in": 2, 28 | //"indent": [2, 4], 29 | "linebreak-style": [2, "unix"], 30 | /* 31 | "max-len": [2, 80, 4, { 32 | "ignoreUrls": true, 33 | "ignorePattern": "^\\s*const\\s.+=\\s*require\\s*\\(" 34 | }], 35 | */ 36 | "no-alert": 2, 37 | "no-array-constructor": 2, 38 | "no-const-assign": 2, 39 | "no-debugger": 2, 40 | "no-dupe-keys": 2, 41 | "no-new-object": 2, 42 | "no-spaced-func": 2, 43 | "no-this-before-super": 2, 44 | "no-throw-literal": 2, 45 | "no-trailing-spaces": 2, 46 | "no-unreachable": 2, 47 | "no-unused-vars": 1, 48 | "no-var": 0, 49 | //"object-curly-spacing": [2, "always"], 50 | "one-var": [2, "never"], 51 | "prefer-const": 1, 52 | "prefer-template": 2, 53 | "quotes": [2, "single"], 54 | "semi": [2, "always"], 55 | "space-after-keywords": [2, "always"], 56 | "space-before-blocks": 2, 57 | "space-before-function-paren": [2, { 58 | "anonymous": "always", 59 | "named": "never" 60 | }], 61 | "space-infix-ops": 2, 62 | "space-return-throw-case": 2, 63 | "strict": [0, "never"], 64 | "valid-jsdoc": 2 65 | }, 66 | "extends": "eslint:recommended" 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _ignore/ 2 | coverage/ 3 | node_modules/ 4 | .DS_Store 5 | .npm-debug.log 6 | .project 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | _ignore/ 3 | .DS_Store 4 | .gitignore 5 | .npm-debug.log 6 | .project 7 | .travis.yml 8 | -------------------------------------------------------------------------------- /.tonic.example.js: -------------------------------------------------------------------------------- 1 | var lib = require('random-seed'); // create a generator 2 | var rand1 = lib.create(); 3 | var rand2 = lib.create('Hello World Seed'); 4 | console.log(rand1(100), rand2(100)); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | - '4.2' 5 | - '4.1' 6 | - '4.0' 7 | - '0.12' 8 | - '0.11' 9 | - '0.10' 10 | before_script: 11 | - npm install -g gulp 12 | script: 13 | - gulp test 14 | - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls --verbose 15 | -------------------------------------------------------------------------------- /LICENSE-GRC: -------------------------------------------------------------------------------- 1 | LICENSE AND COPYRIGHT: THIS CODE IS HEREBY RELEASED INTO THE PUBLIC DOMAIN 2 | Gibson Research Corporation releases and disclaims ALL RIGHTS AND TITLE IN 3 | THIS CODE OR ANY DERIVATIVES. Anyone may be freely use it for any purpose. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 skratchdot 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # random-seed 2 | 3 | [![NPM version](https://badge.fury.io/js/random-seed.svg)](http://badge.fury.io/js/random-seed) 4 | [![Build Status](https://travis-ci.org/skratchdot/random-seed.png?branch=master)](https://travis-ci.org/skratchdot/random-seed) 5 | [![Code Climate](https://codeclimate.com/github/skratchdot/random-seed.png)](https://codeclimate.com/github/skratchdot/random-seed) 6 | [![Coverage Status](https://coveralls.io/repos/skratchdot/random-seed/badge.svg?branch=master&service=github)](https://coveralls.io/github/skratchdot/random-seed?branch=master) 7 | [![Dependency Status](https://david-dm.org/skratchdot/random-seed.svg)](https://david-dm.org/skratchdot/random-seed) 8 | [![devDependency Status](https://david-dm.org/skratchdot/random-seed/dev-status.svg)](https://david-dm.org/skratchdot/random-seed#info=devDependencies) 9 | 10 | [![NPM](https://nodei.co/npm/random-seed.png)](https://npmjs.org/package/random-seed) 11 | 12 | 13 | ## Description 14 | 15 | Gibson Research Corporation's Ultra-High Entropy Pseudo-Random Number Generator 16 | ported to node. 17 | 18 | The original library / project page is located here: https://www.grc.com/otg/uheprng.htm 19 | 20 | The node project page is here: https://github.com/skratchdot/random-seed 21 | 22 | There were a few modifications made to the original library to allow seeding, and to 23 | pass jshint. 24 | 25 | I've also added the following helper methods: 26 | 27 | - random() 28 | - range(range) 29 | - floatBetween(min, max) 30 | - intBetween(min, max) 31 | 32 | 33 | ## Getting Started 34 | 35 | Install the module with: `npm install random-seed` 36 | 37 | ```javascript 38 | var rand = require('random-seed').create(); 39 | var n = rand(100); // generate a random number between 0 - 99 40 | ``` 41 | 42 | 43 | ## Documentation 44 | 45 | ### Create a random number generator 46 | 47 | ```javascript 48 | var gen = require('random-seed'); // create a generator 49 | 50 | // these generators produce different numbers 51 | var rand1 = gen.create(); // method 1 52 | var rand2 = new gen(); // method 2 53 | var rand3 = gen(); // method 3 54 | 55 | // these generators will produce 56 | // the same sequence of numbers 57 | var seed = 'My Secret String Value'; 58 | var rand4 = gen.create(seed); 59 | var rand5 = new gen(seed); 60 | var rand6 = gen(seed); 61 | ``` 62 | 63 | ### Random Generator Methods 64 | 65 | Once a random generator is created, you have the following methods available. 66 | 67 | I typically create a random generator like this: 68 | 69 | ```javascript 70 | var rand = require('random-seed').create(); 71 | ``` 72 | 73 | #### rand(range) 74 | 75 | Returns a random integer between 0 (inclusive) and range (exclusive) 76 | 77 | #### rand.range(range) 78 | 79 | Returns a random integer between 0 (inclusive) and range (exclusive) 80 | 81 | #### rand.random() 82 | 83 | Returns a random float between 0 (inclusive) and 1 (exclusive) 84 | 85 | Works the same as Math.random() 86 | 87 | #### rand.floatBetween(min, max) 88 | 89 | Returns a random float between min (inclusive) and max (exclusive) 90 | 91 | #### rand.intBetween(min, max) 92 | 93 | Returns a random integer between min (inclusive) and max (inclusive) 94 | 95 | #### rand.seed(seed) 96 | 97 | Same as calling rand.initState() followed by rand.hashString(seed). If seed is not 98 | a string, then the seed value will be converted to a string. If you don't pass a 99 | seed argument, then the generator uses Math.random() as the seed. 100 | 101 | #### rand.string(count) 102 | 103 | Returns a pseudo-random string of 'count' printable characters 104 | ranging from chr(33) to chr(126) inclusive. 105 | 106 | #### rand.cleanString(inStr) 107 | 108 | Removes leading and trailing spaces and non-printing control characters, 109 | including any embedded carriage-return (CR) and line-feed (LF) characters, 110 | from any string it is handed. This is also used by the 'hashstring' function (below) 111 | to help users always obtain the same EFFECTIVE uheprng seeding key. 112 | 113 | #### rand.hashString(inStr) 114 | 115 | Hashes the provided character string after first removing any leading or trailing spaces 116 | and ignoring any embedded carriage returns (CR) or Line Feeds (LF). 117 | 118 | #### rand.addEntropy(/* accept zero or more arguments */) 119 | 120 | This handy exported function is used to add entropy to our uheprng at any time. 121 | 122 | #### rand.initState() 123 | 124 | If we want to provide a deterministic startup context for our PRNG, 125 | but without directly setting the internal state variables, this allows 126 | us to initialize the mash hash and PRNG's internal state before providing 127 | some hashing input. 128 | 129 | #### rand.done() 130 | 131 | We use this (optional) exported function to signal the JavaScript interpreter 132 | that we're finished using the internal "Mash" hash function so that it can free up the 133 | local "instance variables" it will have been maintaining. It's not strictly 134 | necessary, of course, but it's good JavaScript citizenship. 135 | 136 | 137 | ## Examples 138 | 139 | ### Default Usage: create 1 random number between 0 - 99 140 | ```javascript 141 | var rand = require('random-seed').create(); 142 | var n = rand(100); // generate a random number between 0 - 99 143 | ``` 144 | 145 | ### Always create same sequence of random numbers 146 | ```javascript 147 | var rand = require('random-seed').create(); 148 | rand.initState(); 149 | var n1 = rand(100); // n1 === 58 150 | var n2 = rand(100); // n2 === 26 151 | rand.initState(); // re-init 152 | var n3 = rand(100); // n3 === 58 && n3 === n1 153 | ``` 154 | 155 | ### Create 2 random number generators 156 | ```javascript 157 | var rand1 = require('random-seed').create(), 158 | rand2 = require('random-seed').create(); 159 | console.log(rand1(100), rand2(100)); 160 | ``` 161 | 162 | ### Create 2 random number generators with the same seed 163 | ```javascript 164 | var seed = 'Hello World', 165 | rand1 = require('random-seed').create(seed), 166 | rand2 = require('random-seed').create(seed); 167 | console.log(rand1(100), rand2(100)); 168 | ``` 169 | 170 | ### Replace Math.random() 171 | ```javascript 172 | var math = require('random-seed').create(); 173 | console.log(math.random()); 174 | ``` 175 | 176 | 177 | ## Release History 178 | 179 | #### v0.2.0 (Released June 1, 2014) 180 | 181 | - Adding the following helper methods: 182 | - rand.random(min, max) 183 | - rand.floatBetween(min, max) 184 | - rand.intBetween(min, max) 185 | 186 | #### v0.1.0 (Released October 26, 2013) 187 | 188 | - Initial Release 189 | 190 | 191 | ## License 192 | 193 | Copyright (c) 2013 skratchdot 194 | 195 | Dual Licensed under the MIT license and the original Public Domain License by GRC. 196 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /*eslint no-console: 0 */ 2 | 'use strict'; 3 | var gulp = require('gulp'); 4 | var eslint = require('gulp-eslint'); 5 | var isparta = require('isparta'); 6 | var istanbul = require('gulp-istanbul'); 7 | var mocha = require('gulp-mocha'); 8 | var files = { 9 | lint: ['*.js'], 10 | src: ['index.js'], 11 | test: ['test.js'] 12 | }; 13 | 14 | gulp.task('lint', function () { 15 | return gulp.src(files.lint) 16 | // eslint() attaches the lint output to the eslint property 17 | // of the file object so it can be used by other modules. 18 | .pipe(eslint()) 19 | // eslint.format() outputs the lint results to the console. 20 | // Alternatively use eslint.formatEach() (see Docs). 21 | .pipe(eslint.format('stylish')) 22 | // To have the process exit with an error code (1) on 23 | // lint error, return the stream and pipe to failAfterError last. 24 | .pipe(eslint.failAfterError()); 25 | }); 26 | 27 | gulp.task('test', function () { 28 | return gulp.src(files.src) 29 | .pipe(istanbul({ 30 | instrumenter: isparta.Instrumenter, 31 | includeUntested: true 32 | })) 33 | // Force `require` to return covered files 34 | .pipe(istanbul.hookRequire()) 35 | .on('finish', function () { 36 | gulp.src(files.test, {read: false}) 37 | // gulp-mocha needs filepaths so you can't have any plugins before it 38 | .pipe(mocha({ 39 | reporter: 'spec' 40 | })) 41 | .on('error', function (err) { 42 | console.error(err.toString()); 43 | this.emit('end'); 44 | }) 45 | .pipe(istanbul.writeReports({ 46 | dir: './coverage', 47 | reporters: ['lcov', 'json', 'text-summary'] 48 | })); 49 | }); 50 | }); 51 | 52 | gulp.task('watch', function () { 53 | gulp.watch([files.lint], ['lint', 'test']); 54 | }); 55 | 56 | // setup default task 57 | gulp.task('default', ['lint', 'test', 'watch']); 58 | 59 | // handle errors 60 | process.on('uncaughtException', function (e) { 61 | console.error(e); 62 | }); 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * random-seed 3 | * https://github.com/skratchdot/random-seed 4 | * 5 | * This code was originally written by Steve Gibson and can be found here: 6 | * 7 | * https://www.grc.com/otg/uheprng.htm 8 | * 9 | * It was slightly modified for use in node, to pass jshint, and a few additional 10 | * helper functions were added. 11 | * 12 | * Copyright (c) 2013 skratchdot 13 | * Dual Licensed under the MIT license and the original GRC copyright/license 14 | * included below. 15 | */ 16 | /* ============================================================================ 17 | Gibson Research Corporation 18 | UHEPRNG - Ultra High Entropy Pseudo-Random Number Generator 19 | ============================================================================ 20 | LICENSE AND COPYRIGHT: THIS CODE IS HEREBY RELEASED INTO THE PUBLIC DOMAIN 21 | Gibson Research Corporation releases and disclaims ALL RIGHTS AND TITLE IN 22 | THIS CODE OR ANY DERIVATIVES. Anyone may be freely use it for any purpose. 23 | ============================================================================ 24 | This is GRC's cryptographically strong PRNG (pseudo-random number generator) 25 | for JavaScript. It is driven by 1536 bits of entropy, stored in an array of 26 | 48, 32-bit JavaScript variables. Since many applications of this generator, 27 | including ours with the "Off The Grid" Latin Square generator, may require 28 | the deteriministic re-generation of a sequence of PRNs, this PRNG's initial 29 | entropic state can be read and written as a static whole, and incrementally 30 | evolved by pouring new source entropy into the generator's internal state. 31 | ---------------------------------------------------------------------------- 32 | ENDLESS THANKS are due Johannes Baagoe for his careful development of highly 33 | robust JavaScript implementations of JS PRNGs. This work was based upon his 34 | JavaScript "Alea" PRNG which is based upon the extremely robust Multiply- 35 | With-Carry (MWC) PRNG invented by George Marsaglia. MWC Algorithm References: 36 | http://www.GRC.com/otg/Marsaglia_PRNGs.pdf 37 | http://www.GRC.com/otg/Marsaglia_MWC_Generators.pdf 38 | ---------------------------------------------------------------------------- 39 | The quality of this algorithm's pseudo-random numbers have been verified by 40 | multiple independent researchers. It handily passes the fermilab.ch tests as 41 | well as the "diehard" and "dieharder" test suites. For individuals wishing 42 | to further verify the quality of this algorithm's pseudo-random numbers, a 43 | 256-megabyte file of this algorithm's output may be downloaded from GRC.com, 44 | and a Microsoft Windows scripting host (WSH) version of this algorithm may be 45 | downloaded and run from the Windows command prompt to generate unique files 46 | of any size: 47 | The Fermilab "ENT" tests: http://fourmilab.ch/random/ 48 | The 256-megabyte sample PRN file at GRC: https://www.GRC.com/otg/uheprng.bin 49 | The Windows scripting host version: https://www.GRC.com/otg/wsh-uheprng.js 50 | ---------------------------------------------------------------------------- 51 | Qualifying MWC multipliers are: 187884, 686118, 898134, 1104375, 1250205, 52 | 1460910 and 1768863. (We use the largest one that's < 2^21) 53 | ============================================================================ */ 54 | 'use strict'; 55 | var stringify = require('json-stringify-safe'); 56 | 57 | /* ============================================================================ 58 | This is based upon Johannes Baagoe's carefully designed and efficient hash 59 | function for use with JavaScript. It has a proven "avalanche" effect such 60 | that every bit of the input affects every bit of the output 50% of the time, 61 | which is good. See: http://baagoe.com/en/RandomMusings/hash/avalanche.xhtml 62 | ============================================================================ 63 | */ 64 | var Mash = function () { 65 | var n = 0xefc8249d; 66 | var mash = function (data) { 67 | if (data) { 68 | data = data.toString(); 69 | for (var i = 0; i < data.length; i++) { 70 | n += data.charCodeAt(i); 71 | var h = 0.02519603282416938 * n; 72 | n = h >>> 0; 73 | h -= n; 74 | h *= n; 75 | n = h >>> 0; 76 | h -= n; 77 | n += h * 0x100000000; // 2^32 78 | } 79 | return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 80 | } else { 81 | n = 0xefc8249d; 82 | } 83 | }; 84 | return mash; 85 | }; 86 | 87 | var uheprng = function (seed) { 88 | return (function () { 89 | var o = 48; // set the 'order' number of ENTROPY-holding 32-bit values 90 | var c = 1; // init the 'carry' used by the multiply-with-carry (MWC) algorithm 91 | var p = o; // init the 'phase' (max-1) of the intermediate variable pointer 92 | var s = new Array(o); // declare our intermediate variables array 93 | var i; // general purpose local 94 | var j; // general purpose local 95 | var k = 0; // general purpose local 96 | 97 | // when our "uheprng" is initially invoked our PRNG state is initialized from the 98 | // browser's own local PRNG. This is okay since although its generator might not 99 | // be wonderful, it's useful for establishing large startup entropy for our usage. 100 | var mash = new Mash(); // get a pointer to our high-performance "Mash" hash 101 | 102 | // fill the array with initial mash hash values 103 | for (i = 0; i < o; i++) { 104 | s[i] = mash(Math.random()); 105 | } 106 | 107 | // this PRIVATE (internal access only) function is the heart of the multiply-with-carry 108 | // (MWC) PRNG algorithm. When called it returns a pseudo-random number in the form of a 109 | // 32-bit JavaScript fraction (0.0 to <1.0) it is a PRIVATE function used by the default 110 | // [0-1] return function, and by the random 'string(n)' function which returns 'n' 111 | // characters from 33 to 126. 112 | var rawprng = function () { 113 | if (++p >= o) { 114 | p = 0; 115 | } 116 | var t = 1768863 * s[p] + c * 2.3283064365386963e-10; // 2^-32 117 | return s[p] = t - (c = t | 0); 118 | }; 119 | 120 | // this EXPORTED function is the default function returned by this library. 121 | // The values returned are integers in the range from 0 to range-1. We first 122 | // obtain two 32-bit fractions (from rawprng) to synthesize a single high 123 | // resolution 53-bit prng (0 to <1), then we multiply this by the caller's 124 | // "range" param and take the "floor" to return a equally probable integer. 125 | var random = function (range) { 126 | return Math.floor(range * (rawprng() + (rawprng() * 0x200000 | 0) * 1.1102230246251565e-16)); // 2^-53 127 | }; 128 | 129 | // this EXPORTED function 'string(n)' returns a pseudo-random string of 130 | // 'n' printable characters ranging from chr(33) to chr(126) inclusive. 131 | random.string = function (count) { 132 | var i; 133 | var s = ''; 134 | for (i = 0; i < count; i++) { 135 | s += String.fromCharCode(33 + random(94)); 136 | } 137 | return s; 138 | }; 139 | 140 | // this PRIVATE "hash" function is used to evolve the generator's internal 141 | // entropy state. It is also called by the EXPORTED addEntropy() function 142 | // which is used to pour entropy into the PRNG. 143 | var hash = function () { 144 | var args = Array.prototype.slice.call(arguments); 145 | for (i = 0; i < args.length; i++) { 146 | for (j = 0; j < o; j++) { 147 | s[j] -= mash(args[i]); 148 | if (s[j] < 0) { 149 | s[j] += 1; 150 | } 151 | } 152 | } 153 | }; 154 | 155 | // this EXPORTED "clean string" function removes leading and trailing spaces and non-printing 156 | // control characters, including any embedded carriage-return (CR) and line-feed (LF) characters, 157 | // from any string it is handed. this is also used by the 'hashstring' function (below) to help 158 | // users always obtain the same EFFECTIVE uheprng seeding key. 159 | random.cleanString = function (inStr) { 160 | inStr = inStr.replace(/(^\s*)|(\s*$)/gi, ''); // remove any/all leading spaces 161 | inStr = inStr.replace(/[\x00-\x1F]/gi, ''); // remove any/all control characters 162 | inStr = inStr.replace(/\n /, '\n'); // remove any/all trailing spaces 163 | return inStr; // return the cleaned up result 164 | }; 165 | 166 | // this EXPORTED "hash string" function hashes the provided character string after first removing 167 | // any leading or trailing spaces and ignoring any embedded carriage returns (CR) or Line Feeds (LF) 168 | random.hashString = function (inStr) { 169 | inStr = random.cleanString(inStr); 170 | mash(inStr); // use the string to evolve the 'mash' state 171 | for (i = 0; i < inStr.length; i++) { // scan through the characters in our string 172 | k = inStr.charCodeAt(i); // get the character code at the location 173 | for (j = 0; j < o; j++) { // "mash" it into the UHEPRNG state 174 | s[j] -= mash(k); 175 | if (s[j] < 0) { 176 | s[j] += 1; 177 | } 178 | } 179 | } 180 | }; 181 | 182 | // this EXPORTED function allows you to seed the random generator. 183 | random.seed = function (seed) { 184 | if (typeof seed === 'undefined' || seed === null) { 185 | seed = Math.random(); 186 | } 187 | if (typeof seed !== 'string') { 188 | seed = stringify(seed, function (key, value) { 189 | if (typeof value === 'function') { 190 | return (value).toString(); 191 | } 192 | return value; 193 | }); 194 | } 195 | random.initState(); 196 | random.hashString(seed); 197 | }; 198 | 199 | // this handy exported function is used to add entropy to our uheprng at any time 200 | random.addEntropy = function ( /* accept zero or more arguments */ ) { 201 | var args = []; 202 | for (i = 0; i < arguments.length; i++) { 203 | args.push(arguments[i]); 204 | } 205 | hash((k++) + (new Date().getTime()) + args.join('') + Math.random()); 206 | }; 207 | 208 | // if we want to provide a deterministic startup context for our PRNG, 209 | // but without directly setting the internal state variables, this allows 210 | // us to initialize the mash hash and PRNG's internal state before providing 211 | // some hashing input 212 | random.initState = function () { 213 | mash(); // pass a null arg to force mash hash to init 214 | for (i = 0; i < o; i++) { 215 | s[i] = mash(' '); // fill the array with initial mash hash values 216 | } 217 | c = 1; // init our multiply-with-carry carry 218 | p = o; // init our phase 219 | }; 220 | 221 | // we use this (optional) exported function to signal the JavaScript interpreter 222 | // that we're finished using the "Mash" hash function so that it can free up the 223 | // local "instance variables" is will have been maintaining. It's not strictly 224 | // necessary, of course, but it's good JavaScript citizenship. 225 | random.done = function () { 226 | mash = null; 227 | }; 228 | 229 | // if we called "uheprng" with a seed value, then execute random.seed() before returning 230 | if (typeof seed !== 'undefined') { 231 | random.seed(seed); 232 | } 233 | 234 | // Returns a random integer between 0 (inclusive) and range (exclusive) 235 | random.range = function (range) { 236 | return random(range); 237 | }; 238 | 239 | // Returns a random float between 0 (inclusive) and 1 (exclusive) 240 | random.random = function () { 241 | return random(Number.MAX_VALUE - 1) / Number.MAX_VALUE; 242 | }; 243 | 244 | // Returns a random float between min (inclusive) and max (exclusive) 245 | random.floatBetween = function (min, max) { 246 | return random.random() * (max - min) + min; 247 | }; 248 | 249 | // Returns a random integer between min (inclusive) and max (inclusive) 250 | random.intBetween = function (min, max) { 251 | return Math.floor(random.random() * (max - min + 1)) + min; 252 | }; 253 | 254 | // when our main outer "uheprng" function is called, after setting up our 255 | // initial variables and entropic state, we return an "instance pointer" 256 | // to the internal anonymous function which can then be used to access 257 | // the uheprng's various exported functions. As with the ".done" function 258 | // above, we should set the returned value to 'null' once we're finished 259 | // using any of these functions. 260 | return random; 261 | }()); 262 | }; 263 | 264 | // Modification for use in node: 265 | uheprng.create = function (seed) { 266 | return new uheprng(seed); 267 | }; 268 | module.exports = uheprng; 269 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "random-seed", 3 | "version": "0.3.0", 4 | "description": "GRC's UHE PRNG in node (Ultra-High Entropy Pseudo-Random Number Generator by Gibson Research Corporation)", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "gulp test" 8 | }, 9 | "author": "skratchdot", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/skratchdot/random-seed/issues" 13 | }, 14 | "homepage": "https://github.com/skratchdot/random-seed", 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/skratchdot/random-seed.git" 18 | }, 19 | "engines": { 20 | "node": ">= 0.6.0" 21 | }, 22 | "dependencies": { 23 | "json-stringify-safe": "^5.0.1" 24 | }, 25 | "devDependencies": { 26 | "chai": "^3.4.1", 27 | "coveralls": "^2.11.4", 28 | "gulp": "^3.9.0", 29 | "gulp-eslint": "^1.1.1", 30 | "gulp-istanbul": "^0.10.3", 31 | "gulp-mocha": "^2.2.0", 32 | "isparta": "^4.0.0" 33 | }, 34 | "tonicExampleFilename": ".tonic.example.js", 35 | "keywords": [ 36 | "random", 37 | "number", 38 | "generator", 39 | "seed", 40 | "uhe", 41 | "prng" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var lib = require('./index.js'); 5 | var testLoop = 1000; 6 | var arr; 7 | var rand; 8 | var rand1; 9 | var rand2; 10 | var val; 11 | var i; 12 | var j; 13 | 14 | describe('random-seed', function () { 15 | beforeEach(function () { 16 | rand = lib.create(); 17 | rand1 = lib.create(); 18 | rand2 = lib.create(); 19 | }); 20 | it('should work with no arguments', function () { 21 | expect(rand()).to.be.nan; 22 | }); 23 | it('should work when calling rand(5) by returning 0 - 4', function () { 24 | var iterations = 100; 25 | for (i = 0; i < iterations; i++) { 26 | val = rand(5); 27 | expect(val).to.be.within(0, 4).and.to.be.a.number; 28 | expect(val % 1).to.equal(0); // integer 29 | } 30 | }); 31 | it('should work when calling initState()', function () { 32 | arr = [58, 26, 63, 93, 30]; 33 | rand.initState(); 34 | for (i = 0; i < arr.length; i++) { 35 | expect(rand(100)).to.equal(arr[i]); 36 | } 37 | }); 38 | it('should work when calling initState() multiple times', function () { 39 | var iterations = 5; 40 | arr = [58, 26, 63, 93, 30]; 41 | for (i = 0; i < iterations; i++) { 42 | rand.initState(); 43 | for (j = 0; j < arr.length; j++) { 44 | expect(rand(100)).to.equal(arr[j]); 45 | } 46 | } 47 | }); 48 | it('multiple rand without init should work (be different)', function () { 49 | expect(rand1(Number.MAX_VALUE)).to.not.equal(rand2(Number.MAX_VALUE)); 50 | }); 51 | it('multiple rand with init should work (equal each other)', function () { 52 | rand1.initState(); 53 | rand2.initState(); 54 | expect(rand1(Number.MAX_VALUE)).to.equal(rand2(Number.MAX_VALUE)); 55 | }); 56 | it('should work when calling string(n)', function () { 57 | var iterations = 100; 58 | for (i = 0; i < iterations; i++) { 59 | for (j = 1; j < 10; j++) { 60 | expect(rand.string(j)).to.have.length(j); 61 | } 62 | } 63 | }); 64 | it('should work when calling cleanString(str)', function () { 65 | expect(rand.cleanString(' \n hello world \n \n')).to.equal('hello world'); 66 | }); 67 | it('should work when calling hashString(str)', function () { 68 | rand1.initState(); 69 | rand1.hashString(' \nhello world \n \n'); 70 | rand2.initState(); 71 | rand2.hashString('hello world'); 72 | for (i = 0; i < testLoop; i++) { 73 | expect(rand1(100)).to.equal(rand2(100)); 74 | } 75 | }); 76 | it('should work when calling seed(str)', function () { 77 | // same 78 | rand1.seed('hello world'); 79 | rand2.seed('hello world'); 80 | for (i = 0; i < testLoop; i++) { 81 | expect(rand1(Number.MAX_VALUE)).to.equal(rand2(Number.MAX_VALUE)); 82 | } 83 | // same 84 | /*eslint-disable */ 85 | rand1.seed({a: 1, b: [1,2]}); 86 | rand2.seed({a: 1 , b : [1, 2]}); 87 | /*eslint-enable */ 88 | for (i = 0; i < testLoop; i++) { 89 | expect(rand1(Number.MAX_VALUE)).to.equal(rand2(Number.MAX_VALUE)); 90 | } 91 | // different 92 | rand1.seed(); 93 | rand2.seed(); 94 | for (i = 0; i < testLoop; i++) { 95 | expect(rand1(Number.MAX_VALUE)).to.not.equal(rand2(Number.MAX_VALUE)); 96 | } 97 | // different 98 | rand1.seed(null); 99 | rand2.seed(null); 100 | for (i = 0; i < testLoop; i++) { 101 | expect(rand1(Number.MAX_VALUE)).to.not.equal(rand2(Number.MAX_VALUE)); 102 | } 103 | }); 104 | it('should work when calling addEntropy(...args)', function () { 105 | var maxInt = 1000000; 106 | var expected = 588734; 107 | rand.initState(); 108 | expect(rand(maxInt)).to.equal(expected); 109 | rand.initState(); 110 | rand.addEntropy(1); 111 | expect(rand(maxInt)).to.not.equal(expected); 112 | rand.initState(); 113 | expect(rand(maxInt)).to.equal(expected); 114 | }); 115 | it('calling done() works', function () { 116 | rand.initState(); 117 | expect(rand(10)).to.equal(5); 118 | rand.done(); 119 | expect(function () { 120 | rand(10); 121 | }).to.not.throw(Error); 122 | expect(function () { 123 | rand.initState(); 124 | }).to.throw(Error); 125 | }); 126 | it('should be able to create() with a seed value', function () { 127 | var r1 = lib.create('hi'); 128 | expect(r1(10)).to.equal(0); 129 | expect(r1(10)).to.equal(6); 130 | expect(r1(10)).to.equal(7); 131 | }); 132 | it('random() >=0 && < 1', function () { 133 | for (i = 0; i < testLoop; i++) { 134 | expect(rand.random()).to.be.gte(0).and.to.be.lt(1); 135 | } 136 | }); 137 | it('should work when calling range(n)', function () { 138 | for (i = 0; i < testLoop; i++) { 139 | expect(rand.range(10)).to.be.gte(0).and.to.be.lte(10); 140 | expect(rand.range(100)).to.be.gte(0).and.to.be.lte(100); 141 | expect(rand.range(100) % 1).to.equal(0); 142 | expect(rand.range(0)).to.equal(0); 143 | expect(rand.range(-100)).to.be.lte(0).and.to.be.gte(-100); 144 | } 145 | }); 146 | it('should work when calling floatBetween(x, y)', function () { 147 | for (i = 0; i < testLoop; i++) { 148 | expect(rand.floatBetween(5, 10)).to.be.gte(5).and.to.be.lte(10); 149 | expect(rand.floatBetween(-5, 5)).to.be.gte(-5).and.to.be.lte(5); 150 | expect(rand.floatBetween(-100, 100) % 1).to.not.equal(0); 151 | } 152 | }); 153 | it('should work when calling intBetween(x, y)', function () { 154 | for (i = 0; i < testLoop; i++) { 155 | expect(rand.intBetween(5, 10)).to.be.gte(5).and.to.be.lte(10); 156 | expect(rand.intBetween(-5, 5)).to.be.gte(-5).and.to.be.lte(5); 157 | expect(rand.intBetween(-100, 100) % 1).to.equal(0); 158 | } 159 | }); 160 | it('should work with seed values', function () { 161 | var a = { b: 1 }; 162 | a.a = a; 163 | expect(function () { 164 | lib.create(); 165 | lib.create(null); 166 | lib.create(undefined); 167 | lib.create('wow'); 168 | lib.create({a: 1}); 169 | lib.create(a); // cycle 170 | lib.create(function () {}); 171 | lib.create(NaN); 172 | lib.create(-Infinity); 173 | }).to.not.throw(Error); 174 | }); 175 | }); 176 | --------------------------------------------------------------------------------