factory.js (7714B)
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 randu = require( './../../../base/mt19937' ).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 triangular0 = require( './triangular.js' ); 36 37 38 // MAIN // 39 40 /** 41 * Returns a pseudorandom number generator for generating random numbers drawn from a triangular distribution. 42 * 43 * @param {number} [a] - minimum support 44 * @param {number} [b] - maximum support 45 * @param {number} [c] - mode 46 * @param {Options} [options] - function options 47 * @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers 48 * @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed 49 * @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state 50 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state 51 * @throws {TypeError} `a` must be a number 52 * @throws {TypeError} `b` must be a number 53 * @throws {TypeError} `c` must be a number 54 * @throws {RangeError} arguments must satisfy `a <= c <= b` 55 * @throws {TypeError} options argument must be an object 56 * @throws {TypeError} must provide valid options 57 * @throws {Error} must provide a valid state 58 * @returns {PRNG} pseudorandom number generator 59 * 60 * @example 61 * var triangular = factory( 0.0, 1.0, 0.8 ); 62 * 63 * var v = triangular(); 64 * // returns <number> 65 * 66 * @example 67 * var triangular = factory( -3.0, -1.0, -2.0, { 68 * 'seed': 297 69 * }); 70 * var v = triangular(); 71 * // returns <number> 72 */ 73 function factory() { 74 var opts; 75 var rand; 76 var prng; 77 var err; 78 var a; 79 var b; 80 var c; 81 82 if ( arguments.length === 0 ) { 83 rand = randu(); 84 } else if ( arguments.length === 1 ) { 85 opts = arguments[ 0 ]; 86 if ( !isObject( opts ) ) { 87 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `'+opts+'`.' ); 88 } 89 if ( hasOwnProp( opts, 'prng' ) ) { 90 if ( !isFunction( opts.prng ) ) { 91 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 92 } 93 rand = opts.prng; 94 } else { 95 rand = randu( opts ); 96 } 97 } else { 98 a = arguments[ 0 ]; 99 b = arguments[ 1 ]; 100 c = arguments[ 2 ]; 101 err = validate( a, b, c ); 102 if ( err ) { 103 throw err; 104 } 105 if ( arguments.length > 3 ) { 106 opts = arguments[ 3 ]; 107 if ( !isObject( opts ) ) { 108 throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' ); 109 } 110 if ( hasOwnProp( opts, 'prng' ) ) { 111 if ( !isFunction( opts.prng ) ) { 112 throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' ); 113 } 114 rand = opts.prng; 115 } else { 116 rand = randu( opts ); 117 } 118 } else { 119 rand = randu(); 120 } 121 } 122 if ( a === void 0 ) { 123 prng = triangular2; 124 } else { 125 prng = triangular1; 126 } 127 setReadOnly( prng, 'NAME', 'triangular' ); 128 129 // 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. 130 if ( opts && opts.prng ) { 131 setReadOnly( prng, 'seed', null ); 132 setReadOnly( prng, 'seedLength', null ); 133 setReadWriteAccessor( prng, 'state', constantFunction( null ), noop ); 134 setReadOnly( prng, 'stateLength', null ); 135 setReadOnly( prng, 'byteLength', null ); 136 setReadOnly( prng, 'toJSON', constantFunction( null ) ); 137 setReadOnly( prng, 'PRNG', rand ); 138 } else { 139 setReadOnlyAccessor( prng, 'seed', getSeed ); 140 setReadOnlyAccessor( prng, 'seedLength', getSeedLength ); 141 setReadWriteAccessor( prng, 'state', getState, setState ); 142 setReadOnlyAccessor( prng, 'stateLength', getStateLength ); 143 setReadOnlyAccessor( prng, 'byteLength', getStateSize ); 144 setReadOnly( prng, 'toJSON', toJSON ); 145 setReadOnly( prng, 'PRNG', rand ); 146 rand = rand.normalized; 147 } 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 ( a === void 0 ) { 227 out.params = []; 228 } else { 229 out.params = [ a, b, c ]; 230 } 231 return out; 232 } 233 234 /** 235 * Returns a pseudorandom number drawn from a triangular distribution with bound parameters. 236 * 237 * @private 238 * @returns {number} pseudorandom number 239 * 240 * @example 241 * var v = triangular1(); 242 * // returns <number> 243 */ 244 function triangular1() { 245 return triangular0( rand, a, b, c ); 246 } 247 248 /** 249 * Returns a pseudorandom number drawn from a triangular distribution with minimum support `a`, maximum support `b`, and mode `c`. 250 * 251 * @private 252 * @param {number} a - minimum support 253 * @param {number} b - maximum support 254 * @param {number} c - mode 255 * @returns {number} pseudorandom number 256 * 257 * @example 258 * var v = triangular2( 0.0, 1.0, 0.5 ); 259 * // returns <number> 260 * 261 * @example 262 * var v = triangular2( 1.0, 0.0, 0.5 ); 263 * // returns NaN 264 * 265 * @example 266 * var v = triangular2( 1.0, 2.0, NaN ); 267 * // returns NaN 268 */ 269 function triangular2( a, b, c ) { 270 if ( 271 isnan( a ) || 272 isnan( b ) || 273 isnan( c ) || 274 !(a <= c && c <= b) 275 ) { 276 return NaN; 277 } 278 return triangular0( rand, a, b, c ); 279 } 280 } 281 282 283 // EXPORTS // 284 285 module.exports = factory;