factory.js (7569B)
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 isFunction = require( '@stdlib/assert/is-function' ); 28 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 29 var constantFunction = require( '@stdlib/utils/constant-function' ); 30 var noop = require( '@stdlib/utils/noop' ); 31 var isnan = require( '@stdlib/math/base/assert/is-nan' ); 32 var isPositiveInteger = require( '@stdlib/math/base/assert/is-positive-integer' ); 33 var isProbability = require( '@stdlib/math/base/assert/is-probability' ); 34 var randu = require( './../../../base/mt19937' ).factory; 35 var typedarray2json = require( '@stdlib/array/to-json' ); 36 var validate = require( './validate.js' ); 37 var binomial0 = require( './binomial.js' ); 38 39 40 // MAIN // 41 42 /** 43 * Returns a pseudorandom number generator for generating binomially distributed random numbers. 44 * 45 * @param {PositiveInteger} [n] - number of trials 46 * @param {Probability} [p] - success probability 47 * @param {Options} [options] - function options 48 * @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers 49 * @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed 50 * @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state 51 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state 52 * @throws {TypeError} `n` must be a positive integer 53 * @throws {TypeError} `p` must be a probability 54 * @throws {TypeError} options argument must be an object 55 * @throws {TypeError} must provide valid options 56 * @throws {Error} must provide a valid state 57 * @returns {PRNG} pseudorandom number generator 58 * 59 * @example 60 * var binomial = factory( 17, 0.5 ); 61 * var v = binomial(); 62 * // returns <number> 63 * 64 * @example 65 * var binomial = factory( 8, 0.8, { 66 * 'seed': 297 67 * }); 68 * var v = binomial(); 69 * // returns <number> 70 * 71 * @example 72 * var binomial = factory(); 73 * var v = binomial( 20, 0.5 ); 74 * // returns <number> 75 */ 76 function factory() { 77 var opts; 78 var rand; 79 var prng; 80 var err; 81 var n; 82 var p; 83 84 if ( arguments.length === 0 ) { 85 rand = randu(); 86 } else if ( arguments.length === 1 ) { 87 opts = arguments[ 0 ]; 88 if ( !isObject( opts ) ) { 89 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' ); 90 } 91 if ( hasOwnProp( opts, 'prng' ) ) { 92 if ( !isFunction( opts.prng ) ) { 93 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 94 } 95 rand = opts.prng; 96 } else { 97 rand = randu( opts ); 98 } 99 } else { 100 n = arguments[ 0 ]; 101 p = arguments[ 1 ]; 102 err = validate( n, p ); 103 if ( err ) { 104 throw err; 105 } 106 if ( arguments.length > 2 ) { 107 opts = arguments[ 2 ]; 108 if ( !isObject( opts ) ) { 109 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' ); 110 } 111 if ( hasOwnProp( opts, 'prng' ) ) { 112 if ( !isFunction( opts.prng ) ) { 113 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 114 } 115 rand = opts.prng; 116 } else { 117 rand = randu( opts ); 118 } 119 } else { 120 rand = randu(); 121 } 122 } 123 if ( n === void 0 ) { 124 prng = binomial2; 125 } 126 else { 127 prng = binomial1; 128 } 129 setReadOnly( prng, 'NAME', 'binomial' ); 130 131 // If we are provided an "external" PRNG, we don't support getting or setting PRNG state, as we'd need to check for compatible state value types, etc, entailing considerable complexity. 132 if ( opts && opts.prng ) { 133 setReadOnly( prng, 'seed', null ); 134 setReadOnly( prng, 'seedLength', null ); 135 setReadWriteAccessor( prng, 'state', constantFunction( null ), noop ); 136 setReadOnly( prng, 'stateLength', null ); 137 setReadOnly( prng, 'byteLength', null ); 138 setReadOnly( prng, 'toJSON', constantFunction( null ) ); 139 setReadOnly( prng, 'PRNG', rand ); 140 } else { 141 setReadOnlyAccessor( prng, 'seed', getSeed ); 142 setReadOnlyAccessor( prng, 'seedLength', getSeedLength ); 143 setReadWriteAccessor( prng, 'state', getState, setState ); 144 setReadOnlyAccessor( prng, 'stateLength', getStateLength ); 145 setReadOnlyAccessor( prng, 'byteLength', getStateSize ); 146 setReadOnly( prng, 'toJSON', toJSON ); 147 setReadOnly( prng, 'PRNG', rand ); 148 rand = rand.normalized; 149 } 150 return prng; 151 152 /** 153 * Returns the PRNG seed. 154 * 155 * @private 156 * @returns {PRNGSeedMT19937} seed 157 */ 158 function getSeed() { 159 return rand.seed; 160 } 161 162 /** 163 * Returns the PRNG seed length. 164 * 165 * @private 166 * @returns {PositiveInteger} seed length 167 */ 168 function getSeedLength() { 169 return rand.seedLength; 170 } 171 172 /** 173 * Returns the PRNG state length. 174 * 175 * @private 176 * @returns {PositiveInteger} state length 177 */ 178 function getStateLength() { 179 return rand.stateLength; 180 } 181 182 /** 183 * Returns the PRNG state size (in bytes). 184 * 185 * @private 186 * @returns {PositiveInteger} state size (in bytes) 187 */ 188 function getStateSize() { 189 return rand.byteLength; 190 } 191 192 /** 193 * Returns the current pseudorandom number generator state. 194 * 195 * @private 196 * @returns {PRNGStateMT19937} current state 197 */ 198 function getState() { 199 return rand.state; 200 } 201 202 /** 203 * Sets the pseudorandom number generator state. 204 * 205 * @private 206 * @param {PRNGStateMT19937} s - generator state 207 * @throws {Error} must provide a valid state 208 */ 209 function setState( s ) { 210 rand.state = s; 211 } 212 213 /** 214 * Serializes the pseudorandom number generator as a JSON object. 215 * 216 * ## Notes 217 * 218 * - `JSON.stringify()` implicitly calls this method when stringifying a PRNG. 219 * 220 * @private 221 * @returns {Object} JSON representation 222 */ 223 function toJSON() { 224 var out = {}; 225 out.type = 'PRNG'; 226 out.name = prng.NAME; 227 out.state = typedarray2json( rand.state ); 228 if ( n === void 0 ) { 229 out.params = []; 230 } else { 231 out.params = [ n, p ]; 232 } 233 return out; 234 } 235 236 /** 237 * Returns a random number drawn from a binomial distribution with bound parameter values. 238 * 239 * @private 240 * @returns {NonNegativeInteger} pseudorandom number 241 * 242 * @example 243 * var r = binomial1(); 244 * // returns <number> 245 */ 246 function binomial1() { 247 return binomial0( rand, n, p ); 248 } 249 250 /** 251 * Returns a random number drawn from a binomial distribution. 252 * 253 * @private 254 * @param {PositiveInteger} n - number of trials 255 * @param {Probability} p - success probability 256 * @returns {NonNegativeInteger} pseudorandom number 257 * 258 * @example 259 * var r = binomial2( 20, 0.8 ); 260 * // returns <number> 261 */ 262 function binomial2( n, p ) { 263 if ( 264 isnan( n ) || 265 isnan( p ) || 266 !isPositiveInteger( n ) || 267 !isProbability( p ) 268 ) { 269 return NaN; 270 } 271 return binomial0( rand, n, p ); 272 } 273 } 274 275 276 // EXPORTS // 277 278 module.exports = factory;