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