Function() 공개 정적인 메소드

Normal cumulative distribution function.
public static Function ( double value ) : double
value double
리턴 double
예제 #1
0
        /// <summary>
        ///   Computes Owen's T function for arbitrary H and A.
        /// </summary>
        ///
        /// <param name="h">Owen's T function argument H.</param>
        /// <param name="a">Owen's T function argument A.</param>
        ///
        /// <returns>The value of Owen's T function.</returns>
        ///
        public static double Function(double h, double a)
        {
            double       absa;
            double       absh;
            double       ah;
            const double cut = 0.67;
            double       normah;
            double       normh;
            double       value;

            absh = Math.Abs(h);
            absa = Math.Abs(a);
            ah   = absa * absh;

            if (absa <= 1.0)
            {
                value = Function(absh, absa, ah);
            }
            else if (absh <= cut)
            {
                value = 0.25 - (-0.5 + Normal.Function(absh)) * (-0.5 + Normal.Function(ah))
                        - Function(ah, 1.0 / absa, absh);
            }
            else
            {
                normh  = Normal.Complemented(absh);
                normah = Normal.Complemented(ah);
                value  = 0.5 * (normh + normah) - normh * normah
                         - Function(ah, 1.0 / absa, absh);
            }

            if (a < 0.0)
            {
                value = -value;
            }

            return(value);
        }
예제 #2
0
        /// <summary>
        ///   Owen's T function for a restricted range of parameters.
        /// </summary>
        ///
        /// <param name="h">Owen's T function argument H (where 0 &lt;= H).</param>
        /// <param name="a">Owen's T function argument A (where 0 &lt;= A &lt;= 1).</param>
        /// <param name="ah">The value of A*H.</param>
        ///
        /// <returns>The value of Owen's T function.</returns>
        ///
        public static double Function(double h, double a, double ah)
        {
            double ai;
            double aj;
            double AS;
            double dhs;
            double dj;
            double gj;

            double hs;
            int    i;
            int    iaint;
            int    icode;
            int    ihint;
            int    ii;
            int    j;
            int    jj;
            int    m;
            int    maxii;
            double normh;

            double       r;
            const double rrtpi  = 0.39894228040143267794;
            const double rtwopi = 0.15915494309189533577;

            double y;
            double yi;
            double z;
            double zi;

            double value = 0;
            double vi;

            double[] arange =
            {
                0.025, 0.09, 0.15, 0.36, 0.5,
                0.9, 0.99999
            };

            double[] c2 =
            {
                0.99999999999999987510,
                -0.99999999999988796462,         0.99999999998290743652,
                -0.99999999896282500134,         0.99999996660459362918,
                -0.99999933986272476760,         0.99999125611136965852,
                -0.99991777624463387686,         0.99942835555870132569,
                -0.99697311720723000295,         0.98751448037275303682,
                -0.95915857980572882813,         0.89246305511006708555,
                -0.76893425990463999675,         0.58893528468484693250,
                -0.38380345160440256652,         0.20317601701045299653,
                -0.82813631607004984866E-01, 0.24167984735759576523E-01,
                -0.44676566663971825242E-02, 0.39141169402373836468E-03
            };

            double[] hrange =
            {
                0.02, 0.06, 0.09, 0.125, 0.26,
                0.4,   0.6,  1.6,   1.7, 2.33,
                2.4,  3.36,  3.4, 4.8
            };


            int[] meth =
            {
                1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 6
            };

            int[] ord =
            {
                2, 3, 4, 5, 7, 10, 12, 18, 10, 20, 30, 20, 4, 7, 8, 20, 13, 0
            };

            double[] pts =
            {
                0.35082039676451715489E-02,
                0.31279042338030753740E-01, 0.85266826283219451090E-01,
                0.16245071730812277011,         0.25851196049125434828,
                0.36807553840697533536,         0.48501092905604697475,
                0.60277514152618576821,         0.71477884217753226516,
                0.81475510988760098605,         0.89711029755948965867,
                0.95723808085944261843, 0.99178832974629703586
            };

            int[] select =
            {
                1, 1, 2, 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16,  9,
                1, 2, 2,  3,  3,  5,  5, 14, 14, 15, 15, 16, 16, 16,  9,
                2, 2, 3,  3,  3,  5,  5, 15, 15, 15, 15, 16, 16, 16, 10,
                2, 2, 3,  5,  5,  5,  5,  7,  7, 16, 16, 16, 16, 16, 10,
                2, 3, 3,  5,  5,  6,  6,  8,  8, 17, 17, 17, 12, 12, 11,
                2, 3, 5,  5,  5,  6,  6,  8,  8, 17, 17, 17, 12, 12, 12,
                2, 3, 4,  4,  6,  6,  8,  8, 17, 17, 17, 17, 17, 12, 12,
                2, 3, 4,  4,  6,  6, 18, 18, 18, 18, 17, 17, 17, 12, 12
            };



            double[] wts =
            {
                0.18831438115323502887E-01,
                0.18567086243977649478E-01, 0.18042093461223385584E-01,
                0.17263829606398753364E-01, 0.16243219975989856730E-01,
                0.14994592034116704829E-01, 0.13535474469662088392E-01,
                0.11886351605820165233E-01, 0.10070377242777431897E-01,
                0.81130545742299586629E-02, 0.60419009528470238773E-02,
                0.38862217010742057883E-02, 0.16793031084546090448E-02
            };



            /*
             * Determine appropriate method from t1...t6
             */
            ihint = 15;

            for (i = 1; i <= 14; i++)
            {
                if (h <= hrange[i - 1])
                {
                    ihint = i;
                    break;
                }
            }

            iaint = 8;

            for (i = 1; i <= 7; i++)
            {
                if (a <= arange[i - 1])
                {
                    iaint = i;
                    break;
                }
            }

            icode = select[ihint - 1 + (iaint - 1) * 15];
            m     = ord[icode - 1];

            /*
             * t1(h, a, m) ; m = 2, 3, 4, 5, 7, 10, 12 or 18
             * jj = 2j - 1 ; gj = exp(-h*h/2) * (-h*h/2)**j / j
             * aj = a**(2j-1) / (2*pi)
             */

            if (meth[icode - 1] == 1)
            {
                hs    = -0.5 * h * h;
                dhs   = Math.Exp(hs);
                AS    = a * a;
                j     = 1;
                jj    = 1;
                aj    = rtwopi * a;
                value = rtwopi * Math.Atan(a);
                dj    = dhs - 1.0;
                gj    = hs * dhs;

                for (; ;)
                {
                    value = value + dj * aj / (double)(jj);

                    if (m <= j)
                    {
                        return(value);
                    }
                    j  = j + 1;
                    jj = jj + 2;
                    aj = aj * AS;
                    dj = gj - dj;
                    gj = gj * hs / (double)(j);
                }
            }

            /*
             * t2(h, a, m) ; m = 10, 20 or 30
             * z = (-1)^(i-1) * zi ; ii = 2i - 1
             * vi = (-1)^(i-1) * a^(2i-1) * exp[-(a*h)^2/2] / sqrt(2*pi)
             */
            else if (meth[icode - 1] == 2)
            {
                maxii = m + m + 1;
                ii    = 1;
                value = 0.0;
                hs    = h * h;
                AS    = -a * a;
                vi    = rrtpi * a * Math.Exp(-0.5 * ah * ah);
                z     = 0.5 * (-0.5 + Normal.Function(ah)) / h;
                y     = 1.0 / hs;

                for (; ;)
                {
                    value = value + z;

                    if (maxii <= ii)
                    {
                        value = value * rrtpi * Math.Exp(-0.5 * hs);
                        return(value);
                    }
                    z  = y * (vi - (double)(ii) * z);
                    vi = AS * vi;
                    ii = ii + 2;
                }
            }

            /*
             * t3(h, a, m) ; m = 20
             * ii = 2i - 1
             * vi = a**(2i-1) * exp[-(a*h)**2/2] / sqrt(2*pi)
             */
            else if (meth[icode - 1] == 3)
            {
                i     = 1;
                ii    = 1;
                value = 0.0;
                hs    = h * h;
                AS    = a * a;
                vi    = rrtpi * a * Math.Exp(-0.5 * ah * ah);
                zi    = 0.5 * (-0.5 + Normal.Function(ah)) / h;
                y     = 1.0 / hs;

                for (; ;)
                {
                    value = value + zi * c2[i - 1];

                    if (m < i)
                    {
                        value = value * rrtpi * Math.Exp(-0.5 * hs);
                        return(value);
                    }
                    zi = y * ((double)(ii) * zi - vi);
                    vi = AS * vi;
                    i  = i + 1;
                    ii = ii + 2;
                }
            }

            /*
             * t4(h, a, m) ; m = 4, 7, 8 or 20;  ii = 2i + 1
             * ai = a * exp[-h*h*(1+a*a)/2] * (-a*a)**i / (2*pi)
             */
            else if (meth[icode - 1] == 4)
            {
                maxii = m + m + 1;
                ii    = 1;
                hs    = h * h;
                AS    = -a * a;
                value = 0.0;
                ai    = rtwopi * a * Math.Exp(-0.5 * hs * (1.0 - AS));
                yi    = 1.0;

                for (; ;)
                {
                    value = value + ai * yi;

                    if (maxii <= ii)
                    {
                        return(value);
                    }
                    ii = ii + 2;
                    yi = (1.0 - hs * yi) / (double)(ii);
                    ai = ai * AS;
                }
            }

            /*
             * t5(h, a, m) ; m = 13
             * 2m - point gaussian quadrature
             */
            else if (meth[icode - 1] == 5)
            {
                value = 0.0;
                AS    = a * a;
                hs    = -0.5 * h * h;
                for (i = 1; i <= m; i++)
                {
                    r     = 1.0 + AS * pts[i - 1];
                    value = value + wts[i - 1] * Math.Exp(hs * r) / r;
                }
                value = a * value;
            }

            /*
             * t6(h, a);  approximation for a near 1, (a<=1)
             */
            else if (meth[icode - 1] == 6)
            {
                normh = Normal.Complemented(h);
                value = 0.5 * normh * (1.0 - normh);
                y     = 1.0 - a;
                r     = Math.Atan(y / (1.0 + a));

                if (r != 0.0)
                {
                    value = value - rtwopi * r * Math.Exp(-0.5 * y * h * h / r);
                }
            }
            return(value);
        }
예제 #3
0
        /// <summary>
        ///   A function for computing bivariate normal probabilities.
        ///   BVND calculates the probability that X > DH and Y > DK.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method is based on the work done by Alan Genz, Department of
        ///   Mathematics, Washington State University. Pullman, WA 99164-3113
        ///   Email: [email protected]. This work was shared under a 3-clause BSD
        ///   license. Please see source file for more details and the actual
        ///   license text.</para>
        ///
        /// <para>
        ///   This function is based on the method described by Drezner, Z and G.O.
        ///   Wesolowsky, (1989), On the computation of the bivariate normal integral,
        ///   Journal of Statist. Comput. Simul. 35, pp. 101-107, with major modifications
        ///   for double precision, and for |R| close to 1.</para>
        /// </remarks>
        ///
        private static double BVND(double DH, double DK, double R)
        {
            // Copyright (C) 2013, Alan Genz,  All rights reserved.
            //
            //  Redistribution and use in source and binary forms, with or without
            //  modification, are permitted provided the following conditions are met:
            //    1. Redistributions of source code must retain the above copyright
            //       notice, this list of conditions and the following disclaimer.
            //    2. Redistributions in binary form must reproduce the above copyright
            //       notice, this list of conditions and the following disclaimer in
            //       the documentation and/or other materials provided with the
            //       distribution.
            //    3. The contributor name(s) may not be used to endorse or promote
            //       products derived from this software without specific prior
            //       written permission.
            //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
            //  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
            //  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
            //  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
            //  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
            //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
            //  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
            //  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
            //  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
            //  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE
            //  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

            double TWOPI = 2.0 * Math.PI;

            double[] x;
            double[] w;

            if (Math.Abs(R) < 0.3)
            {
                // Gauss Legendre Points and Weights N =  6
                x = new double[] { -0.9324695142031522, -0.6612093864662647, -0.2386191860831970 };
                w = new double[] { 0.1713244923791705, 0.3607615730481384, 0.4679139345726904 };
            }
            else if (Math.Abs(R) < 0.75)
            {
                // Gauss Legendre Points and Weights N =  12
                x = new double[]
                {
                    -0.9815606342467191, -0.9041172563704750, -0.7699026741943050,
                    -0.5873179542866171, -0.3678314989981802, -0.1252334085114692
                };

                w = new double[]
                {
                    0.04717533638651177, 0.1069393259953183, 0.1600783285433464,
                    0.2031674267230659, 0.2334925365383547, 0.2491470458134029,
                };
            }
            else
            {
                // Gauss Legendre Points and Weights N =  20
                x = new double[]
                {
                    -0.9931285991850949, -0.9639719272779138,
                    -0.9122344282513259, -0.8391169718222188,
                    -0.7463319064601508, -0.6360536807265150,
                    -0.5108670019508271, -0.3737060887154196,
                    -0.2277858511416451, -0.07652652113349733
                };

                w = new double[]
                {
                    0.01761400713915212, 0.04060142980038694,
                    0.06267204833410906, 0.08327674157670475,
                    0.1019301198172404, 0.1181945319615184,
                    0.1316886384491766, 0.1420961093183821,
                    0.1491729864726037, 0.1527533871307259
                };
            }

            double H   = DH;
            double K   = DK;
            double HK  = H * K;
            double BVN = 0;

            if (Math.Abs(R) < 0.925)
            {
                if (Math.Abs(R) > 0)
                {
                    double HS  = (H * H + K * K) / 2;
                    double ASR = Math.Asin(R);

                    for (int I = 0; I < x.Length; I++)
                    {
                        for (int IS = -1; IS <= 1; IS += 2)
                        {
                            double SN = Math.Sin(ASR * (IS * x[I] + 1) / 2);
                            BVN = BVN + w[I] * Math.Exp((SN * HK - HS) / (1 - SN * SN));
                        }
                    }
                    BVN = BVN * ASR / (2 * TWOPI);
                }

                return(BVN + Normal.Function(-H) * Normal.Function(-K));
            }


            if (R < 0)
            {
                K  = -K;
                HK = -HK;
            }

            if (Math.Abs(R) < 1)
            {
                double AS = (1 - R) * (1 + R);
                double A  = Math.Sqrt(AS);
                double BS = (H - K);
                BS = BS * BS;
                double C   = (4 - HK) / 8;
                double D   = (12 - HK) / 16;
                double ASR = -(BS / AS + HK) / 2;

                if (ASR > -100)
                {
                    BVN = A * Math.Exp(ASR) * (1 - C * (BS - AS) * (1 - D * BS / 5) / 3 + C * D * AS * AS / 5);
                }

                if (-HK < 100)
                {
                    double B = Math.Sqrt(BS);
                    BVN = BVN - Math.Exp(-HK / 2) * Math.Sqrt(TWOPI) * Normal.Function(-B / A) * B
                          * (1 - C * BS * (1 - D * BS / 5) / 3);
                }

                A = A / 2;

                for (int I = 0; I < x.Length; I++)
                {
                    for (int IS = -1; IS <= 1; IS += 2)
                    {
                        double XS = (A * (IS * x[I] + 1));
                        XS = XS * XS;
                        double RS = Math.Sqrt(1 - XS);
                        ASR = -(BS / XS + HK) / 2;

                        if (ASR > -100)
                        {
                            BVN = BVN + A * w[I] * Math.Exp(ASR)
                                  * (Math.Exp(-HK * XS / (2 * (1 + RS) * (1 + RS))) / RS
                                     - (1 + C * XS * (1 + D * XS)));
                        }
                    }
                }

                BVN = -BVN / TWOPI;
            }

            if (R > 0)
            {
                return(BVN + Normal.Function(-Math.Max(H, K)));
            }

            BVN = -BVN;

            if (K <= H)
            {
                return(BVN);
            }

            if (H < 0)
            {
                return(BVN + Normal.Function(K) - Normal.Function(H));
            }

            return(BVN + Normal.Function(-H) - Normal.Function(-K));
        }
예제 #4
0
        /// <summary>
        ///   Owen's T function for a restricted range of parameters.
        /// </summary>
        ///
        /// <param name="h">Owen's T function argument H (where 0 &lt;= H).</param>
        /// <param name="a">Owen's T function argument A (where 0 &lt;= A &lt;= 1).</param>
        /// <param name="ah">The value of A*H.</param>
        ///
        /// <returns>The value of Owen's T function.</returns>
        ///
        public static double Function(double h, double a, double ah)
        {
            double ai;
            double aj;
            double AS;
            double dhs;
            double dj;
            double gj;

            double hs;
            int    i;
            int    iaint;
            int    icode;
            int    ihint;
            int    ii;
            int    j;
            int    jj;
            int    m;
            int    maxii;
            double normh;

            double       r;
            const double rrtpi  = 0.39894228040143267794;
            const double rtwopi = 0.15915494309189533577;

            double y;
            double yi;
            double z;
            double zi;

            double value = 0;
            double vi;


            /*
             * Determine appropriate method from t1...t6
             */
            ihint = 15;

            for (i = 1; i <= 14; i++)
            {
                if (h <= hrange[i - 1])
                {
                    ihint = i;
                    break;
                }
            }

            iaint = 8;

            for (i = 1; i <= 7; i++)
            {
                if (a <= arange[i - 1])
                {
                    iaint = i;
                    break;
                }
            }

            icode = select[ihint - 1 + (iaint - 1) * 15];
            m     = ord[icode - 1];

            /*
             * t1(h, a, m) ; m = 2, 3, 4, 5, 7, 10, 12 or 18
             * jj = 2j - 1 ; gj = exp(-h*h/2) * (-h*h/2)**j / j
             * aj = a**(2j-1) / (2*pi)
             */

            if (meth[icode - 1] == 1)
            {
                hs    = -0.5 * h * h;
                dhs   = Math.Exp(hs);
                AS    = a * a;
                j     = 1;
                jj    = 1;
                aj    = rtwopi * a;
                value = rtwopi * Math.Atan(a);
                dj    = dhs - 1.0;
                gj    = hs * dhs;

                for (; ;)
                {
                    value = value + dj * aj / (double)(jj);

                    if (m <= j)
                    {
                        return(value);
                    }
                    j  = j + 1;
                    jj = jj + 2;
                    aj = aj * AS;
                    dj = gj - dj;
                    gj = gj * hs / (double)(j);
                }
            }

            /*
             * t2(h, a, m) ; m = 10, 20 or 30
             * z = (-1)^(i-1) * zi ; ii = 2i - 1
             * vi = (-1)^(i-1) * a^(2i-1) * exp[-(a*h)^2/2] / sqrt(2*pi)
             */
            else if (meth[icode - 1] == 2)
            {
                maxii = m + m + 1;
                ii    = 1;
                value = 0.0;
                hs    = h * h;
                AS    = -a * a;
                vi    = rrtpi * a * Math.Exp(-0.5 * ah * ah);
                z     = 0.5 * (-0.5 + Normal.Function(ah)) / h;
                y     = 1.0 / hs;

                for (; ;)
                {
                    value = value + z;

                    if (maxii <= ii)
                    {
                        value = value * rrtpi * Math.Exp(-0.5 * hs);
                        return(value);
                    }
                    z  = y * (vi - (double)(ii) * z);
                    vi = AS * vi;
                    ii = ii + 2;
                }
            }

            /*
             * t3(h, a, m) ; m = 20
             * ii = 2i - 1
             * vi = a**(2i-1) * exp[-(a*h)**2/2] / sqrt(2*pi)
             */
            else if (meth[icode - 1] == 3)
            {
                i     = 1;
                ii    = 1;
                value = 0.0;
                hs    = h * h;
                AS    = a * a;
                vi    = rrtpi * a * Math.Exp(-0.5 * ah * ah);
                zi    = 0.5 * (-0.5 + Normal.Function(ah)) / h;
                y     = 1.0 / hs;

                for (; ;)
                {
                    value = value + zi * c2[i - 1];

                    if (m < i)
                    {
                        value = value * rrtpi * Math.Exp(-0.5 * hs);
                        return(value);
                    }
                    zi = y * ((double)(ii) * zi - vi);
                    vi = AS * vi;
                    i  = i + 1;
                    ii = ii + 2;
                }
            }

            /*
             * t4(h, a, m) ; m = 4, 7, 8 or 20;  ii = 2i + 1
             * ai = a * exp[-h*h*(1+a*a)/2] * (-a*a)**i / (2*pi)
             */
            else if (meth[icode - 1] == 4)
            {
                maxii = m + m + 1;
                ii    = 1;
                hs    = h * h;
                AS    = -a * a;
                value = 0.0;
                ai    = rtwopi * a * Math.Exp(-0.5 * hs * (1.0 - AS));
                yi    = 1.0;

                for (; ;)
                {
                    value = value + ai * yi;

                    if (maxii <= ii)
                    {
                        return(value);
                    }

                    ii = ii + 2;
                    yi = (1.0 - hs * yi) / (double)(ii);
                    ai = ai * AS;
                }
            }

            /*
             * t5(h, a, m) ; m = 13
             * 2m - point gaussian quadrature
             */
            else if (meth[icode - 1] == 5)
            {
                value = 0.0;
                AS    = a * a;
                hs    = -0.5 * h * h;
                for (i = 1; i <= m; i++)
                {
                    r     = 1.0 + AS * pts[i - 1];
                    value = value + wts[i - 1] * Math.Exp(hs * r) / r;
                }
                value = a * value;
            }

            /*
             * t6(h, a);  approximation for a near 1, (a<=1)
             */
            else if (meth[icode - 1] == 6)
            {
                normh = Normal.Complemented(h);
                value = 0.5 * normh * (1.0 - normh);
                y     = 1.0 - a;
                r     = Math.Atan(y / (1.0 + a));

                if (r != 0.0)
                {
                    value = value - rtwopi * r * Math.Exp(-0.5 * y * h * h / r);
                }
            }

            return(value);
        }
예제 #5
0
        /// <summary>
        ///   A function for computing bivariate normal probabilities.
        ///   BVND calculates the probability that X > DH and Y > DK.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method is based on the work done by Alan Genz, Department of
        ///   Mathematics, Washington State University. Pullman, WA 99164-3113
        ///   Email: [email protected]. This work was shared under a 3-clause BSD
        ///   license. Please see source file for more details and the actual
        ///   license text.</para>
        ///
        /// <para>
        ///   This function is based on the method described by Drezner, Z and G.O.
        ///   Wesolowsky, (1989), On the computation of the bivariate normal integral,
        ///   Journal of Statist. Comput. Simul. 35, pp. 101-107, with major modifications
        ///   for double precision, and for |R| close to 1.</para>
        /// </remarks>
        ///
        private static double BVND(double dh, double dk, double r)
        {
            // Copyright (C) 2013, Alan Genz,  All rights reserved.
            //
            //  Redistribution and use in source and binary forms, with or without
            //  modification, are permitted provided the following conditions are met:
            //    1. Redistributions of source code must retain the above copyright
            //       notice, this list of conditions and the following disclaimer.
            //    2. Redistributions in binary form must reproduce the above copyright
            //       notice, this list of conditions and the following disclaimer in
            //       the documentation and/or other materials provided with the
            //       distribution.
            //    3. The contributor name(s) may not be used to endorse or promote
            //       products derived from this software without specific prior
            //       written permission.
            //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
            //  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
            //  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
            //  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
            //  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
            //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
            //  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
            //  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
            //  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
            //  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF USE
            //  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

            const double TWOPI = 2.0 * Math.PI;

            double[] x;
            double[] w;

            if (Math.Abs(r) < 0.3)
            {
                // Gauss Legendre Points and Weights N =  6
                x = BVND_XN6;
                w = BVND_WN6;
            }
            else if (Math.Abs(r) < 0.75)
            {
                // Gauss Legendre Points and Weights N =  12
                x = BVND_XN12;
                w = BVND_WN12;
            }
            else
            {
                // Gauss Legendre Points and Weights N =  20
                x = BVND_XN20;
                w = BVND_WN20;
            }

            double h   = dh;
            double k   = dk;
            double hk  = h * k;
            double bvn = 0;

            if (Math.Abs(r) < 0.925)
            {
                if (Math.Abs(r) > 0)
                {
                    double sh  = (h * h + k * k) / 2;
                    double asr = Math.Asin(r);

                    for (int i = 0; i < x.Length; i++)
                    {
                        for (int j = -1; j <= 1; j += 2)
                        {
                            double sn = Math.Sin(asr * (j * x[i] + 1) / 2);
                            bvn = bvn + w[i] * Math.Exp((sn * hk - sh) / (1 - sn * sn));
                        }
                    }
                    bvn = bvn * asr / (2 * TWOPI);
                }

                return(bvn + Normal.Function(-h) * Normal.Function(-k));
            }


            if (r < 0)
            {
                k  = -k;
                hk = -hk;
            }

            if (Math.Abs(r) < 1)
            {
                double sa = (1 - r) * (1 + r);
                double A  = Math.Sqrt(sa);
                double sb = (h - k);
                sb = sb * sb;
                double c   = (4 - hk) / 8;
                double d   = (12 - hk) / 16;
                double asr = -(sb / sa + hk) / 2;

                if (asr > -100)
                {
                    bvn = A * Math.Exp(asr) * (1 - c * (sb - sa) * (1 - d * sb / 5) / 3 + c * d * sa * sa / 5);
                }

                if (-hk < 100)
                {
                    double B = Math.Sqrt(sb);
                    bvn = bvn - Math.Exp(-hk / 2) * Math.Sqrt(TWOPI) * Normal.Function(-B / A) * B
                          * (1 - c * sb * (1 - d * sb / 5) / 3);
                }

                A = A / 2;

                for (int i = 0; i < x.Length; i++)
                {
                    for (int j = -1; j <= 1; j += 2)
                    {
                        double xs = (A * (j * x[i] + 1));
                        xs = xs * xs;
                        double rs = Math.Sqrt(1 - xs);
                        asr = -(sb / xs + hk) / 2;

                        if (asr > -100)
                        {
                            bvn = bvn + A * w[i] * Math.Exp(asr)
                                  * (Math.Exp(-hk * xs / (2 * (1 + rs) * (1 + rs))) / rs
                                     - (1 + c * xs * (1 + d * xs)));
                        }
                    }
                }

                bvn = -bvn / TWOPI;
            }

            if (r > 0)
            {
                return(bvn + Normal.Function(-Math.Max(h, k)));
            }

            bvn = -bvn;

            if (k <= h)
            {
                return(bvn);
            }

            if (h < 0)
            {
                return(bvn + Normal.Function(k) - Normal.Function(h));
            }

            return(bvn + Normal.Function(-h) - Normal.Function(-k));
        }