Skip to content

Instantly share code, notes, and snippets.

@okikio
Last active January 4, 2023 02:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okikio/3b99abc593ba8b51c5f614f3a6d4894e to your computer and use it in GitHub Desktop.
Save okikio/3b99abc593ba8b51c5f614f3a6d4894e to your computer and use it in GitHub Desktop.
beta-distribution.ts
/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Double-precision floating-point positive infinity.
*
* ## Notes
*
* Double-precision floating-point positive infinity has the bit sequence
*
* ```binarystring
* 0 11111111111 00000000000000000000 00000000000000000000000000000000
* ```
*
* @constant
* @type {number}
* @default Number.POSITIVE_INFINITY
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_PINF: number = Number.POSITIVE_INFINITY; // eslint-disable-line stdlib/require-globals
/**
* Double-precision floating-point negative infinity.
*
* ## Notes
*
* Double-precision floating-point negative infinity has the bit sequence
*
* ```binarystring
* 1 11111111111 00000000000000000000 00000000000000000000000000000000
* ```
*
* @constant
* @type {number}
* @default Number.NEGATIVE_INFINITY
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_NINF: number = Number.NEGATIVE_INFINITY;
const PINF = FLOAT64_PINF;
const NINF = FLOAT64_NINF;
// 1/LN2
const INV_LN2 = 1.44269504088896338700e+00; // 0x3FF71547, 0x652B82FE
// High (24 bits): 1/LN2
const INV_LN2_HI = 1.44269502162933349609e+00; // 0x3FF71547, 0x60000000
// Low: 1/LN2
const INV_LN2_LO = 1.92596299112661746887e-08; // 0x3E54AE0B, 0xF85DDF44
// 0x3fefffff = 1072693247 => 0 01111111110 11111111111111111111 => biased exponent: 1022 = -1+1023 => 2^-1
const HIGH_MAX_NEAR_UNITY = 0x3fefffff|0; // asm type annotation
// 0x41e00000 = 1105199104 => 0 10000011110 00000000000000000000 => biased exponent: 1054 = 31+1023 => 2^31
const HIGH_BIASED_EXP_31 = 0x41e00000|0; // asm type annotation
// 0x43f00000 = 1139802112 => 0 10000111111 00000000000000000000 => biased exponent: 1087 = 64+1023 => 2^64
const HIGH_BIASED_EXP_64 = 0x43f00000|0; // asm type annotation
// 0x40900000 = 1083179008 => 0 10000001001 00000000000000000000 => biased exponent: 1033 = 10+1023 => 2^10 = 1024
const HIGH_BIASED_EXP_10 = 0x40900000|0; // asm type annotation
// 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1
const HIGH_BIASED_EXP_0 = 0x3ff00000|0; // asm type annotation
// 0x4090cc00 = 1083231232 => 0 10000001001 00001100110000000000
const HIGH_1075 = 0x4090cc00|0; // asm type annotation
// 0xc090cc00 = 3230714880 => 1 10000001001 00001100110000000000
const HIGH_NEG_1075 = 0xc090cc00>>>0; // asm type annotation
const HIGH_NUM_NONSIGN_BITS = 31|0; // asm type annotation
const HUGE = 1.0e300;
const TINY = 1.0e-300;
// -(1024-log2(ovfl+.5ulp))
const OVT = 8.0085662595372944372e-17;
// Log workspace:
const LOG_WORKSPACE = [ 0.0, 0.0 ];
/**
* The smallest positive double-precision floating-point normal number.
*
* ## Notes
*
* The number has the value
*
* ```tex
* \frac{1}{2^{1023-1}}
* ```
*
* which corresponds to the bit sequence
*
* ```binarystring
* 0 00000000001 00000000000000000000 00000000000000000000000000000000
* ```
*
* @constant
* @type {number}
* @default 2.2250738585072014e-308
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_SMALLEST_NORMAL: number = 2.2250738585072014e-308;
const FLOAT64_MIN_NORM = FLOAT64_SMALLEST_NORMAL;
// (1<<52)
const SCALAR = 4503599627370496;
/**
* Returns a boolean indicating if an environment is little endian.
*
* @private
* @returns {boolean} boolean indicating if an environment is little endian
*
* @example
* var bool = isLittleEndian();
* // returns <boolean>
*/
function isLittleEndian(): boolean {
var uint16view;
var uint8view;
uint16view = new Uint16Array( 1 );
/*
* Set the uint16 view to a value having distinguishable lower and higher order words.
*
* 4660 => 0x1234 => 0x12 0x34 => '00010010 00110100' => (0x12,0x34) == (18,52)
*/
uint16view[ 0 ] = 0x1234;
// Create a uint8 view on top of the uint16 buffer:
uint8view = new Uint8Array( uint16view.buffer );
// If little endian, the least significant byte will be first...
return ( uint8view[ 0 ] === 0x34 );
}
const isLittleEndianBool = isLittleEndian();
const HIGH = isLittleEndianBool === true ? 1 : 0 ;
const LOW = isLittleEndianBool === true ? 0 : 1;
const FLOAT64_VIEW = new Float64Array( 1 );
const UINT32_VIEW = new Uint32Array(FLOAT64_VIEW.buffer);
const S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549
const S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6
const S3 = -1.98412698298579493134e-04; // 0xBF2A01A0, 0x19C161D5
const S4 = 2.75573137070700676789e-06; // 0x3EC71DE3, 0x57B1FE7D
const S5 = -2.50507602534068634195e-08; // 0xBE5AE5E6, 0x8A2B9CEB
const S6 = 1.58969099521155010221e-10; // 0x3DE5D93A, 0x5ACFD57C
/*
* Table of constants for `2/π` (`396` hex digits, `476` decimal).
*
* Integer array which contains the (`24*i`)-th to (`24*i+23`)-th bit of `2/π` after binary point. The corresponding floating value is
*
* ```tex
* \operatorname{ipio2}[i] \cdot 2^{-24(i+1)}
* ```
*
* This table must have at least `(e0-3)/24 + jk` terms. For quad precision (`e0 <= 16360`, `jk = 6`), this is `686`.
*/
const IPIO2 = [
0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8,
0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5,
0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08,
0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3,
0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B
];
// Double precision array, obtained by cutting `π/2` into `24` bits chunks...
const PIO2 = [
1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000
7.54978941586159635335e-08, // 0x3E74442D, 0x00000000
5.39030252995776476554e-15, // 0x3CF84698, 0x80000000
3.28200341580791294123e-22, // 0x3B78CC51, 0x60000000
1.27065575308067607349e-29, // 0x39F01B83, 0x80000000
1.22933308981111328932e-36, // 0x387A2520, 0x40000000
2.73370053816464559624e-44, // 0x36E38222, 0x80000000
2.16741683877804819444e-51 // 0x3569F31D, 0x00000000
];
const TWO24 = 1.67772160000000000000e+07; // 0x41700000, 0x00000000
const TWON24 = 5.96046447753906250000e-08; // 0x3E700000, 0x00000000
// Arrays for storing temporary values (note that, in C, this is not thread safe):
const F = zeros( 20 );
const Q = zeros( 20 );
const FQ = zeros( 20 );
const IQ = zeros( 20 );
// 53 bits of 2/π:
const INVPIO2 = 6.36619772367581382433e-01; // 0x3FE45F30, 0x6DC9C883
// First 33 bits of π/2:
const PIO2_1 = 1.57079632673412561417e+00; // 0x3FF921FB, 0x54400000
// PIO2_1T = π/2 - PIO2_1:
const PIO2_1T = 6.07710050650619224932e-11; // 0x3DD0B461, 0x1A626331
// Another 33 bits of π/2:
const PIO2_2 = 6.07710050630396597660e-11; // 0x3DD0B461, 0x1A600000
// PIO2_2T = π/2 - ( PIO2_1 + PIO2_2 ):
const PIO2_2T = 2.02226624879595063154e-21; // 0x3BA3198A, 0x2E037073
// Another 33 bits of π/2:
const PIO2_3 = 2.02226624871116645580e-21; // 0x3BA3198A, 0x2E000000
// PIO2_3T = π/2 - ( PIO2_1 + PIO2_2 + PIO2_3 ):
const PIO2_3T = 8.47842766036889956997e-32; // 0x397B839A, 0x252049C1
// Exponent mask (2047 => 0x7ff):
const EXPONENT_MASK_REMPIO = 0x7ff|0; // asm type annotation
const ZERO = 0.00000000000000000000e+00; // 0x00000000, 0x00000000
const TWO24 = 1.67772160000000000000e+07; // 0x41700000, 0x00000000
// 33 bits of π/2:
const PIO2_1 = 1.57079632673412561417e+00; // 0x3FF921FB, 0x54400000
// PIO2_1T = π/2 - PIO2_1:
const PIO2_1T = 6.07710050650619224932e-11; // 0x3DD0B461, 0x1A626331
const TWO_PIO2_1T = 2.0 * PIO2_1T;
const THREE_PIO2_1T = 3.0 * PIO2_1T;
const FOUR_PIO2_1T = 4.0 * PIO2_1T;
// Absolute value mask: 0x7fffffff = 2147483647 => 01111111111111111111111111111111
const ABS_MASK = 0x7fffffff | 0; // asm type annotation
// Exponent mask: 0x7ff00000 = 2146435072 => 01111111111100000000000000000000
const EXPONENT_MASK = 0x7ff00000|0; // asm type annotation
// High word significand mask: 0xfffff = 1048575 => 00000000000011111111111111111111
const SIGNIFICAND_MASK = 0xfffff|0; // asm type annotation
// High word significand for π and π/2: 0x921fb = 598523 => 00000000000010010010000111111011
const PI_HIGH_WORD_SIGNIFICAND = 0x921fb|0; // asm type annotation
// High word for PI/4: 0x3fe921fb = 1072243195 => 00111111111010010010000111111011
const PIO4_HIGH_WORD = 0x3fe921fb|0; // asm type annotation
// High word for 3π/4: 0x4002d97c = 1073928572 => 01000000000000101101100101111100
const THREE_PIO4_HIGH_WORD = 0x4002d97c|0; // asm type annotation
// High word for 5π/4: 0x400f6a7a = 1074752122 => 01000000000011110110101001111010
const FIVE_PIO4_HIGH_WORD = 0x400f6a7a|0; // asm type annotation
// High word for 6π/4: 0x4012d97c = 1074977148 => 01000000000100101101100101111100
const THREE_PIO2_HIGH_WORD = 0x4012d97c|0; // asm type annotation
// High word for 7π/4: 0x4015fdbc = 1075183036 => 01000000000101011111110110111100
const SEVEN_PIO4_HIGH_WORD = 0x4015fdbc|0; // asm type annotation
// High word for 8π/4: 0x401921fb = 1075388923 => 01000000000110010010000111111011
const TWO_PI_HIGH_WORD = 0x401921fb|0; // asm type annotation
// High word for 9π/4: 0x401c463b = 1075594811 => 01000000000111000100011000111011
const NINE_PIO4_HIGH_WORD = 0x401c463b|0; // asm type annotation
// 2^20*π/2 = 1647099.3291652855 => 0100000100111001001000011111101101010100010001000010110100011000 => high word => 0x413921fb = 1094263291 => 01000001001110010010000111111011
const MEDIUM = 0x413921fb|0; // asm type annotation
// Arrays for storing temporary values:
const TX = [ 0.0, 0.0, 0.0 ]; // WARNING: not thread safe
const TY = [ 0.0, 0.0 ]; // WARNING: not thread safe
// 2^-26 = 1.4901161193847656e-8 => 0011111001010000000000000000000000000000000000000000000000000000 => high word => 00111110010100000000000000000000 => 0x3e500000 = 1045430272
const SMALL_HIGH_WORD = 0x3e500000|0; // asm type annotation
// Array for storing remainder elements:
const Y = [ 0.0, 0.0 ]; // WARNING: not thread safe
/**
* The mathematical constant `π`.
*
* @constant
* @type {number}
* @default 3.141592653589793
* @see [Wikipedia]{@link https://en.wikipedia.org/wiki/Pi}
*/
const PI: number = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; // eslint-disable-line max-len
/**
* Square root of the mathematical constant `π` times `2`.
*
* @constant
* @type {number}
* @default 2.5066282746310007
* @see [Wikipedia]{@link https://en.wikipedia.org/wiki/Pi}
*/
const SQRT_TWO_PI: number = 2.506628274631000502415765284811045253e+00;
const MAX_STIRLING = 143.01608;
/**
* The Euler-Mascheroni constant.
*
* @constant
* @type {number}
* @default 0.5772156649015329
* @see [OEIS]{@link http://oeis.org/A001620}
* @see [Mathworld]{@link http://mathworld.wolfram.com/Euler-MascheroniConstant.html}
*/
const EULER: number = 0.577215664901532860606512090082402431042;
/**
* Natural logarithm of the square root of `2π`.
*
* ```tex
* \ln \sqrt{2\pi}
* ```
*
* @constant
* @type {number}
* @default 0.9189385332046728
*/
const LN_SQRT_TWO_PI: number = 9.18938533204672741780329736405617639861397473637783412817151540482765695927260397694743298635954197622005646625e-01; // eslint-disable-line max-len
/**
* Bias of a double-precision floating-point number's exponent.
*
* ## Notes
*
* The bias can be computed via
*
* ```tex
* \mathrm{bias} = 2^{k-1} - 1
* ```
*
* where \\(k\\) is the number of bits in the exponent; here, \\(k = 11\\).
*
* @constant
* @type {integer32}
* @default 1023
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_EXPONENT_BIAS: number = 1023|0; // asm type annotation
const BIAS = FLOAT64_EXPONENT_BIAS;
/**
* The maximum biased base 2 exponent for a double-precision floating-point number.
*
* ```text
* 11111111110 => 2046 - BIAS = 1023
* ```
*
* where `BIAS = 1023`.
*
* @constant
* @type {integer32}
* @default 1023
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_MAX_BASE2_EXPONENT: number = 1023|0; // asm type annotation
const MAX_EXPONENT = FLOAT64_MAX_BASE2_EXPONENT;
/**
* The maximum biased base 2 exponent for a subnormal double-precision floating-point number.
*
* ```text
* 00000000000 => 0 - BIAS = -1023
* ```
*
* where `BIAS = 1023`.
*
* @constant
* @type {integer32}
* @default -1023
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_MAX_BASE2_EXPONENT_SUBNORMAL: number = -1023|0; // asm type annotation
const MAX_SUBNORMAL_EXPONENT = FLOAT64_MAX_BASE2_EXPONENT_SUBNORMAL;
/**
* The minimum biased base 2 exponent for a subnormal double-precision floating-point number.
*
* ```text
* -(BIAS+(52-1)) = -(1023+51) = -1074
* ```
*
* where `BIAS = 1023` and `52` is the number of digits in the significand.
*
* @constant
* @type {integer32}
* @default -1074
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_MIN_BASE2_EXPONENT_SUBNORMAL: number = -1074|0; // asm type annotation
const MIN_SUBNORMAL_EXPONENT = FLOAT64_MIN_BASE2_EXPONENT_SUBNORMAL;
// 1/(1<<52) = 1/(2**52) = 1/4503599627370496
const TWO52_INV = 2.220446049250313e-16;
// Exponent all 0s: 1 00000000000 11111111111111111111 => 2148532223
const CLEAR_EXP_MASK = 0x800fffff>>>0; // asm type annotation
// Normalization workspace:
const FRAC = [ 0.0, 0.0 ];
// High and low words of ln(2):
const LN2_HI = 6.93147180369123816490e-01; // 0x3FE62E42 0xFEE00000
const LN2_LO = 1.90821492927058770002e-10; // 0x3DEA39EF 0x35793C76
const LOG2_E = 1.44269504088896338700e+00;
const OVERFLOW = 7.09782712893383973096e+02;
const UNDERFLOW = -7.45133219101941108420e+02;
const NEARZERO = 1.0 / (1 << 28); // 2^-28;
const NEG_NEARZERO = -NEARZERO;
const XBIG = 94906265.62425156;
const XMAX = 3.745194030963158e306;
const ALGMCS = [
+0.1276642195630062933333333333333e-30,
-0.3401102254316748799999999999999e-29,
+0.1025680058010470912000000000000e-27,
-0.3547598158101070547199999999999e-26,
+0.1429227355942498147573333333333e-24,
-0.6831888753985766870111999999999e-23,
+0.3962837061046434803679306666666e-21,
-0.2868042435334643284144622399999e-19,
+0.2683181998482698748957538846666e-17,
-0.3399615005417721944303330599666e-15,
+0.6221098041892605227126015543416e-13,
-0.1809129475572494194263306266719e-10,
+0.9810825646924729426157171547487e-8,
-0.1384948176067563840732986059135e-4,
+0.1666389480451863247205729650822e+0
];
const LEN = ALGMCS.length;
// 0x3fefffff = 1072693247 => 0 01111111110 11111111111111111111 => biased exponent: 1022 = -1+1023 => 2^-1
const HIGH_MAX_NEAR_UNITY = 0x3fefffff|0; // asm type annotation
const HUGE = 1.0e300;
const TINY = 1.0e-300;
/**
* High word mask for the significand of a double-precision floating-point number.
*
* ## Notes
*
* The high word mask for the significand of a double-precision floating-point number is an unsigned 32-bit integer with the value \\( 1048575 \\), which corresponds to the bit sequence
*
* ```binarystring
* 0 00000000000 11111111111111111111
* ```
*
* @constant
* @type {uinteger32}
* @default 0x000fffff
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
// 0x000fffff = 1048575 => 0 00000000000 11111111111111111111
const HIGH_SIGNIFICAND_MASK: number = 0x000fffff|0; // asm type annotation
// 0x00100000 = 1048576 => 0 00000000001 00000000000000000000 => biased exponent: 1 = -1022+1023 => 2^-1022
const HIGH_MIN_NORMAL_EXP: number = 0x00100000|0; // asm type annotation
// 0x7ff00000 = 2146435072 => 0 11111111111 00000000000000000000 => biased exponent: 2047 = 1023+1023 => 2^1023
const HIGH_MAX_NORMAL_EXP: number = 0x7ff00000|0; // asm type annotation
// 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1
const HIGH_BIASED_EXP_0: number = 0x3ff00000|0; // asm type annotation
// 0x20000000 = 536870912 => 0 01000000000 00000000000000000000 => biased exponent: 512 = -511+1023
const HIGH_BIASED_EXP_NEG_512 = 0x20000000|0; // asm type annotation
// 0x00080000 = 524288 => 0 00000000000 10000000000000000000
const HIGH_SIGNIFICAND_HALF = 0x00080000|0; // asm type annotation
// TODO: consider making an external constant
const HIGH_NUM_SIGNIFICAND_BITS = 20|0; // asm type annotation
const TWO53 = 9007199254740992.0; // 0x43400000, 0x00000000
// 2/(3*LN2)
const CP = 9.61796693925975554329e-01; // 0x3FEEC709, 0xDC3A03FD
// (float)CP
const CP_HI = 9.61796700954437255859e-01; // 0x3FEEC709, 0xE0000000
// Low: CP_HI
const CP_LO = -7.02846165095275826516e-09; // 0xBE3E2FE0, 0x145B01F5
const BP = [
1.0,
1.5
];
const DP_HI = [
0.0,
5.84962487220764160156e-01 // 0x3FE2B803, 0x40000000
];
const DP_LO = [
0.0,
1.35003920212974897128e-08 // 0x3E4CFDEB, 0x43CFD006
];
const TWO54 = 1.80143985094819840000e+16; // 0x43500000, 0x00000000
const ONE_THIRD = 1.0 / 3.0;
/**
* One fourth times the mathematical constant `π`.
*
* @constant
* @type {number}
* @default 7.85398163397448309616e-1
* @see [Wikipedia]{@link https://en.wikipedia.org/wiki/Pi}
*/
const FOURTH_PI: number = 7.85398163397448309616e-1;
const PIO4: number = FOURTH_PI;
const MOREBITS: number = 6.123233995736765886130e-17; // pi/2 = PIO2 + MOREBITS
/**
* Square root of `2`.
*
* ```tex
* \sqrt{2}
* ```
*
* @constant
* @type {number}
* @default 1.4142135623730951
*/
const SQRT2: number = 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623e+00; // eslint-disable-line max-len
// Scratch array for storing temporary values:
let buffer = [ 0.0, 0.0 ]; // WARNING: not thread safe
// High word absolute value mask: 0x7fffffff => 01111111111111111111111111111111
const HIGH_WORD_ABS_MASK = 0x7fffffff|0; // asm type annotation
// High word of π/4: 0x3fe921fb => 00111111111010010010000111111011
const HIGH_WORD_PIO4 = 0x3fe921fb|0; // asm type annotation
// High word of 2^-27: 0x3e400000 => 00111110010000000000000000000000
const HIGH_WORD_TWO_NEG_27 = 0x3e400000|0; // asm type annotation
// High word exponent mask: 0x7ff00000 => 01111111111100000000000000000000
const HIGH_WORD_EXPONENT_MASK = 0x7ff00000|0; // asm type annotation
const DF_THRESHOLD = 0x10000000; // 2^28
const EXP = ( 2.0 * 53.0 ) / 3.0;
const C = 0.85498797333834849467655443627193;
const Y1 = 8.91314744949340820313e-2;
const Y2 = 2.249481201171875;
const Y3 = 8.07220458984375e-1;
const Y4 = 9.3995571136474609375e-1;
const Y5 = 9.8362827301025390625e-1;
const OVERFLOW_THRESHOLD = 7.09782712893383973096e+02; // 0x40862E42 0xFEFA39EF
// 1 / ln(2):
const LN2_INV = 1.44269504088896338700e+00; // 0x3FF71547 0x652B82FE
// ln(2) * 56:
const LN2x56 = 3.88162421113569373274e+01; // 0x4043687A 0x9F1AF2B1
// ln(2) * 1.5:
const LN2_HALFX3 = 1.03972077083991796413e+00; // 0x3FF0A2B2 0x3F3BAB73
/**
* Difference between one and the smallest value greater than one that can be represented as a double-precision floating-point number.
*
* ## Notes
*
* The difference is
*
* ```tex
* \frac{1}{2^{52}}
* ```
*
* @constant
* @type {number}
* @default 2.220446049250313e-16
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
* @see [Machine Epsilon]{@link https://en.wikipedia.org/wiki/Machine_epsilon}
*/
const FLOAT64_EPSILON: number = 2.2204460492503130808472633361816E-16;
const EPSILON = FLOAT64_EPSILON;
/**
* Euler's number.
*
* @constant
* @type {number}
* @default 2.718281828459045
* @see [OEIS]{@link https://oeis.org/A001113}
* @see [Wikipedia]{@link https://en.wikipedia.org/wiki/E_(mathematical_constant)}
*/
const E: number = 2.718281828459045235360287471352662497757247093699959574966;
/**
* Arbitrary constant `g` to be used in Lanczos approximation functions.
*
* @constant
* @type {number}
* @default 10.900511
* @see [Lanczos Approximation]{@link https://en.wikipedia.org/wiki/Lanczos_approximation}
*/
const FLOAT64_GAMMA_LANCZOS_G: number = 10.90051099999999983936049829935654997826;
const G = FLOAT64_GAMMA_LANCZOS_G;
const MAX_FACTORIAL = 170; // TODO: consider moving to pkg
const FACTORIAL_169 = 4.269068009004705e+304;
const ceil = Math.ceil;
const round = Math.round;
const floor = Math.floor;
const sqrt = Math.sqrt;
/**
* One half times the natural logarithm of 2.
*
* ```tex
* \frac{\ln 2}{2}
* ```
*
* @constant
* @type {number}
* @default 3.46573590279972654709e-01
*/
const HALF_LN2: number = 3.46573590279972654709e-01; // 0x3FD62E42 0xFEFA39EF
const ONE_OVER_PI = 1.0 / PI;
var log1p = require( '@stdlib/math-base-special-log1p' );
var gammaln = require('@stdlib/math-base-special-gammaln');
// var evalpoly = require( '@stdlib/math-base-tools-evalpoly' );
// var betainc = require( '@stdlib/math-base-special-betainc' );
// var expm1 = require( '@stdlib/math-base-special-expm1' );
// var findIBetaInvFromTDist = require( './find_ibeta_inv_from_t_dist.js' );
var temme1 = require( './temme1.js' );
var temme2 = require( './temme2.js' );
var temme3 = require( './temme3.js' );
var halleyIterate = require( './halley_iterate.js' );
var ibetaRoots = require( './ibeta_roots.js' );
const lanczosSumExpGScaled = require( './lanczos_sum_expg_scaled.js' ); // Lanczos approximation scaled by exp(G)
// var kernelBetainc = require( '@stdlib/math-base-special-kernel-betainc' ).assign;
var MAX_FLOAT64 = require( '@stdlib/constants-float64-max' );
var MIN_FLOAT64 = require( '@stdlib/constants-float64-smallest-normal' );
var MAX_INT32 = require('@stdlib/constants-int32-max');
var betaSmallBLargeASeries = require( './beta_small_b_large_a_series.js' );
var risingFactorialRatio = require( './rising_factorial_ratio.js' );
var ibetaPowerTerms = require( './ibeta_power_terms.js' );
var ibetaFraction2 = require( './ibeta_fraction2.js');
var binomialCCDF = require( './binomial_ccdf.js' );
var ibetaAStep = require( './ibeta_a_step.js' );
var ibetaSeries = require( './ibeta_series.js' );
/**
* Evaluates the incomplete beta function and its first derivative and assigns results to a provided output array.
*
* ## Notes
*
* - This function divides up the input range and selects the right implementation method for each domain.
*
* @param {Probability} x - function input
* @param {NonNegativeNumber} a - function parameter
* @param {NonNegativeNumber} b - function parameter
* @param {boolean} regularized - boolean indicating if the function should evaluate the regularized boolean beta function
* @param {boolean} upper - boolean indicating if the function should return the upper tail of the incomplete beta function instead
* @param {(Array|TypedArray|Object)} out - output array
* @param {integer} stride - output array stride
* @param {NonNegativeInteger} offset - output array index offset
* @returns {(Array|TypedArray|Object)} function value and first derivative
*
* @example
* var out = ibetaImp( 0.5, 2.0, 2.0, false, false, [ 0.0, 0.0 ], 1, 0 );
* // returns [ ~0.083, ~1.5 ]
*
* @example
* var out = ibetaImp( 0.2, 1.0, 2.0, false, true, [ 0.0, 0.0 ], 1, 0 );
* // returns [ 0.32, 1.6 ]
*
* @example
* var out = ibetaImp( 0.2, 1.0, 2.0, true, true, [ 0.0, 0.0 ], 1, 0 );
* // returns [ 0.64, 1.6 ]
*/
function kernelBetainc( x, a, b, regularized, upper, out, stride, offset ) {
var lambda;
var prefix;
var fract;
var bbar;
var div;
var tmp;
var i0;
var i1;
var k;
var n;
var p;
var y;
y = 1.0 - x;
i0 = offset;
i1 = offset + stride;
// Derivative not set...
out[ i1 ] = -1;
if ( isnan( x ) || x < 0.0 || x > 1.0 ) {
out[ i0 ] = NaN;
out[ i1 ] = NaN;
return out;
}
if ( regularized ) {
if ( a < 0.0 || b < 0.0 ) {
out[ i0 ] = NaN;
out[ i1 ] = NaN;
return out;
}
// Extend to a few very special cases...
if ( a === 0.0 ) {
if ( b === 0.0 ) {
out[ i0 ] = NaN;
out[ i1 ] = NaN;
return out;
}
if ( b > 0.0 ) {
out[ i0 ] = ( upper ) ? 0.0 : 1.0;
return out;
}
} else if ( b === 0.0 ) {
if ( a > 0.0 ) {
out[ i0 ] = ( upper ) ? 1.0 : 0.0;
return out;
}
}
} else if ( a <= 0.0 || b <= 0.0 ) {
out[ i0 ] = NaN;
out[ i1 ] = NaN;
return out;
}
if ( x === 0.0 ) {
if ( a === 1.0 ) {
out[ i1 ] = 1.0;
} else {
out[ i1 ] = ( a < 1.0 ) ? MAX_FLOAT64 / 2.0 : MIN_FLOAT64 * 2.0;
}
if ( upper ) {
out[ i0 ] = ( regularized ) ? 1.0 : beta( a, b );
return out;
}
out[ i0 ] = 0.0;
return out;
}
if ( x === 1.0 ) {
if ( b === 1.0 ) {
out[ i1 ] = 1.0;
} else {
out[ i1 ] = ( b < 1.0 ) ? MAX_FLOAT64 / 2.0 : MIN_FLOAT64 * 2.0;
}
if ( upper ) {
out[ i0 ] = 0.0;
} else {
out[ i0 ] = ( regularized ) ? 1.0 : beta( a, b );
}
return out;
}
if ( a === 0.5 && b === 0.5 ) {
out[ i1 ] = ONE_OVER_PI * sqrt( y * x );
// We have an arcsine distribution:
p = ( upper ) ? asin( sqrt(y) ) : asin( sqrt(x) );
p /= HALF_PI;
if ( !regularized ) {
p *= PI;
}
out[ i0 ] = p;
return out;
}
if ( a === 1.0 ) {
tmp = b;
b = a;
a = tmp;
tmp = y;
y = x;
x = tmp;
upper = !upper;
}
if ( b === 1.0 ) {
// Special case see: http://functions.wolfram.com/GammaBetaErf/BetaRegularized/03/01/01/
if ( a === 1.0 ) {
out[ i0 ] = ( upper ) ? y : x;
out[ i1 ] = 1.0;
return out;
}
out[ i1 ] = a * pow( x, a - 1.0 );
if ( y < 0.5 ) {
p = ( upper ) ? -expm1( a * log1p(-y) ) : exp( a * log1p(-y) );
} else {
p = ( upper ) ? -( pow( x, a ) - 1.0 ) : pow( x, a );
}
if ( !regularized ) {
p /= a;
}
out[ i0 ] = p;
return out;
}
if ( min( a, b ) <= 1.0 ) {
if ( x > 0.5 ) {
tmp = b;
b = a;
a = tmp;
tmp = y;
y = x;
x = tmp;
upper = !upper;
}
if ( max( a, b ) <= 1.0 ) {
// Both a,b < 1:
if ( (a >= min( 0.2, b ) ) || ( pow(x, a) <= 0.9 ) ) {
if ( upper ) {
fract = -( ( regularized ) ? 1.0 : beta( a, b ) );
upper = false;
fract = -ibetaSeries( a, b, x, fract, regularized, out, y );
} else {
fract = ibetaSeries( a, b, x, 0, regularized, out, y );
}
} else {
tmp = b;
b = a;
a = tmp;
tmp = y;
y = x;
x = tmp;
upper = !upper;
if ( y >= 0.3 ) {
if ( upper ) {
fract = -( ( regularized ) ? 1.0 : beta( a, b ) );
upper = false;
fract = -ibetaSeries( a, b, x, fract, regularized, out, y ); // eslint-disable-line max-len
} else {
fract = ibetaSeries( a, b, x, 0, regularized, out, y );
}
} else {
// Sidestep on a, and then use the series representation:
if ( regularized ) {
prefix = 1;
} else {
prefix = risingFactorialRatio( a + b, a, 20 );
}
fract = ibetaAStep( a, b, x, y, 20, regularized, out );
if ( upper ) {
fract -= ( ( regularized ) ? 1 : beta( a, b ) );
upper = false;
fract = -betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len
} else {
fract = betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len
}
}
}
} else if ( b <= 1.0 || ( x < 0.1 && ( pow( b * x, a ) <= 0.7 ) ) ) {
if ( upper ) {
fract = -( ( regularized ) ? 1 : beta( a, b ) );
upper = false;
fract = -ibetaSeries( a, b, x, fract, regularized, out, y );
} else {
fract = ibetaSeries( a, b, x, 0.0, regularized, out, y );
}
} else {
tmp = b;
b = a;
a = tmp;
tmp = y;
y = x;
x = tmp;
upper = !upper;
if ( y >= 0.3 ) {
if (upper) {
fract = -(( regularized ) ? 1.0 : beta( a, b ));
upper = false;
fract = -ibetaSeries( a, b, x, fract, regularized, out, y );
} else {
fract = ibetaSeries( a, b, x, 0.0, regularized, out, y );
}
}
else if ( a >= 15.0 ) {
if ( upper ) {
fract = -(( regularized ) ? 1.0 : beta( a, b ));
upper = false;
fract = -betaSmallBLargeASeries( a, b, x, y, fract, 1.0, regularized ); // eslint-disable-line max-len
} else {
fract = betaSmallBLargeASeries( a, b, x, y, 0.0, 1.0, regularized ); // eslint-disable-line max-len
}
}
else {
if ( regularized ) {
prefix = 1;
} else {
// Sidestep to improve errors:
prefix = risingFactorialRatio( a + b, a, 20.0 );
}
fract = ibetaAStep( a, b, x, y, 20.0, regularized, out );
if ( upper ) {
fract -= ( ( regularized ) ? 1.0 : beta( a, b ) );
upper = false;
fract = -betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len
} else {
fract = betaSmallBLargeASeries( a + 20.0, b, x, y, fract, prefix, regularized ); // eslint-disable-line max-len
}
}
}
} else {
// Both a,b >= 1:
if ( a < b ) {
lambda = a - ( (a + b) * x );
} else {
lambda = ( (a + b) * y ) - b;
}
if ( lambda < 0.0 ) {
tmp = b;
b = a;
a = tmp;
tmp = y;
y = x;
x = tmp;
upper = !upper;
}
if ( b < 40.0 ) {
if (
floor(a) === a &&
floor(b) === b &&
a < MAX_INT32 - 100
) {
// Relate to the binomial distribution and use a finite sum:
k = a - 1.0;
n = b + k;
fract = binomialCCDF( n, k, x, y );
if ( !regularized ) {
fract *= beta( a, b );
}
}
else if ( b * x <= 0.7 ) {
if ( upper ) {
fract = -( ( regularized ) ? 1.0 : beta( a, b ) );
upper = false;
fract = -ibetaSeries( a, b, x, fract, regularized, out, y );
} else {
fract = ibetaSeries( a, b, x, 0.0, regularized, out, y );
}
}
else if ( a > 15.0 ) {
// Sidestep so we can use the series representation:
n = floor( b );
if ( n === b ) {
n -= 1;
}
bbar = b - n;
if ( regularized ) {
prefix = 1;
} else {
prefix = risingFactorialRatio( a + bbar, bbar, n );
}
fract = ibetaAStep( bbar, a, y, x, n, regularized );
fract = betaSmallBLargeASeries( a, bbar, x, y, fract, 1.0, regularized ); // eslint-disable-line max-len
fract /= prefix;
}
else if ( regularized ) {
n = floor( b );
bbar = b - n;
if ( bbar <= 0 ) {
n -= 1;
bbar += 1;
}
fract = ibetaAStep( bbar, a, y, x, n, regularized );
fract += ibetaAStep( a, bbar, x, y, 20.0, regularized );
if ( upper ) {
fract -= 1;
}
fract = betaSmallBLargeASeries( a + 20.0, bbar, x, y, fract, 1, regularized ); // eslint-disable-line max-len
if ( upper ) {
fract = -fract;
upper = false;
}
}
else {
fract = ibetaFraction2( a, b, x, y, regularized, out );
}
} else {
fract = ibetaFraction2( a, b, x, y, regularized, out );
}
}
if ( out[ i1 ] < 0.0 ) {
out[ i1 ] = ibetaPowerTerms( a, b, x, y, true );
}
div = y * x;
if ( out[ i1 ] !== 0.0 ) {
if ( ( MAX_FLOAT64 * div < out[ i1 ] ) ) {
// Overflow, return an arbitrarily large value:
out[ i1 ] = MAX_FLOAT64 / 2.0;
} else {
out[ i1 ] /= div;
}
}
out[ i0 ] = ( upper ) ? ( ( regularized ) ? 1.0 : beta( a, b ) ) - fract : fract; // eslint-disable-line max-len
return out;
}
/**
* Evaluates the incomplete beta function.
*
* @param {Probability} x - function parameter
* @param {NonNegativeNumber} a - function parameter
* @param {NonNegativeNumber} b - function parameter
* @param {boolean} [regularized=true] - boolean indicating if the function should evaluate the regularized or non-regularized incomplete beta function
* @param {boolean} [upper=false] - boolean indicating if the function should return the upper tail of the incomplete beta function
* @returns {number} function value
*
* @example
* var y = betainc( 0.5, 2.0, 2.0 );
* // returns 0.5
*
* @example
* var y = betainc( 0.5, 2.0, 2.0, false );
* // returns ~0.083
*
* @example
* var y = betainc( 0.2, 1.0, 2.0 );
* // returns 0.36
*/
function betainc( x: number, a: number, b: number, regularized: boolean, upper: boolean ): number {
var out = [ 0.0, 0.0 ];
regularized = ( regularized === false ) ? false : true; // eslint-disable-line no-unneeded-ternary
upper = ( upper === true ) ? true : false; // eslint-disable-line no-unneeded-ternary
kernelBetainc( x, a, b, regularized, upper, out, 1, 0 );
return out[ 0 ];
}
// var inverseStudentsT = require( './inverse_students_t.js' );
// var inverseStudentsTBodySeries = require( './inverse_students_t_body_series.js' );
// var inverseStudentsTTailSeries = require( './inverse_students_t_tail_series.js' );
// var inverseStudentsTHill = require( './inverse_students_t_hill.js' );
// var gammaDeltaRatio = require( '@stdlib/math-base-special-gamma-delta-ratio' );
// var gammaDeltaRatioLanczos = require('./gamma_delta_ratio_lanczos.js');
// var lanczosSum = require( '@stdlib/math-base-special-gamma-lanczos-sum' );
// var evalpoly = require('@stdlib/math-base-tools-evalpoly');
// var expm1 = require('@stdlib/math-base-special-expm1');
// var HALF_LN2 = require( '@stdlib/constants-float64-half-ln-two' );
// var polyval = require( './polyval_q.js' );
// var gamma = require( '@stdlib/math-base-special-gamma' );
// var factorial = require('@stdlib/math-base-special-factorial');
const FACTORIALS = [
1,
1,
2,
6,
24,
120,
720,
5040,
40320,
362880.0,
3628800.0,
39916800.0,
479001600.0,
6227020800.0,
87178291200.0,
1307674368000.0,
20922789888000.0,
355687428096000.0,
6402373705728000.0,
121645100408832000.0,
0.243290200817664e19,
0.5109094217170944e20,
0.112400072777760768e22,
0.2585201673888497664e23,
0.62044840173323943936e24,
0.15511210043330985984e26,
0.403291461126605635584e27,
0.10888869450418352160768e29,
0.304888344611713860501504e30,
0.8841761993739701954543616e31,
0.26525285981219105863630848e33,
0.822283865417792281772556288e34,
0.26313083693369353016721801216e36,
0.868331761881188649551819440128e37,
0.29523279903960414084761860964352e39,
0.103331479663861449296666513375232e41,
0.3719933267899012174679994481508352e42,
0.137637530912263450463159795815809024e44,
0.5230226174666011117600072241000742912e45,
0.203978820811974433586402817399028973568e47,
0.815915283247897734345611269596115894272e48,
0.3345252661316380710817006205344075166515e50,
0.1405006117752879898543142606244511569936e52,
0.6041526306337383563735513206851399750726e53,
0.265827157478844876804362581101461589032e55,
0.1196222208654801945619631614956577150644e57,
0.5502622159812088949850305428800254892962e58,
0.2586232415111681806429643551536119799692e60,
0.1241391559253607267086228904737337503852e62,
0.6082818640342675608722521633212953768876e63,
0.3041409320171337804361260816606476884438e65,
0.1551118753287382280224243016469303211063e67,
0.8065817517094387857166063685640376697529e68,
0.427488328406002556429801375338939964969e70,
0.2308436973392413804720927426830275810833e72,
0.1269640335365827592596510084756651695958e74,
0.7109985878048634518540456474637249497365e75,
0.4052691950487721675568060190543232213498e77,
0.2350561331282878571829474910515074683829e79,
0.1386831185456898357379390197203894063459e81,
0.8320987112741390144276341183223364380754e82,
0.507580213877224798800856812176625227226e84,
0.3146997326038793752565312235495076408801e86,
0.1982608315404440064116146708361898137545e88,
0.1268869321858841641034333893351614808029e90,
0.8247650592082470666723170306785496252186e91,
0.5443449390774430640037292402478427526443e93,
0.3647111091818868528824985909660546442717e95,
0.2480035542436830599600990418569171581047e97,
0.1711224524281413113724683388812728390923e99,
0.1197857166996989179607278372168909873646e101,
0.8504785885678623175211676442399260102886e102,
0.6123445837688608686152407038527467274078e104,
0.4470115461512684340891257138125051110077e106,
0.3307885441519386412259530282212537821457e108,
0.2480914081139539809194647711659403366093e110,
0.188549470166605025498793226086114655823e112,
0.1451830920282858696340707840863082849837e114,
0.1132428117820629783145752115873204622873e116,
0.8946182130782975286851441715398316520698e117,
0.7156945704626380229481153372318653216558e119,
0.5797126020747367985879734231578109105412e121,
0.4753643337012841748421382069894049466438e123,
0.3945523969720658651189747118012061057144e125,
0.3314240134565353266999387579130131288001e127,
0.2817104114380550276949479442260611594801e129,
0.2422709538367273238176552320344125971528e131,
0.210775729837952771721360051869938959523e133,
0.1854826422573984391147968456455462843802e135,
0.1650795516090846108121691926245361930984e137,
0.1485715964481761497309522733620825737886e139,
0.1352001527678402962551665687594951421476e141,
0.1243841405464130725547532432587355307758e143,
0.1156772507081641574759205162306240436215e145,
0.1087366156656743080273652852567866010042e147,
0.103299784882390592625997020993947270954e149,
0.9916779348709496892095714015418938011582e150,
0.9619275968248211985332842594956369871234e152,
0.942689044888324774562618574305724247381e154,
0.9332621544394415268169923885626670049072e156,
0.9332621544394415268169923885626670049072e158,
0.9425947759838359420851623124482936749562e160,
0.9614466715035126609268655586972595484554e162,
0.990290071648618040754671525458177334909e164,
0.1029901674514562762384858386476504428305e167,
0.1081396758240290900504101305800329649721e169,
0.1146280563734708354534347384148349428704e171,
0.1226520203196137939351751701038733888713e173,
0.132464181945182897449989183712183259981e175,
0.1443859583202493582204882102462797533793e177,
0.1588245541522742940425370312709077287172e179,
0.1762952551090244663872161047107075788761e181,
0.1974506857221074023536820372759924883413e183,
0.2231192748659813646596607021218715118256e185,
0.2543559733472187557120132004189335234812e187,
0.2925093693493015690688151804817735520034e189,
0.339310868445189820119825609358857320324e191,
0.396993716080872089540195962949863064779e193,
0.4684525849754290656574312362808384164393e195,
0.5574585761207605881323431711741977155627e197,
0.6689502913449127057588118054090372586753e199,
0.8094298525273443739681622845449350829971e201,
0.9875044200833601362411579871448208012564e203,
0.1214630436702532967576624324188129585545e206,
0.1506141741511140879795014161993280686076e208,
0.1882677176888926099743767702491600857595e210,
0.237217324288004688567714730513941708057e212,
0.3012660018457659544809977077527059692324e214,
0.3856204823625804217356770659234636406175e216,
0.4974504222477287440390234150412680963966e218,
0.6466855489220473672507304395536485253155e220,
0.8471580690878820510984568758152795681634e222,
0.1118248651196004307449963076076169029976e225,
0.1487270706090685728908450891181304809868e227,
0.1992942746161518876737324194182948445223e229,
0.269047270731805048359538766214698040105e231,
0.3659042881952548657689727220519893345429e233,
0.5012888748274991661034926292112253883237e235,
0.6917786472619488492228198283114910358867e237,
0.9615723196941089004197195613529725398826e239,
0.1346201247571752460587607385894161555836e242,
0.1898143759076170969428526414110767793728e244,
0.2695364137888162776588507508037290267094e246,
0.3854370717180072770521565736493325081944e248,
0.5550293832739304789551054660550388118e250,
0.80479260574719919448490292577980627711e252,
0.1174997204390910823947958271638517164581e255,
0.1727245890454638911203498659308620231933e257,
0.2556323917872865588581178015776757943262e259,
0.380892263763056972698595524350736933546e261,
0.571338395644585459047893286526105400319e263,
0.8627209774233240431623188626544191544816e265,
0.1311335885683452545606724671234717114812e268,
0.2006343905095682394778288746989117185662e270,
0.308976961384735088795856467036324046592e272,
0.4789142901463393876335775239063022722176e274,
0.7471062926282894447083809372938315446595e276,
0.1172956879426414428192158071551315525115e279,
0.1853271869493734796543609753051078529682e281,
0.2946702272495038326504339507351214862195e283,
0.4714723635992061322406943211761943779512e285,
0.7590705053947218729075178570936729485014e287,
0.1229694218739449434110178928491750176572e290,
0.2004401576545302577599591653441552787813e292,
0.3287218585534296227263330311644146572013e294,
0.5423910666131588774984495014212841843822e296,
0.9003691705778437366474261723593317460744e298,
0.1503616514864999040201201707840084015944e301,
0.2526075744973198387538018869171341146786e303,
0.4269068009004705274939251888899566538069e305,
0.7257415615307998967396728211129263114717e307
];
/**
* Evaluates the factorial of `x`.
*
* @param {number} x - input value
* @returns {number} factorial
*
* @example
* var v = factorial( 3.0 );
* // returns 6.0
*
* @example
* var v = factorial( -1.5 );
* // returns ~-3.545
*
* @example
* var v = factorial( -0.5 );
* // returns ~1.772
*
* @example
* var v = factorial( 0.5 );
* // returns ~0.886
*
* @example
* var v = factorial( -10.0 );
* // returns NaN
*
* @example
* var v = factorial( 171.0 );
* // returns Infinity
*
* @example
* var v = factorial( NaN );
* // returns NaN
*/
function factorial( x: number ): number {
if ( isnan( x ) ) {
return NaN;
}
if ( isInteger( x ) ) {
if ( x < 0 ) {
return NaN;
}
if ( x <= MAX_FACTORIAL ) {
return FACTORIALS[ x ];
}
return PINF;
}
return gamma( x + 1.0 );
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function lanczosSum( x ) {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return Infinity;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = 38474670393.31777 + (x * (36857665043.51951 + (x * (15889202453.72942 + (x * (4059208354.298835 + (x * (680547661.1834733 + (x * (78239755.00312005 + (x * (6246580.776401795 + (x * (341986.3488721347 + (x * (12287.194511824551 + (x * (261.61404416416684 + (x * 2.5066282746310007))))))))))))))))))); // eslint-disable-line max-len
s2 = 0.0 + (x * (362880.0 + (x * (1026576.0 + (x * (1172700.0 + (x * (723680.0 + (x * (269325.0 + (x * (63273.0 + (x * (9450.0 + (x * (870.0 + (x * (45.0 + (x * 1.0))))))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 2.5066282746310007 + (x * (261.61404416416684 + (x * (12287.194511824551 + (x * (341986.3488721347 + (x * (6246580.776401795 + (x * (78239755.00312005 + (x * (680547661.1834733 + (x * (4059208354.298835 + (x * (15889202453.72942 + (x * (36857665043.51951 + (x * 38474670393.31777))))))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (45.0 + (x * (870.0 + (x * (9450.0 + (x * (63273.0 + (x * (269325.0 + (x * (723680.0 + (x * (1172700.0 + (x * (1026576.0 + (x * (362880.0 + (x * 0.0))))))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Calculates the ratio of two gamma functions via Lanczos approximation.
*
* ## Notes
*
* - When \\( z < \epsilon \\), we get spurious numeric overflow unless we're very careful. This can occur either inside `lanczosSum(z)` or in the final combination of terms. To avoid this, split the product up into 2 (or 3) parts:
*
* ```tex
* \begin{align*}
* G(z) / G(L) &= 1 / (z \cdot G(L)) ; z < \eps, L = z + \delta = \delta \\
* z * G(L) &= z * G(lim) \cdot (G(L)/G(lim)) ; lim = \text{largest factorial}
* \end{align*}
* ```
*
* @private
* @param {number} z - first gamma parameter
* @param {number} delta - difference
* @returns {number} gamma ratio
*/
function gammaDeltaRatioLanczos( z, delta ) {
var result;
var ratio;
var zgh;
if ( z < EPSILON ) {
if ( delta > MAX_FACTORIAL ) {
ratio = gammaDeltaRatioLanczos( delta, MAX_FACTORIAL-delta );
ratio *= z;
ratio *= FACTORIAL_169;
return 1.0 / ratio;
}
return 1.0 / ( z * gamma( z+delta ) );
}
zgh = z + G - 0.5;
if ( z + delta === z ) {
if ( abs(delta) < 10.0 ) {
result = exp( ( 0.5-z ) * log1p( delta/zgh ) );
} else {
result = 1.0;
}
} else {
if ( abs(delta) < 10.0 ) {
result = exp( ( 0.5-z ) * log1p( delta/zgh ));
} else {
result = pow( zgh / (zgh+delta), z-0.5 );
}
// Split up the calculation to avoid spurious overflow:
result *= lanczosSum( z ) / lanczosSum( z + delta );
}
result *= pow( E / ( zgh+delta ), delta );
return result;
}
/**
* Computes the ratio of two gamma functions.
*
* ## Notes
*
* - Specifically, the function evaluates
*
* ```tex
* \frac{ \Gamma( z ) }{ \Gamma( z + \delta ) }
* ```
*
* @param {number} z - first gamma parameter
* @param {number} delta - difference
* @returns {number} gamma ratio
*
* @example
* var y = gammaDeltaRatio( 2.0, 3.0 );
* // returns ~0.042
*
* @example
* var y = gammaDeltaRatio( 4.0, 0.5 );
* // returns ~0.516
*
* @example
* var y = gammaDeltaRatio( 100.0, 0.0 );
* // returns 1.0
*/
function gammaDeltaRatio( z: number, delta: number ): number {
var result;
var idelta;
var iz;
if ( z <= 0.0 || z + delta <= 0.0 ) {
// This isn't very sophisticated, or accurate, but it does work:
return gamma( z ) / gamma( z + delta );
}
idelta = floor( delta );
if ( idelta === delta ) {
iz = floor( z );
if ( iz === z ) {
// As both `z` and `delta` are integers, see if we can use a table lookup:
if ( z <= MAX_FACTORIAL && ( z + delta <= MAX_FACTORIAL ) ) {
return factorial( iz - 1.0 ) / factorial( idelta + iz - 1.0 );
}
}
if ( abs(delta) < 20.0 ) {
// As `delta` is a small integer, we can use a finite product:
if ( delta === 0.0 ) {
return 1.0;
}
if ( delta < 0.0 ) {
z -= 1.0;
result = z;
delta += 1.0;
while ( delta !== 0.0 ) {
z -= 1.0;
result *= z;
delta += 1.0;
}
return result;
}
result = 1.0 / z;
delta -= 1.0;
while ( delta !== 0.0 ) {
z += 1.0;
result /= z;
delta -= 1.0;
}
return result;
}
}
return gammaDeltaRatioLanczos( z, delta );
}
// Array for the coefficients d(k), these depend only on the number of degrees of freedom df, so at least in theory we could tabulate these for fixed df, see p15 of Shaw:
let d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]; // WARNING: not thread safe
/**
* Evaluates Student's t quantiles via a tail series expansion. Tail and body series are due to Shaw.
*
* ## References
*
* - Shaw, William T. 2006. "Sampling Student's T distribution – use of the inverse cumulative distribution function." _Journal of Computational Finance_ 9 (4): 37–73. [www.mth.kcl.ac.uk/~shaww/web\_page/papers/Tdistribution06.pdf](www.mth.kcl.ac.uk/~shaww/web_page/papers/Tdistribution06.pdf).
*
* @private
* @param {number} df - degrees of freedom
* @param {number} v - function value
* @returns {number} tail value
*/
function inverseStudentsTTailSeries( df: number, v: number ): number { // eslint-disable-line id-length
var result;
var power;
var div;
var np2;
var np4;
var np6;
var rn;
var w;
// Tail series expansion, see section 6 of Shaw's paper. `w` is calculated using Eq 60:
w = gammaDeltaRatio( df/2.0, 0.5 ) * sqrt( df*PI ) * v;
// Define some variables:
np2 = df + 2.0;
np4 = df + 4.0;
np6 = df + 6.0;
d[ 0 ] = 1.0;
d[ 1 ] = -(df+1.0) / (2.0*np2);
np2 *= (df + 2.0);
d[ 2 ] = -df * (df+1.0) * (df+3.0) / (8.0*np2*np4);
np2 *= df + 2.0;
d[ 3 ] = -df * (df+1.0) * (df+5.0) * (((3.0*df) + 7.0) * df - 2.0) / (48.0*np2*np4*np6); // eslint-disable-line max-len, no-mixed-operators
np2 *= (df + 2.0);
np4 *= (df + 4.0);
d[ 4 ] = -df * (df+1.0) * (df+7.0) * ( (((((15.0*df) + 154.0) * df + 465.0) * df + 286.0) * df - 336.0) * df + 64.0) / (384.0*np2*np4*np6*(df+8.0)); // eslint-disable-line max-len, no-mixed-operators
np2 *= (df + 2.0);
d[ 5 ] = -df * (df+1.0) * (df+3.0) * (df+9.0) * (((((((35.0 * df + 452.0) * df+1573.0) * df + 600.0) * df - 2020.0) * df) + 928.0) * df - 128.0) / (1280.0*np2*np4*np6*(df+8.0) * (df+10.0)); // eslint-disable-line max-len, no-mixed-operators
np2 *= (df + 2.0);
np4 *= (df + 4.0);
np6 *= (df + 6.0);
d[ 6 ] = -df * (df+1.0) * (df+11.0) * ((((((((((((945.0*df) + 31506.0) * df + 425858.0) * df + 2980236.0) * df + 11266745.0) * df + 20675018.0) * df + 7747124.0) * df - 22574632.0) * df - 8565600.0) * df + 18108416.0) * df - 7099392.0) * df + 884736.0) / (46080.0*np2*np4*np6*(df+8.0) * (df+10.0) * (df+12.0)); // eslint-disable-line max-len, no-mixed-operators
// Now bring everything together to provide the result this is Eq 62 of Shaw:
rn = sqrt( df );
div = pow( rn*w, 1.0/df );
power = div * div;
result = evalpoly( d, power );
result *= rn;
result /= div;
return -result;
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyval( x: number ): number {
if ( x === 0.0 ) {
return -0.03333333333333313;
}
return -0.03333333333333313 + (x * (0.0015873015872548146 + (x * (-0.0000793650757867488 + (x * (0.000004008217827329362 + (x * -2.0109921818362437e-7))))))); // eslint-disable-line max-len
}
/**
* Computes `exp(x) - 1`.
*
* ## Method
*
* 1. Given \\(x\\), we use argument reduction to find \\(r\\) and an integer \\(k\\) such that
*
* ```tex
* x = k \cdot \ln(2) + r
* ```
*
* where
*
* ```tex
* |r| \leq \frac{\ln(2)}{2} \approx 0.34658
* ```
*
* <!-- <note> -->
*
* A correction term \\(c\\) will need to be computed to compensate for the error in \\(r\\) when rounded to a floating-point number.
*
* <!-- </note> -->
*
* 2. To approximate \\(\operatorname{expm1}(r)\\), we use a special rational function on the interval \\(\[0,0.34658]\\). Since
*
* ```tex
* r \frac{e^r + 1}{e^r - 1} = 2 + \frac{r^2}{6} - \frac{r^4}{360} + \ldots
* ```
*
* we define \\(\operatorname{R1}(r^2)\\) by
*
* ```tex
* r \frac{e^r + 1}{e^r - 1} = 2 + \frac{r^2}{6} \operatorname{R1}(r^2)
* ```
*
* That is,
*
* ```tex
* \begin{align*}
* \operatorname{R1}(r^2) &= \frac{6}{r} \biggl(\frac{e^r+1}{e^r-1} - \frac{2}{r}\biggr) \\
* &= \frac{6}{r} \biggl( 1 + 2 \biggl(\frac{1}{e^r-1} - \frac{1}{r}\biggr)\biggr) \\
* &= 1 - \frac{r^2}{60} + \frac{r^4}{2520} - \frac{r^6}{100800} + \ldots
* \end{align*}
* ```
*
* We use a special Remes algorithm on \\(\[0,0.347]\\) to generate a polynomial of degree \\(5\\) in \\(r^2\\) to approximate \\(\mathrm{R1}\\). The maximum error of this polynomial approximation is bounded by \\(2^{-61}\\). In other words,
*
* ```tex
* \operatorname{R1}(z) \approx 1 + \mathrm{Q1} \cdot z + \mathrm{Q2} \cdot z^2 + \mathrm{Q3} \cdot z^3 + \mathrm{Q4} \cdot z^4 + \mathrm{Q5} \cdot z^5
* ```
*
* where
*
* ```tex
* \begin{align*}
* \mathrm{Q1} &= -1.6666666666666567384\mbox{e-}2 \\
* \mathrm{Q2} &= 3.9682539681370365873\mbox{e-}4 \\
* \mathrm{Q3} &= -9.9206344733435987357\mbox{e-}6 \\
* \mathrm{Q4} &= 2.5051361420808517002\mbox{e-}7 \\
* \mathrm{Q5} &= -6.2843505682382617102\mbox{e-}9
* \end{align*}
* ```
*
* where \\(z = r^2\\) and the values of \\(\mathrm{Q1}\\) to \\(\mathrm{Q5}\\) are listed in the source. The error is bounded by
*
* ```tex
* \biggl| 1 + \mathrm{Q1} \cdot z + \ldots + \mathrm{Q5} \cdot z - \operatorname{R1}(z) \biggr| \leq 2^{-61}
* ```
*
* \\(\operatorname{expm1}(r) = e^r - 1\\) is then computed by the following specific way which minimizes the accumulated rounding error
*
* ```tex
* \operatorname{expm1}(r) = r + \frac{r^2}{2} + \frac{r^3}{2} \biggl( \frac{3 - (\mathrm{R1} + \mathrm{R1} \cdot \frac{r}{2})}{6 - r ( 3 - \mathrm{R1} \cdot \frac{r}{2})} \biggr)
* ```
*
* To compensate for the error in the argument reduction, we use
*
* ```tex
* \begin{align*}
* \operatorname{expm1}(r+c) &= \operatorname{expm1}(r) + c + \operatorname{expm1}(r) \cdot c \\
* &\approx \operatorname{expm1}(r) + c + rc
* \end{align*}
* ```
*
* Thus, \\(c + rc\\) will be added in as the correction terms for \\(\operatorname{expm1}(r+c)\\). Now, we can rearrange the term to avoid optimization screw up.
*
* ```tex
* \begin{align*}
* \operatorname{expm1}(r+c) &\approx r - \biggl( \biggl( r + \biggl( \frac{r^2}{2} \biggl( \frac{\mathrm{R1} - (3 - \mathrm{R1} \cdot \frac{r}{2})}{6 - r (3 - \mathrm{R1} \cdot \frac{r}{2})} \biggr) - c \biggr) - c \biggr) - \frac{r^2}{2} \biggr) \\
* &= r - \mathrm{E}
* \end{align*}
* ```
*
* 3. To scale back to obtain \\(\operatorname{expm1}(x)\\), we have (from step 1)
*
* ```tex
* \operatorname{expm1}(x) = \begin{cases}
* 2^k (\operatorname{expm1}(r) + 1) - 1 \\
* 2^k (\operatorname{expm1}(r) + (1-2^{-k}))
* \end{cases}
* ```
*
* ## Special Cases
*
* ```tex
* \begin{align*}
* \operatorname{expm1}(\infty) &= \infty \\
* \operatorname{expm1}(-\infty) &= -1 \\
* \operatorname{expm1}(\mathrm{NaN}) &= \mathrm{NaN}
* \end{align*}
* ```
*
*
* ## Notes
*
* - For finite arguments, only \\(\operatorname{expm1}(0) = 0\\) is exact.
*
* - To save one multiplication, we scale the coefficient \\(\mathrm{Qi}\\) to \\(\mathrm{Qi} \cdot {2^i}\\) and replace \\(z\\) by \\(\frac{x^2}{2}\\).
*
* - To achieve maximum accuracy, we compute \\(\operatorname{expm1}(x)\\) by
*
* - if \\(x < -56 \cdot \ln(2)\\), return \\(-1.0\\) (raise inexact if \\(x\\) does not equal \\(\infty\\))
*
* - if \\(k = 0\\), return \\(r-\mathrm{E}\\)
*
* - if \\(k = -1\\), return \\(\frac{(r-\mathrm{E})-1}{2}\\)
*
* - if \\(k = 1\\),
*
* - if \\(r < -0.25\\), return \\(2((r+0.5)- \mathrm{E})\\)
* - else return \\(1+2(r-\mathrm{E})\\)
*
* - if \\(k < -2\\) or \\(k > 56\\), return \\(2^k(1-(\mathrm{E}-r)) - 1\\) (or \\(e^x-1\\))
*
* - if \\(k \leq 20\\), return \\(2^k((1-2^{-k})-(\mathrm{E}-r))\\)
*
* - else return \\(2^k(1-((\mathrm{E}+2^{-k})-r))\\)
*
* - For IEEE 754 double, if \\(x > 7.09782712893383973096\mbox{e+}02\\), then \\(\operatorname{expm1}(x)\\) will overflow.
*
* - The hexadecimal values listed in the source are the intended ones for the implementation constants. Decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the intended hexadecimal values.
*
* - According to an error analysis, the error is always less than \\(1\\) ulp (unit in the last place).
*
*
* @param {number} x - input value
* @returns {number} function value
*
* @example
* var v = expm1( 0.2 );
* // returns ~0.221
*
* @example
* var v = expm1( -9.0 );
* // returns ~-0.9999
*
* @example
* var v = expm1( 0.0 );
* // returns 0.0
*
* @example
* var v = expm1( NaN );
* // returns NaN
*/
function expm1( x: number ): number {
var halfX;
var sign;
var hi;
var lo;
var hx;
var r1;
var y;
var z;
var c;
var t;
var e;
var k;
if ( x === PINF || isnan( x ) ) {
return x;
}
if ( x === NINF ) {
return -1.0;
}
if ( x === 0.0 ) {
return x; // handles +-0 (IEEE 754-2008)
}
// Set y = |x|:
if ( x < 0.0 ) {
sign = true;
y = -x;
} else {
sign = false;
y = x;
}
// Filter out huge and non-finite arguments...
if ( y >= LN2x56 ) { // if |x| >= 56*ln(2)
if ( sign ) { // if x <= -56*ln(2)
return -1.0;
}
if ( y >= OVERFLOW_THRESHOLD ) { // if |x| >= 709.78...
return PINF;
}
}
// Extract the more significant bits from |x|:
hx = getHighWord( y )|0; // asm type annotation
// Argument reduction...
if ( y > HALF_LN2 ) { // if |x| > 0.5*ln(2)
if ( y < LN2_HALFX3 ) { // if |x| < 1.5*ln(2)
if ( sign ) {
hi = x + LN2_HI;
lo = -LN2_LO;
k = -1;
} else {
hi = x - LN2_HI;
lo = LN2_LO;
k = 1;
}
} else {
if ( sign ) {
k = (LN2_INV*x) - 0.5;
} else {
k = (LN2_INV*x) + 0.5;
}
k |= 0; // use a bitwise OR to cast `k` to an integer (see also asm.js type annotations: http://asmjs.org/spec/latest/#annotations)
t = k;
hi = x - (t*LN2_HI); // t*ln2_hi is exact here
lo = t * LN2_LO;
}
x = hi - lo;
c = (hi-x) - lo;
}
// if |x| < 2**-54 => high word: 0 01111001001 00000000000000000000 => 0x3c900000 = 1016070144 => exponent = 01111001001 = 969 = 1023-54
else if ( hx < 1016070144 ) {
return x;
}
else {
k = 0;
}
// x is now in primary range...
halfX = 0.5 * x;
z = x * halfX;
r1 = 1.0 + ( z * polyval( z ) );
t = 3.0 - (r1*halfX);
e = z * ( (r1-t) / (6.0 - (x*t)) );
if ( k === 0 ) {
return x - ( (x*e) - z ); // c is 0
}
e = ( x * (e-c) ) - c;
e -= z;
if ( k === -1 ) {
return ( 0.5*(x-e) )- 0.5;
}
if ( k === 1 ) {
if ( x < -0.25 ) {
return -2.0 * ( e - (x+0.5) );
}
return 1 + ( 2.0 * (x-e) );
}
if ( k <= -2 || k > 56 ) { // suffice to return exp(x)-1
y = 1.0 - (e-x);
// Add k to y's exponent:
hi = (getHighWord( y ) + (k<<20))|0; // asm type annotation
y = setHighWord( y, hi );
return y - 1.0;
}
t = 1.0;
if ( k < 20 ) {
// 0x3ff00000 - (0x200000>>k) = 1072693248 - (0x200000>>k) => 0x200000 = 0 00000000010 00000000000000000000
hi = (1072693248 - (0x200000>>k))|0; // asm type annotation
t = setHighWord( t, hi ); // t=1-2^-k
y = t - (e-x);
} else {
hi = ( (FLOAT64_EXPONENT_BIAS-k)<<20 )|0; // asm type annotation
t = setHighWord( t, hi ); // t=2^-k
y = x - (e+t);
y += 1.0;
}
// Add k to y's exponent:
hi = (getHighWord( y ) + (k<<20))|0; // asm type annotation
return setHighWord( y, hi );
}
/**
* Evaluates Student's t quantiles via a method due to Hill.
*
* ## References
*
* - Hill, G. W. 1970. "Algorithm 396: Student's T-Quantiles." _Communications of the ACM_ 13 (10). New York, NY, USA: ACM: 619–20. doi:[10.1145/355598.355600](https://doi.org/10.1145/355598.355600).
*
* @private
* @param {PositiveNumber} ndf - degrees of freedom
* @param {Probability} u - input probability
* @returns {number} function value
*/
function inverseStudentsTHill( ndf: number, u: number ): number {
var a;
var b;
var c;
var d;
var q;
var x;
var y;
if ( ndf > 1e20 ) {
return -erfcinv( 2 * u ) * SQRT2;
}
a = 1.0 / ( ndf - 0.5 );
b = 48.0 / (a * a);
c = ( ( ( ( (20700.0*a/b) - 98.0 ) * a ) - 16.0 ) * a ) + 96.36;
d = ( ( ( (94.5/(b+c)) - 3.0 ) / b ) + 1.0 ) * sqrt( a * HALF_PI ) * ndf;
y = pow( d * 2.0 * u, 2.0 / ndf );
if ( y > ( 0.05 + a ) ) {
// Asymptotic inverse expansion about normal:
x = -erfcinv( 2.0 * u ) * SQRT2;
y = x * x;
if ( ndf < 5.0 ) {
c += 0.3 * ( ndf-4.5 ) * ( x + 0.6 );
}
c += ( ( ( ( ( ( (0.05*d*x)-5.0 ) * x ) - 7.0 ) * x )- 2.0 ) * x ) + b;
y = ((((((0.4*y+6.3)*y)+36.0) * y + 94.5) / c - y - 3.0) / b + 1.0) * x;
y = expm1( a * y * y );
} else {
y = ((1.0 / ( ( (ndf+6.0) / (ndf*y) - 0.089 * d - 0.822 ) *
(ndf+2.0) * 3.0 ) + 0.5 / (ndf+4.0)) * y - 1.0) *
(ndf+1.0) / (ndf+2.0) + 1.0 / y;
}
q = sqrt( ndf * y );
return -q;
}
var polyval1 = require( './polyval_co14.js' );
var polyval2 = require( './polyval_co15.js' );
var polyval3 = require( './polyval_co16.js' );
var polyval4 = require( './polyval_co17.js' );
var polyval5 = require( './polyval_co18.js' );
var polyval6 = require( './polyval_co19.js' );
var polyval7 = require( './polyval_co20.js' );
var polyval8 = require( './polyval_co21.js' );
var polyval9 = require( './polyval_co22.js' );
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @param {NumericArray} c - polynomial coefficients sorted in ascending degree
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*
* @example
* var v = evalpoly( [3.0,2.0,1.0], 10.0 ); // 3*10^0 + 2*10^1 + 1*10^2
* // returns 123.0
*/
function evalpoly( c: number[], x: number ): number {
var p;
var i;
i = c.length;
if ( i < 2 || x === 0.0 ) {
if ( i === 0 ) {
return 0.0;
}
return c[ 0 ];
}
i -= 1;
p = ( c[ i ] * x ) + c[ i-1 ];
i -= 2;
while ( i >= 0 ) {
p = ( p * x ) + c[ i ];
i -= 1;
}
return p;
}
let c0 = 0.0;
// Workspace for the polynomial coefficients:
let c = [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]; // WARNING: not thread safe
/**
* Evaluates Student's t quantiles via a body series expansion. Tail and body series are due to Shaw.
*
* ## References
*
* - Shaw, William T. 2006. "Sampling Student's T distribution – use of the inverse cumulative distribution function." _Journal of Computational Finance_ 9 (4): 37–73. [www.mth.kcl.ac.uk/~shaww/web\_page/papers/Tdistribution06.pdf](www.mth.kcl.ac.uk/~shaww/web_page/papers/Tdistribution06.pdf).
*
* @private
* @param {PositiveNumber} df - degrees of freedom
* @param {Probability} u - input probability
* @returns {number} function value
*/
function inverseStudentsTBodySeries( df: number, u: number ) {
var idf;
var v;
// Body series for small N, start with Eq 56 of Shaw:
v = gammaDeltaRatio( df/2, 0.5 ) * sqrt( df*PI ) * ( u-0.5 );
// Figure out what the coefficients are. They depend only on the degrees of freedom (Eq 57 of Shaw):
idf = 1.0 / df;
c[ 1 ] = polyval1( idf );
c[ 2 ] = polyval2( idf );
c[ 3 ] = polyval3( idf );
c[ 4 ] = polyval4( idf );
c[ 5 ] = polyval5( idf );
c[ 6 ] = polyval6( idf );
c[ 7 ] = polyval7( idf );
c[ 8 ] = polyval8( idf );
c[ 9 ] = polyval9( idf );
// Result is then an odd polynomial in v (see Eq 56 of Shaw)...
return c0 + ( v*evalpoly( c, v*v ) );
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rationalFcnR1( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return -0.0005087819496582806;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -0.0005087819496582806 + (x * (-0.008368748197417368 + (x * (0.03348066254097446 + (x * (-0.012692614766297404 + (x * (-0.03656379714117627 + (x * (0.02198786811111689 + (x * (0.008226878746769157 + (x * (-0.005387729650712429 + (x * (0.0 + (x * 0.0))))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (-0.9700050433032906 + (x * (-1.5657455823417585 + (x * (1.5622155839842302 + (x * (0.662328840472003 + (x * (-0.7122890234154284 + (x * (-0.05273963823400997 + (x * (0.07952836873415717 + (x * (-0.0023339375937419 + (x * 0.0008862163904564247))))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 0.0 + (x * (0.0 + (x * (-0.005387729650712429 + (x * (0.008226878746769157 + (x * (0.02198786811111689 + (x * (-0.03656379714117627 + (x * (-0.012692614766297404 + (x * (0.03348066254097446 + (x * (-0.008368748197417368 + (x * -0.0005087819496582806))))))))))))))))); // eslint-disable-line max-len
s2 = 0.0008862163904564247 + (x * (-0.0023339375937419 + (x * (0.07952836873415717 + (x * (-0.05273963823400997 + (x * (-0.7122890234154284 + (x * (0.662328840472003 + (x * (1.5622155839842302 + (x * (-1.5657455823417585 + (x * (-0.9700050433032906 + (x * 1.0))))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rationalFcnR2( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return -0.20243350835593876;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -0.20243350835593876 + (x * (0.10526468069939171 + (x * (8.3705032834312 + (x * (17.644729840837403 + (x * (-18.851064805871424 + (x * (-44.6382324441787 + (x * (17.445385985570866 + (x * (21.12946554483405 + (x * -3.6719225470772936))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (6.242641248542475 + (x * (3.971343795334387 + (x * (-28.66081804998 + (x * (-20.14326346804852 + (x * (48.560921310873994 + (x * (10.826866735546016 + (x * (-22.643693341313973 + (x * 1.7211476576120028))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = -3.6719225470772936 + (x * (21.12946554483405 + (x * (17.445385985570866 + (x * (-44.6382324441787 + (x * (-18.851064805871424 + (x * (17.644729840837403 + (x * (8.3705032834312 + (x * (0.10526468069939171 + (x * -0.20243350835593876))))))))))))))); // eslint-disable-line max-len
s2 = 1.7211476576120028 + (x * (-22.643693341313973 + (x * (10.826866735546016 + (x * (48.560921310873994 + (x * (-20.14326346804852 + (x * (-28.66081804998 + (x * (3.971343795334387 + (x * (6.242641248542475 + (x * 1.0))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rationalFcnR3( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return -0.1311027816799519;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -0.1311027816799519 + (x * (-0.16379404719331705 + (x * (0.11703015634199525 + (x * (0.38707973897260434 + (x * (0.3377855389120359 + (x * (0.14286953440815717 + (x * (0.029015791000532906 + (x * (0.0021455899538880526 + (x * (-6.794655751811263e-7 + (x * (2.8522533178221704e-8 + (x * -6.81149956853777e-10))))))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (3.4662540724256723 + (x * (5.381683457070069 + (x * (4.778465929458438 + (x * (2.5930192162362027 + (x * (0.848854343457902 + (x * (0.15226433829533179 + (x * (0.011059242293464892 + (x * (0.0 + (x * (0.0 + (x * 0.0))))))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = -6.81149956853777e-10 + (x * (2.8522533178221704e-8 + (x * (-6.794655751811263e-7 + (x * (0.0021455899538880526 + (x * (0.029015791000532906 + (x * (0.14286953440815717 + (x * (0.3377855389120359 + (x * (0.38707973897260434 + (x * (0.11703015634199525 + (x * (-0.16379404719331705 + (x * -0.1311027816799519))))))))))))))))))); // eslint-disable-line max-len
s2 = 0.0 + (x * (0.0 + (x * (0.0 + (x * (0.011059242293464892 + (x * (0.15226433829533179 + (x * (0.848854343457902 + (x * (2.5930192162362027 + (x * (4.778465929458438 + (x * (5.381683457070069 + (x * (3.4662540724256723 + (x * 1.0))))))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rationalFcnR4( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return -0.0350353787183178;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -0.0350353787183178 + (x * (-0.0022242652921344794 + (x * (0.018557330651423107 + (x * (0.009508047013259196 + (x * (0.0018712349281955923 + (x * (0.00015754461742496055 + (x * (0.00000460469890584318 + (x * (-2.304047769118826e-10 + (x * 2.6633922742578204e-12))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (1.3653349817554064 + (x * (0.7620591645536234 + (x * (0.22009110576413124 + (x * (0.03415891436709477 + (x * (0.00263861676657016 + (x * (0.00007646752923027944 + (x * (0.0 + (x * 0.0))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 2.6633922742578204e-12 + (x * (-2.304047769118826e-10 + (x * (0.00000460469890584318 + (x * (0.00015754461742496055 + (x * (0.0018712349281955923 + (x * (0.009508047013259196 + (x * (0.018557330651423107 + (x * (-0.0022242652921344794 + (x * -0.0350353787183178))))))))))))))); // eslint-disable-line max-len
s2 = 0.0 + (x * (0.0 + (x * (0.00007646752923027944 + (x * (0.00263861676657016 + (x * (0.03415891436709477 + (x * (0.22009110576413124 + (x * (0.7620591645536234 + (x * (1.3653349817554064 + (x * 1.0))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rationalFcnR5( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return -0.016743100507663373;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -0.016743100507663373 + (x * (-0.0011295143874558028 + (x * (0.001056288621524929 + (x * (0.00020938631748758808 + (x * (0.000014962478375834237 + (x * (4.4969678992770644e-7 + (x * (4.625961635228786e-9 + (x * (-2.811287356288318e-14 + (x * 9.905570997331033e-17))))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (0.5914293448864175 + (x * (0.1381518657490833 + (x * (0.016074608709367652 + (x * (0.0009640118070051656 + (x * (0.000027533547476472603 + (x * (2.82243172016108e-7 + (x * (0.0 + (x * 0.0))))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 9.905570997331033e-17 + (x * (-2.811287356288318e-14 + (x * (4.625961635228786e-9 + (x * (4.4969678992770644e-7 + (x * (0.000014962478375834237 + (x * (0.00020938631748758808 + (x * (0.001056288621524929 + (x * (-0.0011295143874558028 + (x * -0.016743100507663373))))))))))))))); // eslint-disable-line max-len
s2 = 0.0 + (x * (0.0 + (x * (2.82243172016108e-7 + (x * (0.000027533547476472603 + (x * (0.0009640118070051656 + (x * (0.016074608709367652 + (x * (0.1381518657490833 + (x * (0.5914293448864175 + (x * 1.0))))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates the inverse complementary error function.
*
* Note that
*
* ```tex
* \operatorname{erfc^{-1}}(1-z) = \operatorname{erf^{-1}}(z)
* ```
*
* ## Method
*
* 1. For \\(|x| \leq 0.5\\), we evaluate the inverse error function using the rational approximation
*
* ```tex
* \operatorname{erf^{-1}}(x) = x(x+10)(\mathrm{Y} + \operatorname{R}(x))
* ```
*
* where \\(Y\\) is a constant and \\(\operatorname{R}(x)\\) is optimized for a low absolute error compared to \\(|Y|\\).
*
* <!-- <note> -->
*
* Max error \\(2.001849\mbox{e-}18\\). Maximum deviation found (error term at infinite precision) \\(8.030\mbox{e-}21\\).
*
* <!-- </note> -->
*
* 2. For \\(0.5 > 1-|x| \geq 0\\), we evaluate the inverse error function using the rational approximation
*
* ```tex
* \operatorname{erf^{-1}} = \frac{\sqrt{-2 \cdot \ln(1-x)}}{\mathrm{Y} + \operatorname{R}(1-x)}
* ```
*
* where \\(Y\\) is a constant, and \\(\operatorname{R}(q)\\) is optimized for a low absolute error compared to \\(Y\\).
*
* <!-- <note> -->
*
* Max error \\(7.403372\mbox{e-}17\\). Maximum deviation found (error term at infinite precision) \\(4.811\mbox{e-}20\\).
*
* <!-- </note> -->
*
* 3. For \\(1-|x| < 0.25\\), we have a series of rational approximations all of the general form
*
* ```tex
* p = \sqrt{-\ln(1-x)}
* ```
*
* Accordingly, the result is given by
*
* ```tex
* \operatorname{erf^{-1}}(x) = p(\mathrm{Y} + \operatorname{R}(p-B))
* ```
*
* where \\(Y\\) is a constant, \\(B\\) is the lowest value of \\(p\\) for which the approximation is valid, and \\(\operatorname{R}(x-B)\\) is optimized for a low absolute error compared to \\(Y\\).
*
* <!-- <note> -->
*
* Almost all code will only go through the first or maybe second approximation. After that we are dealing with very small input values.
*
* - If \\(p < 3\\), max error \\(1.089051\mbox{e-}20\\).
* - If \\(p < 6\\), max error \\(8.389174\mbox{e-}21\\).
* - If \\(p < 18\\), max error \\(1.481312\mbox{e-}19\\).
* - If \\(p < 44\\), max error \\(5.697761\mbox{e-}20\\).
* - If \\(p \geq 44\\), max error \\(1.279746\mbox{e-}20\\).
*
* <!-- </note> -->
*
* <!-- <note> -->
*
* The Boost library can accommodate \\(80\\) and \\(128\\) bit long doubles. JavaScript only supports a \\(64\\) bit double (IEEE 754). Accordingly, the smallest \\(p\\) (in JavaScript at the time of this writing) is \\(\sqrt{-\ln(\sim5\mbox{e-}324)} = 27.284429111150214\\).
*
* <!-- </note> -->
*
*
* @param {number} x - input value
* @returns {number} function value
*
* @example
* var y = erfcinv( 0.5 );
* // returns ~0.4769
*
* @example
* var y = erfcinv( 0.8 );
* // returns ~0.1791
*
* @example
* var y = erfcinv( 0.0 );
* // returns Infinity
*
* @example
* var y = erfcinv( 2.0 );
* // returns -Infinity
*
* @example
* var y = erfcinv( NaN );
* // returns NaN
*/
function erfcinv( x: number ): number {
var sign;
var qs;
var q;
var g;
var r;
// Special case: NaN
if ( isnan( x ) ) {
return NaN;
}
// Special case: 0
if ( x === 0.0 ) {
return PINF;
}
// Special case: 2
if ( x === 2.0 ) {
return NINF;
}
// Special case: 1
if ( x === 1.0 ) {
return 0.0;
}
if ( x > 2.0 || x < 0.0 ) {
return NaN;
}
// Argument reduction (reduce to interval [0,1]). If `x` is outside [0,1], we can take advantage of the complementary error function reflection formula: `erfc(-z) = 2 - erfc(z)`, by negating the result once finished.
if ( x > 1.0 ) {
sign = -1.0;
q = 2.0 - x;
} else {
sign = 1.0;
q = x;
}
x = 1.0 - q;
// x = 1-q <= 0.5
if ( x <= 0.5 ) {
g = x * ( x + 10.0 );
r = rationalFcnR1( x );
return sign * ( (g*Y1) + (g*r) );
}
// q >= 0.25
if ( q >= 0.25 ) {
g = sqrt( -2.0 * ln(q) );
q -= 0.25;
r = rationalFcnR2( q );
return sign * ( g / (Y2+r) );
}
q = sqrt( -ln( q ) );
// q < 3
if ( q < 3.0 ) {
qs = q - 1.125;
r = rationalFcnR3( qs );
return sign * ( (Y3*q) + (r*q) );
}
// q < 6
if ( q < 6.0 ) {
qs = q - 3.0;
r = rationalFcnR4( qs );
return sign * ( (Y4*q) + (r*q) );
}
// q < 18
qs = q - 6.0;
r = rationalFcnR5( qs );
return sign * ( (Y5*q) + (r*q) );
}
/**
* Evaluates Student's t quantiles.
*
* @private
* @param {PositiveNumber} df - degrees of freedom
* @param {Probability} u - input probability
* @param {Probability} v - probability equal to `1-u`
* @returns {number} function value
*/
function inverseStudentsT( df: number, u: number, v: number ): number {
var crossover;
var tolerance;
var rootAlpha;
var invert;
var result;
var alpha;
var tmp;
var p0;
var p2;
var p4;
var p5;
var p;
var r;
var x;
var a;
var b;
result = 0;
if ( u > v ) {
// Function is symmetric, so invert it:
tmp = v;
v = u;
u = tmp;
invert = true;
} else {
invert = false;
}
if ( floor(df) === df && df < 20 ) {
// We have integer degrees of freedom, try for the special cases first:
tolerance = ldexp( 1.0, EXP );
switch ( floor( df ) ) {
case 1:
// `df = 1` is the same as the Cauchy distribution, see Shaw Eq 35:
if ( u === 0.5 ) {
result = 0.0;
} else {
result = -cos( PI * u ) / sin( PI * u );
}
break;
case 2:
// `df = 2` has an exact result, see Shaw Eq 36:
result = ( (2.0*u) - 1.0 ) / sqrt( 2.0 * u * v );
break;
case 4:
// `df = 4` has an exact result, see Shaw Eq 38 & 39:
alpha = 4.0 * u * v;
rootAlpha = Math.sqrt( alpha );
r = 4 * cos( acos( rootAlpha ) / 3.0 ) / rootAlpha;
x = sqrt( r - 4.0 );
result = ( u - 0.5 < 0.0 ) ? -x : x;
break;
case 6:
// We get numeric overflow in this area:
if ( u < 1.0e-150 ) {
return ( ( invert ) ? -1 : 1 ) * inverseStudentsTHill( df, u );
}
// Newton-Raphson iteration of a polynomial case, choice of seed value is taken from Shaw's online supplement:
a = 4.0 * ( u - (u*u) ); // 1 - 4 * (u - 0.5f) * (u - 0.5f);
b = pow( a, ONE_THIRD );
p = 6.0 * ( 1.0 + ( C * ( (1.0/b) - 1.0 ) ) );
do {
p2 = p * p;
p4 = p2 * p2;
p5 = p * p4;
p0 = p;
// Next term is given by Eq 41:
p = 2.0 * ( (8.0*a*p5) - (270.0*p2) + 2187 ) /
( 5.0 * ( (4.0*a*p4) - (216.0*p) - 243.0 ) );
} while ( abs( (p - p0) / p ) > tolerance );
// Use Eq 45 to extract the result:
p = sqrt( p - df );
result = ( u - 0.5 < 0.0 ) ? -p : p;
break;
default:
if ( df > DF_THRESHOLD ) { // 2^28
result = erfcinv( 2.0 * u ) * SQRT2;
} else if ( df < 3 ) {
// Use a roughly linear scheme to choose between Shaw's tail series and body series:
crossover = 0.2742 - ( df * 0.0242143 );
if ( u > crossover ) {
result = inverseStudentsTBodySeries( df, u );
} else {
result = inverseStudentsTTailSeries( df, u );
}
} else {
// Use Hill's method except in the extreme tails where we use Shaw's tail series. The crossover point is roughly exponential in -df:
crossover = ldexp( 1.0, Math.round( df / -0.654 ) );
if ( u > crossover ) {
result = inverseStudentsTHill( df, u );
} else {
result = inverseStudentsTTailSeries( df, u );
}
}
}
} else if ( df > DF_THRESHOLD ) {
result = -erfcinv( 2.0 * u ) * SQRT2;
} else if ( df < 3 ) {
// Use a roughly linear scheme to choose between Shaw's tail series and body series:
crossover = 0.2742 - ( df * 0.0242143 );
if ( u > crossover ) {
result = inverseStudentsTBodySeries( df, u );
} else {
result = inverseStudentsTTailSeries( df, u );
}
} else {
// Use Hill's method except in the extreme tails where we use Shaw's tail series. The crossover point is roughly exponential in -df:
crossover = ldexp( 1.0, round( df / -0.654 ) );
if ( u > crossover ) {
result = inverseStudentsTHill( df, u );
} else {
result = inverseStudentsTTailSeries( df, u );
}
}
return ( invert ) ? -result : result;
}
/**
* Returns the inverse of the incomplete beta function via the Student t distribution.
*
* @private
* @param {PositiveNumber} a - function parameter
* @param {Probability} p - probability value
* @param {Object} py - placeholder object holding one minus the returned value
* @returns {number} function value
*/
function findIBetaInvFromTDist(a: number, p: number, py?: { value: number} ): number {
var df;
var u;
var v;
var t;
u = p / 2.0;
v = 1.0 - u;
df = a * 2.0;
t = inverseStudentsT( df, u, v );
if ( py ) {
py.value = t * t / ( df + ( t*t ) );
}
return df / ( df + ( t*t ) );
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function ratevalPQ( x: number ) {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return 0.16666666666666713;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = -8.198089802484825 + (x * (19.562619833175948 + (x * (-16.262479672107002 + (x * (5.444622390564711 + (x * (-0.6019598008014124 + (x * 0.004253011369004428))))))))); // eslint-disable-line max-len
s2 = -49.18853881490881 + (x * (139.51056146574857 + (x * (-147.1791292232726 + (x * (70.49610280856842 + (x * (-14.740913729888538 + (x * 1.0))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 0.004253011369004428 + (x * (-0.6019598008014124 + (x * (5.444622390564711 + (x * (-16.262479672107002 + (x * (19.562619833175948 + (x * -8.198089802484825))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (-14.740913729888538 + (x * (70.49610280856842 + (x * (-147.1791292232726 + (x * (139.51056146574857 + (x * -49.18853881490881))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function ratevalRS( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return 0.08333333333333809;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = 28.536655482610616 + (x * (-25.56901049652825 + (x * (6.968710824104713 + (x * (-0.5634242780008963 + (x * 0.002967721961301243))))))); // eslint-disable-line max-len
s2 = 342.43986579130785 + (x * (-383.8770957603691 + (x * (147.0656354026815 + (x * (-21.947795316429207 + (x * 1.0))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 0.002967721961301243 + (x * (-0.5634242780008963 + (x * (6.968710824104713 + (x * (-25.56901049652825 + (x * 28.536655482610616))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (-21.947795316429207 + (x * (147.0656354026815 + (x * (-383.8770957603691 + (x * 342.43986579130785))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Computes the arcsine of a number.
*
* ## Method
*
* - A rational function of the form
*
* ```tex
* x + x^3 \frac{P(x^2)}{Q(x^2)}
* ```
*
* is used for \\(\|x\|\\) in the interval \\(\[0, 0.5\]\\). If \\(\|x\| > 0.5\\), it is transformed by the identity
*
* ```tex
* \operatorname{asin}(x) = \frac{\pi}{2} - 2 \operatorname{asin}( \sqrt{ (1-x)/2 } )
* ```
*
* ## Notes
*
* - Relative error:
*
* | arithmetic | domain | # trials | peak | rms |
* |:-----------|:-------|:---------|:--------|:--------|
* | DEC | -1, 1 | 40000 | 2.6e-17 | 7.1e-18 |
* | IEEE | -1, 1 | 10^6 | 1.9e-16 | 5.4e-17 |
*
* @param {number} x - input value
* @returns {number} arcsine (in radians)
*
* @example
* var v = asin( 0.0 );
* // returns ~0.0
*
* @example
* var v = asin( 3.141592653589793/4.0 );
* // returns ~0.903
*
* @example
* var v = asin( -3.141592653589793/6.0 );
* // returns ~-0.551
*
* @example
* var v = asin( NaN );
* // returns NaN
*/
function asin( x: number ): number {
var sgn;
var zz;
var a;
var p;
var z;
if ( isnan( x ) ) {
return NaN;
}
if ( x > 0.0 ) {
a = x;
} else {
sgn = true;
a = -x;
}
if ( a > 1.0 ) {
return NaN;
}
if ( a > 0.625 ) {
// arcsin(1-x) = pi/2 - sqrt(2x)(1+R(x))
zz = 1.0 - a;
p = zz * ratevalRS( zz );
zz = Math.sqrt( zz + zz );
z = PIO4 - zz;
zz = ( zz*p ) - MOREBITS;
z -= zz;
z += PIO4;
} else {
if ( a < 1.0e-8 ) {
return x;
}
zz = a * a;
z = zz * ratevalPQ( zz );
z = ( a*z ) + a;
}
return ( sgn ) ? -z : z;
}
/**
* Computes the arccosine of a number.
*
* ## Method
*
* - Analytically,
*
* ```tex
* \operatorname{acos}(x) = \frac{\pi}{2} - \operatorname{asin}(x)
* ```
*
* However, if \\(\|x\|\\) is near \\(1\\), there is cancellation error in subtracting \\(\opertorname{asin}(x)\\) from \\(\pi/2\\). Hence, if \\(x < -0.5\\),
*
* ```tex
* \operatorname{acos}(x) = \pi - 2.0 \cdot \operatorname{asin}(\sqrt{(1+x)/2})
* ```
*
* or, if \\(x > +0.5\\),
*
* ```tex
* \operatorname{acos}(x) = 2.0 \cdot \operatorname{asin}( \sqrt{(1-x)/2} )}
* ```
*
* ## Notes
*
* - Relative error:
*
* | arithmetic | domain | # trials | peak | rms |
* |:-----------|:------:|:---------|:--------|:--------|
* | DEC | -1, 1 | 50000 | 3.3e-17 | 8.2e-18 |
* | IEEE | -1, 1 | 10^6 | 2.2e-16 | 6.5e-17 |
*
*
* @param {number} x - input value
* @returns {number} arccosine (in radians)
*
* @example
* var v = acos( 1.0 );
* // returns 0.0
*
* @example
* var v = acos( 0.707 ); // ~pi/4
* // returns ~0.7855
*
* @example
* var v = acos( NaN );
* // returns NaN
*/
function acos( x ) {
var z;
if ( isnan( x ) ) {
return NaN;
}
if ( x < -1.0 || x > 1.0 ) {
return NaN;
}
if ( x > 0.5 ) {
return 2.0 * asin( sqrt( 0.5 - (0.5*x) ) );
}
z = PIO4 - asin( x );
z += MOREBITS;
z += PIO4;
return z;
}
/**
* Evaluate the beta function.
*
* @param {NonNegativeNumber} a - input value
* @param {NonNegativeNumber} b - input value
* @returns {number} evaluated beta function
*
* @example
* var v = beta( 0.0, 0.5 );
* // returns Infinity
*
* @example
* var v = beta( 1.0, 1.0 );
* // returns 1.0
*
* @example
* var v = beta( -1.0, 2.0 );
* // returns NaN
*
* @example
* var v = beta( 5.0, 0.2 );
* // returns ~3.382
*
* @example
* var v = beta( 4.0, 1.0 );
* // returns 0.25
*
* @example
* var v = beta( NaN, 2.0 );
* // returns NaN
*/
function beta( a: number, b: number ) {
var ambh;
var agh;
var bgh;
var cgh;
var res;
var tmp;
var c;
if ( isnan( a ) || isnan( b ) ) {
return NaN;
}
if ( a < 0.0 || b < 0.0 ) {
return NaN;
}
if ( b === 1.0 ) {
return 1.0 / a;
}
if ( a === 1.0 ) {
return 1.0 / b;
}
c = a + b;
if ( c < EPSILON ) {
res = c / a;
res /= b;
return res;
}
// Special cases:
if ( c === a && b < EPSILON ) {
return 1.0 / b;
}
if ( c === b && a < EPSILON ) {
return 1.0 / a;
}
if ( a < b ) {
// Swap `a` and `b`:
tmp = b;
b = a;
a = tmp;
}
// Lanczos calculation:
agh = a + G - 0.5;
bgh = b + G - 0.5;
cgh = c + G - 0.5;
res = lanczosSumExpGScaled( a ) * ( lanczosSumExpGScaled( b )/lanczosSumExpGScaled( c ) ); // eslint-disable-line max-len
ambh = a - 0.5 - b;
if ( ( abs( b*ambh ) < ( cgh*100.0 ) ) && a > 100.0 ) {
// Special case where the base of the power term is close to 1; compute `(1+x)^y` instead:
res *= exp( ambh * log1p( -b/cgh ) );
} else {
res *= pow( agh/cgh, ambh );
}
if ( cgh > 1.0e10 ) {
// This avoids possible overflow, but appears to be marginally less accurate:
res *= pow( (agh/cgh)*(bgh/cgh), b );
} else {
res *= pow( (agh*bgh)/(cgh*cgh), b );
}
res *= Math.sqrt( E/bgh);
return res;
}
/**
* One half times the mathematical constant `π`.
*
* @constant
* @type {number}
* @default 1.5707963267948966
* @see [Wikipedia]{@link https://en.wikipedia.org/wiki/Pi}
*/
const HALF_PI: number = 1.5707963267948966;
const DIGITS: number = 32;
const MAX_ITERATIONS: number = 1000;
// Workspace for the polynomial coefficients:
let terms = [ 0.0, 0.0, 0.0, 0.0, 0.0 ]; // WARNING: not thread safe
/**
* Calculates the inverse of the incomplete beta function.
*
* @private
* @param {PositiveNumber} a - function parameter
* @param {PositiveNumber} b - function parameter
* @param {Probability} p - function parameter
* @param {Probability} q - probability equal to `1 - p`
* @returns {Array} two-element array holding function value `y` and `1-y`
*/
const kernelBetaincinv = function ibetaInvImp( a: number, b: number, p: number, q: number ) {
var digits;
var invert;
var lambda;
var lower;
var theta;
var upper;
var roots;
var maxv;
var minv;
var bet;
var ppa;
var tmp;
var xs2;
var ap1;
var bm1;
var fs;
var lx;
var ps;
var xg;
var xs;
var yp;
var a2;
var a3;
var b2;
var r;
var l;
var u;
var x;
var y;
// The flag invert is set to true if we swap a for b and p for q, in which case the result has to be subtracted from 1:
invert = false;
// Handle trivial cases first...
if ( q === 0.0 ) {
return [ 1.0, 0.0 ];
}
if ( p === 0.0 ) {
return [ 0.0, 1.0 ];
}
if ( a === 1.0 ) {
if ( b === 1.0 ) {
return [ p, 1.0-p ];
}
// Change things around so we can handle as b == 1 special case below:
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = true;
}
// Depending upon which approximation method we use, we may end up calculating either x or y initially (where y = 1-x):
x = 0.0; // Set to a safe zero to avoid a
// For some of the methods we can put tighter bounds on the result than simply [0,1]:
lower = 0.0;
upper = 1.0;
// Student's T with b = 0.5 gets handled as a special case, swap around if the arguments are in the "wrong" order:
if ( a === 0.5 ) {
if ( b === 0.5 ) {
x = sin( p*HALF_PI );
x *= x;
y = sin( q*HALF_PI );
y *= y;
return [ x, y ];
}
if ( b > 0.5 ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = !invert;
}
}
// Select calculation method for the initial estimate:
if ( b === 0.5 && a >= 0.5 && p !== 1.0 ) {
// We have a Student's T distribution:
yp = {};
x = findIBetaInvFromTDist( a, p, yp );
y = yp.value;
}
else if ( b === 1.0 ) {
if ( p < q ) {
if ( a > 1.0 ) {
x = pow( p, 1.0/a );
y = -expm1( ln(p) / a );
} else {
x = pow( p, 1.0/a );
y = 1.0 - x;
}
} else {
x = exp( log1p(-q) / a );
y = -expm1( log1p(-q) / a );
}
if ( invert ) {
tmp = y;
y = x;
x = tmp;
}
return [ x, y ];
}
else if ( a+b > 5.0 ) {
// When a+b is large then we can use one of Prof Temme's asymptotic expansions, begin by swapping things around so that p < 0.5, we do this to avoid cancellations errors when p is large.
if ( p > 0.5 ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = !invert;
}
minv = min( a, b );
maxv = max( a, b );
if ( ( Math.sqrt(minv) > (maxv-minv) ) && minv > 5.0 ) {
// When a and b differ by a small amount the curve is quite symmetrical and we can use an error function to approximate the inverse. This is the cheapest of the three Temme expansions, and the calculated value for x will never be much larger than p, so we don't have to worry about cancellation as long as p is small.
x = temme1( a, b, p );
y = 1.0 - x;
} else {
r = a + b;
theta = asin( Math.sqrt( a/r ) );
lambda = minv / r;
if (
lambda >= 0.2 &&
lambda <= 0.8 &&
r >= 10
) {
// The second error function case is the next cheapest to use, it breaks down when the result is likely to be very small, if `a+b` is also small, but we can use a cheaper expansion there in any case. As before `x` won't be much larger than `p`, so as long as `p` is small we should be free of cancellation error.
ppa = pow( p, 1.0/a );
if ( ppa < 0.0025 && ( a+b ) < 200.0 ) {
x = ppa * pow( a*beta( a, b ), 1.0/a );
} else {
x = temme2( p, r, theta );
}
y = 1.0 - x;
} else {
// If we get here then a and b are very different in magnitude and we need to use the third of Temme's methods which involves inverting the incomplete gamma. This is much more expensive than the other methods. We also can only use this method when a > b, which can lead to cancellation errors if we really want y (as we will when x is close to 1), so a different expansion is used in that case.
if ( a < b ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = !invert;
}
// Try and compute the easy way first:
bet = 0.0;
if ( b < 2.0 ) {
bet = beta( a, b );
}
if ( bet === 0.0 ) {
y = 1.0;
} else {
y = pow( b*q*bet, 1.0/b );
x = 1.0 - y;
}
}
if ( y > 1.0e-5 ) {
x = temme3( a, b, p, q );
y = 1.0 - x;
}
}
}
else if ( a < 1.0 && b < 1.0 ) {
// Both a and b less than 1, there is a point of inflection at xs:
xs = ( 1.0-a ) / ( 2.0-a-b );
// Now we need to ensure that we start our iteration from the right side of the inflection point:
fs = betainc( xs, a, b ) - p;
if ( abs(fs)/p < EPSILON*3.0 ) {
// The result is at the point of inflection, best just return it:
if ( invert ) {
return [ 1.0-xs, xs ];
}
return [ xs, 1.0-xs ];
}
if ( fs < 0.0 ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = !invert;
xs = 1.0 - xs;
}
xg = pow( a*p*beta( a, b ), 1.0/a );
x = xg / ( 1.0+xg );
y = 1.0 / ( 1.0+xg );
// And finally we know that our result is below the inflection point, so set an upper limit on our search:
if ( x > xs ) {
x = xs;
}
upper = xs;
}
else if ( a > 1.0 && b > 1.0 ) {
// Small a and b, both greater than 1, there is a point of inflection at xs, and it's complement is xs2, we must always start our iteration from the right side of the point of inflection.
xs = ( a-1.0 ) / ( a+b-2.0 );
xs2 = ( b-1.0 ) / ( a+b-2.0 );
ps = betainc( xs, a, b ) - p;
if ( ps < 0.0 ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
tmp = xs2;
xs2 = xs;
xs = tmp;
invert = !invert;
}
// Estimate x and y, using expm1 to get a good estimate for y when it's very small:
lx = ln( p*a*beta( a, b ) ) / a;
x = exp( lx );
y = ( x < 0.9 ) ? 1.0-x : -expm1(lx);
if ( b < a && x < 0.2 ) {
// Under a limited range of circumstances we can improve our estimate for x...
ap1 = a - 1.0;
bm1 = b - 1.0;
a2 = a * a;
a3 = a * a2;
b2 = b * b;
terms[ 0 ] = 0.0;
terms[ 1 ] = 1.0;
terms[ 2 ] = bm1 / ap1;
ap1 *= ap1;
terms[ 3 ] = bm1 * (3.0*a*b + 5.0*b + a2 - a - 4.0) / (2.0 * (a+2.0) * ap1); // eslint-disable-line max-len, no-mixed-operators
ap1 *= (a + 1.0);
terms[ 4 ] = bm1 * (33.0*a*b2 + 31.0*b2 + 8.0*a2*b2 - 30.0*a*b - 47.0*b + 11.0*a2*b + 6.0*a3*b + 18.0 + 4.0*a - a3 + a2*a2 - 10.0*a2); // eslint-disable-line max-len, no-mixed-operators
terms[ 4 ] /= (3.0 * (a+3.0) * (a+2.0) * ap1);
x = evalpoly( terms, x );
}
// Know that result is below the inflection point, so set an upper limit on search...
if ( x > xs ) {
x = xs;
}
upper = xs;
} else {
// Case: ( a <= 1 ) != ( b <= 1 ). If all else fails we get here, only one of a and b is above 1, and a+b is small. Start by swapping things around so that we have a concave curve with b > a and no points of inflection in [0,1]. As long as we expect x to be small then we can use the simple (and cheap) power term to estimate x, but when we expect x to be large then this greatly underestimates x and leaves us trying to iterate "round the corner" which may take almost forever. We could use Temme's inverse gamma function case in that case, this works really rather well (albeit expensively) even though strictly speaking we're outside it's defined range. However it's expensive to compute, and an alternative approach which models the curve as a distorted quarter circle is much cheaper to compute, and still keeps the number of iterations required down to a reasonable level. With thanks to Prof. Temme for this suggestion.
if ( b < a ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
invert = !invert;
}
if ( pow( p, 1.0/a ) < 0.5 ) {
x = pow( p*a*beta( a, b ), 1.0/a );
if ( x === 0.0 ) {
x = FLOAT64_MIN_NORM;
}
y = 1.0 - x;
}
// Case: pow(q, 1/b) < 0.1
else {
// Model a distorted quarter circle:
y = pow( 1.0-pow( p, b*beta( a, b ) ), 1.0/b );
if ( y === 0 ) {
y = FLOAT64_MIN_NORM;
}
x = 1.0 - y;
}
}
// Now we have a guess for x (and for y) we can set things up for iteration. If x > 0.5 it pays to swap things round:
if ( x > 0.5 ) {
tmp = b;
b = a;
a = tmp;
tmp = q;
q = p;
p = tmp;
tmp = y;
y = x;
x = tmp;
invert = !invert;
l = 1.0 - upper;
u = 1.0 - lower;
lower = l;
upper = u;
}
// Lower bound for our search: We're not interested in denormalized answers as these tend to take up lots of iterations, given that we can't get accurate derivatives in this area (they tend to be infinite).
if ( lower === 0 ) {
if ( invert ) {
// We're not interested in answers smaller than machine epsilon:
lower = EPSILON;
if ( x < lower ) {
x = lower;
}
} else {
lower = FLOAT64_MIN_NORM;
}
if ( x < lower ) {
x = lower;
}
}
// Figure out how many digits to iterate towards:
digits = DIGITS;
if ( x < 1.0e-50 && ( a < 1.0 || b < 1.0 ) ) {
// If we're in a region where the first derivative is very large, then we have to take care that the root-finder doesn't terminate prematurely. We'll bump the precision up to avoid this, but we have to take care not to set the precision too high or the last few iterations will just thrash around and convergence may be slow in this case. Try 3/4 of machine epsilon:
digits *= 3;
digits /= 2;
}
// Now iterate, we can use either p or q as the target here depending on which is smaller:
roots = ibetaRoots( a, b, ( (p < q) ? p : q ), p >= q );
x = halleyIterate( roots, x, lower, upper, digits, MAX_ITERATIONS );
// Tidy up, if we "lower" was too high then zero is the best answer we have:
if ( x === lower ) {
x = 0.0;
}
if ( invert ) {
return [ 1.0-x, x ];
}
return [ x, 1.0-x ];
}
/**
* Returns a value `p` such that `p = betainc(a, b, x)`.
*
* @param {Probability} p - function parameter
* @param {PositiveNumber} a - function parameter
* @param {PositiveNumber} b - function parameter
* @param {boolean} [upper=false] - boolean indicating if the function should return the inverse of the upper tail of the incomplete beta function
* @returns {number} function value
*
* @example
* var y = betaincinv( 0.2, 3.0, 3.0 );
* // returns ~0.327
*
* @example
* var y = betaincinv( 0.4, 3.0, 3.0 );
* // returns ~0.446
*
* @example
* var y = betaincinv( 0.4, 3.0, 3.0, true );
* // returns ~0.554
*
* @example
* var y = betaincinv( 0.4, 1.0, 6.0 );
* // returns ~0.082
*
* @example
* var y = betaincinv( 0.8, 1.0, 6.0 );
* // returns ~0.235
*/
function betaincinv( p: number, a: number, b: number, upper: boolean ): number {
if (
isnan( p ) ||
isnan( a ) ||
isnan( b )
) {
return NaN;
}
if ( a <= 0.0 || b <= 0.0 ) {
return NaN;
}
if ( p < 0.0 || p > 1.0 ) {
return NaN;
}
if ( upper ) {
return kernelBetaincinv( a, b, 1.0 - p, p )[ 0 ];
}
return kernelBetaincinv( a, b, p, 1.0 - p )[ 0 ];
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function lnpolyvalP( x: number ): number {
if ( x === 0.0 ) {
return 0.3999999999940942;
}
return 0.3999999999940942 + (x * (0.22222198432149784 + (x * 0.15313837699209373))); // eslint-disable-line max-len
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function lnpolyvalQ( x: number ): number {
if ( x === 0.0 ) {
return 0.6666666666666735;
}
return 0.6666666666666735 + (x * (0.2857142874366239 + (x * (0.1818357216161805 + (x * 0.14798198605116586))))); // eslint-disable-line max-len
}
/**
* Evaluates the natural logarithm.
*
* @param {NonNegativeNumber} x - input value
* @returns {number} function value
*
* @example
* var v = ln( 4.0 );
* // returns ~1.386
*
* @example
* var v = ln( 0.0 );
* // returns -Infinity
*
* @example
* var v = ln( Infinity );
* // returns Infinity
*
* @example
* var v = ln( NaN );
* // returns NaN
*
* @example
* var v = ln( -4.0 );
* // returns NaN
*/
function ln( x: number ): number {
var hfsq;
var hx;
var t2;
var t1;
var k;
const R;
var f;
var i;
var j;
var s;
var w;
var z;
if ( x === 0.0 ) {
return NINF;
}
if ( isnan( x ) || x < 0.0 ) {
return NaN;
}
hx = getHighWord( x );
k = 0|0; // asm type annotation
if ( hx < HIGH_MIN_NORMAL_EXP ) {
// Case: 0 < x < 2**-1022
k -= 54|0; // asm type annotation
// Subnormal number, scale up `x`:
x *= TWO54;
hx = getHighWord( x );
}
if ( hx >= HIGH_MAX_NORMAL_EXP ) {
return x + x;
}
k += ( ( hx>>20 ) - BIAS )|0; // asm type annotation
hx &= HIGH_SIGNIFICAND_MASK;
i = ( (hx+0x95f64) & 0x100000 )|0; // asm type annotation
// Normalize `x` or `x/2`...
x = setHighWord( x, hx|(i^HIGH_BIASED_EXP_0) );
k += ( i>>20 )|0; // asm type annotation
f = x - 1.0;
if ( (HIGH_SIGNIFICAND_MASK&(2+hx)) < 3 ) {
// Case: -2**-20 <= f < 2**-20
if ( f === 0.0 ) {
if ( k === 0 ) {
return 0.0;
}
return (k * LN2_HI) + (k * LN2_LO);
}
R = f * f * ( 0.5 - (ONE_THIRD*f) );
if ( k === 0 ) {
return f - R;
}
return (k * LN2_HI) - ( (R-(k*LN2_LO)) - f );
}
s = f / (2.0 + f);
z = s * s;
i = ( hx - 0x6147a )|0; // asm type annotation
w = z * z;
j = ( 0x6b851 - hx )|0; // asm type annotation
t1 = w * lnpolyvalP( w );
t2 = z * lnpolyvalQ( w );
i |= j;
R = t2 + t1;
if ( i > 0 ) {
hfsq = 0.5 * f * f;
if ( k === 0 ) {
return f - ( hfsq - (s * (hfsq+R)) );
}
return (k * LN2_HI) - ( hfsq - ((s*(hfsq+R))+(k*LN2_LO)) - f );
}
if ( k === 0 ) {
return f - (s*(f-R));
}
return (k * LN2_HI) - ( ( (s*(f-R)) - (k*LN2_LO) ) - f );
}
/**
* High word mask for the sign bit of a double-precision floating-point number.
*
* ## Notes
*
* The high word mask for the sign bot of a double-precision floating-point number is an unsigned 32-bit integer with the value \\( 2147483648 \\), which corresponds to the bit sequence
*
* ```binarystring
* 1 00000000000 00000000000000000000
* ```
*
* @constant
* @type {uinteger32}
* @default 0x80000000
* @see [IEEE 754]{@link https://en.wikipedia.org/wiki/IEEE_754-1985}
*/
const FLOAT64_HIGH_WORD_SIGN_MASK: number = 0x80000000>>>0; // eslint-disable-line id-length
const SIGN_MASK = FLOAT64_HIGH_WORD_SIGN_MASK;
// High/low words workspace:
const WORDS = [ 0, 0 ];
/**
* Returns a double-precision floating-point number with the magnitude of `x` and the sign of `y`.
*
* @param {number} x - number from which to derive a magnitude
* @param {number} y - number from which to derive a sign
* @returns {number} a double-precision floating-point number
*
* @example
* var z = copysign( -3.14, 10.0 );
* // returns 3.14
*
* @example
* var z = copysign( 3.14, -1.0 );
* // returns -3.14
*
* @example
* var z = copysign( 1.0, -0.0 );
* // returns -1.0
*
* @example
* var z = copysign( -3.14, -0.0 );
* // returns -3.14
*
* @example
* var z = copysign( -0.0, 1.0 );
* // returns 0.0
*/
function copysign( x: number, y: number ): number {
var hx;
var hy;
// Split `x` into higher and lower order words:
toWords.assign( x, WORDS, 1, 0 );
hx = WORDS[ 0 ];
// Turn off the sign bit of `x`:
hx &= ABS_MASK;
// Extract the higher order word from `y`:
hy = getHighWord( y );
// Leave only the sign bit of `y` turned on:
hy &= SIGN_MASK;
// Copy the sign bit of `y` to `x`:
hx |= hy;
// Return a new value having the same magnitude as `x`, but with the sign of `y`:
return fromWords( hx, WORDS[ 1 ] );
}
/**
* Returns an integer corresponding to the unbiased exponent of a double-precision floating-point number.
*
* @param {number} x - input value
* @returns {integer32} unbiased exponent
*
* @example
* var exp = exponent( 3.14e-307 ); // => 2**-1019 ~ 1e-307
* // returns -1019
*
* @example
* var exp = exponent( -3.14 );
* // returns 1
*
* @example
* var exp = exponent( 0.0 );
* // returns -1023
*
* @example
* var exp = exponent( NaN );
* // returns 1024
*/
function floatExp( x: number ): number {
// Extract from the input value a higher order word (unsigned 32-bit integer) which contains the exponent:
var high = getHighWord( x );
// Apply a mask to isolate only the exponent bits and then shift off all bits which are part of the fraction:
high = ( high & EXPONENT_MASK ) >>> 20;
// Remove the bias and return:
return (high - BIAS)|0; // asm type annotation
}
/**
* Natural logarithm of `2`.
*
* ```tex
* \ln 2
* ```
*
* @constant
* @type {number}
* @default 0.6931471805599453
*/
const LN2: number = 6.93147180559945309417232121458176568075500134360255254120680009493393621969694715605863326996418687542001481021e-01; // eslint-disable-line max-len
// 0x3fe00000 = 1071644672 => 0 01111111110 00000000000000000000 => biased exponent: 1022 = -1+1023 => 2^-1
const HIGH_BIASED_EXP_NEG_1 = 0x3fe00000|0; // asm type annotation
// High: LN2
const POW2_LN2_HI = 6.93147182464599609375e-01; // 0x3FE62E43, 0x00000000
// Low: LN2
const POW2_LN2_LO = -1.90465429995776804525e-09; // 0xBE205C61, 0x0CA86C39
/**
* Computes \\(2^{\mathrm{hp} + \mathrm{lp}\\).
*
* @private
* @param {number} j - high word of `hp + lp`
* @param {number} hp - first power summand
* @param {number} lp - second power summand
* @returns {number} function value
*
* @example
* var z = pow2( 1065961648, -0.3398475646972656, -0.000002438187359100815 );
* // returns ~0.79
*/
function pow2( j: number, hp: number, lp: number ): number {
var tmp;
var t1;
var t;
var r;
var u;
var v;
var w;
var z;
var n;
var i;
var k;
i = (j & ABS_MASK)|0; // asm type annotation
k = ((i>>HIGH_NUM_SIGNIFICAND_BITS) - BIAS)|0; // asm type annotation
n = 0;
// `|z| > 0.5`, set `n = z+0.5`
if ( i > HIGH_BIASED_EXP_NEG_1 ) {
n = (j + (HIGH_MIN_NORMAL_EXP>>(k+1)))>>>0; // asm type annotation
k = (((n & ABS_MASK)>>HIGH_NUM_SIGNIFICAND_BITS) - BIAS)|0; // new k for n
tmp = ((n & ~(HIGH_SIGNIFICAND_MASK >> k)))>>>0; // asm type annotation
t = setHighWord( 0.0, tmp );
n = (((n & HIGH_SIGNIFICAND_MASK)|HIGH_MIN_NORMAL_EXP) >> (HIGH_NUM_SIGNIFICAND_BITS-k))>>>0; // eslint-disable-line max-len
if ( j < 0 ) {
n = -n;
}
hp -= t;
}
t = lp + hp;
t = setLowWord( t, 0 );
u = t * POW2_LN2_HI;
v = ( (lp - (t-hp))*LN2 ) + ( t*POW2_LN2_LO );
z = u + v;
w = v - (z - u);
t = z * z;
t1 = z - ( t*polyvalP( t ) );
r = ( (z*t1) / (t1-2.0) ) - ( w + (z*w) );
z = 1.0 - (r - z);
j = getHighWord( z );
j = uint32ToInt32( j );
j += (n << HIGH_NUM_SIGNIFICAND_BITS)>>>0; // asm type annotation
// Check for subnormal output...
if ( (j>>HIGH_NUM_SIGNIFICAND_BITS) <= 0 ) {
z = ldexp( z, n );
} else {
z = setHighWord( z, j );
}
return z;
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyvalW( x: number ): number {
if ( x === 0.0 ) {
return 0.5;
}
return 0.5 + (x * (-0.3333333333333333 + (x * 0.25)));
}
/**
* Computes \\(\operatorname{log}(x)\\) assuming \\(|1-x|\\) is small and using the approximation \\(x - x^2/2 + x^3/3 - x^4/4\\).
*
* @private
* @param {Array} out - output array
* @param {number} ax - absolute value of `x`
* @returns {Array} output array containing a tuple comprised of high and low parts
*
* @example
* var t = logx( [ 0.0, 0.0 ], 9.0 ); // => [ t1, t2 ]
* // returns [ -1265.7236328125, -0.0008163940840404393 ]
*/
function logx( out: Array<any>, ax: number ): Array<any> {
var t2;
var t1;
var t;
var w;
var u;
var v;
t = ax - 1.0; // `t` has `20` trailing zeros
w = t * t * polyvalW( t );
u = INV_LN2_HI * t; // `INV_LN2_HI` has `21` significant bits
v = ( t*INV_LN2_LO ) - ( w*INV_LN2 );
t1 = u + v;
t1 = setLowWord( t1, 0 );
t2 = v - (t1 - u);
out[ 0 ] = t1;
out[ 1 ] = t2;
return out;
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyvalL( x: number ): number {
if ( x === 0.0 ) {
return 0.5999999999999946;
}
return 0.5999999999999946 + (x * (0.4285714285785502 + (x * (0.33333332981837743 + (x * (0.272728123808534 + (x * (0.23066074577556175 + (x * 0.20697501780033842))))))))); // eslint-disable-line max-len
}
/**
* Computes \\(\operatorname{log2}(ax)\\).
*
* @private
* @param {Array} out - output array
* @param {number} ax - absolute value of `x`
* @param {number} ahx - high word of `ax`
* @returns {Array} output array containing a tuple comprised of high and low parts
*
* @example
* var t = log2ax( [ 0.0, 0.0 ], 9.0, 1075970048 ); // => [ t1, t2 ]
* // returns [ 3.169923782348633, 0.0000012190936795504075 ]
*/
function log2ax( out: Array<any>, ax: number, ahx: number ): Array<any> {
var tmp;
var ss; // `hs + ls`
var s2; // `ss` squared
var hs;
var ls;
var ht;
var lt;
var bp; // `BP` constant
var dp; // `DP` constant
var hp;
var lp;
var hz;
var lz;
var t1;
var t2;
var t;
var r;
var u;
var v;
var n;
var j;
var k;
n = 0|0; // asm type annotation
// Check if `x` is subnormal...
if ( ahx < HIGH_MIN_NORMAL_EXP ) {
ax *= TWO53;
n -= 53|0; // asm type annotation
ahx = getHighWord( ax );
}
// Extract the unbiased exponent of `x`:
n += ((ahx >> HIGH_NUM_SIGNIFICAND_BITS) - BIAS)|0; // asm type annotation
// Isolate the significand bits of `x`:
j = (ahx & HIGH_SIGNIFICAND_MASK)|0; // asm type annotation
// Normalize `ahx` by setting the (biased) exponent to `1023`:
ahx = (j | HIGH_BIASED_EXP_0)|0; // asm type annotation
// Determine the interval of `|x|` by comparing significand bits...
// |x| < sqrt(3/2)
if ( j <= 0x3988E ) { // 0 00000000000 00111001100010001110
k = 0;
}
// |x| < sqrt(3)
else if ( j < 0xBB67A ) { // 0 00000000000 10111011011001111010
k = 1;
}
// |x| >= sqrt(3)
else {
k = 0;
n += 1|0; // asm type annotation
ahx -= HIGH_MIN_NORMAL_EXP;
}
// Load the normalized high word into `|x|`:
ax = setHighWord( ax, ahx );
// Compute `ss = hs + ls = (x-1)/(x+1)` or `(x-1.5)/(x+1.5)`:
bp = BP[ k ]; // BP[0] = 1.0, BP[1] = 1.5
u = ax - bp; // (x-1) || (x-1.5)
v = 1.0 / (ax + bp); // 1/(x+1) || 1/(x+1.5)
ss = u * v;
hs = setLowWord( ss, 0 ); // set all low word (less significant significand) bits to 0s
// Compute `ht = ax + bp` (via manipulation, i.e., bit flipping, of the high word):
tmp = ((ahx>>1) | HIGH_BIASED_EXP_NEG_512) + HIGH_SIGNIFICAND_HALF;
tmp += (k << 18); // `(k<<18)` can be considered the word equivalent of `1.0` or `1.5`
ht = setHighWord( 0.0, tmp );
lt = ax - (ht - bp);
ls = v * ( ( u - (hs*ht) ) - ( hs*lt ) );
// Compute `log(ax)`...
s2 = ss * ss;
r = s2 * s2 * polyvalL( s2 );
r += ls * (hs + ss);
s2 = hs * hs;
ht = 3.0 + s2 + r;
ht = setLowWord( ht, 0 );
lt = r - ((ht-3.0) - s2);
// u+v = ss*(1+...):
u = hs * ht;
v = ( ls*ht ) + ( lt*ss );
// 2/(3LN2) * (ss+...):
hp = u + v;
hp = setLowWord( hp, 0 );
lp = v - (hp - u);
hz = CP_HI * hp; // CP_HI+CP_LO = 2/(3*LN2)
lz = ( CP_LO*hp ) + ( lp*CP ) + DP_LO[ k ];
// log2(ax) = (ss+...)*2/(3*LN2) = n + dp + hz + lz
dp = DP_HI[ k ];
t = n;
t1 = ((hz+lz) + dp) + t; // log2(ax)
t1 = setLowWord( t1, 0 );
t2 = lz - (((t1-t) - dp) - hz);
out[ 0 ] = t1;
out[ 1 ] = t2;
return out;
}
/**
* Evaluates the exponential function when \\( y = \pm \infty\\).
*
* @private
* @param {number} x - base
* @param {number} y - exponent
* @returns {number} function value
*
* @example
* var v = pow( -1.0, Infinity );
* // returns NaN
*
* @example
* var v = pow( -1.0, -Infinity );
* // returns NaN
*
* @example
* var v = pow( 1.0, Infinity );
* // returns 1.0
*
* @example
* var v = pow( 1.0, -Infinity );
* // returns 1.0
*
* @example
* var v = pow( 0.5, Infinity );
* // returns 0.0
*
* @example
* var v = pow( 0.5, -Infinity );
* // returns Infinity
*
* @example
* var v = pow( 1.5, -Infinity );
* // returns 0.0
*
* @example
* var v = pow( 1.5, Infinity );
* // returns Infinity
*/
function yIsInfinite( x: number, y: number ): number {
if ( x === -1.0 ) {
// Julia (0.4.2) and Python (2.7.9) return `1.0` (WTF???). JavaScript (`Math.pow`), R, and libm return `NaN`. We choose `NaN`, as the value is indeterminate; i.e., we cannot determine whether `y` is odd, even, or somewhere in between.
return (x-x)/(x-x); // signal NaN
}
if ( x === 1.0 ) {
return 1.0;
}
// (|x| > 1 && y === NINF) || (|x| < 1 && y === PINF)
if ( (abs(x) < 1.0) === (y === PINF) ) {
return 0.0;
}
// (|x| > 1 && y === PINF) || (|x| < 1 && y === NINF)
return PINF;
}
/**
* Evaluates the exponential function when \\(|y| > 2^64\\).
*
* @private
* @param {number} x - base
* @param {number} y - exponent
* @returns {number} overflow or underflow result
*
* @example
* var v = pow( 9.0, 3.6893488147419103e19 );
* // returns Infinity
*
* @example
* var v = pow( -3.14, -3.6893488147419103e19 );
* // returns 0.0
*/
function yIsHuge( x: number, y: number ): number {
var ahx;
var hx;
hx = getHighWord( x );
ahx = (hx & ABS_MASK);
if ( ahx <= HIGH_MAX_NEAR_UNITY ) {
if ( y < 0 ) {
// Signal overflow...
return HUGE * HUGE;
}
// Signal underflow...
return TINY * TINY;
}
// `x` has a biased exponent greater than or equal to `0`...
if ( y > 0 ) {
// Signal overflow...
return HUGE * HUGE;
}
// Signal underflow...
return TINY * TINY;
}
/**
* Evaluates the exponential function when \\(|x| = 0\\).
*
* @private
* @param {number} x - base
* @param {number} y - exponent
* @returns {number} function value
*
* @example
* var v = pow( 0.0, 2 );
* // returns 0.0
*
* @example
* var v = pow( -0.0, -9 );
* // returns -Infinity
*
* @example
* var v = pow( 0.0, -9 );
* // returns Infinity
*
* @example
* var v = pow( -0.0, 9 );
* // returns 0.0
*
* @example
* var v = pow( 0.0, -Infinity );
* // returns Infinity
*
* @example
* var v = pow( 0.0, Infinity );
* // returns 0.0
*/
function xIsZero( x: number, y: number ): number {
if ( y === NINF ) {
return PINF;
}
if ( y === PINF ) {
return 0.0;
}
if ( y > 0.0 ) {
if ( isOdd( y ) ) {
return x; // handles +-0
}
return 0.0;
}
// y < 0.0
if ( isOdd( y ) ) {
return copysign( PINF, x ); // handles +-0
}
return PINF;
}
/**
* Converts an unsigned 32-bit integer to a signed 32-bit integer.
*
* @param {uinteger32} x - unsigned 32-bit integer
* @returns {integer32} signed 32-bit integer
*
* @example
* var float64ToUint32 = require( '@stdlib/number-float64-base-to-uint32' );
* var y = uint32ToInt32( float64ToUint32( 4294967295 ) );
* // returns -1
*
* @example
* var float64ToUint32 = require( '@stdlib/number-float64-base-to-uint32' );
* var y = uint32ToInt32( float64ToUint32( 3 ) );
* // returns 3
*/
function uint32ToInt32( x: number ): number {
// NOTE: we could also use typed-arrays to achieve the same end.
return x|0; // asm type annotation
}
/**
* Tests if a finite numeric value is an even number.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is an even number
*
* @example
* var bool = isEven( 5.0 );
* // returns false
*
* @example
* var bool = isEven( -2.0 );
* // returns true
*
* @example
* var bool = isEven( 0.0 );
* // returns true
*
* @example
* var bool = isEven( NaN );
* // returns false
*/
function isEven( x: number ): boolean {
return isInteger( x/2.0 );
}
/**
* Tests if a finite numeric value is an odd number.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is an odd number
*
* @example
* var bool = isOdd( 5.0 );
* // returns true
*
* @example
* var bool = isOdd( -2.0 );
* // returns false
*
* @example
* var bool = isOdd( 0.0 );
* // returns false
*
* @example
* var bool = isOdd( NaN );
* // returns false
*/
function isOdd( x: number ): boolean {
// Check sign to prevent overflow...
if ( x > 0.0 ) {
return isEven( x-1.0 );
}
return isEven( x+1.0 );
}
/**
* Evaluates the exponential function.
*
* ## Method
*
* 1. Let \\(x = 2^n (1+f)\\).
*
* 2. Compute \\(\operatorname{log2}(x)\\) as
*
* ```tex
* \operatorname{log2}(x) = w_1 + w_2
* ```
*
* where \\(w_1\\) has \\(53 - 24 = 29\\) bit trailing zeros.
*
* 3. Compute
*
* ```tex
* y \cdot \operatorname{log2}(x) = n + y^\prime
* ```
*
* by simulating multi-precision arithmetic, where \\(|y^\prime| \leq 0.5\\).
*
* 4. Return
*
* ```tex
* x^y = 2^n e^{y^\prime \cdot \mathrm{log2}}
* ```
*
* ## Special Cases
*
* ```tex
* \begin{align*}
* x^{\mathrm{NaN}} &= \mathrm{NaN} & \\
* (\mathrm{NaN})^y &= \mathrm{NaN} & \\
* 1^y &= 1 & \\
* x^0 &= 1 & \\
* x^1 &= x & \\
* (\pm 0)^\infty &= +0 & \\
* (\pm 0)^{-\infty} &= +\infty & \\
* (+0)^y &= +0 & \mathrm{if}\ y > 0 \\
* (+0)^y &= +\infty & \mathrm{if}\ y < 0 \\
* (-0)^y &= -\infty & \mathrm{if}\ y\ \mathrm{is\ an\ odd\ integer\ and}\ y < 0 \\
* (-0)^y &= +\infty & \mathrm{if}\ y\ \mathrm{is\ not\ an\ odd\ integer\ and}\ y < 0 \\
* (-0)^y &= -0 & \mathrm{if}\ y\ \mathrm{is\ an\ odd\ integer\ and}\ y > 0 \\
* (-0)^y &= +0 & \mathrm{if}\ y\ \mathrm{is\ not\ an\ odd\ integer\ and}\ y > 0 \\
* (-1)^{\pm\infty} &= \mathrm{NaN} & \\
* x^{\infty} &= +\infty & |x| > 1 \\
* x^{\infty} &= +0 & |x| < 1 \\
* x^{-\infty} &= +0 & |x| > 1 \\
* x^{-\infty} &= +\infty & |x| < 1 \\
* (-\infty)^y &= (-0)^y & \\
* \infty^y &= +0 & y < 0 \\
* \infty^y &= +\infty & y > 0 \\
* x^y &= \mathrm{NaN} & \mathrm{if}\ y\ \mathrm{is\ not\ a\ finite\ integer\ and}\ x < 0
* \end{align*}
* ```
*
* ## Notes
*
* - \\(\operatorname{pow}(x,y)\\) returns \\(x^y\\) nearly rounded. In particular, \\(\operatorname{pow}(<\mathrm{integer}>,<\mathrm{integer}>)\\) **always** returns the correct integer, provided the value is representable.
* - The hexadecimal values shown in the source code are the intended values for used constants. Decimal values may be used, provided the compiler will accurately convert decimal to binary in order to produce the hexadecimal values.
*
*
* @param {number} x - base
* @param {number} y - exponent
* @returns {number} function value
*
* @example
* var v = pow( 2.0, 3.0 );
* // returns 8.0
*
* @example
* var v = pow( 4.0, 0.5 );
* // returns 2.0
*
* @example
* var v = pow( 100.0, 0.0 );
* // returns 1.0
*
* @example
* var v = pow( 3.141592653589793, 5.0 );
* // returns ~306.0197
*
* @example
* var v = pow( 3.141592653589793, -0.2 );
* // returns ~0.7954
*
* @example
* var v = pow( NaN, 3.0 );
* // returns NaN
*
* @example
* var v = pow( 5.0, NaN );
* // returns NaN
*
* @example
* var v = pow( NaN, NaN );
* // returns NaN
*/
function pow( x: number, y: number ): number {
var ahx; // absolute value high word `x`
var ahy; // absolute value high word `y`
var ax; // absolute value `x`
var hx; // high word `x`
var lx; // low word `x`
var hy; // high word `y`
var ly; // low word `y`
var sx; // sign `x`
var sy; // sign `y`
var y1;
var hp;
var lp;
var t;
var z; // y prime
var j;
var i;
if ( isnan( x ) || isnan( y ) ) {
return NaN;
}
// Split `y` into high and low words:
toWords.assign( y, WORDS, 1, 0 );
hy = WORDS[ 0 ];
ly = WORDS[ 1 ];
// Special cases `y`...
if ( ly === 0 ) {
if ( y === 0.0 ) {
return 1.0;
}
if ( y === 1.0 ) {
return x;
}
if ( y === -1.0 ) {
return 1.0 / x;
}
if ( y === 0.5 ) {
return Math.sqrt( x );
}
if ( y === -0.5 ) {
return 1.0 / Math.sqrt( x );
}
if ( y === 2.0 ) {
return x * x;
}
if ( y === 3.0 ) {
return x * x * x;
}
if ( y === 4.0 ) {
x *= x;
return x * x;
}
if ( isInfinite( y ) ) {
return yIsInfinite( x, y );
}
}
// Split `x` into high and low words:
toWords.assign( x, WORDS, 1, 0 );
hx = WORDS[ 0 ];
lx = WORDS[ 1 ];
// Special cases `x`...
if ( lx === 0 ) {
if ( hx === 0 ) {
return xIsZero( x, y );
}
if ( x === 1.0 ) {
return 1.0;
}
if (
x === -1.0 &&
isOdd( y )
) {
return -1.0;
}
if ( isInfinite( x ) ) {
if ( x === NINF ) {
// `pow( 1/x, -y )`
return pow( -0.0, -y );
}
if ( y < 0.0 ) {
return 0.0;
}
return PINF;
}
}
if (
x < 0.0 &&
isInteger( y ) === false
) {
// Signal NaN...
return (x-x)/(x-x);
}
ax = abs( x );
// Remove the sign bits (i.e., get absolute values):
ahx = (hx & ABS_MASK)|0; // asm type annotation
ahy = (hy & ABS_MASK)|0; // asm type annotation
// Extract the sign bits:
sx = (hx >>> HIGH_NUM_NONSIGN_BITS)|0; // asm type annotation
sy = (hy >>> HIGH_NUM_NONSIGN_BITS)|0; // asm type annotation
// Determine the sign of the result...
if ( sx && isOdd( y ) ) {
sx = -1.0;
} else {
sx = 1.0;
}
// Case 1: `|y|` is huge...
// |y| > 2^31
if ( ahy > HIGH_BIASED_EXP_31 ) {
// `|y| > 2^64`, then must over- or underflow...
if ( ahy > HIGH_BIASED_EXP_64 ) {
return yIsHuge( x, y );
}
// Over- or underflow if `x` is not close to unity...
if ( ahx < HIGH_MAX_NEAR_UNITY ) {
// y < 0
if ( sy === 1 ) {
// Signal overflow...
return sx * HUGE * HUGE;
}
// Signal underflow...
return sx * TINY * TINY;
}
if ( ahx > HIGH_BIASED_EXP_0 ) {
// y > 0
if ( sy === 0 ) {
// Signal overflow...
return sx * HUGE * HUGE;
}
// Signal underflow...
return sx * TINY * TINY;
}
// At this point, `|1-x|` is tiny (`<= 2^-20`). Suffice to compute `log(x)` by `x - x^2/2 + x^3/3 - x^4/4`.
t = logx( LOG_WORKSPACE, ax );
}
// Case 2: `|y|` is not huge...
else {
t = log2ax( LOG_WORKSPACE, ax, ahx );
}
// Split `y` into `y1 + y2` and compute `(y1+y2) * (t1+t2)`...
y1 = setLowWord( y, 0 );
lp = ( (y-y1)*t[0] ) + ( y*t[1] );
hp = y1 * t[0];
z = lp + hp;
// Note: *can* be more performant to use `getHighWord` and `getLowWord` directly, but using `toWords` looks cleaner.
toWords.assign( z, WORDS, 1, 0 );
j = uint32ToInt32( WORDS[0] );
i = uint32ToInt32( WORDS[1] );
// z >= 1024
if ( j >= HIGH_BIASED_EXP_10 ) {
// z > 1024
if ( ((j-HIGH_BIASED_EXP_10)|i) !== 0 ) {
// Signal overflow...
return sx * HUGE * HUGE;
}
if ( (lp+OVT) > (z-hp) ) {
// Signal overflow...
return sx * HUGE * HUGE;
}
}
// z <= -1075
else if ( (j&ABS_MASK) >= HIGH_1075 ) {
// z < -1075
if ( ((j-HIGH_NEG_1075)|i) !== 0 ) {
// Signal underflow...
return sx * TINY * TINY;
}
if ( lp <= (z-hp) ) {
// Signal underflow...
return sx * TINY * TINY;
}
}
// Compute `2^(hp+lp)`...
z = pow2( j, hp, lp );
return sx * z;
}
/**
* Tests if a double-precision floating-point numeric value is infinite.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is infinite
*
* @example
* var bool = isInfinite( Infinity );
* // returns true
*
* @example
* var bool = isInfinite( -Infinity );
* // returns true
*
* @example
* var bool = isInfinite( 5.0 );
* // returns false
*
* @example
* var bool = isInfinite( NaN );
* // returns false
*/
function isInfinite( x: number ): boolean {
return (x === PINF || x === NINF);
}
/**
* Returns a normal number `y` and exponent `exp` satisfying \\(x = y \cdot 2^\mathrm{exp}\\) and assigns results to a provided output array.
*
* @param {number} x - input value
* @param {Collection} out - output array
* @param {integer} stride - output array stride
* @param {NonNegativeInteger} offset - output array index offset
* @returns {Collection} output array
*
* @example
* var pow = require( '@stdlib/math-base-special-pow' );
*
* var out = normalize( 3.14e-319, [ 0.0, 0 ], 1, 0 );
* // returns [ 1.4141234400356668e-303, -52 ]
*
* var y = out[ 0 ];
* var exp = out[ 1 ];
*
* var bool = ( y*pow(2.0,exp) === 3.14e-319 );
* // returns true
*
* @example
* var out = normalize( 0.0, [ 0.0, 0 ], 1, 0 );
* // returns [ 0.0, 0 ];
*
* @example
* const PINF = require( '@stdlib/constants-float64-pinf' );
*
* var out = normalize( PINF, [ 0.0, 0 ], 1, 0 );
* // returns [ Infinity, 0 ]
*
* @example
* const NINF = require( '@stdlib/constants-float64-ninf' );
*
* var out = normalize( NINF, [ 0.0, 0 ], 1, 0 );
* // returns [ -Infinity, 0 ]
*
* @example
* var out = normalize( NaN, [ 0.0, 0 ], 1, 0 );
* // returns [ NaN, 0 ]
*/
function normalize( x: number, out: number[], stride: number, offset: number ): number[] {
if ( isnan( x ) || isInfinite( x ) ) {
out[ offset ] = x;
out[ offset + stride ] = 0;
return out;
}
if ( x !== 0.0 && abs( x ) < FLOAT64_SMALLEST_NORMAL ) {
out[ offset ] = x * SCALAR;
out[ offset + stride ] = -52;
return out;
}
out[ offset ] = x;
out[ offset + stride ] = 0;
return out;
}
/**
* Tests if a finite double-precision floating-point number is an integer.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is an integer
*
* @example
* var bool = isInteger( 1.0 );
* // returns true
*
* @example
* var bool = isInteger( 3.14 );
* // returns false
*/
function isInteger( x: number ): boolean {
return (Math.floor(x) === x);
}
/**
* Computes the absolute value of a double-precision floating-point number `x`.
*
* @param {number} x - input value
* @returns {number} absolute value
*
* @example
* var v = abs( -1.0 );
* // returns 1.0
*
* @example
* var v = abs( 2.0 );
* // returns 2.0
*
* @example
* var v = abs( 0.0 );
* // returns 0.0
*
* @example
* var v = abs( -0.0 );
* // returns 0.0
*
* @example
* var v = abs( NaN );
* // returns NaN
*/
function abs( x: number ): number {
return Math.abs( x ); // eslint-disable-line stdlib/no-builtin-math
}
/**
* Splits a double-precision floating-point number into a higher order word (unsigned 32-bit integer) and a lower order word (unsigned 32-bit integer).
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
* In which Uint32 can we find the higher order bits? If little endian, the second; if big endian, the first.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
*
* @private
* @param {number} x - input value
* @param {Collection} out - output array
* @param {integer} stride - output array stride
* @param {NonNegativeInteger} offset - output array index offset
* @returns {Collection} output array
*
* @example
* const Uint32Array = require( '@stdlib/array-uint32' );
*
* var out = new Uint32Array( 2 );
*
* var w = toWords( 3.14e201, out, 1, 0 );
* // returns <Uint32Array>[ 1774486211, 2479577218 ]
*
* var bool = ( w === out );
* // returns true
*/
function fcn( x: number, out: number[], stride: number, offset: number ): number[] {
FLOAT64_VIEW[ 0 ] = x;
out[ offset ] = UINT32_VIEW[ HIGH ];
out[ offset + stride ] = UINT32_VIEW[ LOW ];
return out;
}
/**
* Splits a double-precision floating-point number into a higher order word (unsigned 32-bit integer) and a lower order word (unsigned 32-bit integer).
*
* @param {number} x - input value
* @returns {Array<number>} output array
*
* @example
* var w = toWords( 3.14e201 );
* // returns [ 1774486211, 2479577218 ]
*/
function toWords( x: number ): Array<number> {
return fcn( x, [ 0>>>0, 0>>>0 ], 1, 0 );
}
toWords.assign = fcn;
/**
* Creates a double-precision floating-point number from a higher order word (unsigned 32-bit integer) and a lower order word (unsigned 32-bit integer).
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
*
* In which Uint32 should we place the higher order bits? If little endian, the second; if big endian, the first.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
* @param {uinteger32} high - higher order word (unsigned 32-bit integer)
* @param {uinteger32} low - lower order word (unsigned 32-bit integer)
* @returns {number} floating-point number
*
* @example
* var v = fromWords( 1774486211, 2479577218 );
* // returns 3.14e201
*
* @example
* var v = fromWords( 3221823995, 1413754136 );
* // returns -3.141592653589793
*
* @example
* var v = fromWords( 0, 0 );
* // returns 0.0
*
* @example
* var v = fromWords( 2147483648, 0 );
* // returns -0.0
*
* @example
* var v = fromWords( 2146959360, 0 );
* // returns NaN
*
* @example
* var v = fromWords( 2146435072, 0 );
* // returns Infinity
*
* @example
* var v = fromWords( 4293918720, 0 );
* // returns -Infinity
*/
function fromWords( high: number, low: number ): number {
UINT32_VIEW[ HIGH ] = high;
UINT32_VIEW[ LOW ] = low;
return FLOAT64_VIEW[ 0 ];
}
/**
* Returns a 32-bit unsigned integer corresponding to the less significant 32 bits of a double-precision floating-point number.
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
* In which Uint32 can we find the lower order bits? If little endian, the first; if big endian, the second.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
* @param {number} x - input value
* @returns {uinteger32} lower order word
*
* @example
* var w = getLowWord( 3.14e201 ); // => 10010011110010110101100010000010
* // returns 2479577218
*/
function getLowWord( x: number ): number {
FLOAT64_VIEW[ 0 ] = x;
return UINT32_VIEW[ LOW ];
}
/**
* Sets the less significant 32 bits of a double-precision floating-point number.
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
* In which Uint32 can we find the lower order bits? If little endian, the first; if big endian, the second.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
* @param {number} x - double
* @param {uinteger32} low - unsigned 32-bit integer to replace the lower order word of `x`
* @returns {number} double having the same higher order word as `x`
*
* @example
* var low = 5 >>> 0; // => 00000000000000000000000000000101
*
* var x = 3.14e201; // => 0 11010011100 01001000001011000011 10010011110010110101100010000010
*
* var y = setLowWord( x, low ); // => 0 11010011100 01001000001011000011 00000000000000000000000000000101
* // returns 3.139998651394392e+201
*
* @example
* const PINF = require( '@stdlib/constants-float64-pinf' );
* const NINF = require( '@stdlib/constants-float64-ninf' );
*
* var low = 12345678;
*
* var y = setLowWord( PINF, low );
* // returns NaN
*
* y = setLowWord( NINF, low );
* // returns NaN
*
* y = setLowWord( NaN, low );
* // returns NaN
*/
function setLowWord( x: number, low: number ): number {
FLOAT64_VIEW[ 0 ] = x;
UINT32_VIEW[ LOW ] = ( low >>> 0 ); // identity bit shift to ensure integer
return FLOAT64_VIEW[ 0 ];
}
/**
* Returns an unsigned 32-bit integer corresponding to the more significant 32 bits of a double-precision floating-point number.
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
* In which Uint32 can we find the higher order bits? If little endian, the second; if big endian, the first.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
* @param {number} x - input value
* @returns {uinteger32} higher order word
*
* @example
* var w = getHighWord( 3.14e201 ); // => 01101001110001001000001011000011
* // returns 1774486211
*/
function getHighWord( x: number ): number {
FLOAT64_VIEW[ 0 ] = x;
return UINT32_VIEW[ HIGH ];
}
/**
* Sets the more significant 32 bits of a double-precision floating-point number.
*
* ## Notes
*
* ```text
* float64 (64 bits)
* f := fraction (significand/mantissa) (52 bits)
* e := exponent (11 bits)
* s := sign bit (1 bit)
*
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Float64 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* | Uint32 | Uint32 |
* |-------- -------- -------- -------- -------- -------- -------- --------|
* ```
*
* If little endian (more significant bits last):
*
* ```text
* <-- lower higher -->
* | f7 f6 f5 f4 f3 f2 e2 | f1 |s| e1 |
* ```
*
* If big endian (more significant bits first):
*
* ```text
* <-- higher lower -->
* |s| e1 e2 | f1 f2 f3 f4 f5 f6 f7 |
* ```
*
* In which Uint32 can we find the higher order bits? If little endian, the second; if big endian, the first.
*
*
* ## References
*
* - [Open Group][1]
*
* [1]: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
*
* @param {number} x - double
* @param {uinteger32} high - unsigned 32-bit integer to replace the higher order word of `x`
* @returns {number} double having the same lower order word as `x`
*
* @example
* var high = 5 >>> 0; // => 0 00000000000 00000000000000000101
*
* var y = setHighWord( 3.14e201, high ); // => 0 00000000000 0000000000000000010110010011110010110101100010000010
* // returns 1.18350528745e-313
*
* @example
* const PINF = require( '@stdlib/constants-float64-pinf' ); // => 0 11111111111 00000000000000000000 00000000000000000000000000000000
*
* var high = 1072693248 >>> 0; // => 0 01111111111 00000000000000000000
*
* // Set the higher order bits of `+infinity` to return `1`:
* var y = setHighWord( PINF, high ); // => 0 01111111111 0000000000000000000000000000000000000000000000000000
* // returns 1.0
*/
function setHighWord( x: number, high: number ): number {
FLOAT64_VIEW[ 0 ] = x;
UINT32_VIEW[ HIGH ] = ( high >>> 0 ); // identity bit shift to ensure integer
return FLOAT64_VIEW[ 0 ];
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyval13 ( x: number ): number {
if ( x === 0.0 ) {
return 0.0416666666666666;
}
return 0.0416666666666666 + (x * (-0.001388888888887411 + (x * 0.00002480158728947673))); // eslint-disable-line max-len
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyval46( x: number ): number {
if ( x === 0.0 ) {
return -2.7557314351390663e-7;
}
return -2.7557314351390663e-7 + (x * (2.087572321298175e-9 + (x * -1.1359647557788195e-11))); // eslint-disable-line max-len
}
/**
* Computes the cosine on \\( \[-\pi/4, \pi/4] \\), where \\( \pi/4 \approx 0.785398164 \\).
*
* ## Method
*
* - Since \\( \cos(-x) = \cos(x) \\), we need only to consider positive \\(x\\).
*
* - If \\( x < 2^{-27} \\), return \\(1\\) which is inexact if \\( x \ne 0 \\).
*
* - \\( cos(x) \\) is approximated by a polynomial of degree \\(14\\) on \\( \[0,\pi/4] \\).
*
* ```tex
* \cos(x) \approx 1 - \frac{x \cdot x}{2} + C_1 \cdot x^4 + \ldots + C_6 \cdot x^{14}
* ```
*
* where the Remez error is
*
* ```tex
* \left| \cos(x) - \left( 1 - \frac{x^2}{2} + C_1x^4 + C_2x^6 + C_3x^8 + C_4x^{10} + C_5x^{12} + C_6x^{15} \right) \right| \le 2^{-58}
* ```
*
* - Let \\( C_1x^4 + C_2x^6 + C_3x^8 + C_4x^{10} + C_5x^{12} + C_6x^{14} \\), then
*
* ```tex
* \cos(x) \approx 1 - \frac{x \cdot x}{2} + r
* ```
*
* Since
*
* ```tex
* \cos(x+y) \approx \cos(x) - \sin(x) \cdot y \approx \cos(x) - x \cdot y
* ```
*
* a correction term is necessary in \\( \cos(x) \\). Hence,
*
* ```tex
* \cos(x+y) = 1 - \left( \frac{x \cdot x}{2} - (r - x \cdot y) \right)
* ```
*
* For better accuracy, rearrange to
*
* ```tex
* \cos(x+y) \approx w + \left( t + ( r - x \cdot y ) \right)
* ```
*
* where \\( w = 1 - \frac{x \cdot x}{2} \\) and \\( t \\) is a tiny correction term (\\( 1 - \frac{x \cdot x}{2} = w + t \\) exactly in infinite precision). The exactness of \\(w + t\\) in infinite precision depends on \\(w\\) and \\(t\\) having the same precision as \\(x\\).
*
*
* @param {number} x - input value (in radians, assumed to be bounded by ~pi/4 in magnitude)
* @param {number} y - tail of `x`
* @returns {number} cosine
*
* @example
* var v = kernelCos( 0.0, 0.0 );
* // returns ~1.0
*
* @example
* var v = kernelCos( 3.141592653589793/6.0, 0.0 );
* // returns ~0.866
*
* @example
* var v = kernelCos( 0.785, -1.144e-17 );
* // returns ~0.707
*
* @example
* var v = kernelCos( NaN, 0.0 );
* // returns NaN
*/
function kernelCos( x: number, y: number ): number {
var hz;
var r;
var w;
var z;
z = x * x;
w = z * z;
r = z * polyval13( z );
r += w * w * polyval46( z );
hz = 0.5 * z;
w = 1.0 - hz;
return w + ( ((1.0-w) - hz) + ((z*r) - (x*y)) );
}
/**
* Computes the sine on \\( \approx \[-\pi/4, \pi/4] \\) (except on \\(-0\\)), where \\( \pi/4 \approx 0.7854 \\).
*
* ## Method
*
* - Since \\( \sin(-x) = -\sin(x) \\), we need only to consider positive \\(x\\).
*
* - Callers must return \\( \sin(-0) = -0 \\) without calling here since our odd polynomial is not evaluated in a way that preserves \\(-0\\). Callers may do the optimization \\( \sin(x) \approx x \\) for tiny \\(x\\).
*
* - \\( \sin(x) \\) is approximated by a polynomial of degree \\(13\\) on \\( \left\[0,\tfrac{pi}{4}\right] \\)
*
* ```tex
* \sin(x) \approx x + S_1 \cdot x^3 + \ldots + S_6 \cdot x^{13}
* ```
*
* where
*
* ```tex
* \left| \frac{\sin(x)}{x} \left( 1 + S_1 \cdot x + S_2 \cdot x + S_3 \cdot x + S_4 \cdot x + S_5 \cdot x + S_6 \cdot x \right) \right| \le 2^{-58}
* ```
*
* - We have
*
* ```tex
* \sin(x+y) = \sin(x) + \sin'(x') \cdot y \approx \sin(x) + (1-x*x/2) \cdot y
* ```
*
* For better accuracy, let
*
* ```tex
* r = x^3 * \left( S_2 + x^2 \cdot \left( S_3 + x^2 * \left( S_4 + x^2 \cdot ( S_5+x^2 \cdot S_6 ) \right) \right) \right)
* ```
*
* then
*
* ```tex
* \sin(x) = x + \left( S_1 \cdot x + ( x \cdot (r-y/2) + y ) \right)
* ```
*
*
* @param {number} x - input value (in radians, assumed to be bounded by `~pi/4` in magnitude)
* @param {number} y - tail of `x`
* @returns {number} sine
*
* @example
* var v = kernelSin( 0.0, 0.0 );
* // returns ~0.0
*
* @example
* var v = kernelSin( 3.141592653589793/6.0, 0.0 );
* // returns ~0.5
*
* @example
* var v = kernelSin( 0.619, 9.279e-18 );
* // returns ~0.58
*
* @example
* var v = kernelSin( NaN, 0.0 );
* // returns NaN
*
* @example
* var v = kernelSin( 3.0, NaN );
* // returns NaN
*
* @example
* var v = kernelSin( NaN, NaN );
* // returns NaN
*/
function kernelSin( x: number, y: number ): number {
var r;
var v;
var w;
var z;
z = x * x;
w = z * z;
r = S2 + (z * (S3 + (z*S4))) + (z * w * (S5 + (z*S6)));
v = z * x;
if ( y === 0.0 ) {
return x + (v * (S1 + (z*r)));
}
return x - (((z*((0.5*y) - (v*r))) - y) - (v*S1));
}
/**
* Returns a filled "generic" array.
*
* @param {*} value - fill value
* @param {NonNegativeInteger} len - array length
* @returns {Array} filled array
*
* @example
* var out = filled( 0.0, 3 );
* // returns [ 0.0, 0.0, 0.0 ]
*
* @example
* var out = filled( 'beep', 3 );
* // returns [ 'beep', 'beep', 'beep' ]
*/
function filled( value: any, len: number ): Array<any> {
var arr;
var i;
// Manually push elements in order to ensure "fast" elements...
arr = [];
for ( i = 0; i < len; i++ ) {
arr.push( value );
}
return arr;
}
/**
* Returns a zero-filled "generic" array.
*
* @param {NonNegativeInteger} len - array length
* @returns {Array} output array
*
* @example
* var out = zeros( 3 );
* // returns [ 0.0, 0.0, 0.0 ]
*/
function zeros( len: number ): Array<any> {
return filled( 0.0, len );
}
// FUNCTIONS //
/**
* Performs the computation for `kernelRempio2()`.
*
* @private
* @param {PositiveNumber} x - input value
* @param {(Array|TypedArray|Object)} y - output object for storing double precision numbers
* @param {integer} jz - number of terms of `ipio2[]` used
* @param {Array<integer>} q - array with integral values, representing the 24-bits chunk of the product of `x` and `2/π`
* @param {integer} q0 - the corresponding exponent of `q[0]` (the exponent for `q[i]` would be `q0-24*i`)
* @param {integer} jk - `jk+1` is the initial number of terms of `IPIO2[]` needed in the computation
* @param {integer} jv - index for pointing to the suitable `ipio2[]` for the computation
* @param {integer} jx - `nx - 1`
* @param {Array<number>} f - `IPIO2[]` in floating point
* @returns {number} last three binary digits of `N`
*/
function compute( x: number, y: (Array<any> | TypedArray | object), jz: number, q: number[], q0: number, jk: number, jv: number, jx: number, f: Array<number> ): number {
var carry;
var fw;
var ih;
var jp;
var i;
var k;
var n;
var j;
var z;
// `jp+1` is the number of terms in `PIO2[]` needed:
jp = jk;
// Distill `q[]` into `IQ[]` in reverse order...
z = q[ jz ];
j = jz;
for ( i = 0; j > 0; i++ ) {
fw = ( TWON24 * z )|0;
IQ[ i ] = ( z - (TWO24*fw) )|0;
z = q[ j-1 ] + fw;
j -= 1;
}
// Compute `n`...
z = ldexp( z, q0 );
z -= 8.0 * Math.floor( z*0.125 ); // Trim off integer >= 8
n = z|0;
z -= n;
ih = 0;
if ( q0 > 0 ) {
// Need `IQ[jz-1]` to determine `n`...
i = ( IQ[ jz-1 ] >> (24-q0) );
n += i;
IQ[ jz-1 ] -= ( i << (24-q0) );
ih = ( IQ[ jz-1 ] >> (23-q0) );
}
else if ( q0 === 0 ) {
ih = ( IQ[ jz-1 ] >> 23 );
}
else if ( z >= 0.5 ) {
ih = 2;
}
// Case: q > 0.5
if ( ih > 0 ) {
n += 1;
carry = 0;
// Compute `1-q`:
for ( i = 0; i < jz; i++ ) {
j = IQ[ i ];
if ( carry === 0 ) {
if ( j !== 0 ) {
carry = 1;
IQ[ i ] = 0x1000000 - j;
}
} else {
IQ[ i ] = 0xffffff - j;
}
}
if ( q0 > 0 ) {
// Rare case: chance is 1 in 12...
switch ( q0 ) { // eslint-disable-line default-case
case 1:
IQ[ jz-1 ] &= 0x7fffff;
break;
case 2:
IQ[ jz-1 ] &= 0x3fffff;
break;
}
}
if ( ih === 2 ) {
z = 1.0 - z;
if ( carry !== 0 ) {
z -= ldexp( 1.0, q0 );
}
}
}
// Check if re-computation is needed...
if ( z === 0.0 ) {
j = 0;
for ( i = jz-1; i >= jk; i-- ) {
j |= IQ[ i ];
}
if ( j === 0 ) {
// Need re-computation...
for ( k = 1; IQ[ jk-k ] === 0; k++ ) {
// `k` is the number of terms needed...
}
for ( i = jz+1; i <= jz+k; i++ ) {
// Add `q[jz+1]` to `q[jz+k]`...
f[ jx+i ] = IPIO2[ jv+i ];
fw = 0.0;
for ( j = 0; j <= jx; j++ ) {
fw += x[ j ] * f[ jx + (i-j) ];
}
q[ i ] = fw;
}
jz += k;
return compute( x, y, jz, q, q0, jk, jv, jx, f );
}
}
// Chop off zero terms...
if ( z === 0.0 ) {
jz -= 1;
q0 -= 24;
while ( IQ[ jz ] === 0 ) {
jz -= 1;
q0 -= 24;
}
} else {
// Break `z` into 24-bit if necessary...
z = ldexp( z, -q0 );
if ( z >= TWO24 ) {
fw = (TWON24*z)|0;
IQ[ jz ] = ( z - (TWO24*fw) )|0;
jz += 1;
q0 += 24;
IQ[ jz ] = fw;
} else {
IQ[ jz ] = z|0;
}
}
// Convert integer "bit" chunk to floating-point value...
fw = ldexp( 1.0, q0 );
for ( i = jz; i >= 0; i-- ) {
q[ i ] = fw * IQ[i];
fw *= TWON24;
}
// Compute `PIO2[0,...,jp]*q[jz,...,0]`...
for ( i = jz; i >= 0; i-- ) {
fw = 0.0;
for ( k = 0; k <= jp && k <= jz-i; k++ ) {
fw += PIO2[ k ] * q[ i+k ];
}
FQ[ jz-i ] = fw;
}
// Compress `FQ[]` into `y[]`...
fw = 0.0;
for ( i = jz; i >= 0; i-- ) {
fw += FQ[ i ];
}
if ( ih === 0 ) {
y[ 0 ] = fw;
} else {
y[ 0 ] = -fw;
}
fw = FQ[ 0 ] - fw;
for ( i = 1; i <= jz; i++ ) {
fw += FQ[i];
}
if ( ih === 0 ) {
y[ 1 ] = fw;
} else {
y[ 1 ] = -fw;
}
return ( n & 7 );
}
/**
* Returns the last three binary digits of `N` with `y = x - Nπ/2` so that `|y| < π/2`.
*
* ## Method
*
* - The method is to compute the integer (`mod 8`) and fraction parts of `2x/π` without doing the full multiplication. In general, we skip the part of the product that is known to be a huge integer (more accurately, equals `0 mod 8` ). Thus, the number of operations is independent of the exponent of the input.
*
* @private
* @param {PositiveNumber} x - input value
* @param {(Array|TypedArray|Object)} y - remainder elements
* @param {PositiveInteger} e0 - the exponent of `x[0]` (must be <= 16360)
* @param {PositiveInteger} nx - dimension of `x[]`
* @returns {number} last three binary digits of `N`
*/
function rempio2Kernel( x: number, y: (Array<any> | TypedArray | object), e0: number, nx: number ): number {
var fw;
var jk;
var jv;
var jx;
var jz;
var q0;
var i;
var j;
var m;
// Initialize `jk` for double-precision floating-point numbers:
jk = 4;
// Determine `jx`, `jv`, `q0` (note that `q0 < 3`):
jx = nx - 1;
jv = ( (e0 - 3) / 24 )|0;
if ( jv < 0 ) {
jv = 0;
}
q0 = e0 - (24 * (jv + 1));
// Set up `F[0]` to `F[jx+jk]` where `F[jx+jk] = IPIO2[jv+jk]`:
j = jv - jx;
m = jx + jk;
for ( i = 0; i <= m; i++ ) {
if ( j < 0 ) {
F[ i ] = 0.0;
} else {
F[ i ] = IPIO2[ j ];
}
j += 1;
}
// Compute `Q[0],Q[1],...,Q[jk]`:
for ( i = 0; i <= jk; i++ ) {
fw = 0.0;
for ( j = 0; j <= jx; j++ ) {
fw += x[ j ] * F[ jx + (i-j) ];
}
Q[ i ] = fw;
}
jz = jk;
return compute( x, y, jz, Q, q0, jk, jv, jx, F );
}
/**
* Computes `x - nπ/2 = r` for medium-sized inputs.
*
* @private
* @param {number} x - input value
* @param {uint32} ix - high word of `x`
* @param {(Array|TypedArray|Object)} y - remainder elements
* @returns {integer} factor of `π/2`
*/
function rempio2Medium( x: number, ix: number, y: (Array<any> | TypedArray | object) ): number {
var high;
var n;
var t;
var r;
var w;
var i;
var j;
n = Math.round( x * INVPIO2 );
r = x - ( n * PIO2_1 );
w = n * PIO2_1T;
// First rounding (good to 85 bits)...
j = (ix >> 20)|0; // asm type annotation
y[ 0 ] = r - w;
high = getHighWord( y[0] );
i = j - ( (high >> 20) & EXPONENT_MASK_REMPIO );
// Check if a second iteration is needed (good to 118 bits)...
if ( i > 16 ) {
t = r;
w = n * PIO2_2;
r = t - w;
w = (n * PIO2_2T) - ((t-r) - w);
y[ 0 ] = r - w;
high = getHighWord( y[0] );
i = j - ( (high >> 20) & EXPONENT_MASK_REMPIO );
// Check if a third iteration is needed (151 bits accumulated)...
if ( i > 49 ) {
t = r;
w = n * PIO2_3;
r = t - w;
w = (n * PIO2_3T) - ((t-r) - w);
y[ 0 ] = r - w;
}
}
y[ 1 ] = (r - y[0]) - w;
return n;
}
/**
* Computes `x - nπ/2 = r`.
*
* ## Notes
*
* - Returns `n` and stores the remainder `r` as two numbers `y[0]` and `y[1]`, such that `y[0]+y[1] = r`.
*
*
* @param {number} x - input value
* @param {(Array|TypedArray|Object)} y - remainder elements
* @returns {integer} factor of `π/2`
*
* @example
* var y = [ 0.0, 0.0 ];
* var n = rempio2( 128.0, y );
* // returns 81
*
* var y1 = y[ 0 ];
* // returns ~0.765
*
* var y2 = y[ 1 ];
* // returns ~3.618e-17
*
* @example
* var y = [ 0.0, 0.0 ];
* var n = rempio2( NaN, y );
* // returns 0
*
* var y1 = y[ 0 ];
* // returns NaN
*
* var y2 = y[ 1 ];
* // returns NaN
*/
function rempio2( x: number, y: (Array<any> | TypedArray | object) ): number {
var low;
var e0;
var hx;
var ix;
var nx;
var i;
var n;
var z;
hx = getHighWord( x );
ix = (hx & ABS_MASK)|0; // asm type annotation
// Case: |x| ~<= π/4 (no need for reduction)
if ( ix <= PIO4_HIGH_WORD ) {
y[ 0 ] = x;
y[ 1 ] = 0.0;
return 0;
}
// Case: |x| ~<= 5π/4
if ( ix <= FIVE_PIO4_HIGH_WORD ) {
// Case: |x| ~= π/2 or π
if ( (ix & SIGNIFICAND_MASK) === PI_HIGH_WORD_SIGNIFICAND ) {
// Cancellation => use medium case
return rempio2Medium( x, ix, y );
}
// Case: |x| ~<= 3π/4
if ( ix <= THREE_PIO4_HIGH_WORD ) {
if ( x > 0.0 ) {
z = x - PIO2_1;
y[ 0 ] = z - PIO2_1T;
y[ 1 ] = (z - y[0]) - PIO2_1T;
return 1;
}
z = x + PIO2_1;
y[ 0 ] = z + PIO2_1T;
y[ 1 ] = (z - y[0]) + PIO2_1T;
return -1;
}
if ( x > 0.0 ) {
z = x - ( 2.0*PIO2_1 );
y[ 0 ] = z - TWO_PIO2_1T;
y[ 1 ] = (z - y[0]) - TWO_PIO2_1T;
return 2;
}
z = x + ( 2.0*PIO2_1 );
y[ 0 ] = z + TWO_PIO2_1T;
y[ 1 ] = (z - y[0]) + TWO_PIO2_1T;
return -2;
}
// Case: |x| ~<= 9π/4
if ( ix <= NINE_PIO4_HIGH_WORD ) {
// Case: |x| ~<= 7π/4
if ( ix <= SEVEN_PIO4_HIGH_WORD ) {
// Case: |x| ~= 3π/2
if ( ix === THREE_PIO2_HIGH_WORD ) {
return rempio2Medium( x, ix, y );
}
if ( x > 0.0 ) {
z = x - ( 3.0*PIO2_1 );
y[ 0 ] = z - THREE_PIO2_1T;
y[ 1 ] = (z - y[0]) - THREE_PIO2_1T;
return 3;
}
z = x + ( 3.0*PIO2_1 );
y[ 0 ] = z + THREE_PIO2_1T;
y[ 1 ] = (z - y[0]) + THREE_PIO2_1T;
return -3;
}
// Case: |x| ~= 4π/2
if ( ix === TWO_PI_HIGH_WORD ) {
return rempio2Medium( x, ix, y );
}
if ( x > 0.0 ) {
z = x - ( 4.0*PIO2_1 );
y[ 0 ] = z - FOUR_PIO2_1T;
y[ 1 ] = (z - y[0]) - FOUR_PIO2_1T;
return 4;
}
z = x + ( 4.0*PIO2_1 );
y[ 0 ] = z + FOUR_PIO2_1T;
y[ 1 ] = (z - y[0]) + FOUR_PIO2_1T;
return -4;
}
// Case: |x| ~< 2^20*π/2 (medium size)
if ( ix < MEDIUM ) {
return rempio2Medium( x, ix, y );
}
// Case: x is NaN or infinity
if ( ix >= EXPONENT_MASK ) {
y[ 0 ] = NaN;
y[ 1 ] = NaN;
return 0.0;
}
// Set z = scalbn(|x|, ilogb(x)-23)...
low = getLowWord( x );
e0 = (ix >> 20) - 1046; // `e0 = ilogb(z) - 23` => unbiased exponent minus 23
z = fromWords( ix - ((e0 << 20)|0), low );
for ( i = 0; i < 2; i++ ) {
TX[ i ] = z|0;
z = (z - TX[i]) * TWO24;
}
TX[ 2 ] = z;
nx = 3;
while ( TX[ nx-1 ] === ZERO ) {
// Skip zero term...
nx -= 1;
}
n = rempio2Kernel( TX, TY, e0, nx, 1 );
if ( x < 0.0 ) {
y[ 0 ] = -TY[ 0 ];
y[ 1 ] = -TY[ 1 ];
return -n;
}
y[ 0 ] = TY[ 0 ];
y[ 1 ] = TY[ 1 ];
return n;
}
/**
* Computes the sine of a number.
*
* ## Method
*
* - Let \\(S\\), \\(C\\), and \\(T\\) denote the \\(\sin\\), \\(\cos\\), and \\(\tan\\), respectively, on \\(\[-\pi/4, +\pi/4\]\\).
*
* - Reduce the argument \\(x\\) to \\(y1+y2 = x-k\pi/2\\) in \\(\[-\pi/4, +\pi/4\]\\), and let \\(n = k \mod 4\\).
*
* - We have
*
* | n | sin(x) | cos(x) | tan(x) |
* | - | ------ | ------ | ------ |
* | 0 | S | C | T |
* | 1 | C | -S | -1/T |
* | 2 | -S | -C | T |
* | 3 | -C | S | -1/T |
*
*
* @param {number} x - input value (in radians)
* @returns {number} sine
*
* @example
* var v = sin( 0.0 );
* // returns ~0.0
*
* @example
* var v = sin( 3.141592653589793/2.0 );
* // returns ~1.0
*
* @example
* var v = sin( -3.141592653589793/6.0 );
* // returns ~-0.5
*
* @example
* var v = sin( NaN );
* // returns NaN
*/
function sin( x: number ): number {
var ix;
var n;
ix = getHighWord( x );
ix &= ABS_MASK;
// Case: |x| ~< π/4
if ( ix <= PIO4_HIGH_WORD ) {
// Case: |x| ~< 2^-26
if ( ix < SMALL_HIGH_WORD ) {
return x;
}
return kernelSin( x, 0.0 );
}
// Case: x is NaN or infinity
if ( ix >= EXPONENT_MASK ) {
return NaN;
}
// Argument reduction...
n = rempio2( x, Y );
switch ( n & 3 ) {
case 0:
return kernelSin( Y[ 0 ], Y[ 1 ] );
case 1:
return kernelCos( Y[ 0 ], Y[ 1 ] );
case 2:
return -kernelSin( Y[ 0 ], Y[ 1 ] );
default:
return -kernelCos( Y[ 0 ], Y[ 1 ] );
}
}
/**
* Computes the cosine of a number.
*
* @param {number} x - input value (in radians)
* @returns {number} cosine
*
* @example
* var v = cos( 0.0 );
* // returns 1.0
*
* @example
* var v = cos( 3.141592653589793/4.0 );
* // returns ~0.707
*
* @example
* var v = cos( -3.141592653589793/6.0 );
* // returns ~0.866
*
* @example
* var v = cos( NaN );
* // returns NaN
*/
function cos( x: number ): number {
var ix;
var n;
ix = getHighWord( x );
ix &= HIGH_WORD_ABS_MASK;
// Case: |x| ~< pi/4
if ( ix <= HIGH_WORD_PIO4 ) {
// Case: x < 2**-27
if ( ix < HIGH_WORD_TWO_NEG_27 ) {
return 1.0;
}
return kernelCos( x, 0.0 );
}
// Case: cos(Inf or NaN) is NaN */
if ( ix >= HIGH_WORD_EXPONENT_MASK ) {
return NaN;
}
// Case: Argument reduction needed...
n = rempio2( x, buffer );
switch ( n & 3 ) {
case 0:
return kernelCos( buffer[ 0 ], buffer[ 1 ] );
case 1:
return -kernelSin( buffer[ 0 ], buffer[ 1 ] );
case 2:
return -kernelCos( buffer[ 0 ], buffer[ 1 ] );
default:
return kernelSin( buffer[ 0 ], buffer[ 1 ] );
}
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyvalS( x: number ): number {
if ( x === 0.0 ) {
return 0.08333333333334822;
}
return 0.08333333333334822 + (x * (0.0034722222160545866 + (x * (-0.0026813261780578124 + (x * (-0.00022954996161337813 + (x * 0.0007873113957930937))))))); // eslint-disable-line max-len
}
/**
* Evaluates the gamma function using Stirling's formula. The polynomial is valid for \\(33 \leq x \leq 172\\).
*
* @private
* @param {number} x - input value
* @returns {number} function value
*/
function stirlingApprox( x: number ): number {
var w;
var y;
var v;
w = 1.0 / x;
w = 1.0 + ( w * polyvalS( w ) );
y = exp( x );
// Check `x` to avoid `pow()` overflow...
if ( x > MAX_STIRLING ) {
v = pow( x, ( 0.5*x ) - 0.25 );
y = v * (v/y);
} else {
y = pow( x, x-0.5 ) / y;
}
return SQRT_TWO_PI * y * w;
}
/**
* Evaluates the gamma function using a small-value approximation.
*
* @private
* @param {number} x - input value
* @param {number} z - scale factor
* @returns {number} function value
*/
function smallApprox( x: number, z: number ): number {
return z / ( (1.0+( EULER*x )) * x );
}
/**
* Evaluates a rational function, i.e., the ratio of two polynomials described by the coefficients stored in \\(P\\) and \\(Q\\).
*
* ## Notes
*
* - Coefficients should be sorted in ascending degree.
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the rational function
* @returns {number} evaluated rational function
*/
function rateval( x: number ): number {
var ax;
var s1;
var s2;
if ( x === 0.0 ) {
return 1.0;
}
if ( x < 0.0 ) {
ax = -x;
} else {
ax = x;
}
if ( ax <= 1.0 ) {
s1 = 1.0 + (x * (0.4942148268014971 + (x * (0.20744822764843598 + (x * (0.04763678004571372 + (x * (0.010421379756176158 + (x * (0.0011913514700658638 + (x * (0.00016011952247675185 + (x * 0.0))))))))))))); // eslint-disable-line max-len
s2 = 1.0 + (x * (0.0714304917030273 + (x * (-0.23459179571824335 + (x * (0.035823639860549865 + (x * (0.011813978522206043 + (x * (-0.004456419138517973 + (x * (0.0005396055804933034 + (x * -0.000023158187332412014))))))))))))); // eslint-disable-line max-len
} else {
x = 1.0 / x;
s1 = 0.0 + (x * (0.00016011952247675185 + (x * (0.0011913514700658638 + (x * (0.010421379756176158 + (x * (0.04763678004571372 + (x * (0.20744822764843598 + (x * (0.4942148268014971 + (x * 1.0))))))))))))); // eslint-disable-line max-len
s2 = -0.000023158187332412014 + (x * (0.0005396055804933034 + (x * (-0.004456419138517973 + (x * (0.011813978522206043 + (x * (0.035823639860549865 + (x * (-0.23459179571824335 + (x * (0.0714304917030273 + (x * 1.0))))))))))))); // eslint-disable-line max-len
}
return s1 / s2;
}
/**
* Evaluates the gamma function.
*
* ## Method
*
* 1. Arguments \\(|x| \leq 34\\) are reduced by recurrence and the function approximated by a rational function of degree \\(6/7\\) in the interval \\((2,3)\\).
* 2. Large negative arguments are made positive using a reflection formula.
* 3. Large arguments are handled by Stirling's formula.
*
*
* ## Notes
*
* - Relative error:
*
* | arithmetic | domain | # trials | peak | rms |
* |:----------:|:---------:|:--------:|:-------:|:-------:|
* | DEC | -34,34 | 10000 | 1.3e-16 | 2.5e-17 |
* | IEEE | -170,-33 | 20000 | 2.3e-15 | 3.3e-16 |
* | IEEE | -33, 33 | 20000 | 9.4e-16 | 2.2e-16 |
* | IEEE | 33, 171.6 | 20000 | 2.3e-15 | 3.2e-16 |
*
* - Error for arguments outside the test range will be larger owing to error amplification by the exponential function.
*
*
* @param {number} x - input value
* @returns {number} function value
*
* @example
* var v = gamma( 4.0 );
* // returns 6.0
*
* @example
* var v = gamma( -1.5 );
* // returns ~2.363
*
* @example
* var v = gamma( -0.5 );
* // returns ~-3.545
*
* @example
* var v = gamma( 0.5 );
* // returns ~1.772
*
* @example
* var v = gamma( 0.0 );
* // returns Infinity
*
* @example
* var v = gamma( -0.0 );
* // returns -Infinity
*
* @example
* var v = gamma( NaN );
* // returns NaN
*/
function gamma( x: number ): number {
var sign;
var q;
var p;
var z;
if (
(isInteger( x ) && x < 0) ||
x === NINF ||
isnan( x )
) {
return NaN;
}
if ( x === 0.0 ) {
if ( isNegativeZero( x ) ) {
return NINF;
}
return PINF;
}
if ( x > 171.61447887182298 ) {
return PINF;
}
if ( x < -170.5674972726612 ) {
return 0.0;
}
q = abs( x );
if ( q > 33.0 ) {
if ( x >= 0.0 ) {
return stirlingApprox( x );
}
p = Math.floor( q );
// Check whether `x` is even...
if ( (p&1) === 0 ) {
sign = -1.0;
} else {
sign = 1.0;
}
z = q - p;
if ( z > 0.5 ) {
p += 1.0;
z = q - p;
}
z = q * sin( PI * z );
return sign * PI / ( abs(z)*stirlingApprox(q) );
}
// Reduce `x`...
z = 1.0;
while ( x >= 3.0 ) {
x -= 1.0;
z *= x;
}
while ( x < 0.0 ) {
if ( x > -1.0e-9 ) {
return smallApprox( x, z );
}
z /= x;
x += 1.0;
}
while ( x < 2.0 ) {
if ( x < 1.0e-9 ) {
return smallApprox( x, z );
}
z /= x;
x += 1.0;
}
if ( x === 2.0 ) {
return z;
}
x -= 2.0;
return z * rateval( x );
}
/**
* Tests if a double-precision floating-point numeric value is `NaN`.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is `NaN`
*
* @example
* var bool = isnan( NaN );
* // returns true
*
* @example
* var bool = isnan( 7.0 );
* // returns false
*/
function isnan( x: number ): boolean {
return ( x !== x );
}
/**
* Rounds a double-precision floating-point number toward zero.
*
* @param {number} x - input value
* @returns {number} rounded value
*
* @example
* var v = trunc( -4.2 );
* // returns -4.0
*
* @example
* var v = trunc( 9.99999 );
* // returns 9.0
*
* @example
* var v = trunc( 0.0 );
* // returns 0.0
*
* @example
* var v = trunc( -0.0 );
* // returns -0.0
*
* @example
* var v = trunc( NaN );
* // returns NaN
*
* @example
* var v = trunc( Infinity );
* // returns Infinity
*
* @example
* var v = trunc( -Infinity );
* // returns -Infinity
*/
function trunc( x: number ): number {
if ( x < 0.0 ) {
return Math.ceil( x );
}
return Math.floor( x );
}
/**
* Multiplies a double-precision floating-point number by an integer power of two.
*
* @param {number} frac - fraction
* @param {integer} exp - exponent
* @returns {number} double-precision floating-point number
*
* @example
* var x = ldexp( 0.5, 3 ); // => 0.5 * 2^3 = 0.5 * 8
* // returns 4.0
*
* @example
* var x = ldexp( 4.0, -2 ); // => 4 * 2^(-2) = 4 * (1/4)
* // returns 1.0
*
* @example
* var x = ldexp( 0.0, 20 );
* // returns 0.0
*
* @example
* var x = ldexp( -0.0, 39 );
* // returns -0.0
*
* @example
* var x = ldexp( NaN, -101 );
* // returns NaN
*
* @example
* var x = ldexp( Infinity, 11 );
* // returns Infinity
*
* @example
* var x = ldexp( -Infinity, -118 );
* // returns -Infinity
*/
function ldexp( frac: number, exp: number ): number {
var high;
var m;
if (
exp === 0 ||
frac === 0.0 || // handles +-0
isnan( frac ) ||
isInfinite( frac )
) {
return frac;
}
// Normalize the input fraction:
normalize( frac, FRAC, 1, 0 );
frac = FRAC[ 0 ];
exp += FRAC[ 1 ];
// Extract the exponent from `frac` and add it to `exp`:
exp += floatExp( frac );
// Check for underflow/overflow...
if ( exp < MIN_SUBNORMAL_EXPONENT ) {
return copysign( 0.0, frac );
}
if ( exp > MAX_EXPONENT ) {
if ( frac < 0.0 ) {
return NINF;
}
return PINF;
}
// Check for a subnormal and scale accordingly to retain precision...
if ( exp <= MAX_SUBNORMAL_EXPONENT ) {
exp += 52;
m = TWO52_INV;
} else {
m = 1.0;
}
// Split the fraction into higher and lower order words:
toWords.assign( frac, WORDS, 1, 0 );
high = WORDS[ 0 ];
// Clear the exponent bits within the higher order word:
high &= CLEAR_EXP_MASK;
// Set the exponent bits to the new exponent:
high |= ((exp+BIAS) << 20);
// Create a new floating-point number:
return m * fromWords( high, WORDS[ 1 ] );
}
/**
* Evaluates a polynomial.
*
* ## Notes
*
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
*
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
*
*
* @private
* @param {number} x - value at which to evaluate the polynomial
* @returns {number} evaluated polynomial
*/
function polyvalP( x: number ): number {
if ( x === 0.0 ) {
return 0.16666666666666602;
}
return 0.16666666666666602 + (x * (-0.0027777777777015593 + (x * (0.00006613756321437934 + (x * (-0.0000016533902205465252 + (x * 4.1381367970572385e-8))))))); // eslint-disable-line max-len
}
/**
* Computes \\(e^{r} 2^k\\) where \\(r = \mathrm{hi} - \mathrm{lo}\\) and \\(|r| \leq \ln(2)/2\\).
*
* @private
* @param {number} hi - upper bound
* @param {number} lo - lower bound
* @param {integer} k - power of 2
* @returns {number} function value
*/
function expmulti( hi: number, lo: number, k: number ): number {
var r;
var t;
var c;
var y;
r = hi - lo;
t = r * r;
c = r - ( t*polyvalP( t ) );
y = 1.0 - ( lo - ( (r*c)/(2.0-c) ) - hi);
return ldexp( y, k );
}
/**
* Evaluates the natural exponential function.
*
* ## Method
*
* 1. We reduce \\( x \\) to an \\( r \\) so that \\( |r| \leq 0.5 \cdot \ln(2) \approx 0.34658 \\). Given \\( x \\), we find an \\( r \\) and integer \\( k \\) such that
*
* ```tex
* \begin{align*}
* x &= k \cdot \ln(2) + r \\
* |r| &\leq 0.5 \cdot \ln(2)
* \end{align*}
* ```
*
* <!-- <note> -->
*
* \\( r \\) can be represented as \\( r = \mathrm{hi} - \mathrm{lo} \\) for better accuracy.
*
* <!-- </note> -->
*
* 2. We approximate of \\( e^{r} \\) by a special rational function on the interval \\(\[0,0.34658]\\):
*
* ```tex
* \begin{align*}
* R\left(r^2\right) &= r \cdot \frac{ e^{r}+1 }{ e^{r}-1 } \\
* &= 2 + \frac{r^2}{6} - \frac{r^4}{360} + \ldots
* \end{align*}
* ```
*
* We use a special Remes algorithm on \\(\[0,0.34658]\\) to generate a polynomial of degree \\(5\\) to approximate \\(R\\). The maximum error of this polynomial approximation is bounded by \\(2^{-59}\\). In other words,
*
* ```tex
* R(z) \sim 2 + P_1 z + P_2 z^2 + P_3 z^3 + P_4 z^4 + P_5 z^5
* ```
*
* where \\( z = r^2 \\) and
*
* ```tex
* \left| 2 + P_1 z + \ldots + P_5 z^5 - R(z) \right| \leq 2^{-59}
* ```
*
* <!-- <note> -->
*
* The values of \\( P_1 \\) to \\( P_5 \\) are listed in the source code.
*
* <!-- </note> -->
*
* The computation of \\( e^{r} \\) thus becomes
*
* ```tex
* \begin{align*}
* e^{r} &= 1 + \frac{2r}{R-r} \\
* &= 1 + r + \frac{r \cdot R_1(r)}{2 - R_1(r)}\ \text{for better accuracy}
* \end{align*}
* ```
*
* where
*
* ```tex
* R_1(r) = r - P_1\ r^2 + P_2\ r^4 + \ldots + P_5\ r^{10}
* ```
*
* 3. We scale back to obtain \\( e^{x} \\). From step 1, we have
*
* ```tex
* e^{x} = 2^k e^{r}
* ```
*
*
* ## Special Cases
*
* ```tex
* \begin{align*}
* e^\infty &= \infty \\
* e^{-\infty} &= 0 \\
* e^{\mathrm{NaN}} &= \mathrm{NaN} \\
* e^0 &= 1\ \mathrm{is\ exact\ for\ finite\ argument\ only}
* \end{align*}
* ```
*
* ## Notes
*
* - According to an error analysis, the error is always less than \\(1\\) ulp (unit in the last place).
*
* - For an IEEE double,
*
* - if \\(x > 7.09782712893383973096\mbox{e+}02\\), then \\(e^{x}\\) overflows
* - if \\(x < -7.45133219101941108420\mbox{e+}02\\), then \\(e^{x}\\) underflows
*
* - The hexadecimal values included in the source code are the intended ones for the used constants. Decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the intended hexadecimal values.
*
*
* @param {number} x - input value
* @returns {number} function value
*
* @example
* var v = exp( 4.0 );
* // returns ~54.5982
*
* @example
* var v = exp( -9.0 );
* // returns ~1.234e-4
*
* @example
* var v = exp( 0.0 );
* // returns 1.0
*
* @example
* var v = exp( NaN );
* // returns NaN
*/
function exp( x: number ): number {
var hi;
var lo;
var k;
if ( isnan( x ) || x === PINF ) {
return x;
}
if ( x === NINF ) {
return 0.0;
}
if ( x > OVERFLOW ) {
return PINF;
}
if ( x < UNDERFLOW ) {
return 0.0;
}
if (
x > NEG_NEARZERO &&
x < NEARZERO
) {
return 1.0 + x;
}
// Reduce and compute `r = hi - lo` for extra precision.
if ( x < 0.0 ) {
k = trunc( (LOG2_E*x) - 0.5 );
} else {
k = trunc( (LOG2_E*x) + 0.5 );
}
hi = x - (k*LN2_HI);
lo = k * LN2_LO;
return expmulti( hi, lo, k );
}
/**
* Tests if a double-precision floating-point numeric value is positive zero.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is positive zero
*
* @example
* var bool = isPositiveZero( 0.0 );
* // returns true
*
* @example
* var bool = isPositiveZero( -0.0 );
* // returns false
*/
function isPositiveZero( x: number ): boolean {
return (x === 0.0 && 1.0/x === PINF);
}
/**
* Tests if a double-precision floating-point numeric value is negative zero.
*
* @param {number} x - value to test
* @returns {boolean} boolean indicating whether the value is negative zero
*
* @example
* var bool = isNegativeZero( -0.0 );
* // returns true
*
* @example
* var bool = isNegativeZero( 0.0 );
* // returns false
*/
function isNegativeZero( x: number ): boolean {
return (x === 0.0 && 1.0/x === NINF);
}
/**
* Returns the minimum value.
*
* @param {number} [x] - first number
* @param {number} [y] - second number
* @param {...number} [args] - numbers
* @returns {number} minimum value
*
* @example
* var v = min( 3.14, 4.2 );
* // returns 3.14
*
* @example
* var v = min( 5.9, 3.14, 4.2 );
* // returns 3.14
*
* @example
* var v = min( 3.14, NaN );
* // returns NaN
*
* @example
* var v = min( +0.0, -0.0 );
* // returns -0.0
*/
function min( x: number, y: number ): number {
var len;
var m;
var v;
var i;
len = arguments.length;
if ( len === 2 ) {
if ( isnan( x ) || isnan( y ) ) {
return NaN;
}
if ( x === NINF || y === NINF ) {
return NINF;
}
if ( x === y && x === 0.0 ) {
if ( isNegativeZero( x ) ) {
return x;
}
return y;
}
if ( x < y ) {
return x;
}
return y;
}
m = PINF;
for ( i = 0; i < len; i++ ) {
v = arguments[ i ];
if ( isnan( v ) || v === NINF ) {
return v;
}
if ( v < m ) {
m = v;
} else if (
v === m &&
v === 0.0 &&
isNegativeZero( v )
) {
m = v;
}
}
return m;
}
/**
* Returns the maximum value.
*
* @param {number} [x] - first number
* @param {number} [y] - second number
* @param {...number} [args] - numbers
* @returns {number} maximum value
*
* @example
* var v = max( 3.14, 4.2 );
* // returns 4.2
*
* @example
* var v = max( 5.9, 3.14, 4.2 );
* // returns 5.9
*
* @example
* var v = max( 3.14, NaN );
* // returns NaN
*
* @example
* var v = max( +0.0, -0.0 );
* // returns +0.0
*/
function max( x: number, y: number ): number {
var len;
var m;
var v;
var i;
len = arguments.length;
if ( len === 2 ) {
if ( isnan( x ) || isnan( y ) ) {
return NaN;
}
if ( x === PINF || y === PINF ) {
return PINF;
}
if ( x === y && x === 0.0 ) {
if ( isPositiveZero( x ) ) {
return x;
}
return y;
}
if ( x > y ) {
return x;
}
return y;
}
m = NINF;
for ( i = 0; i < len; i++ ) {
v = arguments[ i ];
if ( isnan( v ) || v === PINF ) {
return v;
}
if ( v > m ) {
m = v;
} else if (
v === m &&
v === 0.0 &&
isPositiveZero( v )
) {
m = v;
}
}
return m;
}
/**
* Evaluate the n-term Chebyshev series at `x`.
*
* ## References
*
* - Broucke, Roger. 1973. "Algorithm: Ten Subroutines for the Manipulation of Chebyshev Series." _Communications of the ACM_ 16 (4). New York, NY, USA: ACM: 254–56. doi:[10.1145/362003.362037](https://doi.org/10.1145/362003.362037).
* - Fox, Leslie, and Ian Bax Parker. 1968. _Chebyshev polynomials in numerical analysis_. Oxford Mathematical Handbooks. London, United Kingdom: Oxford University Press. <https://books.google.com/books?id=F8NzsEtJCD0C>.
*
* @private
* @param {number} x - value at which the series is to be evaluated
* @returns {number} series value
*/
function dceval( x: number ): number {
var twox;
var b2;
var b1;
var b0;
var i;
if ( x < -1.1 || x > 1.1 ) {
return NaN;
}
b1 = 0.0;
b0 = 0.0;
twox = 2.0 * x;
for ( i = 0; i < LEN; i++ ) {
b2 = b1;
b1 = b0;
b0 = (twox*b1) - b2 + ALGMCS[ i ];
}
return ( b0-b2 ) * 0.5;
}
/**
* Compute the log gamma correction factor for `x >= 10`.
*
* ```tex
* \log(\gamma(x)) = \log(\sqrt{2*\Pi}) + (x-0.5) \cdot \log(x) - x \operatorname{R9LGMC}(x).
* ```
*
* @private
* @param {number} x - input value
* @returns {number} correction value
*/
function gammaCorrection( x: number ): number {
if ( x < 10.0 ) {
return NaN;
}
// Check for underflow...
if ( x >= XMAX ) {
return 0.0;
}
if ( x < XBIG ) {
return dceval( (2.0*pow( 10.0/x, 2.0 )) - 1.0 ) / x;
}
return 1.0 / (x * 12.0);
}
/**
* Evaluate the natural logarithm of the beta function.
*
* @param {NonNegativeNumber} a - first input value
* @param {NonNegativeNumber} b - second input value
* @returns {number} natural logarithm of beta function
*
* @example
* var v = betaln( 0.0, 0.0 );
* // returns Infinity
*
* @example
* var v = betaln( 1.0, 1.0 );
* // returns 0.0
*
* @example
* var v = betaln( -1.0, 2.0 );
* // returns NaN
*
* @example
* var v = betaln( 5.0, 0.2 );
* // returns ~1.218
*
* @example
* var v = betaln( 4.0, 1.0 );
* // returns ~-1.386
*
* @example
* var v = betaln( NaN, 2.0 );
* // returns NaN
*/
function betaln( a: number, b: number ): number {
var corr;
var p;
var q;
p = min( a, b );
q = max( a, b );
if ( p < 0.0 ) {
return NaN;
}
if ( p === 0.0 ) {
return PINF;
}
if ( q === PINF ) {
return NINF;
}
// Case: p and q are big
if ( p >= 10.0 ) {
corr = gammaCorrection( p ) + gammaCorrection( q ) - gammaCorrection( p+q );
return ( -0.5*ln( q ) ) + LN_SQRT_TWO_PI + corr + ( (p-0.5) * ln( p/(p+q) ) ) + ( q*log1p( -p/(p+q) ) ); // eslint-disable-line max-len
}
// Case: p is small, but q is big
if ( q >= 10.0 ) {
corr = gammaCorrection( q ) - gammaCorrection( p+q );
return gammaln( p ) + corr + p - (p*ln( p+q )) + ( (q-0.5)*log1p( -p/(p+q) ) ); // eslint-disable-line max-len
}
// Case: p and q are small
return ln( gamma( p ) * ( gamma( q ) / gamma( p+q ) ) );
}
/**
* Evaluates the probability density function (PDF) for a beta distribution with first shape parameter `alpha` and second shape parameter `beta` at a value `x`.
*
* @param {number} x - input value
* @param {PositiveNumber} alpha - first shape parameter
* @param {PositiveNumber} beta - second shape parameter
* @returns {number} evaluated PDF
*
* @example
* var y = pdf( 0.5, 1.0, 1.0 );
* // returns 1.0
*
* @example
* var y = pdf( 0.5, 2.0, 4.0 );
* // returns 1.25
*
* @example
* var y = pdf( 0.2, 2.0, 2.0 );
* // returns ~0.96
*
* @example
* var y = pdf( 0.8, 4.0, 4.0 );
* // returns ~0.573
*
* @example
* var y = pdf( -0.5, 4.0, 2.0 );
* // returns 0.0
*
* @example
* var y = pdf( 1.5, 4.0, 2.0 );
* // returns 0.0
*
* @example
* var y = pdf( 0.5, -1.0, 0.5 );
* // returns NaN
*
* @example
* var y = pdf( 0.5, 0.5, -1.0 );
* // returns NaN
*
* @example
* var y = pdf( NaN, 1.0, 1.0 );
* // returns NaN
*
* @example
* var y = pdf( 0.5, NaN, 1.0 );
* // returns NaN
*
* @example
* var y = pdf( 0.5, 1.0, NaN );
* // returns NaN
*/
export function pdf( x: number, alpha: number, beta: number ): number {
var out;
if (
isnan( x ) ||
isnan( alpha ) ||
isnan( beta ) ||
alpha <= 0.0 ||
beta <= 0.0
) {
return NaN;
}
if ( x < 0.0 || x > 1.0 ) {
// Support of the Beta distribution: [0,1]
return 0.0;
}
if ( x === 0.0 ) {
if ( alpha < 1.0 ) {
return PINF;
}
if ( alpha > 1.0 ) {
return 0.0;
}
return beta;
}
if ( x === 1.0 ) {
if ( beta < 1.0 ) {
return PINF;
}
if ( beta > 1.0 ) {
return 0.0;
}
return alpha;
}
out = ( alpha-1.0 ) * ln( x );
out += ( beta-1.0 ) * log1p( -x );
out -= betaln( alpha, beta );
return exp( out );
}
/**
* Evaluates the quantile function for a beta distribution with first shape parameter `alpha` and second shape parameter `beta` at a probability `p`.
*
* @param {Probability} p - input value
* @param {PositiveNumber} alpha - first shape parameter
* @param {PositiveNumber} beta - second shape parameter
* @returns {number} evaluated quantile function
*
* @example
* var y = quantile( 0.8, 2.0, 1.0 );
* // returns ~0.894
*
* @example
* var y = quantile( 0.5, 4.0, 2.0 );
* // returns ~0.686
*
* @example
* var y = quantile( 1.1, 1.0, 1.0 );
* // returns NaN
*
* @example
* var y = quantile( -0.2, 1.0, 1.0 );
* // returns NaN
*
* @example
* var y = quantile( NaN, 1.0, 1.0 );
* // returns NaN
*
* @example
* var y = quantile( 0.5, NaN, 1.0 );
* // returns NaN
*
* @example
* var y = quantile( 0.5, 1.0, NaN );
* // returns NaN
*
* @example
* var y = quantile( 0.5, -1.0, 1.0 );
* // returns NaN
*
* @example
* var y = quantile( 0.5, 1.0, -1.0 );
* // returns NaN
*/
export function quantile( p: number, alpha: number, beta: number ): number {
if (
isnan( p ) ||
isnan( alpha ) ||
isnan( beta ) ||
alpha <= 0.0 ||
beta <= 0.0 ||
p < 0.0 ||
p > 1.0
) {
return NaN;
}
return betaincinv( p, alpha, beta );
}
/**
* Returns the mode of a beta distribution.
*
* @param {PositiveNumber} alpha - first shape parameter
* @param {PositiveNumber} beta - second shape parameter
* @returns {PositiveNumber} mode
*
* @example
* var v = mode( 4.0, 12.0 );
* // returns ~0.214
*
* @example
* var v = mode( 8.0, 2.0 );
* // returns ~0.875
*
* @example
* var v = mode( 1.0, 1.0 );
* // returns NaN
*
* @example
* var v = mode( 2.0, 0.8 );
* // returns NaN
*
* @example
* var v = mode( -0.1, 2.0 );
* // returns NaN
*
* @example
* var v = mode( 2.0, NaN );
* // returns NaN
*
* @example
* var v = mode( NaN, 2.0 );
* // returns NaN
*/
export function mode( alpha: number, beta: number ): number {
if ( alpha <= 1.0 || beta <= 1.0 ) {
return NaN;
}
return ( alpha-1.0 ) / ( alpha+beta-2.0 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment