assign.js (11641B)
1 /* eslint-disable max-statements, max-lines */ 2 3 /** 4 * @license Apache-2.0 5 * 6 * Copyright (c) 2018 The Stdlib Authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 * 21 * ## Notice 22 * 23 * The original C++ code and copyright notice are from the [Boost library]{@link http://www.boost.org/doc/libs/1_61_0/boost/math/special_functions/beta.hpp}. The implementation has been modified for JavaScript. 24 * 25 * ```text 26 * (C) Copyright John Maddock 2006. 27 * (C) Copyright Paul A. Bristow 2007. 28 * 29 * Use, modification and distribution are subject to the 30 * Boost Software License, Version 1.0. (See accompanying file 31 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 32 * ``` 33 */ 34 35 'use strict'; 36 37 // MODULES // 38 39 var isnan = require( './../../../../base/assert/is-nan' ); 40 var expm1 = require( './../../../../base/special/expm1' ); 41 var floor = require( './../../../../base/special/floor' ); 42 var log1p = require( './../../../../base/special/log1p' ); 43 var asin = require( './../../../../base/special/asin' ); 44 var beta = require( './../../../../base/special/beta' ); 45 var sqrt = require( './../../../../base/special/sqrt' ); 46 var exp = require( './../../../../base/special/exp' ); 47 var pow = require( './../../../../base/special/pow' ); 48 var max = require( './../../../../base/special/max' ); 49 var min = require( './../../../../base/special/min' ); 50 var MAX_FLOAT64 = require( '@stdlib/constants/float64/max' ); 51 var MIN_FLOAT64 = require( '@stdlib/constants/float64/smallest-normal' ); 52 var MAX_INT32 = require( '@stdlib/constants/int32/max' ); 53 var HALF_PI = require( '@stdlib/constants/float64/half-pi' ); 54 var PI = require( '@stdlib/constants/float64/pi' ); 55 var betaSmallBLargeASeries = require( './beta_small_b_large_a_series.js' ); 56 var risingFactorialRatio = require( './rising_factorial_ratio.js' ); 57 var ibetaPowerTerms = require( './ibeta_power_terms.js' ); 58 var ibetaFraction2 = require( './ibeta_fraction2.js'); 59 var binomialCCDF = require( './binomial_ccdf.js' ); 60 var ibetaAStep = require( './ibeta_a_step.js' ); 61 var ibetaSeries = require( './ibeta_series.js' ); 62 63 64 // VARIABLES // 65 66 var ONE_OVER_PI = 1.0 / PI; 67 68 69 // MAIN // 70 71 /** 72 * Evaluates the incomplete beta function and its first derivative and assigns results to a provided output array. 73 * 74 * ## Notes 75 * 76 * - This function divides up the input range and selects the right implementation method for each domain. 77 * 78 * @param {Probability} x - function input 79 * @param {NonNegativeNumber} a - function parameter 80 * @param {NonNegativeNumber} b - function parameter 81 * @param {boolean} regularized - boolean indicating if the function should evaluate the regularized boolean beta function 82 * @param {boolean} upper - boolean indicating if the function should return the upper tail of the incomplete beta function instead 83 * @param {(Array|TypedArray|Object)} out - output array 84 * @param {integer} stride - output array stride 85 * @param {NonNegativeInteger} offset - output array index offset 86 * @returns {(Array|TypedArray|Object)} function value and first derivative 87 * 88 * @example 89 * var out = ibetaImp( 0.5, 2.0, 2.0, false, false, [ 0.0, 0.0 ], 1, 0 ); 90 * // returns [ ~0.083, ~1.5 ] 91 * 92 * @example 93 * var out = ibetaImp( 0.2, 1.0, 2.0, false, true, [ 0.0, 0.0 ], 1, 0 ); 94 * // returns [ 0.32, 1.6 ] 95 * 96 * @example 97 * var out = ibetaImp( 0.2, 1.0, 2.0, true, true, [ 0.0, 0.0 ], 1, 0 ); 98 * // returns [ 0.64, 1.6 ] 99 */ 100 function ibetaImp( x, a, b, regularized, upper, out, stride, offset ) { 101 var lambda; 102 var prefix; 103 var fract; 104 var bbar; 105 var div; 106 var tmp; 107 var i0; 108 var i1; 109 var k; 110 var n; 111 var p; 112 var y; 113 114 y = 1.0 - x; 115 i0 = offset; 116 i1 = offset + stride; 117 118 // Derivative not set... 119 out[ i1 ] = -1; 120 if ( isnan( x ) || x < 0.0 || x > 1.0 ) { 121 out[ i0 ] = NaN; 122 out[ i1 ] = NaN; 123 return out; 124 } 125 if ( regularized ) { 126 if ( a < 0.0 || b < 0.0 ) { 127 out[ i0 ] = NaN; 128 out[ i1 ] = NaN; 129 return out; 130 } 131 // Extend to a few very special cases... 132 if ( a === 0.0 ) { 133 if ( b === 0.0 ) { 134 out[ i0 ] = NaN; 135 out[ i1 ] = NaN; 136 return out; 137 } 138 if ( b > 0.0 ) { 139 out[ i0 ] = ( upper ) ? 0.0 : 1.0; 140 return out; 141 } 142 } else if ( b === 0.0 ) { 143 if ( a > 0.0 ) { 144 out[ i0 ] = ( upper ) ? 1.0 : 0.0; 145 return out; 146 } 147 } 148 } else if ( a <= 0.0 || b <= 0.0 ) { 149 out[ i0 ] = NaN; 150 out[ i1 ] = NaN; 151 return out; 152 } 153 if ( x === 0.0 ) { 154 if ( a === 1.0 ) { 155 out[ i1 ] = 1.0; 156 } else { 157 out[ i1 ] = ( a < 1.0 ) ? MAX_FLOAT64 / 2.0 : MIN_FLOAT64 * 2.0; 158 } 159 if ( upper ) { 160 out[ i0 ] = ( regularized ) ? 1.0 : beta( a, b ); 161 return out; 162 } 163 out[ i0 ] = 0.0; 164 return out; 165 } 166 if ( x === 1.0 ) { 167 if ( b === 1.0 ) { 168 out[ i1 ] = 1.0; 169 } else { 170 out[ i1 ] = ( b < 1.0 ) ? MAX_FLOAT64 / 2.0 : MIN_FLOAT64 * 2.0; 171 } 172 if ( upper ) { 173 out[ i0 ] = 0.0; 174 } else { 175 out[ i0 ] = ( regularized ) ? 1.0 : beta( a, b ); 176 } 177 return out; 178 } 179 if ( a === 0.5 && b === 0.5 ) { 180 out[ i1 ] = ONE_OVER_PI * sqrt( y * x ); 181 182 // We have an arcsine distribution: 183 p = ( upper ) ? asin( sqrt(y) ) : asin( sqrt(x) ); 184 p /= HALF_PI; 185 if ( !regularized ) { 186 p *= PI; 187 } 188 out[ i0 ] = p; 189 return out; 190 } 191 if ( a === 1.0 ) { 192 tmp = b; 193 b = a; 194 a = tmp; 195 196 tmp = y; 197 y = x; 198 x = tmp; 199 200 upper = !upper; 201 } 202 if ( b === 1.0 ) { 203 // Special case see: http://functions.wolfram.com/GammaBetaErf/BetaRegularized/03/01/01/ 204 if ( a === 1.0 ) { 205 out[ i0 ] = ( upper ) ? y : x; 206 out[ i1 ] = 1.0; 207 return out; 208 } 209 out[ i1 ] = a * pow( x, a - 1.0 ); 210 if ( y < 0.5 ) { 211 p = ( upper ) ? -expm1( a * log1p(-y) ) : exp( a * log1p(-y) ); 212 } else { 213 p = ( upper ) ? -( pow( x, a ) - 1.0 ) : pow( x, a ); 214 } 215 if ( !regularized ) { 216 p /= a; 217 } 218 out[ i0 ] = p; 219 return out; 220 } 221 if ( min( a, b ) <= 1.0 ) { 222 if ( x > 0.5 ) { 223 tmp = b; 224 b = a; 225 a = tmp; 226 227 tmp = y; 228 y = x; 229 x = tmp; 230 231 upper = !upper; 232 } 233 if ( max( a, b ) <= 1.0 ) { 234 // Both a,b < 1: 235 if ( (a >= min( 0.2, b ) ) || ( pow(x, a) <= 0.9 ) ) { 236 if ( upper ) { 237 fract = -( ( regularized ) ? 1.0 : beta( a, b ) ); 238 upper = false; 239 fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); 240 } else { 241 fract = ibetaSeries( a, b, x, 0, regularized, out, y ); 242 } 243 } else { 244 tmp = b; 245 b = a; 246 a = tmp; 247 248 tmp = y; 249 y = x; 250 x = tmp; 251 252 upper = !upper; 253 if ( y >= 0.3 ) { 254 if ( upper ) { 255 fract = -( ( regularized ) ? 1.0 : beta( a, b ) ); 256 upper = false; 257 fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); // eslint-disable-line max-len 258 } else { 259 fract = ibetaSeries( a, b, x, 0, regularized, out, y ); 260 } 261 } else { 262 // Sidestep on a, and then use the series representation: 263 if ( regularized ) { 264 prefix = 1; 265 } else { 266 prefix = risingFactorialRatio( a + b, a, 20 ); 267 } 268 fract = ibetaAStep( a, b, x, y, 20, regularized, out ); 269 if ( upper ) { 270 fract -= ( ( regularized ) ? 1 : beta( a, b ) ); 271 upper = false; 272 fract = -betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len 273 } else { 274 fract = betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len 275 } 276 } 277 } 278 } else if ( b <= 1.0 || ( x < 0.1 && ( pow( b * x, a ) <= 0.7 ) ) ) { 279 if ( upper ) { 280 fract = -( ( regularized ) ? 1 : beta( a, b ) ); 281 upper = false; 282 fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); 283 } else { 284 fract = ibetaSeries( a, b, x, 0.0, regularized, out, y ); 285 } 286 } else { 287 tmp = b; 288 b = a; 289 a = tmp; 290 291 tmp = y; 292 y = x; 293 x = tmp; 294 upper = !upper; 295 296 if ( y >= 0.3 ) { 297 if (upper) { 298 fract = -(( regularized ) ? 1.0 : beta( a, b )); 299 upper = false; 300 fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); 301 } else { 302 fract = ibetaSeries( a, b, x, 0.0, regularized, out, y ); 303 } 304 } 305 else if ( a >= 15.0 ) { 306 if ( upper ) { 307 fract = -(( regularized ) ? 1.0 : beta( a, b )); 308 upper = false; 309 fract = -betaSmallBLargeASeries( a, b, x, y, fract, 1.0, regularized ); // eslint-disable-line max-len 310 } else { 311 fract = betaSmallBLargeASeries( a, b, x, y, 0.0, 1.0, regularized ); // eslint-disable-line max-len 312 } 313 } 314 else { 315 if ( regularized ) { 316 prefix = 1; 317 } else { 318 // Sidestep to improve errors: 319 prefix = risingFactorialRatio( a + b, a, 20.0 ); 320 } 321 fract = ibetaAStep( a, b, x, y, 20.0, regularized, out ); 322 if ( upper ) { 323 fract -= ( ( regularized ) ? 1.0 : beta( a, b ) ); 324 upper = false; 325 fract = -betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len 326 } else { 327 fract = betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len 328 } 329 } 330 } 331 } else { 332 // Both a,b >= 1: 333 if ( a < b ) { 334 lambda = a - ( (a + b) * x ); 335 } else { 336 lambda = ( (a + b) * y ) - b; 337 } 338 if ( lambda < 0.0 ) { 339 tmp = b; 340 b = a; 341 a = tmp; 342 343 tmp = y; 344 y = x; 345 x = tmp; 346 upper = !upper; 347 } 348 if ( b < 40.0 ) { 349 if ( 350 floor(a) === a && 351 floor(b) === b && 352 a < MAX_INT32 - 100 353 ) { 354 // Relate to the binomial distribution and use a finite sum: 355 k = a - 1.0; 356 n = b + k; 357 fract = binomialCCDF( n, k, x, y ); 358 if ( !regularized ) { 359 fract *= beta( a, b ); 360 } 361 } 362 else if ( b * x <= 0.7 ) { 363 if ( upper ) { 364 fract = -( ( regularized ) ? 1.0 : beta( a, b ) ); 365 upper = false; 366 fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); 367 } else { 368 fract = ibetaSeries( a, b, x, 0.0, regularized, out, y ); 369 } 370 } 371 else if ( a > 15.0 ) { 372 // Sidestep so we can use the series representation: 373 n = floor( b ); 374 if ( n === b ) { 375 n -= 1; 376 } 377 bbar = b - n; 378 if ( regularized ) { 379 prefix = 1; 380 } else { 381 prefix = risingFactorialRatio( a + bbar, bbar, n ); 382 } 383 fract = ibetaAStep( bbar, a, y, x, n, regularized ); 384 fract = betaSmallBLargeASeries( a, bbar, x, y, fract, 1.0, regularized ); // eslint-disable-line max-len 385 fract /= prefix; 386 } 387 else if ( regularized ) { 388 n = floor( b ); 389 bbar = b - n; 390 if ( bbar <= 0 ) { 391 n -= 1; 392 bbar += 1; 393 } 394 fract = ibetaAStep( bbar, a, y, x, n, regularized ); 395 fract += ibetaAStep( a, bbar, x, y, 20.0, regularized ); 396 if ( upper ) { 397 fract -= 1; 398 } 399 fract = betaSmallBLargeASeries( a + 20.0, bbar, x, y, fract, 1, regularized ); // eslint-disable-line max-len 400 if ( upper ) { 401 fract = -fract; 402 upper = false; 403 } 404 } 405 else { 406 fract = ibetaFraction2( a, b, x, y, regularized, out ); 407 } 408 } else { 409 fract = ibetaFraction2( a, b, x, y, regularized, out ); 410 } 411 } 412 if ( out[ i1 ] < 0.0 ) { 413 out[ i1 ] = ibetaPowerTerms( a, b, x, y, true ); 414 } 415 div = y * x; 416 if ( out[ i1 ] !== 0.0 ) { 417 if ( ( MAX_FLOAT64 * div < out[ i1 ] ) ) { 418 // Overflow, return an arbitrarily large value: 419 out[ i1 ] = MAX_FLOAT64 / 2.0; 420 } else { 421 out[ i1 ] /= div; 422 } 423 } 424 out[ i0 ] = ( upper ) ? ( ( regularized ) ? 1.0 : beta( a, b ) ) - fract : fract; // eslint-disable-line max-len 425 return out; 426 } 427 428 429 // EXPORTS // 430 431 module.exports = ibetaImp;