/// <summary> /// Returns the value "x" such that: p == GammaP(a, x); /// </summary> /// <param name="a">Requires a > 0</param> /// <param name="p">Requires 0 ≤ p ≤ 1</param> public static double GammaPInv(double a, double p) { if ((!(a > 0) || double.IsInfinity(a)) || (!(p >= 0 && p <= 1))) { Policies.ReportDomainError("GammaPInv(a: {0}, p: {1}): Requires finite a > 0; p in [0,1]", a, p); return(double.NaN); } if (p == 1) { return(double.PositiveInfinity); } if (p == 0) { return(0); } // Since: P(1,x) = p = 1.0 - Math.Exp(-x), therefore: x = ln(1-p) if (a == 1.0) { return(-Math2.Log1p(-p)); } // Since P(1/2, x) = p = erf(sqrt(x)), therefore x = ErfInv(p)^2 if (a == 0.5) { return(Squared(Math2.ErfInv(p))); } const double cutoff = 0.8125; // chosen through experimentation if (p > cutoff) { return(Math2.GammaQInv(a, 1.0 - p)); } double guess = _IgammaInv.GammaPInvGuess(a, p, 1 - p, out bool has10Digits); // set the upper and lower limits var(lower, upper) = _IgammaInv.GammaPInvLimits(a, p); // there can be some numerical uncertainties in the limits, // particularly for denormalized values, so... adjust the limits if (upper == 0 && lower == 0) { return(0.0); } if (guess <= DoubleLimits.MinNormalValue) { return(guess); } if (guess <= lower) { lower = DoubleLimits.MinNormalValue; } if (guess > upper) { upper = guess; } return(_IgammaInv.SolveGivenAP(a, p, guess, lower, upper)); }