time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

factory.js (10222B)


      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 hasOwnProp = require( '@stdlib/assert/has-own-property' );
     27 var isObject = require( '@stdlib/assert/is-plain-object' );
     28 var isUint32Array = require( '@stdlib/assert/is-uint32array' );
     29 var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive;
     30 var isFunction = require( '@stdlib/assert/is-function' );
     31 var constantFunction = require( '@stdlib/utils/constant-function' );
     32 var noop = require( '@stdlib/utils/noop' );
     33 var isnan = require( '@stdlib/math/base/assert/is-nan' );
     34 var poisson = require( './../../../base/poisson' ).factory;
     35 var gamma = require( './../../../base/gamma' ).factory;
     36 var gcopy = require( '@stdlib/blas/base/gcopy' );
     37 var Uint32Array = require( '@stdlib/array/uint32' );
     38 var copy = require( '@stdlib/utils/copy' );
     39 var typedarray2json = require( '@stdlib/array/to-json' );
     40 var validate = require( './validate.js' );
     41 
     42 
     43 // MAIN //
     44 
     45 /**
     46 * Returns a pseudorandom number generator for generating negative binomial distributed random numbers.
     47 *
     48 * @param {PositiveNumber} [r] - number of successes until experiment is stopped
     49 * @param {number} [p] - success probability
     50 * @param {Options} [options] - function options
     51 * @param {PRNG} [options.prng] - pseudorandom number generator which generates uniformly distributed pseudorandom numbers
     52 * @param {PRNGSeedMT19937} [options.seed] - pseudorandom number generator seed
     53 * @param {PRNGStateMT19937} [options.state] - pseudorandom number generator state
     54 * @param {boolean} [options.copy=true] - boolean indicating whether to copy a provided pseudorandom number generator state
     55 * @throws {TypeError} `r` must be a positive number
     56 * @throws {TypeError} `p` must be number
     57 * @throws {RangeError} `p` must be a number on the interval `(0,1)`
     58 * @throws {TypeError} options argument must be an object
     59 * @throws {TypeError} must provide valid options
     60 * @throws {Error} must provide a valid state
     61 * @returns {PRNG} pseudorandom number generator
     62 *
     63 * @example
     64 * var negativeBinomial = factory( 10, 0.5 );
     65 *
     66 * var v = negativeBinomial();
     67 * // returns <number>
     68 *
     69 * @example
     70 * var negativeBinomial = factory( 10, 0.8, {
     71 *     'seed': 297
     72 * });
     73 *
     74 * var v = negativeBinomial();
     75 * // returns <number>
     76 *
     77 * @example
     78 * var negativeBinomial = factory();
     79 *
     80 * var v = negativeBinomial( 10, 0.5 );
     81 * // returns <number>
     82 */
     83 function factory() {
     84 	var rgamma;
     85 	var STATE;
     86 	var rpois;
     87 	var opts;
     88 	var prng;
     89 	var rand;
     90 	var FLG;
     91 	var err;
     92 	var p;
     93 	var r;
     94 
     95 	FLG = true;
     96 	if ( arguments.length === 0 ) {
     97 		opts = {
     98 			'copy': false
     99 		};
    100 		rpois = poisson( opts );
    101 	} else if ( arguments.length === 1 ) {
    102 		opts = arguments[ 0 ];
    103 		if ( !isObject( opts ) ) {
    104 			throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' );
    105 		}
    106 		if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) {
    107 			throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' );
    108 		}
    109 		if ( hasOwnProp( opts, 'prng' ) ) {
    110 			if ( !isFunction( opts.prng ) ) {
    111 				throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' );
    112 			}
    113 			rpois = poisson({
    114 				'prng': opts.prng
    115 			});
    116 		} else {
    117 			if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
    118 				throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
    119 			}
    120 			opts = copy( opts, 1 );
    121 			if ( opts.copy === false ) {
    122 				FLG = false;
    123 			} else if ( opts.state ) {
    124 				opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
    125 			}
    126 			opts.copy = false;
    127 			rpois = poisson( opts );
    128 		}
    129 	} else {
    130 		r = arguments[ 0 ];
    131 		p = arguments[ 1 ];
    132 		err = validate( r, p );
    133 		if ( err ) {
    134 			throw err;
    135 		}
    136 		if ( arguments.length > 2 ) {
    137 			opts = arguments[ 2 ];
    138 			if ( !isObject( opts ) ) {
    139 				throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' );
    140 			}
    141 			if ( hasOwnProp( opts, 'copy' ) && !isBoolean( opts.copy ) ) {
    142 				throw new TypeError( 'invalid option. `copy` option must be a boolean. Option: `' + opts.copy + '`.' );
    143 			}
    144 			if ( hasOwnProp( opts, 'prng' ) ) {
    145 				if ( !isFunction( opts.prng ) ) {
    146 					throw new TypeError( 'invalid option. `prng` option must be a pseudorandom number generator function. Option: `' + opts.prng + '`.' );
    147 				}
    148 				rpois = poisson({
    149 					'prng': opts.prng
    150 				});
    151 			} else {
    152 				if ( hasOwnProp( opts, 'state' ) && !isUint32Array( opts.state ) ) {
    153 					throw new TypeError( 'invalid option. `state` option must be a Uint32Array. Option: `' + opts.state + '`.' );
    154 				}
    155 				opts = copy( opts, 1 );
    156 				if ( opts.copy === false ) {
    157 					FLG = false;
    158 				} else if ( opts.state ) {
    159 					opts.state = gcopy( opts.state.length, opts.state, 1, new Uint32Array( opts.state.length ), 1 ); // eslint-disable-line max-len
    160 				}
    161 				opts.copy = false;
    162 				rpois = poisson( opts );
    163 			}
    164 		} else {
    165 			opts = {
    166 				'copy': false
    167 			};
    168 			rpois = poisson( opts );
    169 		}
    170 	}
    171 	if ( opts && opts.prng ) {
    172 		if ( r === void 0 ) {
    173 			rgamma = gamma({
    174 				'prng': opts.prng
    175 			});
    176 		} else {
    177 			rgamma = gamma( r, p/(1-p), {
    178 				'prng': opts.prng
    179 			});
    180 		}
    181 	} else {
    182 		if ( opts.state ) {
    183 			STATE = opts.state;
    184 		} else {
    185 			STATE = rpois.state;
    186 			rpois.state = STATE; // updates the underlying PRNG to point to a shared state
    187 		}
    188 		if ( r === void 0 ) {
    189 			rgamma = gamma({
    190 				'state': STATE,
    191 				'copy': false
    192 			});
    193 		} else {
    194 			rgamma = gamma( r, p/(1-p), {
    195 				'state': STATE,
    196 				'copy': false
    197 			});
    198 		}
    199 	}
    200 	if ( r === void 0 ) {
    201 		prng = negativeBinomial2;
    202 	} else {
    203 		prng = negativeBinomial1;
    204 	}
    205 	rand = rpois.PRNG;
    206 
    207 	setReadOnly( prng, 'NAME', 'negative-binomial' );
    208 
    209 	// 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.
    210 	if ( opts && opts.prng ) {
    211 		setReadOnly( prng, 'seed', null );
    212 		setReadOnly( prng, 'seedLength', null );
    213 		setReadWriteAccessor( prng, 'state', constantFunction( null ), noop );
    214 		setReadOnly( prng, 'stateLength', null );
    215 		setReadOnly( prng, 'byteLength', null );
    216 		setReadOnly( prng, 'toJSON', constantFunction( null ) );
    217 	} else {
    218 		setReadOnlyAccessor( prng, 'seed', getSeed );
    219 		setReadOnlyAccessor( prng, 'seedLength', getSeedLength );
    220 		setReadWriteAccessor( prng, 'state', getState, setState );
    221 		setReadOnlyAccessor( prng, 'stateLength', getStateLength );
    222 		setReadOnlyAccessor( prng, 'byteLength', getStateSize );
    223 		setReadOnly( prng, 'toJSON', toJSON );
    224 	}
    225 	setReadOnly( prng, 'PRNG', rand );
    226 	return prng;
    227 
    228 	/**
    229 	* Returns the PRNG seed.
    230 	*
    231 	* @private
    232 	* @returns {PRNGSeedMT19937} seed
    233 	*/
    234 	function getSeed() {
    235 		return rand.seed;
    236 	}
    237 
    238 	/**
    239 	* Returns the PRNG seed length.
    240 	*
    241 	* @private
    242 	* @returns {PositiveInteger} seed length
    243 	*/
    244 	function getSeedLength() {
    245 		return rand.seedLength;
    246 	}
    247 
    248 	/**
    249 	* Returns the PRNG state length.
    250 	*
    251 	* @private
    252 	* @returns {PositiveInteger} state length
    253 	*/
    254 	function getStateLength() {
    255 		return rand.stateLength;
    256 	}
    257 
    258 	/**
    259 	* Returns the PRNG state size (in bytes).
    260 	*
    261 	* @private
    262 	* @returns {PositiveInteger} state size (in bytes)
    263 	*/
    264 	function getStateSize() {
    265 		return rand.byteLength;
    266 	}
    267 
    268 	/**
    269 	* Returns the current pseudorandom number generator state.
    270 	*
    271 	* @private
    272 	* @returns {PRNGStateMT19937} current state
    273 	*/
    274 	function getState() {
    275 		return rand.state;
    276 	}
    277 
    278 	/**
    279 	* Sets the pseudorandom number generator state.
    280 	*
    281 	* @private
    282 	* @param {PRNGStateMT19937} s - generator state
    283 	* @throws {TypeError} must provide a `Uint32Array`
    284 	* @throws {Error} must provide a valid state
    285 	*/
    286 	function setState( s ) {
    287 		if ( !isUint32Array( s ) ) {
    288 			throw new TypeError( 'invalid argument. Must provide a Uint32Array. Value: `' + s + '`.' );
    289 		}
    290 		if ( FLG ) {
    291 			s = gcopy( s.length, s, 1, new Uint32Array( s.length ), 1 );
    292 		}
    293 		rand.state = s;
    294 	}
    295 
    296 	/**
    297 	* Serializes the pseudorandom number generator as a JSON object.
    298 	*
    299 	* ## Notes
    300 	*
    301 	* -   `JSON.stringify()` implicitly calls this method when stringifying a PRNG.
    302 	*
    303 	* @private
    304 	* @returns {Object} JSON representation
    305 	*/
    306 	function toJSON() {
    307 		var out = {};
    308 		out.type = 'PRNG';
    309 		out.name = prng.NAME;
    310 		out.state = typedarray2json( rand.state );
    311 		if ( r === void 0 ) {
    312 			out.params = [];
    313 		} else {
    314 			out.params = [ r, p ];
    315 		}
    316 		return out;
    317 	}
    318 
    319 	/**
    320 	* Returns a pseudorandom number drawn from a negative binomial distribution with bound parameters `r` and `p`.
    321 	*
    322 	* @private
    323 	* @returns {NonNegativeInteger} pseudorandom number
    324 	*
    325 	* @example
    326 	* var v = negativeBinomial1();
    327 	* // returns <number>
    328 	*/
    329 	function negativeBinomial1() {
    330 		return rpois( rgamma() );
    331 	}
    332 
    333 	/**
    334 	* Returns a pseudorandom number drawn from a negative binomial distribution with parameters `r` and `p`.
    335 	*
    336 	* @private
    337 	* @param {PositiveNumber} r - number of successes until experiment is stopped
    338 	* @param {number} p - success probability
    339 	* @returns {NonNegativeInteger} pseudorandom number
    340 	*
    341 	* @example
    342 	* var v = negativeBinomial2( 10, 0.5 );
    343 	* // returns <number>
    344 	*/
    345 	function negativeBinomial2( r, p ) {
    346 		if (
    347 			isnan( r ) ||
    348 			isnan( p ) ||
    349 			p <= 0.0 ||
    350 			p >= 1.0
    351 		) {
    352 			return NaN;
    353 		}
    354 		return rpois( rgamma( r, p/(1-p) ) );
    355 	}
    356 }
    357 
    358 
    359 // EXPORTS //
    360 
    361 module.exports = factory;