using System;
namespace Legalsoft.Truffer
{
public class Integrals
{
public Integrals()
{
}
///
/// Exponential Integrals
///
///
///
///
///
public static double expint(int n, double x)
{
const int MAXIT = 100;
const double EULER = 0.577215664901533;
const double EPS = float.Epsilon; //numeric_limits
const double BIG = double.MaxValue * EPS;
int nm1 = n - 1;
//if (n < 0 || x < 0.0 || (x == 0.0 && (n == 0 || n == 1)))
if (n < 0 || x < 0.0 || (Math.Abs(x) <= float.Epsilon && (n == 0 || n == 1)))
{
throw new Exception("bad arguments in expint");
}
double ans;
if (n == 0)
{
ans = Math.Exp(-x) / x;
}
else
{
//if (x == 0.0)
if (Math.Abs(x) <= float.Epsilon)
{
ans = 1.0 / nm1;
}
else
{
if (x > 1.0)
{
double b = x + n;
double c = BIG;
double d = 1.0 / b;
double h = d;
for (int i = 1; i <= MAXIT; i++)
{
double a = -i * (nm1 + i);
b += 2.0;
d = 1.0 / (a * d + b);
c = b + a / c;
double del = c * d;
h *= del;
if (Math.Abs(del - 1.0) <= EPS)
{
ans = h * Math.Exp(-x);
return ans;
}
}
throw new Exception("continued fraction failed in expint");
}
else
{
ans = (nm1 != 0 ? 1.0 / nm1 : -Math.Log(x) - EULER);
double fact = 1.0;
double del;
for (int i = 1; i <= MAXIT; i++)
{
fact *= -x / i;
if (i != nm1)
{
del = -fact / (i - nm1);
}
else
{
double psi = -EULER;
for (int ii = 1; ii <= nm1; ii++)
{
psi += 1.0 / ii;
}
del = fact * (-Math.Log(x) + psi);
}
ans += del;
if (Math.Abs(del) < Math.Abs(ans) * EPS)
{
return ans;
}
}
throw new Exception("series failed in expint");
}
}
}
return ans;
}
///
/// Exponential Integrals
///
///
///
///
public static double ei(double x)
{
const int MAXIT = 100;
const double EULER = 0.577215664901533;
const double EPS = float.Epsilon; //numeric_limits
const double FPMIN = float.MinValue;
if (x <= 0.0)
{
throw new Exception("Bad argument in ei");
}
if (x < FPMIN)
{
return Math.Log(x) + EULER;
}
if (x <= -Math.Log(EPS))
{
double sum = 0.0;
double fact = 1.0;
int k = 1;
for (; k <= MAXIT; k++)
{
fact *= x / k;
double term = fact / k;
sum += term;
if (term < EPS * sum)
{
break;
}
}
if (k > MAXIT)
{
throw new Exception("Series failed in ei");
}
return sum + Math.Log(x) + EULER;
}
else
{
double sum = 0.0;
double term = 1.0;
for (int k = 1; k <= MAXIT; k++)
{
double prev = term;
term *= k / x;
if (term < EPS)
{
break;
}
if (term < prev)
{
sum += term;
}
else
{
sum -= prev;
break;
}
}
return Math.Exp(x) * (1.0 + sum) / x;
}
}
///
/// Fermi-Dirac integrals
///
///
///
///
public static Complex frenel(double x)
{
const int MAXIT = 100;
const double PIBY2 = (Math.PI / 2.0);
const double XMIN = 1.5;
const double EPS = float.Epsilon; //numeric_limits
const double FPMIN = float.MinValue;
const double BIG = double.MaxValue * EPS;
Complex cs = new Complex();
double ax = Math.Abs(x);
if ((ax) < Math.Sqrt(FPMIN))
{
cs = new Complex(ax);
}
else if (ax <= XMIN)
{
double sum = 0.0;
double sums = 0.0;
double sumc = ax;
double sign = 1.0;
double fact = PIBY2 * ax * ax;
bool odd = true;
double term = ax;
int n = 3;
int k = 1;
for (; k <= MAXIT; k++)
{
term *= fact / k;
sum += sign * term / n;
double test = Math.Abs(sum) * EPS;
if (odd)
{
sign = -sign;
sums = sum;
sum = sumc;
}
else
{
sumc = sum;
sum = sums;
}
if (term < test)
{
break;
}
odd = !odd;
n += 2;
}
if (k > MAXIT)
{
throw new Exception("series failed in frenel");
}
cs = new Complex(sumc, sums);
}
else
{
double pix2 = Math.PI * ax * ax;
Complex b = new Complex(1.0, -pix2);
Complex cc = new Complex(BIG);
Complex h = 1.0 / b;
Complex d = h;
int n = -1;
int k = 2;
for (; k <= MAXIT; k++)
{
n += 2;
int a = -n * (n + 1);
b += 4.0;
d = 1.0 / (a * d + b);
cc = b + a / cc;
Complex del = cc * d;
h *= del;
if (Math.Abs((del.re) - 1.0) + Math.Abs((del.im)) <= EPS)
{
break;
}
}
if (k > MAXIT)
{
throw new Exception("cf failed in frenel");
}
h = h * new Complex(ax, -ax);
cs = new Complex(0.5, 0.5) * (new Complex(1.0) - new Complex(Math.Cos(0.5 * pix2), Math.Sin(0.5 * pix2)) * h);
}
if (x < 0.0)
{
cs = -cs;
}
return cs;
}
///
/// cosine and sine integrals
///
///
///
///
public static Complex cisi(double x)
{
const int MAXIT = 100;
const double EULER = 0.577215664901533;
const double PIBY2 = 1.570796326794897;
const double TMIN = 2.0;
double EPS = float.Epsilon;
double FPMIN = float.MinValue * 4.0;// numeric_limits
double BIG = double.MaxValue * EPS;
Complex cs;
double t = Math.Abs(x);
//if ((t) == 0.0)
if (Math.Abs(t) <= float.Epsilon)
{
return new Complex(-BIG, -BIG);
}
if (t > TMIN)
{
Complex b = new Complex(1.0, t);
Complex c = new Complex(BIG, 0.0);
Complex d = 1.0 / b;
Complex h = d;
int i;
for (i = 1; i < MAXIT; i++)
{
double a = -i * i;
b = b + 2.0;
d = 1.0 / (a * d + b);
c = (b + a / c);
Complex del = (c * d);
h *= del;
if (Math.Abs((del.re) - 1.0) + Math.Abs((del.im)) <= EPS)
{
break;
}
}
if (i >= MAXIT)
{
throw new Exception("cf failed in cisi");
}
h = new Complex(Math.Cos(t), -Math.Sin(t)) * h;
cs = -h.conj() + new Complex(0.0, PIBY2);
}
else
{
double err;
double sign;
double fact;
double sum;
double sumc;
double sums;
if (t < Math.Sqrt(FPMIN))
{
sumc = 0.0;
sums = t;
}
else
{
sum = sums = sumc = 0.0;
sign = fact = 1.0;
bool odd = true;
int k;
for (k = 1; k <= MAXIT; k++)
{
fact *= t / k;
double term = fact / k;
sum += sign * term;
err = term / Math.Abs(sum);
if (odd)
{
sign = -sign;
sums = sum;
sum = sumc;
}
else
{
sumc = sum;
sum = sums;
}
if (err < EPS)
{
break;
}
odd = !odd;
}
if (k > MAXIT)
{
throw new Exception("maxits exceeded in cisi");
}
}
cs = new Complex(sumc + Math.Log(t) + EULER, sums);
}
if (x < 0.0)
{
cs = cs.conj();
}
return cs;
}
///
/// Dawson's integral
///
///
///
public static double dawson(double x)
{
const double H = 0.4;
const double A1 = 2.0 / 3.0;
const double A2 = 0.4;
const double A3 = 2.0 / 7.0;
const int NMAX = 6;
double[] c = new double[NMAX];
for (int i = 0; i < NMAX; i++)
{
c[i] = Math.Exp(-Globals.SQR((2.0 * i + 1.0) * H));
}
double ans;
if (Math.Abs(x) < 0.2)
{
double x2 = x * x;
ans = x * (1.0 - A1 * x2 * (1.0 - A2 * x2 * (1.0 - A3 * x2)));
}
else
{
double xx = Math.Abs(x);
int n0 = 2 * (int)(0.5 * xx / H + 0.5);
double xp = xx - n0 * H;
double e1 = Math.Exp(2.0 * xp * H);
double e2 = e1 * e1;
double d1 = n0 + 1;
double d2 = d1 - 2.0;
double sum = 0.0;
for (int i = 0; i < NMAX; i++, d1 += 2.0, d2 -= 2.0, e1 *= e2)
{
sum += c[i] * (e1 / d1 + 1.0 / (d2 * e1));
}
ans = 0.5641895835 * Globals.SIGN(Math.Exp(-xp * xp), x) * sum;
}
return ans;
}
public static double invxlogx(double y)
{
const double ooe = 0.367879441171442322;
double to = 0.0;
if (y >= 0.0 || y <= -ooe)
{
throw new Exception("no such inverse value");
}
double u;
if (y < -0.2)
{
u = Math.Log(ooe - Math.Sqrt(2 * ooe * (y + ooe)));
}
else
{
u = -10.0;
}
double t;
do
{
u += (t = (Math.Log(y / u) - u) * (u / (1.0 + u)));
if (t < 1.0e-8 && Math.Abs(t + to) < 0.01 * Math.Abs(t))
{
break;
}
to = t;
} while (Math.Abs(t / u) > 1.0e-15);
return Math.Exp(u);
}
}
}