/**
         * Regularized lower gamma function 'P'
         * @param s
         * @param x
         * @return Value of the regularized lower gamma function 'P'.
         */
        public static double regularizedGammaLowerP(double s, double x)
        {
            if (Double.IsNaN(x))
            {
                return(Double.NaN);
            }
            if (Double.IsNaN(s))
            {
                return(Double.NaN);
            }
            if (MathFunctions.almostEqual(x, 0))
            {
                return(0);
            }
            if (MathFunctions.almostEqual(s, 0))
            {
                return(1 + SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI);
            }

            if (MathFunctions.almostEqual(s, 1))
            {
                return(1 - Math.Exp(-x));
            }

            if (x < 0)
            {
                return(Double.NaN);
            }

            if (s < 0)
            {
                return(regularizedGammaLowerP(s + 1, x) + (Math.Pow(x, s) * Math.Exp(-x)) / (s * gamma(s)));
            }

            const double epsilon          = 0.000000000000001;
            const double bigNumber        = 4503599627370496.0;
            const double bigNumberInverse = 2.22044604925031308085e-16;

            double ax = (s * Math.Log(x)) - x - logGamma(s);

            if (ax < -709.78271289338399)
            {
                return(1);
            }

            if (x <= 1 || x <= s)
            {
                double r2   = s;
                double c2   = 1;
                double ans2 = 1;
                do
                {
                    r2    = r2 + 1;
                    c2    = c2 * x / r2;
                    ans2 += c2;
                } while ((c2 / ans2) > epsilon);
                return(Math.Exp(ax) * ans2 / s);
            }

            int    c = 0;
            double y = 1 - s;
            double z = x + y + 1;

            double p3  = 1;
            double q3  = x;
            double p2  = x + 1;
            double q2  = z * x;
            double ans = p2 / q2;

            double error;

            do
            {
                c++;
                y += 1;
                z += 2;
                double yc = y * c;

                double p = (p2 * z) - (p3 * yc);
                double q = (q2 * z) - (q3 * yc);

                if (q != 0)
                {
                    double nextans = p / q;
                    error = Math.Abs((ans - nextans) / nextans);
                    ans   = nextans;
                }
                else
                {
                    // zero div, skip
                    error = 1;
                }

                // shift
                p3 = p2;
                p2 = p;
                q3 = q2;
                q2 = q;

                // normalize fraction when the numerator becomes large
                if (Math.Abs(p) > bigNumber)
                {
                    p3 *= bigNumberInverse;
                    p2 *= bigNumberInverse;
                    q3 *= bigNumberInverse;
                    q2 *= bigNumberInverse;
                }
            } while (error > epsilon);

            return(1 - (Math.Exp(ax) * ans));
        }
        /**
         * Regularized upper gamma function 'Q'
         * @param s
         * @param x
         * @return Value of the regularized upper gamma function 'Q'.
         */
        public static double regularizedGammaUpperQ(double s, double x)
        {
            if (Double.IsNaN(x))
            {
                return(Double.NaN);
            }
            if (Double.IsNaN(s))
            {
                return(Double.NaN);
            }
            if (MathFunctions.almostEqual(x, 0))
            {
                return(1);
            }

            if (MathFunctions.almostEqual(s, 0))
            {
                return(-SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI);
            }

            if (MathFunctions.almostEqual(s, 1))
            {
                return(Math.Exp(-x));
            }

            if (x < 0)
            {
                return(Double.NaN);
            }

            if (s < 0)
            {
                return(regularizedGammaUpperQ(s + 1, x) - (Math.Pow(x, s) * Math.Exp(-x)) / (s * gamma(s)));
            }

            double ax = s * Math.Log(x) - x - logGamma(s);

            if (ax < -709.78271289338399)
            {
                return(0);
            }
            double       t;
            const double igammaepsilon      = 0.000000000000001;
            const double igammabignumber    = 4503599627370496.0;
            const double igammabignumberinv = 2.22044604925031308085 * 0.0000000000000001;

            ax = Math.Exp(ax);
            double y    = 1 - s;
            double z    = x + y + 1;
            double c    = 0;
            double pkm2 = 1;
            double qkm2 = x;
            double pkm1 = x + 1;
            double qkm1 = z * x;
            double ans  = pkm1 / qkm1;

            do
            {
                c = c + 1;
                y = y + 1;
                z = z + 2;
                double yc = y * c;
                double pk = pkm1 * z - pkm2 * yc;
                double qk = qkm1 * z - qkm2 * yc;
                if (qk != 0)
                {
                    double r = pk / qk;
                    t   = Math.Abs((ans - r) / r);
                    ans = r;
                }
                else
                {
                    t = 1;
                }

                pkm2 = pkm1;
                pkm1 = pk;
                qkm2 = qkm1;
                qkm1 = qk;

                if (Math.Abs(pk) > igammabignumber)
                {
                    pkm2 = pkm2 * igammabignumberinv;
                    pkm1 = pkm1 * igammabignumberinv;
                    qkm2 = qkm2 * igammabignumberinv;
                    qkm1 = qkm1 * igammabignumberinv;
                }
            } while (t > igammaepsilon);
            return(ans * ax);
        }