factory.js (6007B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 24 var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); 25 var setReadWriteAccessor = require( '@stdlib/utils/define-nonenumerable-read-write-accessor' ); 26 var isObject = require( '@stdlib/assert/is-plain-object' ); 27 var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; 28 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 29 var typedarray2json = require( '@stdlib/array/to-json' ); 30 var defaults = require( './defaults.json' ); 31 var PRNGS = require( './prngs.js' ); 32 33 34 // MAIN // 35 36 /** 37 * Returns a pseudorandom number generator for generating random numbers having integer values. 38 * 39 * @param {Options} [options] - function options 40 * @param {string} [options.name='mt19937'] - name of pseudorandom number generator 41 * @param {*} [options.seed] - pseudorandom number generator seed 42 * @param {*} [options.state] - pseudorandom number generator state 43 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state 44 * @throws {TypeError} must provide an object 45 * @throws {TypeError} must provide valid options 46 * @throws {Error} must provide the name of a supported pseudorandom number generator 47 * @returns {PRNG} pseudorandom number generator 48 * 49 * @example 50 * var randi = factory(); 51 * var v = randi(); 52 * // returns <number> 53 * 54 * @example 55 * var randi = factory({ 56 * 'name': 'minstd' 57 * }); 58 * var v = randi(); 59 * // returns <number> 60 * 61 * @example 62 * var randi = factory({ 63 * 'seed': 12345 64 * }); 65 * var v = randi(); 66 * // returns <number> 67 * 68 * @example 69 * var randi = factory({ 70 * 'name': 'minstd', 71 * 'seed': 12345 72 * }); 73 * var v = randi(); 74 * // returns <number> 75 */ 76 function factory( options ) { 77 var opts; 78 var rand; 79 var prng; 80 81 opts = { 82 'name': defaults.name, 83 'copy': defaults.copy 84 }; 85 if ( arguments.length ) { 86 if ( !isObject( options ) ) { 87 throw new TypeError( 'invalid argument. Must provide an object. Value: `' + options + '`.' ); 88 } 89 if ( hasOwnProp( options, 'name' ) ) { 90 opts.name = options.name; 91 } 92 if ( hasOwnProp( options, 'state' ) ) { 93 opts.state = options.state; 94 if ( opts.state === void 0 ) { 95 throw new TypeError( 'invalid option. `state` option cannot be undefined. Option: `' + opts.state + '`.' ); 96 } 97 } else if ( hasOwnProp( options, 'seed' ) ) { 98 opts.seed = options.seed; 99 if ( opts.seed === void 0 ) { 100 throw new TypeError( 'invalid option. `seed` option cannot be undefined. Option: `' + opts.seed + '`.' ); 101 } 102 } 103 if ( hasOwnProp( options, 'copy' ) ) { 104 opts.copy = options.copy; 105 if ( !isBoolean( opts.copy ) ) { 106 throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' ); 107 } 108 } 109 } 110 prng = PRNGS[ opts.name ]; 111 if ( prng === void 0 ) { 112 throw new Error( 'invalid option. Unrecognized/unsupported PRNG. Option: `' + opts.name + '`.' ); 113 } 114 if ( opts.state === void 0 ) { 115 if ( opts.seed === void 0 ) { 116 rand = prng.factory(); 117 } else { 118 rand = prng.factory({ 119 'seed': opts.seed 120 }); 121 } 122 } else { 123 rand = prng.factory({ 124 'state': opts.state, 125 'copy': opts.copy 126 }); 127 } 128 setReadOnly( randi, 'NAME', 'randi' ); 129 setReadOnlyAccessor( randi, 'seed', getSeed ); 130 setReadOnlyAccessor( randi, 'seedLength', getSeedLength ); 131 setReadWriteAccessor( randi, 'state', getState, setState ); 132 setReadOnlyAccessor( randi, 'stateLength', getStateLength ); 133 setReadOnlyAccessor( randi, 'byteLength', getStateSize ); 134 setReadOnly( randi, 'toJSON', toJSON ); 135 setReadOnly( randi, 'PRNG', rand ); 136 setReadOnly( randi, 'MIN', rand.MIN ); 137 setReadOnly( randi, 'MAX', rand.MAX ); 138 139 return randi; 140 141 /** 142 * Returns the PRNG seed. 143 * 144 * @private 145 * @returns {*} seed 146 */ 147 function getSeed() { 148 return rand.seed; 149 } 150 151 /** 152 * Returns the PRNG seed length. 153 * 154 * @private 155 * @returns {PositiveInteger} seed length 156 */ 157 function getSeedLength() { 158 return rand.seedLength; 159 } 160 161 /** 162 * Returns the PRNG state length. 163 * 164 * @private 165 * @returns {PositiveInteger} state length 166 */ 167 function getStateLength() { 168 return rand.stateLength; 169 } 170 171 /** 172 * Returns the PRNG state size (in bytes). 173 * 174 * @private 175 * @returns {PositiveInteger} state size (in bytes) 176 */ 177 function getStateSize() { 178 return rand.byteLength; 179 } 180 181 /** 182 * Returns the current pseudorandom number generator state. 183 * 184 * @private 185 * @returns {*} current state 186 */ 187 function getState() { 188 return rand.state; 189 } 190 191 /** 192 * Sets the pseudorandom number generator state. 193 * 194 * @private 195 * @param {*} s - generator state 196 * @throws {Error} must provide a valid state 197 */ 198 function setState( s ) { 199 rand.state = s; 200 } 201 202 /** 203 * Serializes the pseudorandom number generator as a JSON object. 204 * 205 * ## Notes 206 * 207 * - `JSON.stringify()` implicitly calls this method when stringifying a PRNG. 208 * 209 * @private 210 * @returns {Object} JSON representation 211 */ 212 function toJSON() { 213 var out = {}; 214 out.type = 'PRNG'; 215 out.name = randi.NAME + '-' + rand.NAME; 216 out.state = typedarray2json( rand.state ); 217 out.params = []; 218 return out; 219 } 220 221 /** 222 * Returns a pseudorandom number having an integer value. 223 * 224 * @private 225 * @returns {integer} pseudorandom number 226 * 227 * @example 228 * var v = randi(); 229 * // returns <number> 230 */ 231 function randi() { 232 return rand(); 233 } 234 } 235 236 237 // EXPORTS // 238 239 module.exports = factory;