using System;
namespace Legalsoft.Truffer
{
public class Gamma : Gauleg18
{
private const int ASWITCH = 100;
private const double EPS = float.Epsilon;
private const double FPMIN = float.MinValue;// / float.Epsilon;
private double gln { get; set; }
public double gammp(double a, double x)
{
if (x < 0.0 || a <= 0.0)
{
throw new Exception("bad args in gammp");
}
//if (x == 0.0)
if (Math.Abs(x) <= float.Epsilon)
{
return 0.0;
}
else if ((int)a >= ASWITCH)
{
return gammpapprox(a, x, 1);
}
else if (x < a + 1.0)
{
return gser(a, x);
}
else
{
return 1.0 - gcf(a, x);
}
}
public double gammq(double a, double x)
{
if (x < 0.0 || a <= 0.0)
{
throw new Exception("bad args in gammq");
}
//if (x == 0.0)
if (Math.Abs(x) <= float.Epsilon)
{
return 1.0;
}
else if ((int)a >= ASWITCH)
{
return gammpapprox(a, x, 0);
}
else if (x < a + 1.0)
{
return 1.0 - gser(a, x);
}
else
{
return gcf(a, x);
}
}
public double gser(double a, double x)
{
gln = Globals.gammln(a);
double ap = a;
double del = 1.0 / a;
double sum = 1.0 / a;
for (; ; )
{
++ap;
del *= x / ap;
sum += del;
if (Math.Abs(del) < Math.Abs(sum) * EPS)
{
return sum * Math.Exp(-x + a * Math.Log(x) - gln);
}
}
}
public double gcf(double a, double x)
{
gln = Globals.gammln(a);
double b = x + 1.0 - a;
double c = 1.0 / FPMIN;
double d = 1.0 / b;
double h = d;
for (int i = 1; ; i++)
{
double an = -i * (i - a);
b += 2.0;
d = an * d + b;
if (Math.Abs(d) < FPMIN)
{
d = FPMIN;
}
c = b + an / c;
if (Math.Abs(c) < FPMIN)
{
c = FPMIN;
}
d = 1.0 / d;
double del = d * c;
h *= del;
if (Math.Abs(del - 1.0) <= EPS)
{
break;
}
}
return Math.Exp(-x + a * Math.Log(x) - gln) * h;
}
public double gammpapprox(double a, double x, int psig)
{
double a1 = a - 1.0;
double lna1 = Math.Log(a1);
double sqrta1 = Math.Sqrt(a1);
gln = Globals.gammln(a);
double xu;
if (x > a1)
{
xu = Math.Max(a1 + 11.5 * sqrta1, x + 6.0 * sqrta1);
}
else
{
xu = Math.Max(0.0, Math.Min(a1 - 7.5 * sqrta1, x - 5.0 * sqrta1));
}
double sum = 0;
for (int j = 0; j < ngau; j++)
{
double t = x + (xu - x) * y[j];
sum += w[j] * Math.Exp(-(t - a1) + a1 * (Math.Log(t) - lna1));
}
double ans = sum * (xu - x) * Math.Exp(a1 * (lna1 - 1.0) - gln);
return (psig != 0 ? (x > a1 ? 1.0 - ans : -ans) : (x > a1 ? ans : 1.0 + ans));
}
public double invgammp(double p, double a)
{
double lna1 = 0.0;
double afac = 0.0;
double a1 = a - 1;
const double EPS = 1.0e-8;
gln = Globals.gammln(a);
if (a <= 0.0)
{
throw new Exception("a must be pos in invgammap");
}
if (p >= 1.0)
{
return Math.Max(100.0, a + 100.0 * Math.Sqrt(a));
}
if (p <= 0.0)
{
return 0.0;
}
double t;
double x;
if (a > 1.0)
{
lna1 = Math.Log(a1);
afac = Math.Exp(a1 * (lna1 - 1.0) - gln);
double pp = (p < 0.5) ? p : 1.0 - p;
t = Math.Sqrt(-2.0 * Math.Log(pp));
x = (2.30753 + t * 0.27061) / (1.0 + t * (0.99229 + t * 0.04481)) - t;
if (p < 0.5)
{
x = -x;
}
x = Math.Max(1.0e-3, a * Math.Pow(1.0 - 1.0 / (9.0 * a) - x / (3.0 * Math.Sqrt(a)), 3));
}
else
{
t = 1.0 - a * (0.253 + a * 0.12);
if (p < t)
{
x = Math.Pow(p / t, 1.0 / a);
}
else
{
x = 1.0 - Math.Log(1.0 - (p - t) / (1.0 - t));
}
}
for (int j = 0; j < 12; j++)
{
if (x <= 0.0)
{
return 0.0;
}
double err = gammp(a, x) - p;
if (a > 1.0)
{
t = afac * Math.Exp(-(x - a1) + a1 * (Math.Log(x) - lna1));
}
else
{
t = Math.Exp(-x + a1 * Math.Log(x) - gln);
}
double u = err / t;
x -= (t = u / (1.0 - 0.5 * Math.Min(1.0, u * ((a - 1.0) / x - 1))));
if (x <= 0.0)
{
x = 0.5 * (x + t);
}
if (Math.Abs(t) < EPS * x)
{
break;
}
}
return x;
}
}
}