Accord.Math.Beta.IncompleteInverse C# (CSharp) Method

IncompleteInverse() public static method

Inverse of incomplete beta integral.
public static IncompleteInverse ( double aa, double bb, double yy0 ) : double
aa double
bb double
yy0 double
return double
        public static double IncompleteInverse(double aa, double bb, double yy0)
        {
            double a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh;
            int i, dir;

            bool nflg;
            bool rflg;


            if (yy0 <= 0)
                return (0.0);
            if (yy0 >= 1.0)
                return (1.0);

            if (aa <= 1.0 || bb <= 1.0)
            {
                nflg = true;
                dithresh = 4.0 * Constants.DoubleEpsilon;
                rflg = false;
                a = aa;
                b = bb;
                y0 = yy0;
                x = a / (a + b);
                y = Incomplete(a, b, x);
                goto ihalve;
            }
            else
            {
                nflg = false;
                dithresh = 1.0e-4;
            }

            /* approximation to inverse function */

            yp = -Normal.Inverse(yy0);

            if (yy0 > 0.5)
            {
                rflg = true;
                a = bb;
                b = aa;
                y0 = 1.0 - yy0;
                yp = -yp;
            }
            else
            {
                rflg = false;
                a = aa;
                b = bb;
                y0 = yy0;
            }

            lgm = (yp * yp - 3.0) / 6.0;
            x0 = 2.0 / (1.0 / (2.0 * a - 1.0) + 1.0 / (2.0 * b - 1.0));
            y = yp * Math.Sqrt(x0 + lgm) / x0
                - (1.0 / (2.0 * b - 1.0) - 1.0 / (2.0 * a - 1.0))
                * (lgm + 5.0 / 6.0 - 2.0 / (3.0 * x0));
            y = 2.0 * y;

            if (y < Constants.LogMin)
            {
                x0 = 1.0;
                throw new ArithmeticException("underflow");
            }

            x = a / (a + b * Math.Exp(y));
            y = Incomplete(a, b, x);
            yp = (y - y0) / y0;

            if (Math.Abs(yp) < 1.0e-2)
                goto newt;

        ihalve:

            /* Resort to interval halving if not close enough */
            x0 = 0.0;
            yl = 0.0;
            x1 = 1.0;
            yh = 1.0;
            di = 0.5;
            dir = 0;

            for (i = 0; i < 400; i++)
            {
                if (i != 0)
                {
                    x = x0 + di * (x1 - x0);
                    if (x == 1.0)
                        x = 1.0 - Constants.DoubleEpsilon;
                    y = Incomplete(a, b, x);
                    yp = (x1 - x0) / (x1 + x0);
                    if (Math.Abs(yp) < dithresh)
                    {
                        x0 = x;
                        goto newt;
                    }
                }

                if (y < y0)
                {
                    x0 = x;
                    yl = y;
                    if (dir < 0)
                    {
                        dir = 0;
                        di = 0.5;
                    }
                    else if (dir > 1)
                        di = 0.5 * di + 0.5;
                    else
                        di = (y0 - y) / (yh - yl);
                    dir += 1;
                    if (x0 > 0.75)
                    {
                        if (rflg)
                        {
                            rflg = false;
                            a = aa;
                            b = bb;
                            y0 = yy0;
                        }
                        else
                        {
                            rflg = true;
                            a = bb;
                            b = aa;
                            y0 = 1.0 - yy0;
                        }
                        x = 1.0 - x;
                        y = Incomplete(a, b, x);
                        goto ihalve;
                    }
                }
                else
                {
                    x1 = x;
                    if (rflg && x1 < Constants.DoubleEpsilon)
                    {
                        x0 = 0.0;
                        goto done;
                    }
                    yh = y;
                    if (dir > 0)
                    {
                        dir = 0;
                        di = 0.5;
                    }
                    else if (dir < -1)
                        di = 0.5 * di;
                    else
                        di = (y - y0) / (yh - yl);
                    dir -= 1;
                }
            }

            if (x0 >= 1.0)
            {
                x0 = 1.0 - Constants.DoubleEpsilon;
                goto done;
            }

            if (x == 0.0)
                throw new ArithmeticException("underflow");

        newt:

            if (nflg)
                goto done;

            x0 = x;
            lgm = Gamma.Log(a + b) - Gamma.Log(a) - Gamma.Log(b);

            for (i = 0; i < 10; i++)
            {
                /* Compute the function at this point. */
                if (i != 0)
                    y = Incomplete(a, b, x0);

                /* Compute the derivative of the function at this point. */
                d = (a - 1.0) * Math.Log(x0) + (b - 1.0) * Math.Log(1.0 - x0) + lgm;

                if (d < Constants.LogMin)
                    throw new ArithmeticException("underflow");

                d = Math.Exp(d);

                /* compute the step to the next approximation of x */
                d = (y - y0) / d;
                x = x0;
                x0 = x0 - d;

                if (x0 <= 0.0)
                    throw new ArithmeticException("underflow");

                if (x0 >= 1.0)
                {
                    x0 = 1.0 - Constants.DoubleEpsilon;
                    goto done;
                }

                if (Math.Abs(d / x0) < 64.0 * Constants.DoubleEpsilon)
                    goto done;
            }

        done:
            if (rflg)
            {
                if (x0 <= Double.Epsilon)
                    x0 = 1.0 - Double.Epsilon;
                else
                    x0 = 1.0 - x0;
            }
            return (x0);
        }