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);
}