Esempio n. 1
0
        /// <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));
        }