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