/**
* A math namespace - all functions can be exported from base/math.mjs.
* Also all these functions can be used with TFormula calculations
* @namespace Math
*/
/* eslint-disable curly */
/* eslint-disable @stylistic/js/comma-spacing */
/* eslint-disable @stylistic/js/no-floating-decimal */
/* eslint-disable @stylistic/js/space-in-parens */
// this can be improved later
/* eslint-disable eqeqeq */
const kMACHEP = 1.11022302462515654042363166809e-16,
kMINLOG = -708.396418532264078748994506896,
kMAXLOG = 709.782712893383973096206318587,
kMAXSTIR = 108.116855767857671821730036754,
kBig = 4.503599627370496e15,
kBiginv = 2.22044604925031308085e-16,
kSqrt2 = 1.41421356237309515,
M_PI = 3.14159265358979323846264338328;
/** @summary Polynomialeval function
* @desc calculates a value of a polynomial of the form:
* a[0]x^N+a[1]x^(N-1) + ... + a[N]
* @memberof Math */
function Polynomialeval(x, a, N) {
if (!N) return a[0];
let pom = a[0];
for (let i = 1; i <= N; ++i)
pom = pom *x + a[i];
return pom;
}
/** @summary Polynomial1eval function
* @desc calculates a value of a polynomial of the form:
* x^N+a[0]x^(N-1) + ... + a[N-1]
* @memberof Math */
function Polynomial1eval(x, a, N) {
if (!N) return a[0];
let pom = x + a[0];
for (let i = 1; i < N; ++i)
pom = pom *x + a[i];
return pom;
}
/** @summary lgam function, logarithm from gamma
* @memberof Math */
function lgam(x) {
let p, q, u, w, z;
const kMAXLGM = 2.556348e305,
LS2PI = 0.91893853320467274178,
A = [
8.11614167470508450300E-4,
-5.95061904284301438324E-4,
7.93650340457716943945E-4,
-2.77777777730099687205E-3,
8.33333333333331927722E-2
], B = [
-1.37825152569120859100E3,
-3.88016315134637840924E4,
-3.31612992738871184744E5,
-1.16237097492762307383E6,
-1.72173700820839662146E6,
-8.53555664245765465627E5
], C = [
/* 1.00000000000000000000E0, */
-3.51815701436523470549E2,
-1.70642106651881159223E4,
-2.20528590553854454839E5,
-1.13933444367982507207E6,
-2.53252307177582951285E6,
-2.01889141433532773231E6
];
if ((x >= Number.MAX_VALUE) || (x == Number.POSITIVE_INFINITY))
return Number.POSITIVE_INFINITY;
if ( x < -34.0 ) {
q = -x;
w = lgam(q);
p = Math.floor(q);
if ( p === q ) // _unur_FP_same(p,q)
return Number.POSITIVE_INFINITY;
z = q - p;
if ( z > 0.5 ) {
p += 1.0;
z = p - q;
}
z = q * Math.sin( Math.PI * z );
if ( z < 1e-300 )
return Number.POSITIVE_INFINITY;
z = Math.log(Math.PI) - Math.log( z ) - w;
return z;
}
if ( x < 13.0 ) {
z = 1.0;
p = 0.0;
u = x;
while ( u >= 3.0 ) {
p -= 1.0;
u = x + p;
z *= u;
}
while ( u < 2.0 ) {
if ( u < 1e-300 )
return Number.POSITIVE_INFINITY;
z /= u;
p += 1.0;
u = x + p;
}
if ( z < 0.0 ) {
z = -z;
}
if ( u === 2.0 )
return Math.log(z);
p -= 2.0;
x = x + p;
p = x * Polynomialeval(x, B, 5 ) / Polynomial1eval( x, C, 6);
return Math.log(z) + p;
}
if ( x > kMAXLGM )
return Number.POSITIVE_INFINITY;
q = ( x - 0.5 ) * Math.log(x) - x + LS2PI;
if ( x > 1.0e8 )
return q;
p = 1.0/(x*x);
if ( x >= 1000.0 )
q += ((7.9365079365079365079365e-4 * p
- 2.7777777777777777777778e-3) *p
+ 0.0833333333333333333333) / x;
else
q += Polynomialeval( p, A, 4 ) / x;
return q;
}
/** @summary Stirling formula for the gamma function
* @memberof Math */
function stirf(x) {
let y, w, v;
const STIR = [
7.87311395793093628397E-4,
-2.29549961613378126380E-4,
-2.68132617805781232825E-3,
3.47222221605458667310E-3,
8.33333333333482257126E-2
], SQTPI = Math.sqrt(2*Math.PI);
w = 1.0/x;
w = 1.0 + w * Polynomialeval( w, STIR, 4 );
y = Math.exp(x);
/* #define kMAXSTIR kMAXLOG/log(kMAXLOG) */
if ( x > kMAXSTIR )
{ /* Avoid overflow in pow() */
v = Math.pow( x, 0.5 * x - 0.25 );
y = v * (v / y);
}
else
{
y = Math.pow( x, x - 0.5 ) / y;
}
y = SQTPI * y * w;
return y;
}
/** @summary complementary error function
* @memberof Math */
function erfc(a) {
const erfP = [
2.46196981473530512524E-10,
5.64189564831068821977E-1,
7.46321056442269912687E0,
4.86371970985681366614E1,
1.96520832956077098242E2,
5.26445194995477358631E2,
9.34528527171957607540E2,
1.02755188689515710272E3,
5.57535335369399327526E2
], erfQ = [
1.32281951154744992508E1,
8.67072140885989742329E1,
3.54937778887819891062E2,
9.75708501743205489753E2,
1.82390916687909736289E3,
2.24633760818710981792E3,
1.65666309194161350182E3,
5.57535340817727675546E2
], erfR = [
5.64189583547755073984E-1,
1.27536670759978104416E0,
5.01905042251180477414E0,
6.16021097993053585195E0,
7.40974269950448939160E0,
2.97886665372100240670E0
], erfS = [
2.26052863220117276590E0,
9.39603524938001434673E0,
1.20489539808096656605E1,
1.70814450747565897222E1,
9.60896809063285878198E0,
3.36907645100081516050E0
];
let p,q,x,y,z;
if ( a < 0.0 )
x = -a;
else
x = a;
if ( x < 1.0 )
return 1.0 - erf(a);
z = -a * a;
if (z < -kMAXLOG)
return (a < 0) ? 2.0 : 0.0;
z = Math.exp(z);
if ( x < 8.0 ) {
p = Polynomialeval( x, erfP, 8 );
q = Polynomial1eval( x, erfQ, 8 );
} else {
p = Polynomialeval( x, erfR, 5 );
q = Polynomial1eval( x, erfS, 6 );
}
y = (z * p)/q;
if (a < 0)
y = 2.0 - y;
if (y == 0)
return (a < 0) ? 2.0 : 0.0;
return y;
}
/** @summary error function
* @memberof Math */
function erf(x) {
if (Math.abs(x) > 1.0)
return 1.0 - erfc(x);
const erfT = [
9.60497373987051638749E0,
9.00260197203842689217E1,
2.23200534594684319226E3,
7.00332514112805075473E3,
5.55923013010394962768E4
], erfU = [
3.35617141647503099647E1,
5.21357949780152679795E2,
4.59432382970980127987E3,
2.26290000613890934246E4,
4.92673942608635921086E4
],
z = x * x;
return x * Polynomialeval(z, erfT, 4) / Polynomial1eval(z, erfU, 5);
}
/** @summary lognormal_cdf_c function
* @memberof Math */
function lognormal_cdf_c(x, m, s, x0) {
if (x0 === undefined) x0 = 0;
const z = (Math.log((x-x0))-m)/(s*kSqrt2);
if (z > 1.) return 0.5*erfc(z);
else return 0.5*(1.0 - erf(z));
}
/** @summary lognormal_cdf_c function
* @memberof Math */
function lognormal_cdf(x, m, s, x0 = 0) {
const z = (Math.log((x-x0))-m)/(s*kSqrt2);
if (z < -1.) return 0.5*erfc(-z);
else return 0.5*(1.0 + erf(z));
}
/** @summary normal_cdf_c function
* @memberof Math */
function normal_cdf_c(x, sigma, x0 = 0) {
const z = (x-x0)/(sigma*kSqrt2);
if (z > 1.) return 0.5*erfc(z);
else return 0.5*(1.-erf(z));
}
/** @summary normal_cdf function
* @memberof Math */
function normal_cdf(x, sigma, x0 = 0) {
const z = (x-x0)/(sigma*kSqrt2);
if (z < -1.) return 0.5*erfc(-z);
else return 0.5*(1.0 + erf(z));
}
/** @summary log normal pdf
* @memberof Math */
function lognormal_pdf(x, m, s, x0 = 0) {
if ((x-x0) <= 0)
return 0.0;
const tmp = (Math.log((x-x0)) - m)/s;
return 1.0 / ((x-x0) * Math.abs(s) * Math.sqrt(2 * M_PI)) * Math.exp(-(tmp * tmp) /2);
}
/** @summary normal pdf
* @memberof Math */
function normal_pdf(x, sigma = 1, x0 = 0) {
const tmp = (x-x0)/sigma;
return (1.0/(Math.sqrt(2 * M_PI) * Math.abs(sigma))) * Math.exp(-tmp*tmp/2);
}
/** @summary gamma calculation
* @memberof Math */
function gamma(x) {
let p, q, z, i, sgngam = 1;
if (x >= Number.MAX_VALUE)
return x;
q = Math.abs(x);
if ( q > 33.0 )
{
if ( x < 0.0 )
{
p = Math.floor(q);
if ( p == q )
return Number.POSITIVE_INFINITY;
i = Math.round(p);
if ( (i & 1) == 0 )
sgngam = -1;
z = q - p;
if ( z > 0.5 )
{
p += 1.0;
z = q - p;
}
z = q * Math.sin(Math.PI * z);
if ( z == 0 )
{
return sgngam > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
}
z = Math.abs(z);
z = Math.PI / (z * stirf(q));
}
else
{
z = stirf(x);
}
return sgngam * z;
}
z = 1.0;
while ( x >= 3.0 ) {
x -= 1.0;
z *= x;
}
let small = false;
while (( x < 0.0 ) && !small) {
if ( x > -1.E-9 )
small = true;
else {
z /= x;
x += 1.0;
}
}
while (( x < 2.0 ) && !small) {
if ( x < 1.e-9 )
small = true;
else {
z /= x;
x += 1.0;
}
}
if (small) {
if ( x == 0 )
return Number.POSITIVE_INFINITY;
else
return z/((1.0 + 0.5772156649015329 * x) * x);
}
if ( x == 2.0 )
return z;
const P = [
1.60119522476751861407E-4,
1.19135147006586384913E-3,
1.04213797561761569935E-2,
4.76367800457137231464E-2,
2.07448227648435975150E-1,
4.94214826801497100753E-1,
9.99999999999999996796E-1
], Q = [
-2.31581873324120129819E-5,
5.39605580493303397842E-4,
-4.45641913851797240494E-3,
1.18139785222060435552E-2,
3.58236398605498653373E-2,
-2.34591795718243348568E-1,
7.14304917030273074085E-2,
1.00000000000000000320E0];
x -= 2.0;
p = Polynomialeval( x, P, 6 );
q = Polynomialeval( x, Q, 7 );
return z * p / q;
}
/** @summary ndtri function
* @memberof Math */
function ndtri(y0) {
if ( y0 <= 0.0 )
return Number.NEGATIVE_INFINITY;
if ( y0 >= 1.0 )
return Number.POSITIVE_INFINITY;
const P0 = [
-5.99633501014107895267E1,
9.80010754185999661536E1,
-5.66762857469070293439E1,
1.39312609387279679503E1,
-1.23916583867381258016E0
], Q0 = [
1.95448858338141759834E0,
4.67627912898881538453E0,
8.63602421390890590575E1,
-2.25462687854119370527E2,
2.00260212380060660359E2,
-8.20372256168333339912E1,
1.59056225126211695515E1,
-1.18331621121330003142E0
], P1 = [
4.05544892305962419923E0,
3.15251094599893866154E1,
5.71628192246421288162E1,
4.40805073893200834700E1,
1.46849561928858024014E1,
2.18663306850790267539E0,
-1.40256079171354495875E-1,
-3.50424626827848203418E-2,
-8.57456785154685413611E-4
], Q1 = [
1.57799883256466749731E1,
4.53907635128879210584E1,
4.13172038254672030440E1,
1.50425385692907503408E1,
2.50464946208309415979E0,
-1.42182922854787788574E-1,
-3.80806407691578277194E-2,
-9.33259480895457427372E-4
], P2 = [
3.23774891776946035970E0,
6.91522889068984211695E0,
3.93881025292474443415E0,
1.33303460815807542389E0,
2.01485389549179081538E-1,
1.23716634817820021358E-2,
3.01581553508235416007E-4,
2.65806974686737550832E-6,
6.23974539184983293730E-9
], Q2 = [
6.02427039364742014255E0,
3.67983563856160859403E0,
1.37702099489081330271E0,
2.16236993594496635890E-1,
1.34204006088543189037E-2,
3.28014464682127739104E-4,
2.89247864745380683936E-6,
6.79019408009981274425E-9
], s2pi = 2.50662827463100050242e0, dd = 0.13533528323661269189;
let code = 1, y = y0, x, y2, x1;
if (y > (1.0 - dd)) {
y = 1.0 - y;
code = 0;
}
if ( y > dd ) {
y = y - 0.5;
y2 = y * y;
x = y + y * (y2 * Polynomialeval( y2, P0, 4)/ Polynomial1eval( y2, Q0, 8 ));
x = x * s2pi;
return x;
}
x = Math.sqrt(-2.0 * Math.log(y));
const x0 = x - Math.log(x)/x,
z = 1.0/x;
if ( x < 8.0 )
x1 = z * Polynomialeval( z, P1, 8 )/ Polynomial1eval( z, Q1, 8 );
else
x1 = z * Polynomialeval( z, P2, 8 )/ Polynomial1eval( z, Q2, 8 );
x = x0 - x1;
if ( code != 0 )
x = -x;
return x;
}
/** @summary normal_quantile function
* @memberof Math */
function normal_quantile(z, sigma) {
return sigma * ndtri(z);
}
/** @summary normal_quantile_c function
* @memberof Math */
function normal_quantile_c(z, sigma) {
return -sigma * ndtri(z);
}
/** @summary igamc function
* @memberof Math */
function igamc(a,x) {
// LM: for negative values returns 0.0
// This is correct if a is a negative integer since Gamma(-n) = +/- inf
if (a <= 0) return 0.0;
if (x <= 0) return 1.0;
if ((x < 1.0) || (x < a))
return (1.0 - igam(a,x));
let ax = a * Math.log(x) - x - lgam(a);
if ( ax < -kMAXLOG )
return 0.0;
ax = Math.exp(ax);
/* continued fraction */
let y = 1.0 - a,
z = x + y + 1.0,
c = 0.0,
pkm2 = 1.0,
qkm2 = x,
pkm1 = x + 1.0,
qkm1 = z * x,
ans = pkm1/qkm1,
yc, r, t, pk, qk;
do {
c += 1.0;
y += 1.0;
z += 2.0;
yc = y * c;
pk = pkm1 * z - pkm2 * yc;
qk = qkm1 * z - qkm2 * yc;
if (qk)
{
r = pk/qk;
t = Math.abs( (ans - r)/r );
ans = r;
}
else
t = 1.0;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if ( Math.abs(pk) > kBig )
{
pkm2 *= kBiginv;
pkm1 *= kBiginv;
qkm2 *= kBiginv;
qkm1 *= kBiginv;
}
} while ( t > kMACHEP );
return ans * ax;
}
/** @summary igam function
* @memberof Math */
function igam(a, x) {
// LM: for negative values returns 1.0 instead of zero
// This is correct if a is a negative integer since Gamma(-n) = +/- inf
if (a <= 0) return 1.0;
if (x <= 0) return 0.0;
if ((x > 1.0) && (x > a))
return 1.0 - igamc(a,x);
/* Compute x**a * exp(-x) / gamma(a) */
let ax = a * Math.log(x) - x - lgam(a);
if ( ax < -kMAXLOG )
return 0.0;
ax = Math.exp(ax);
/* power series */
let r = a, c = 1.0, ans = 1.0;
do {
r += 1.0;
c *= x/r;
ans += c;
} while ( c/ans > kMACHEP );
return ans * ax/a;
}
/** @summary igami function
* @memberof Math */
function igami(a, y0) {
// check the domain
if (a <= 0) {
console.error(`igami : Wrong domain for parameter a = ${a} (must be > 0)`);
return 0;
}
if (y0 <= 0) {
return Number.POSITIVE_INFINITY;
}
if (y0 >= 1) {
return 0;
}
const kMAXNUM = Number.MAX_VALUE, dithresh = 5.0 * kMACHEP;
let x0 = kMAXNUM, x1 = 0, x, yl = 0, yh = 1, y, d, lgm, i, dir;
/* approximation to inverse function */
d = 1.0/(9.0*a);
y = 1.0 - d - ndtri(y0) * Math.sqrt(d);
x = a * y * y * y;
lgm = lgam(a);
for ( i=0; i<10; ++i ) {
if ( x > x0 || x < x1 )
break;
y = igamc(a,x);
if ( y < yl || y > yh )
break;
if ( y < y0 ) {
x0 = x;
yl = y;
}
else {
x1 = x;
yh = y;
}
/* compute the derivative of the function at this point */
d = (a - 1.0) * Math.log(x) - x - lgm;
if ( d < -kMAXLOG )
break;
d = -Math.exp(d);
/* compute the step to the next approximation of x */
d = (y - y0)/d;
if ( Math.abs(d/x) < kMACHEP )
return x;
x = x - d;
}
/* Resort to interval halving if Newton iteration did not converge. */
d = 0.0625;
if ( x0 == kMAXNUM ) {
if ( x <= 0.0 )
x = 1.0;
while ( x0 == kMAXNUM ) {
x = (1.0 + d) * x;
y = igamc( a, x );
if ( y < y0 ) {
x0 = x;
yl = y;
break;
}
d = d + d;
}
}
d = 0.5;
dir = 0;
for ( i=0; i<400; ++i ) {
x = x1 + d * (x0 - x1);
y = igamc( a, x );
lgm = (x0 - x1)/(x1 + x0);
if ( Math.abs(lgm) < dithresh )
break;
lgm = (y - y0)/y0;
if ( Math.abs(lgm) < dithresh )
break;
if ( x <= 0.0 )
break;
if ( y >= y0 ) {
x1 = x;
yh = y;
if ( dir < 0 ) {
dir = 0;
d = 0.5;
}
else if ( dir > 1 )
d = 0.5 * d + 0.5;
else
d = (y0 - yl)/(yh - yl);
dir += 1;
}
else {
x0 = x;
yl = y;
if ( dir > 0 ) {
dir = 0;
d = 0.5;
}
else if ( dir < -1 )
d = 0.5 * d;
else
d = (y0 - yl)/(yh - yl);
dir -= 1;
}
}
return x;
}
/** @summary landau_pdf function
* @desc LANDAU pdf : algorithm from CERNLIB G110 denlan
* same algorithm is used in GSL
* @memberof Math */
function landau_pdf(x, xi, x0 = 0) {
if (xi <= 0) return 0;
const v = (x - x0)/xi;
let u, ue, us, denlan;
const p1 = [0.4259894875,-0.1249762550, 0.03984243700, -0.006298287635, 0.001511162253],
q1 = [1.0 ,-0.3388260629, 0.09594393323, -0.01608042283, 0.003778942063],
p2 = [0.1788541609, 0.1173957403, 0.01488850518, -0.001394989411, 0.0001283617211],
q2 = [1.0 , 0.7428795082, 0.3153932961, 0.06694219548, 0.008790609714],
p3 = [0.1788544503, 0.09359161662,0.006325387654, 0.00006611667319,-0.000002031049101],
q3 = [1.0 , 0.6097809921, 0.2560616665, 0.04746722384, 0.006957301675],
p4 = [0.9874054407, 118.6723273, 849.2794360, -743.7792444, 427.0262186],
q4 = [1.0 , 106.8615961, 337.6496214, 2016.712389, 1597.063511],
p5 = [1.003675074, 167.5702434, 4789.711289, 21217.86767, -22324.94910],
q5 = [1.0 , 156.9424537, 3745.310488, 9834.698876, 66924.28357],
p6 = [1.000827619, 664.9143136, 62972.92665, 475554.6998, -5743609.109],
q6 = [1.0 , 651.4101098, 56974.73333, 165917.4725, -2815759.939],
a1 = [0.04166666667,-0.01996527778, 0.02709538966],
a2 = [-1.845568670,-4.284640743];
if (v < -5.5) {
u = Math.exp(v+1.0);
if (u < 1e-10) return 0.0;
ue = Math.exp(-1/u);
us = Math.sqrt(u);
denlan = 0.3989422803*(ue/us)*(1+(a1[0]+(a1[1]+a1[2]*u)*u)*u);
} else if (v < -1) {
u = Math.exp(-v-1);
denlan = Math.exp(-u)*Math.sqrt(u)*
(p1[0]+(p1[1]+(p1[2]+(p1[3]+p1[4]*v)*v)*v)*v)/
(q1[0]+(q1[1]+(q1[2]+(q1[3]+q1[4]*v)*v)*v)*v);
} else if (v < 1) {
denlan = (p2[0]+(p2[1]+(p2[2]+(p2[3]+p2[4]*v)*v)*v)*v)/
(q2[0]+(q2[1]+(q2[2]+(q2[3]+q2[4]*v)*v)*v)*v);
} else if (v < 5) {
denlan = (p3[0]+(p3[1]+(p3[2]+(p3[3]+p3[4]*v)*v)*v)*v)/
(q3[0]+(q3[1]+(q3[2]+(q3[3]+q3[4]*v)*v)*v)*v);
} else if (v < 12) {
u = 1/v;
denlan = u*u*(p4[0]+(p4[1]+(p4[2]+(p4[3]+p4[4]*u)*u)*u)*u)/
(q4[0]+(q4[1]+(q4[2]+(q4[3]+q4[4]*u)*u)*u)*u);
} else if (v < 50) {
u = 1/v;
denlan = u*u*(p5[0]+(p5[1]+(p5[2]+(p5[3]+p5[4]*u)*u)*u)*u)/
(q5[0]+(q5[1]+(q5[2]+(q5[3]+q5[4]*u)*u)*u)*u);
} else if (v < 300) {
u = 1/v;
denlan = u*u*(p6[0]+(p6[1]+(p6[2]+(p6[3]+p6[4]*u)*u)*u)*u)/
(q6[0]+(q6[1]+(q6[2]+(q6[3]+q6[4]*u)*u)*u)*u);
} else {
u = 1/(v-v*Math.log(v)/(v+1));
denlan = u*u*(1+(a2[0]+a2[1]*u)*u);
}
return denlan/xi;
}
/** @summary Landau function
* @memberof Math */
function Landau(x, mpv, sigma, norm) {
if (sigma <= 0) return 0;
const den = landau_pdf((x - mpv) / sigma, 1, 0);
if (!norm) return den;
return den/sigma;
}
/** @summary inc_gamma_c
* @memberof Math */
function inc_gamma_c(a,x) {
return igamc(a,x);
}
/** @summary inc_gamma
* @memberof Math */
function inc_gamma(a,x) {
return igam(a,x);
}
/** @summary lgamma
* @memberof Math */
function lgamma(z) {
return lgam(z);
}
/** @summary Probability density function of the beta distribution.
* @memberof Math */
function beta_pdf(x, a, b) {
if (x < 0 || x > 1.0) return 0;
if (x == 0 ) {
if (a < 1) return Number.POSITIVE_INFINITY;
else if (a > 1) return 0;
else if ( a == 1) return b; // to avoid a nan from log(0)*0
}
if (x == 1 ) {
if (b < 1) return Number.POSITIVE_INFINITY;
else if (b > 1) return 0;
else if ( b == 1) return a; // to avoid a nan from log(0)*0
}
return Math.exp(lgamma(a + b) - lgamma(a) - lgamma(b) +
Math.log(x) * (a -1.) + Math.log1p(-x) * (b - 1.));
}
/** @summary beta
* @memberof Math */
function beta(x,y) {
return Math.exp(lgamma(x)+lgamma(y)-lgamma(x+y));
}
/** @summary chisquared_cdf_c
* @memberof Math */
function chisquared_cdf_c(x,r,x0 = 0) {
return inc_gamma_c(0.5 * r, 0.5*(x-x0));
}
/** @summary Continued fraction expansion #1 for incomplete beta integral
* @memberof Math */
function incbcf(a,b,x) {
let xk, pk, pkm1, pkm2, qk, qkm1, qkm2,
k1, k2, k3, k4, k5, k6, k7, k8,
r, t, ans, n;
const thresh = 3.0 * kMACHEP;
k1 = a;
k2 = a + b;
k3 = a;
k4 = a + 1.0;
k5 = 1.0;
k6 = b - 1.0;
k7 = k4;
k8 = a + 2.0;
pkm2 = 0.0;
qkm2 = 1.0;
pkm1 = 1.0;
qkm1 = 1.0;
ans = 1.0;
r = 1.0;
n = 0;
do {
xk = -( x * k1 * k2 )/( k3 * k4 );
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
xk = ( x * k5 * k6 )/( k7 * k8 );
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if ( qk !=0 )
r = pk/qk;
if ( r != 0 )
{
t = Math.abs( (ans - r)/r );
ans = r;
}
else
t = 1.0;
if ( t < thresh )
break; // goto cdone;
k1 += 1.0;
k2 += 1.0;
k3 += 2.0;
k4 += 2.0;
k5 += 1.0;
k6 -= 1.0;
k7 += 2.0;
k8 += 2.0;
if ((Math.abs(qk) + Math.abs(pk)) > kBig) {
pkm2 *= kBiginv;
pkm1 *= kBiginv;
qkm2 *= kBiginv;
qkm1 *= kBiginv;
}
if ((Math.abs(qk) < kBiginv) || (Math.abs(pk) < kBiginv)) {
pkm2 *= kBig;
pkm1 *= kBig;
qkm2 *= kBig;
qkm1 *= kBig;
}
}
while ( ++n < 300 );
// cdone:
return ans;
}
/** @summary Continued fraction expansion #2 for incomplete beta integral
* @memberof Math */
function incbd(a,b,x) {
const z = x / (1.0-x),
thresh = 3.0 * kMACHEP;
let xk, pk, pkm1, pkm2, qk, qkm1, qkm2,
k1, k2, k3, k4, k5, k6, k7, k8,
r, t, ans, n;
k1 = a;
k2 = b - 1.0;
k3 = a;
k4 = a + 1.0;
k5 = 1.0;
k6 = a + b;
k7 = a + 1.0;
k8 = a + 2.0;
pkm2 = 0.0;
qkm2 = 1.0;
pkm1 = 1.0;
qkm1 = 1.0;
ans = 1.0;
r = 1.0;
n = 0;
do {
xk = -( z * k1 * k2 )/( k3 * k4 );
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
xk = ( z * k5 * k6 )/( k7 * k8 );
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if ( qk != 0 )
r = pk/qk;
if ( r != 0 )
{
t = Math.abs( (ans - r)/r );
ans = r;
}
else
t = 1.0;
if ( t < thresh )
break; // goto cdone;
k1 += 1.0;
k2 -= 1.0;
k3 += 2.0;
k4 += 2.0;
k5 += 1.0;
k6 += 1.0;
k7 += 2.0;
k8 += 2.0;
if ((Math.abs(qk) + Math.abs(pk)) > kBig) {
pkm2 *= kBiginv;
pkm1 *= kBiginv;
qkm2 *= kBiginv;
qkm1 *= kBiginv;
}
if ((Math.abs(qk) < kBiginv) || (Math.abs(pk) < kBiginv)) {
pkm2 *= kBig;
pkm1 *= kBig;
qkm2 *= kBig;
qkm1 *= kBig;
}
}
while ( ++n < 300 );
// cdone:
return ans;
}
/** @summary ROOT::Math::Cephes::pseries
* @memberof Math */
function pseries(a,b,x) {
let s, t, u, v, n;
const ai = 1.0 / a;
u = (1.0 - b) * x;
v = u / (a + 1.0);
const t1 = v;
t = u;
n = 2.0;
s = 0.0;
const z = kMACHEP * ai;
while ( Math.abs(v) > z ) {
u = (n - b) * x / n;
t *= u;
v = t / (a + n);
s += v;
n += 1.0;
}
s += t1;
s += ai;
u = a * Math.log(x);
if ( (a+b) < kMAXSTIR && Math.abs(u) < kMAXLOG )
{
t = gamma(a+b) / (gamma(a)*gamma(b));
s = s * t * Math.pow(x,a);
}
else
{
t = lgam(a+b) - lgam(a) - lgam(b) + u + Math.log(s);
if ( t < kMINLOG )
s = 0.0;
else
s = Math.exp(t);
}
return s;
}
/** @summary ROOT::Math::Cephes::incbet
* @memberof Math */
function incbet(aa,bb,xx) {
let a, b, t, x, xc, w, y, flag;
if ( aa <= 0.0 || bb <= 0.0 )
return 0.0;
// LM: changed: for X > 1 return 1.
if (xx <= 0.0) return 0.0;
if ( xx >= 1.0) return 1.0;
flag = 0;
/* - to test if that way is better for large b/ (comment out from Cephes version)
if ( (bb * xx) <= 1.0 && xx <= 0.95)
{
t = pseries(aa, bb, xx);
goto done;
}
**/
w = 1.0 - xx;
/* Reverse a and b if x is greater than the mean. */
/* aa,bb > 1 -> sharp rise at x=aa/(aa+bb) */
if (xx > (aa/(aa+bb)))
{
flag = 1;
a = bb;
b = aa;
xc = xx;
x = w;
}
else
{
a = aa;
b = bb;
xc = w;
x = xx;
}
if ( flag == 1 && (b * x) <= 1.0 && x <= 0.95) {
t = pseries(a, b, x);
// goto done;
} else {
/* Choose expansion for better convergence. */
y = x * (a+b-2.0) - (a-1.0);
if ( y < 0.0 )
w = incbcf( a, b, x );
else
w = incbd( a, b, x ) / xc;
/* Multiply w by the factor
a b _ _ _
x (1-x) | (a+b) / (a | (a) | (b)) . */
y = a * Math.log(x);
t = b * Math.log(xc);
if ( (a+b) < kMAXSTIR && Math.abs(y) < kMAXLOG && Math.abs(t) < kMAXLOG )
{
t = Math.pow(xc,b);
t *= Math.pow(x,a);
t /= a;
t *= w;
t *= gamma(a+b) / (gamma(a) * gamma(b));
// goto done;
} else {
/* Resort to logarithms. */
y += t + lgam(a+b) - lgam(a) - lgam(b);
y += Math.log(w/a);
if ( y < kMINLOG )
t = 0.0;
else
t = Math.exp(y);
}
}
// done:
if (flag == 1) {
if ( t <= kMACHEP )
t = 1.0 - kMACHEP;
else
t = 1.0 - t;
}
return t;
}
/** @summary copy of ROOT::Math::Cephes::incbi
* @memberof Math */
function incbi(aa,bb,yy0) {
let a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt,
i, rflg, dir, nflg, ihalve = true;
// check the domain
if (aa <= 0) {
// MATH_ERROR_MSG('Cephes::incbi','Wrong domain for parameter a (must be > 0)');
return 0;
}
if (bb <= 0) {
// MATH_ERROR_MSG('Cephes::incbi','Wrong domain for parameter b (must be > 0)');
return 0;
}
const process_done = () => {
if ( rflg ) {
if ( x <= kMACHEP )
x = 1.0 - kMACHEP;
else
x = 1.0 - x;
}
return x;
};
i = 0;
if ( yy0 <= 0 )
return 0.0;
if ( yy0 >= 1.0 )
return 1.0;
x0 = 0.0;
yl = 0.0;
x1 = 1.0;
yh = 1.0;
nflg = 0;
if ( aa <= 1.0 || bb <= 1.0 )
{
dithresh = 1.0e-6;
rflg = 0;
a = aa;
b = bb;
y0 = yy0;
x = a/(a+b);
y = incbet( a, b, x );
// goto ihalve; // will start
}
else
{
dithresh = 1.0e-4;
/* approximation to inverse function */
yp = -ndtri(yy0);
if ( yy0 > 0.5 )
{
rflg = 1;
a = bb;
b = aa;
y0 = 1.0 - yy0;
yp = -yp;
}
else
{
rflg = 0;
a = aa;
b = bb;
y0 = yy0;
}
lgm = (yp * yp - 3.0)/6.0;
x = 2.0/(1.0/(2.0*a-1.0) + 1.0/(2.0*b-1.0));
d = yp * Math.sqrt( x + lgm ) / x
- (1.0/(2.0*b-1.0) - 1.0/(2.0*a-1.0))
* (lgm + 5.0/6.0 - 2.0/(3.0*x));
d = 2.0 * d;
if ( d < kMINLOG ) {
// x = 1.0;
// goto under;
x = 0.0;
return process_done();
}
x = a/(a + b * Math.exp(d));
y = incbet( a, b, x );
yp = (y - y0)/y0;
if ( Math.abs(yp) < 0.2 )
ihalve = false; // instead goto newt; exclude ihalve for the first time
}
let mainloop = 1000;
// endless loop until coverage
while (mainloop-- > 0) {
/* Resort to interval halving if not close enough. */
// ihalve:
if (ihalve) {
dir = 0;
di = 0.5;
for ( i=0; i<100; i++ )
{
if ( i != 0 )
{
x = x0 + di * (x1 - x0);
if ( x == 1.0 )
x = 1.0 - kMACHEP;
if ( x == 0.0 )
{
di = 0.5;
x = x0 + di * (x1 - x0);
if ( x == 0.0 )
return process_done(); // goto under;
}
y = incbet( a, b, x );
yp = (x1 - x0)/(x1 + x0);
if ( Math.abs(yp) < dithresh )
break; // goto newt;
yp = (y-y0)/y0;
if ( Math.abs(yp) < dithresh )
break; // goto newt;
}
if ( y < y0 )
{
x0 = x;
yl = y;
if ( dir < 0 )
{
dir = 0;
di = 0.5;
}
else if ( dir > 3 )
di = 1.0 - (1.0 - di) * (1.0 - di);
else if ( dir > 1 )
di = 0.5 * di + 0.5;
else
di = (y0 - y)/(yh - yl);
dir += 1;
if ( x0 > 0.75 )
{
if ( rflg == 1 )
{
rflg = 0;
a = aa;
b = bb;
y0 = yy0;
}
else
{
rflg = 1;
a = bb;
b = aa;
y0 = 1.0 - yy0;
}
x = 1.0 - x;
y = incbet( a, b, x );
x0 = 0.0;
yl = 0.0;
x1 = 1.0;
yh = 1.0;
continue; // goto ihalve;
}
}
else
{
x1 = x;
if ( rflg == 1 && x1 < kMACHEP )
{
x = 0.0;
return process_done(); // goto done;
}
yh = y;
if ( dir > 0 )
{
dir = 0;
di = 0.5;
}
else if ( dir < -3 )
di = di * di;
else if ( dir < -1 )
di = 0.5 * di;
else
di = (y - y0)/(yh - yl);
dir -= 1;
}
}
// math_error( 'incbi', PLOSS );
if ( x0 >= 1.0 ) {
x = 1.0 - kMACHEP;
return process_done(); // goto done;
}
if ( x <= 0.0 ) {
// math_error( 'incbi', UNDERFLOW );
x = 0.0;
return process_done(); // goto done;
}
break; // if here, break ihalve
} // end of ihalve
ihalve = true; // enter loop next time
// newt:
if ( nflg )
return process_done(); // goto done;
nflg = 1;
lgm = lgam(a+b) - lgam(a) - lgam(b);
for ( i=0; i<8; i++ )
{
/* Compute the function at this point. */
if ( i != 0 )
y = incbet(a,b,x);
if ( y < yl )
{
x = x0;
y = yl;
}
else if ( y > yh )
{
x = x1;
y = yh;
}
else if ( y < y0 )
{
x0 = x;
yl = y;
}
else
{
x1 = x;
yh = y;
}
if ( x == 1.0 || x == 0.0 )
break;
/* Compute the derivative of the function at this point. */
d = (a - 1.0) * Math.log(x) + (b - 1.0) * Math.log(1.0-x) + lgm;
if ( d < kMINLOG )
return process_done(); // goto done;
if ( d > kMAXLOG )
break;
d = Math.exp(d);
/* Compute the step to the next approximation of x. */
d = (y - y0)/d;
xt = x - d;
if ( xt <= x0 )
{
y = (x - x0) / (x1 - x0);
xt = x0 + 0.5 * y * (x - x0);
if ( xt <= 0.0 )
break;
}
if ( xt >= x1 )
{
y = (x1 - x) / (x1 - x0);
xt = x1 - 0.5 * y * (x1 - x);
if ( xt >= 1.0 )
break;
}
x = xt;
if ( Math.abs(d/x) < 128.0 * kMACHEP )
return process_done(); // goto done;
}
/* Did not converge. */
dithresh = 256.0 * kMACHEP;
} // endless loop instead of // goto ihalve;
// done:
return process_done();
}
/** @summary Calculates the normalized (regularized) incomplete beta function.
* @memberof Math */
function inc_beta(x,a,b) {
return incbet(a,b,x);
}
const BetaIncomplete = inc_beta;
/** @summary ROOT::Math::beta_quantile
* @memberof Math */
function beta_quantile(z,a,b) {
return incbi(a,b,z);
}
/** @summary Complement of the cumulative distribution function of the beta distribution.
* @memberof Math */
function beta_cdf_c(x,a,b) {
return inc_beta(1-x, b, a);
}
/** @summary chisquared_cdf
* @memberof Math */
function chisquared_cdf(x,r,x0=0) {
return inc_gamma(0.5 * r, 0.5*(x-x0));
}
/** @summary gamma_quantile_c function
* @memberof Math */
function gamma_quantile_c(z, alpha, theta) {
return theta * igami( alpha, z);
}
/** @summary gamma_quantile function
* @memberof Math */
function gamma_quantile(z, alpha, theta) {
return theta * igami( alpha, 1.- z);
}
/** @summary breitwigner_cdf_c function
* @memberof Math */
function breitwigner_cdf_c(x,gamma, x0 = 0) {
return 0.5 - Math.atan(2.0 * (x-x0) / gamma) / M_PI;
}
/** @summary breitwigner_cdf function
* @memberof Math */
function breitwigner_cdf(x, gamma, x0 = 0) {
return 0.5 + Math.atan(2.0 * (x-x0) / gamma) / M_PI;
}
/** @summary cauchy_cdf_c function
* @memberof Math */
function cauchy_cdf_c(x, b, x0 = 0) {
return 0.5 - Math.atan( (x-x0) / b) / M_PI;
}
/** @summary cauchy_cdf function
* @memberof Math */
function cauchy_cdf(x, b, x0 = 0) {
return 0.5 + Math.atan( (x-x0) / b) / M_PI;
}
/** @summary cauchy_pdf function
* @memberof Math */
function cauchy_pdf(x, b = 1, x0 = 0) {
return b/(M_PI * ((x-x0)*(x-x0) + b*b));
}
/** @summary gaussian_pdf function
* @memberof Math */
function gaussian_pdf(x, sigma = 1, x0 = 0) {
const tmp = (x-x0)/sigma;
return (1.0/(Math.sqrt(2 * M_PI) * Math.abs(sigma))) * Math.exp(-tmp*tmp/2);
}
/** @summary gamma_pdf function
* @memberof Math */
function gamma_pdf(x, alpha, theta, x0 = 0) {
if ((x - x0) < 0) {
return 0.0;
} else if ((x - x0) == 0) {
return (alpha == 1) ? 1.0 / theta : 0;
} else if (alpha == 1) {
return Math.exp(-(x - x0) / theta) / theta;
}
return Math.exp((alpha - 1) * Math.log((x - x0) / theta) - (x - x0) / theta - lgamma(alpha)) / theta;
}
/** @summary tdistribution_cdf_c function
* @memberof Math */
function tdistribution_cdf_c(x, r, x0 = 0) {
const p = x - x0,
sign = (p > 0) ? 1. : -1;
return .5 - .5*inc_beta(p*p/(r + p*p), .5, .5*r)*sign;
}
/** @summary tdistribution_cdf function
* @memberof Math */
function tdistribution_cdf(x, r, x0 = 0) {
const p = x - x0,
sign = (p > 0) ? 1. : -1;
return .5 + .5*inc_beta(p*p/(r + p*p), .5, .5*r)*sign;
}
/** @summary tdistribution_pdf function
* @memberof Math */
function tdistribution_pdf(x, r, x0 = 0) {
return (Math.exp(lgamma((r + 1.0)/2.0) - lgamma(r/2.0)) / Math.sqrt(M_PI * r))
* Math.pow((1.0 + (x-x0)*(x-x0)/r), -(r + 1.0)/2.0);
}
/** @summary exponential_cdf_c function
* @memberof Math */
function exponential_cdf_c(x, lambda, x0 = 0) {
return ((x-x0) < 0) ? 1.0 : Math.exp(-lambda * (x-x0));
}
/** @summary exponential_cdf function
* @memberof Math */
function exponential_cdf(x, lambda, x0 = 0) {
return ((x-x0) < 0) ? 0.0 : -Math.expm1(-lambda * (x-x0));
}
/** @summary chisquared_pdf
* @memberof Math */
function chisquared_pdf(x, r, x0 = 0) {
if ((x-x0) < 0) return 0.0;
const a = r/2 -1.;
// let return inf for case x = x0 and treat special case of r = 2 otherwise will return nan
if (x == x0 && a == 0) return 0.5;
return Math.exp((r/2 - 1) * Math.log((x-x0)/2) - (x-x0)/2 - lgamma(r/2))/2;
}
/** @summary Probability density function of the F-distribution.
* @memberof Math */
function fdistribution_pdf(x, n, m, x0 = 0) {
if (n < 0 || m < 0)
return Number.NaN;
if ((x-x0) < 0)
return 0.0;
return Math.exp((n/2) * Math.log(n) + (m/2) * Math.log(m) + lgamma((n+m)/2) - lgamma(n/2) - lgamma(m/2)
+ (n/2 -1) * Math.log(x-x0) - ((n+m)/2) * Math.log(m + n*(x-x0)));
}
/** @summary fdistribution_cdf_c function
* @memberof Math */
function fdistribution_cdf_c(x, n, m, x0 = 0) {
if (n < 0 || m < 0) return Number.NaN;
const z = m / (m + n * (x - x0));
// fox z->1 and large a and b IB looses precision use complement function
if (z > 0.9 && n > 1 && m > 1) return 1. - fdistribution_cdf(x, n, m, x0);
// for the complement use the fact that IB(x,a,b) = 1. - IB(1-x,b,a)
return inc_beta(m / (m + n * (x - x0)), .5 * m, .5 * n);
}
/** @summary fdistribution_cdf function
* @memberof Math */
function fdistribution_cdf(x, n, m, x0 = 0) {
if (n < 0 || m < 0) return Number.NaN;
const z = n * (x - x0) / (m + n * (x - x0));
// fox z->1 and large a and b IB looses precision use complement function
if (z > 0.9 && n > 1 && m > 1)
return 1. - fdistribution_cdf_c(x, n, m, x0);
return inc_beta(z, .5 * n, .5 * m);
}
/** @summary Prob function
* @memberof Math */
function Prob(chi2, ndf) {
if (ndf <= 0) return 0; // Set CL to zero in case ndf <= 0
if (chi2 <= 0) {
if (chi2 < 0) return 0;
else return 1;
}
return chisquared_cdf_c(chi2,ndf,0);
}
/** @summary Gaus function
* @memberof Math */
function Gaus(x, mean, sigma, norm) {
if (!sigma) return 1e30;
const arg = (x - mean) / sigma;
if (arg < -39 || arg > 39) return 0;
const res = Math.exp(-0.5*arg*arg);
return norm ? res/(2.50662827463100024*sigma) : res; // sqrt(2*Pi)=2.50662827463100024
}
/** @summary BreitWigner function
* @memberof Math */
function BreitWigner(x, mean, gamma) {
return gamma/((x-mean)*(x-mean) + gamma*gamma/4) / 2 / Math.PI;
}
/** @summary Calculates Beta-function Gamma(p)*Gamma(q)/Gamma(p+q).
* @memberof Math */
function Beta(x,y) {
return Math.exp(lgamma(x) + lgamma(y) - lgamma(x+y));
}
/** @summary GammaDist function
* @memberof Math */
function GammaDist(x, gamma, mu = 0, beta = 1) {
if ((x < mu) || (gamma <= 0) || (beta <= 0)) return 0;
return gamma_pdf(x, gamma, beta, mu);
}
/** @summary probability density function of Laplace distribution
* @memberof Math */
function LaplaceDist(x, alpha = 0, beta = 1) {
return Math.exp(-Math.abs((x-alpha)/beta)) / (2.*beta);
}
/** @summary distribution function of Laplace distribution
* @memberof Math */
function LaplaceDistI(x, alpha = 0, beta = 1) {
return (x <= alpha) ? 0.5*Math.exp(-Math.abs((x-alpha)/beta)) : 1 - 0.5*Math.exp(-Math.abs((x-alpha)/beta));
}
/** @summary density function for Student's t- distribution
* @memberof Math */
function Student(T, ndf) {
if (ndf < 1) return 0;
const r = ndf,
rh = 0.5*r,
rh1 = rh + 0.5,
denom = Math.sqrt(r*Math.PI)*gamma(rh)*Math.pow(1+T*T/r, rh1);
return gamma(rh1)/denom;
}
/** @summary cumulative distribution function of Student's
* @memberof Math */
function StudentI(T, ndf) {
const r = ndf;
return (T > 0)
? (1 - 0.5*BetaIncomplete((r/(r + T*T)), r*0.5, 0.5))
: 0.5*BetaIncomplete((r/(r + T*T)), r*0.5, 0.5);
}
/** @summary LogNormal function
* @memberof Math */
function LogNormal(x, sigma, theta = 0, m = 1) {
if ((x < theta) || (sigma <= 0) || (m <= 0)) return 0;
return lognormal_pdf(x, Math.log(m), sigma, theta);
}
/** @summary Computes the probability density function of the Beta distribution
* @memberof Math */
function BetaDist(x, p, q) {
if ((x < 0) || (x > 1) || (p <= 0) || (q <= 0))
return 0;
const beta = Beta(p, q);
return Math.pow(x, p-1) * Math.pow(1-x, q-1) / beta;
}
/** @summary Computes the distribution function of the Beta distribution.
* @memberof Math */
function BetaDistI(x, p, q) {
if ((x < 0) || (x > 1) || (p <= 0) || (q <= 0)) return 0;
return BetaIncomplete(x, p, q);
}
/** @summary gaus function for TFormula
* @memberof Math */
function gaus(f, x, i) {
return f.GetParValue(i+0) * Math.exp(-0.5 * Math.pow((x-f.GetParValue(i+1)) / f.GetParValue(i+2), 2));
}
/** @summary gausn function for TFormula
* @memberof Math */
function gausn(f, x, i) {
return gaus(f, x, i)/(Math.sqrt(2 * Math.PI) * f.GetParValue(i+2));
}
/** @summary gausxy function for TFormula
* @memberof Math */
function gausxy(f, x, y, i) {
return f.GetParValue(i+0) * Math.exp(-0.5 * Math.pow((x-f.GetParValue(i+1)) / f.GetParValue(i+2), 2))
* Math.exp(-0.5 * Math.pow((y-f.GetParValue(i+3)) / f.GetParValue(i+4), 2));
}
/** @summary expo function for TFormula
* @memberof Math */
function expo(f, x, i) {
return Math.exp(f.GetParValue(i+0) + f.GetParValue(i+1) * x);
}
/** @summary landau function for TFormula
* @memberof Math */
function landau(f, x, i) {
return Landau(x, f.GetParValue(i+1),f.GetParValue(i+2), false);
}
/** @summary landaun function for TFormula
* @memberof Math */
function landaun(f, x, i) {
return Landau(x, f.GetParValue(i+1),f.GetParValue(i+2), true);
}
/** @summary Crystal ball function
* @memberof Math */
function crystalball_function(x, alpha, n, sigma, mean = 0) {
if (sigma < 0.) return 0.;
let z = (x - mean)/sigma;
if (alpha < 0) z = -z;
const abs_alpha = Math.abs(alpha);
if (z > -abs_alpha)
return Math.exp(-0.5 * z * z);
const nDivAlpha = n/abs_alpha,
AA = Math.exp(-0.5*abs_alpha*abs_alpha),
B = nDivAlpha - abs_alpha,
arg = nDivAlpha/(B-z);
return AA * Math.pow(arg,n);
}
/** @summary pdf definition of the crystal_ball which is defined only for n > 1 otherwise integral is diverging
* @memberof Math */
function crystalball_pdf(x, alpha, n, sigma, mean = 0) {
if (sigma < 0.) return 0.;
if (n <= 1) return Number.NaN; // pdf is not normalized for n <=1
const abs_alpha = Math.abs(alpha),
C = n/abs_alpha * 1./(n-1.) * Math.exp(-alpha*alpha/2.),
D = Math.sqrt(M_PI/2.)*(1.+erf(abs_alpha/Math.sqrt(2.))),
N = 1./(sigma*(C+D));
return N * crystalball_function(x,alpha,n,sigma,mean);
}
/** @summary compute the integral of the crystal ball function
* @memberof Math */
function crystalball_integral(x, alpha, n, sigma, mean = 0) {
if (sigma == 0) return 0;
if (alpha == 0) return 0.;
const useLog = (n == 1.0),
abs_alpha = Math.abs(alpha);
let z = (x-mean)/sigma, intgaus = 0., intpow = 0.;
if (alpha < 0 ) z = -z;
const sqrtpiover2 = Math.sqrt(M_PI/2.),
sqrt2pi = Math.sqrt( 2.*M_PI),
oneoversqrt2 = 1./Math.sqrt(2.);
if (z <= -abs_alpha) {
const A = Math.pow(n/abs_alpha,n) * Math.exp(-0.5 * alpha*alpha),
B = n/abs_alpha - abs_alpha;
if (!useLog) {
const C = (n/abs_alpha) * (1./(n-1)) * Math.exp(-alpha*alpha/2.);
intpow = C - A /(n-1.) * Math.pow(B-z,-n+1);
}
else {
// for n=1 the primitive of 1/x is log(x)
intpow = -A * Math.log( n / abs_alpha ) + A * Math.log(B - z);
}
intgaus = sqrtpiover2*(1. + erf(abs_alpha*oneoversqrt2));
} else {
intgaus = normal_cdf_c(z, 1);
intgaus *= sqrt2pi;
intpow = 0;
}
return sigma * (intgaus + intpow);
}
/** @summary crystalball_cdf function
* @memberof Math */
function crystalball_cdf(x, alpha, n, sigma, mean = 0) {
if (n <= 1.)
return Number.NaN;
const abs_alpha = Math.abs(alpha),
C = n/abs_alpha * 1./(n-1.) * Math.exp(-alpha*alpha/2.),
D = Math.sqrt(M_PI/2.)*(1. + erf(abs_alpha/Math.sqrt(2.))),
totIntegral = sigma*(C+D),
integral = crystalball_integral(x,alpha,n,sigma,mean);
return (alpha > 0) ? 1. - integral/totIntegral : integral/totIntegral;
}
/** @summary crystalball_cdf_c function
* @memberof Math */
function crystalball_cdf_c(x, alpha, n, sigma, mean = 0) {
if (n <= 1.)
return Number.NaN;
const abs_alpha = Math.abs(alpha),
C = n/abs_alpha * 1./(n-1.) * Math.exp(-alpha*alpha/2.),
D = Math.sqrt(M_PI/2.)*(1. + erf(abs_alpha/Math.sqrt(2.))),
totIntegral = sigma*(C+D),
integral = crystalball_integral(x,alpha,n,sigma,mean);
return (alpha > 0) ? integral/totIntegral : 1. - (integral/totIntegral);
}
/** @summary ChebyshevN function
* @memberof Math */
function ChebyshevN(n, x, c) {
let d1 = 0.0, d2 = 0.0;
const y2 = 2.0 * x;
for (let i = n; i >= 1; i--) {
const temp = d1;
d1 = y2 * d1 - d2 + c[i];
d2 = temp;
}
return x * d1 - d2 + c[0];
}
/** @summary Chebyshev0 function
* @memberof Math */
function Chebyshev0(_x, c0) {
return c0;
}
/** @summary Chebyshev1 function
* @memberof Math */
function Chebyshev1(x, c0, c1) {
return c0 + c1*x;
}
/** @summary Chebyshev2 function
* @memberof Math */
function Chebyshev2(x, c0, c1, c2) {
return c0 + c1*x + c2*(2.0*x*x - 1.0);
}
/** @summary Chebyshev3 function
* @memberof Math */
function Chebyshev3(x, ...args) {
return ChebyshevN(3, x, args);
}
/** @summary Chebyshev4 function
* @memberof Math */
function Chebyshev4(x, ...args) {
return ChebyshevN(4, x, args);
}
/** @summary Chebyshev5 function
* @memberof Math */
function Chebyshev5(x, ...args) {
return ChebyshevN(5, x, args);
}
/** @summary Chebyshev6 function
* @memberof Math */
function Chebyshev6(x, ...args) {
return ChebyshevN(6, x, args);
}
/** @summary Chebyshev7 function
* @memberof Math */
function Chebyshev7(x, ...args) {
return ChebyshevN(7, x, args);
}
/** @summary Chebyshev8 function
* @memberof Math */
function Chebyshev8(x, ...args) {
return ChebyshevN(8, x, args);
}
/** @summary Chebyshev9 function
* @memberof Math */
function Chebyshev9(x, ...args) {
return ChebyshevN(9, x, args);
}
/** @summary Chebyshev10 function
* @memberof Math */
function Chebyshev10(x, ...args) {
return ChebyshevN(10, x, args);
}
// =========================================================================
/** @summary Calculate ClopperPearson
* @memberof Math */
function eff_ClopperPearson(total,passed,level,bUpper) {
const alpha = (1.0 - level) / 2;
if (bUpper)
return ((passed == total) ? 1.0 : beta_quantile(1 - alpha,passed + 1,total-passed));
return ((passed == 0) ? 0.0 : beta_quantile(alpha,passed,total-passed+1.0));
}
/** @summary Calculate normal
* @memberof Math */
function eff_Normal(total,passed,level,bUpper) {
if (total == 0) return bUpper ? 1 : 0;
const alpha = (1.0 - level)/2,
average = passed / total,
sigma = Math.sqrt(average * (1 - average) / total),
delta = normal_quantile(1 - alpha, sigma);
if (bUpper)
return ((average + delta) > 1) ? 1.0 : (average + delta);
return ((average - delta) < 0) ? 0.0 : (average - delta);
}
/** @summary Calculates the boundaries for the frequentist Wilson interval
* @memberof Math */
function eff_Wilson(total,passed,level,bUpper) {
const alpha = (1.0 - level)/2;
if (total == 0) return bUpper ? 1 : 0;
const average = passed / total,
kappa = normal_quantile(1 - alpha,1),
mode = (passed + 0.5 * kappa * kappa) / (total + kappa * kappa),
delta = kappa / (total + kappa*kappa) * Math.sqrt(total * average * (1 - average) + kappa * kappa / 4);
if (bUpper)
return ((mode + delta) > 1) ? 1.0 : (mode + delta);
return ((mode - delta) < 0) ? 0.0 : (mode - delta);
}
/** @summary Calculates the boundaries for the frequentist Agresti-Coull interval
* @memberof Math */
function eff_AgrestiCoull(total,passed,level,bUpper) {
const alpha = (1.0 - level)/2,
kappa = normal_quantile(1 - alpha,1),
mode = (passed + 0.5 * kappa * kappa) / (total + kappa * kappa),
delta = kappa * Math.sqrt(mode * (1 - mode) / (total + kappa * kappa));
if (bUpper)
return ((mode + delta) > 1) ? 1.0 : (mode + delta);
return ((mode - delta) < 0) ? 0.0 : (mode - delta);
}
/** @summary Calculates the boundaries using the mid-P binomial
* @memberof Math */
function eff_MidPInterval(total,passed,level,bUpper) {
const alpha = 1. - level, equal_tailed = true, alpha_min = equal_tailed ? alpha/2 : alpha, tol = 1e-9; // tolerance
let pmin = 0, pmax = 1, p = 0;
// treat special case for 0<passed<1
// do a linear interpolation of the upper limit values
if (passed > 0 && passed < 1) {
const p0 = eff_MidPInterval(total, 0.0, level, bUpper),
p1 = eff_MidPInterval(total, 1.0, level, bUpper);
p = (p1 - p0) * passed + p0;
return p;
}
while (Math.abs(pmax - pmin) > tol) {
p = (pmin + pmax)/2;
// double v = 0.5 * ROOT::Math::binomial_pdf(int(passed), p, int(total));
// make it work for non integer using the binomial - beta relationship
let v = 0.5 * beta_pdf(p, passed+1., total-passed+1)/(total+1);
// if (passed > 0) v += ROOT::Math::binomial_cdf(int(passed - 1), p, int(total));
// compute the binomial cdf at passed -1
if ( (passed-1) >= 0) v += beta_cdf_c(p, passed, total-passed+1);
const vmin = bUpper ? alpha_min : 1.- alpha_min;
if (v > vmin)
pmin = p;
else
pmax = p;
}
return p;
}
/** @summary for a central confidence interval for a Beta distribution
* @memberof Math */
function eff_Bayesian(total,passed,level,bUpper,alpha,beta) {
const a = passed + alpha,
b = total - passed + beta;
if (bUpper) {
if ((a > 0) && (b > 0))
return beta_quantile((1+level)/2,a,b);
else
return 1;
} else {
if ((a > 0) && (b > 0))
return beta_quantile((1-level)/2,a,b);
else
return 0;
}
}
/** @summary Return function to calculate boundary of TEfficiency
* @memberof Math */
function getTEfficiencyBoundaryFunc(option, isbayessian) {
const kFCP = 0, // Clopper-Pearson interval (recommended by PDG)
kFNormal = 1, // Normal approximation
kFWilson = 2, // Wilson interval
kFAC = 3, // Agresti-Coull interval
kFFC = 4, // Feldman-Cousins interval, too complicated for JavaScript
// kBJeffrey = 5, // Jeffrey interval (Prior ~ Beta(0.5,0.5)
// kBUniform = 6, // Prior ~ Uniform = Beta(1,1)
// kBBayesian = 7, // User specified Prior ~ Beta(fBeta_alpha,fBeta_beta)
kMidP = 8; // Mid-P Lancaster interval
if (isbayessian)
return eff_Bayesian;
switch (option) {
case kFCP: return eff_ClopperPearson;
case kFNormal: return eff_Normal;
case kFWilson: return eff_Wilson;
case kFAC: return eff_AgrestiCoull;
case kFFC: console.log('Feldman-Cousins interval kFFC not supported; using kFCP'); return eff_ClopperPearson;
case kMidP: return eff_MidPInterval;
// case kBJeffrey:
// case kBUniform:
// case kBBayesian: return eff_ClopperPearson;
}
console.log(`Not recognized stat option ${option}, using kFCP`);
return eff_ClopperPearson;
}
/** @summary Square function
* @memberof Math */
function Sq(x) {
return x * x;
}
/** @summary Pi function
* @memberof Math */
function Pi() {
return Math.PI;
}
/** @summary TwoPi function
* @memberof Math */
function TwoPi() {
return 2 * Math.PI;
}
/** @summary PiOver2 function
* @memberof Math */
function PiOver2()
{
return Math.PI / 2;
}
/** @summary PiOver4 function
* @memberof Math */
function PiOver4()
{
return Math.PI / 4;
}
/** @summary InvPi function
* @memberof Math */
function InvPi()
{
return 1 / Math.PI;
}
export { gamma, gamma as tgamma, gamma as Gamma,
Polynomialeval, Polynomial1eval, stirf,
gamma_pdf, ndtri, normal_quantile, normal_quantile_c, lognormal_cdf_c, lognormal_cdf,
igami, igamc, igam, lgam, lgamma, erfc, erf,
beta_pdf, inc_beta, BetaIncomplete,
pseries, incbet, incbi, beta_quantile, chisquared_cdf_c,
beta, inc_gamma, inc_gamma_c, landau_pdf, beta_cdf_c, Landau,
fdistribution_pdf, fdistribution_pdf as FDist,
fdistribution_cdf, fdistribution_cdf as FDistI,
fdistribution_cdf_c,
normal_cdf_c, normal_cdf_c as gaussian_cdf_c,
normal_cdf, normal_cdf as gaussian_cdf,
lognormal_pdf, normal_pdf, crystalball_function, crystalball_pdf, crystalball_cdf, crystalball_cdf_c,
chisquared_cdf, gamma_quantile_c, gamma_quantile, breitwigner_cdf_c, breitwigner_cdf,
cauchy_cdf_c, cauchy_cdf, cauchy_pdf, gaussian_pdf,
tdistribution_cdf_c, tdistribution_cdf, tdistribution_pdf, exponential_cdf_c, exponential_cdf, chisquared_pdf,
Beta, GammaDist, LaplaceDist, LaplaceDistI, LogNormal, Student, StudentI,
gaus, gausn, gausxy, expo,
Prob, Gaus, BreitWigner, BetaDist, BetaDistI, landau, landaun,
ChebyshevN, Chebyshev0, Chebyshev1, Chebyshev2, Chebyshev3, Chebyshev4,
Chebyshev5, Chebyshev6, Chebyshev7, Chebyshev8, Chebyshev9, Chebyshev10,
getTEfficiencyBoundaryFunc,
Sq, Pi, TwoPi, PiOver2, PiOver4, InvPi };