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