357 lines
7.6 KiB
MQL5
357 lines
7.6 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| gamma.mqh |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#include "config.mqh"
|
|
#include "error.mqh"
|
|
#include "const.mqh"
|
|
#include "polevl.mqh"
|
|
#include "trig.mqh"
|
|
namespace special
|
|
{
|
|
namespace cephes
|
|
{
|
|
namespace detail
|
|
{
|
|
double gamma_P[] = {1.60119522476751861407E-4, 1.19135147006586384913E-3, 1.04213797561761569935E-2,
|
|
4.76367800457137231464E-2, 2.07448227648435975150E-1, 4.94214826801497100753E-1,
|
|
9.99999999999999996796E-1
|
|
};
|
|
|
|
double gamma_Q[] = {-2.31581873324120129819E-5, 5.39605580493303397842E-4, -4.45641913851797240494E-3,
|
|
1.18139785222060435552E-2, 3.58236398605498653373E-2, -2.34591795718243348568E-1,
|
|
7.14304917030273074085E-2, 1.00000000000000000320E0
|
|
};
|
|
|
|
/* Stirling's formula for the Gamma function */
|
|
double gamma_STIR[5] =
|
|
{
|
|
7.87311395793093628397E-4, -2.29549961613378126380E-4, -2.68132617805781232825E-3,
|
|
3.47222221605458667310E-3, 8.33333333333482257126E-2,
|
|
};
|
|
|
|
double MAXSTIR = 143.01608;
|
|
|
|
/* Gamma function computed by Stirling's formula.
|
|
* The polynomial STIR is valid for 33 <= x <= 172.
|
|
*/
|
|
inline double stirf(double x)
|
|
{
|
|
double y, w, v;
|
|
|
|
if(x >= MAXGAM)
|
|
{
|
|
return (infinity());
|
|
}
|
|
w = 1.0 / x;
|
|
w = 1.0 + w * special::cephes::polevl(w, gamma_STIR, 4);
|
|
y = exp(x);
|
|
if(x > MAXSTIR) /* Avoid overflow in pow() */
|
|
{
|
|
v = pow(x, 0.5 * x - 0.25);
|
|
y = v * (v / y);
|
|
}
|
|
else
|
|
{
|
|
y = pow(x, x - 0.5) / y;
|
|
}
|
|
y = SQRTPI * y * w;
|
|
return (y);
|
|
}
|
|
} // namespace detail
|
|
|
|
inline double Gamma(double x)
|
|
{
|
|
double p, q, z;
|
|
int i;
|
|
int sgngam = 1;
|
|
|
|
if(!isfinite(x))
|
|
{
|
|
return x;
|
|
}
|
|
q = abs(x);
|
|
|
|
if(q > 33.0)
|
|
{
|
|
if(x < 0.0)
|
|
{
|
|
p = floor(q);
|
|
if(p == q)
|
|
{
|
|
set_error("Gamma", SF_ERROR_OVERFLOW, NULL);
|
|
return (infinity());
|
|
}
|
|
i = (int)p;
|
|
if((i & 1) == 0)
|
|
{
|
|
sgngam = -1;
|
|
}
|
|
z = q - p;
|
|
if(z > 0.5)
|
|
{
|
|
p += 1.0;
|
|
z = q - p;
|
|
}
|
|
z = q * sinpi(z);
|
|
if(z == 0.0)
|
|
{
|
|
return (sgngam * infinity());
|
|
}
|
|
z = abs(z);
|
|
z = M_PI / (z * detail::stirf(q));
|
|
}
|
|
else
|
|
{
|
|
z = detail::stirf(x);
|
|
}
|
|
return (sgngam * z);
|
|
}
|
|
|
|
z = 1.0;
|
|
while(x >= 3.0)
|
|
{
|
|
x -= 1.0;
|
|
z *= x;
|
|
}
|
|
|
|
while(x < 0.0)
|
|
{
|
|
if(x > -1.E-9)
|
|
{
|
|
if(x == 0.0)
|
|
{
|
|
set_error("Gamma", SF_ERROR_OVERFLOW, NULL);
|
|
return (infinity());
|
|
}
|
|
else
|
|
return (z / ((1.0 + 0.5772156649015329 * x) * x));
|
|
}
|
|
z /= x;
|
|
x += 1.0;
|
|
}
|
|
|
|
while(x < 2.0)
|
|
{
|
|
if(x < 1.e-9)
|
|
{
|
|
if(x == 0.0)
|
|
{
|
|
set_error("Gamma", SF_ERROR_OVERFLOW, NULL);
|
|
return (infinity());
|
|
}
|
|
else
|
|
return (z / ((1.0 + 0.5772156649015329 * x) * x));
|
|
}
|
|
z /= x;
|
|
x += 1.0;
|
|
}
|
|
|
|
if(x == 2.0)
|
|
{
|
|
return (z);
|
|
}
|
|
|
|
x -= 2.0;
|
|
p = polevl(x, detail::gamma_P, 6);
|
|
q = polevl(x, detail::gamma_Q, 7);
|
|
return (z * p / q);
|
|
|
|
if(x == 0.0)
|
|
{
|
|
set_error("Gamma", SF_ERROR_OVERFLOW, NULL);
|
|
return (infinity());
|
|
}
|
|
else
|
|
return (z / ((1.0 + 0.5772156649015329 * x) * x));
|
|
}
|
|
|
|
namespace detail
|
|
{
|
|
/* A[]: Stirling's formula expansion of log Gamma
|
|
* B[], C[]: log Gamma function between 2 and 3
|
|
*/
|
|
double gamma_A[] = {8.11614167470508450300E-4, -5.95061904284301438324E-4, 7.93650340457716943945E-4,
|
|
-2.77777777730099687205E-3, 8.33333333333331927722E-2
|
|
};
|
|
|
|
double gamma_B[] = {-1.37825152569120859100E3, -3.88016315134637840924E4, -3.31612992738871184744E5,
|
|
-1.16237097492762307383E6, -1.72173700820839662146E6, -8.53555664245765465627E5
|
|
};
|
|
|
|
double gamma_C[] =
|
|
{
|
|
/* 1.00000000000000000000E0, */
|
|
-3.51815701436523470549E2, -1.70642106651881159223E4, -2.20528590553854454839E5,
|
|
-1.13933444367982507207E6, -2.53252307177582951285E6, -2.01889141433532773231E6
|
|
};
|
|
|
|
/* log( sqrt( 2*pi ) ) */
|
|
double LS2PI = 0.91893853320467274178;
|
|
|
|
double MAXLGM = 2.556348e305;
|
|
|
|
/* Disable optimizations for this function on 32 bit systems when compiling with GCC.
|
|
* We've found that enabling optimizations can result in degraded precision
|
|
* for this asymptotic approximation in that case. */
|
|
inline double lgam_large_x(double x)
|
|
{
|
|
double q = (x - 0.5) * log(x) - x + LS2PI;
|
|
if(x > 1.0e8)
|
|
{
|
|
return (q);
|
|
}
|
|
double p = 1.0 / (x * x);
|
|
p = ((7.9365079365079365079365e-4 * p - 2.7777777777777777777778e-3) * p + 0.0833333333333333333333) / x;
|
|
return q + p;
|
|
}
|
|
|
|
inline double lgam_sgn(double x, int &sign)
|
|
{
|
|
double p, q, u, w, z;
|
|
int i;
|
|
|
|
sign = 1;
|
|
|
|
if(!isfinite(x))
|
|
{
|
|
return x;
|
|
}
|
|
|
|
if(x < -34.0)
|
|
{
|
|
q = -x;
|
|
w = lgam_sgn(q, sign);
|
|
p = floor(q);
|
|
if(p == q)
|
|
{
|
|
set_error("lgam", SF_ERROR_SINGULAR, NULL);
|
|
return (infinity());
|
|
}
|
|
i = (int)p;
|
|
if((i & 1) == 0)
|
|
{
|
|
sign = -1;
|
|
}
|
|
else
|
|
{
|
|
sign = 1;
|
|
}
|
|
z = q - p;
|
|
if(z > 0.5)
|
|
{
|
|
p += 1.0;
|
|
z = p - q;
|
|
}
|
|
z = q * sinpi(z);
|
|
if(z == 0.0)
|
|
{
|
|
set_error("lgam", SF_ERROR_SINGULAR, NULL);
|
|
return (infinity());
|
|
}
|
|
/* z = log(M_PI) - log( z ) - w; */
|
|
z = LOGPI - log(z) - w;
|
|
return (z);
|
|
}
|
|
|
|
if(x < 13.0)
|
|
{
|
|
z = 1.0;
|
|
p = 0.0;
|
|
u = x;
|
|
while(u >= 3.0)
|
|
{
|
|
p -= 1.0;
|
|
u = x + p;
|
|
z *= u;
|
|
}
|
|
while(u < 2.0)
|
|
{
|
|
if(u == 0.0)
|
|
{
|
|
set_error("lgam", SF_ERROR_SINGULAR, NULL);
|
|
return (infinity());
|
|
}
|
|
z /= u;
|
|
p += 1.0;
|
|
u = x + p;
|
|
}
|
|
if(z < 0.0)
|
|
{
|
|
sign = -1;
|
|
z = -z;
|
|
}
|
|
else
|
|
{
|
|
sign = 1;
|
|
}
|
|
if(u == 2.0)
|
|
{
|
|
return (log(z));
|
|
}
|
|
p -= 2.0;
|
|
x = x + p;
|
|
p = x * polevl(x, gamma_B, 5) / p1evl(x, gamma_C, 6);
|
|
return (log(z) + p);
|
|
}
|
|
|
|
if(x > MAXLGM)
|
|
{
|
|
return (sign * infinity());
|
|
}
|
|
|
|
if(x >= 1000.0)
|
|
{
|
|
return lgam_large_x(x);
|
|
}
|
|
|
|
q = (x - 0.5) * log(x) - x + LS2PI;
|
|
p = 1.0 / (x * x);
|
|
return q + polevl(p, gamma_A, 4) / x;
|
|
}
|
|
} // namespace detail
|
|
|
|
/* Logarithm of Gamma function */
|
|
inline double lgam(double x)
|
|
{
|
|
int sign;
|
|
return detail::lgam_sgn(x, sign);
|
|
}
|
|
|
|
/* Sign of the Gamma function */
|
|
inline double gammasgn(double x)
|
|
{
|
|
double fx;
|
|
|
|
if(isnan(x))
|
|
{
|
|
return x;
|
|
}
|
|
if(x > 0)
|
|
{
|
|
return 1.0;
|
|
}
|
|
else
|
|
{
|
|
fx = floor(x);
|
|
if(x - fx == 0.0)
|
|
{
|
|
return 0.0;
|
|
}
|
|
else
|
|
if(fmod(int(fx), 2)!=0)
|
|
{
|
|
return -1.0;
|
|
}
|
|
else
|
|
{
|
|
return 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace cephes
|
|
} // namespace special
|