| //+------------------------------------------------------------------+
|
| //| specialfunctions.mqh |
|
| //| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) |
|
| //| Copyright 2012-2023, MetaQuotes Ltd. |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| //| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
| //| |
|
| //| The features of the library include: |
|
| //| - Linear algebra (direct algorithms, EVD, SVD) |
|
| //| - Solving systems of linear and non-linear equations |
|
| //| - Interpolation |
|
| //| - Optimization |
|
| //| - FFT (Fast Fourier Transform) |
|
| //| - Numerical integration |
|
| //| - Linear and nonlinear least-squares fitting |
|
| //| - Ordinary differential equations |
|
| //| - Computation of special functions |
|
| //| - Descriptive statistics and hypothesis testing |
|
| //| - Data analysis - classification, regression |
|
| //| - Implementing linear algebra algorithms, interpolation, etc. |
|
| //| in high-precision arithmetic (using MPFR) |
|
| //| |
|
| //| This file is free software; you can redistribute it and/or |
|
| //| modify it under the terms of the GNU General Public License as |
|
| //| published by the Free Software Foundation (www.fsf.org); either |
|
| //| version 2 of the License, or (at your option) any later version. |
|
| //| |
|
| //| This program is distributed in the hope that it will be useful, |
|
| //| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| //| GNU General Public License for more details. |
|
| //+------------------------------------------------------------------+
|
| #include "ap.mqh"
|
| #include "alglibinternal.mqh"
|
| //+------------------------------------------------------------------+
|
| //| Gamma function |
|
| //+------------------------------------------------------------------+
|
| class CGammaFunc
|
| {
|
| public:
|
| static double GammaFunc(double x);
|
| static double LnGamma(double x,double &sgngam);
|
|
|
| private:
|
| static double GammaStirlFunc(double x);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Gamma function |
|
| //| Input parameters: |
|
| //| X - argument |
|
| //| Domain: |
|
| //| 0 < X < 171.6 |
|
| //| -170 < X < 0, X is not an integer. |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -170,-33 20000 2.3e-15 3.3e-16 |
|
| //| IEEE -33, 33 20000 9.4e-16 2.2e-16 |
|
| //| IEEE 33, 171.6 20000 2.3e-15 3.2e-16 |
|
| //+------------------------------------------------------------------+
|
| double CGammaFunc::GammaFunc(double x)
|
| {
|
| //--- create variables
|
| double p;
|
| double pp;
|
| double q=MathAbs(x);
|
| double qq;
|
| double z;
|
| int i;
|
| double sgngam=1;
|
| //--- check
|
| if(q>33.0)
|
| {
|
| //--- check
|
| if(x<0.0)
|
| {
|
| p=(int)MathFloor(q);
|
| i=(int)MathRound(p);
|
| //--- check
|
| if(i%2==0)
|
| sgngam=-1;
|
| z=q-p;
|
| //--- check
|
| if(z>0.5)
|
| {
|
| p++;
|
| z=q-p;
|
| }
|
| //--- calculation z
|
| z=q*MathSin(M_PI*z);
|
| z=MathAbs(z);
|
| z=M_PI/(z*GammaStirlFunc(q));
|
| }
|
| else
|
| z=GammaStirlFunc(x);
|
| //--- return result
|
| return(sgngam*z);
|
| }
|
| //--- q<=33
|
| z=1;
|
| while(x>=3)
|
| {
|
| x--;
|
| z*=x;
|
| }
|
| //--- x<0
|
| while(x<0)
|
| {
|
| //--- check
|
| if(x>-0.000000001)
|
| return(z/((1+0.5772156649015329*x)*x));
|
| z/=x;
|
| x++;
|
| }
|
| //--- 0<x<2
|
| while(x<2)
|
| {
|
| //--- check
|
| if(x<0.000000001)
|
| return(z/((1+0.5772156649015329*x)*x));
|
| z=z/x;
|
| x=x+1.0;
|
| }
|
| //--- check
|
| if(x==2)
|
| return(z);
|
| //--- calculation
|
| x-=2.0;
|
| pp=1.60119522476751861407E-4;
|
| pp=1.19135147006586384913E-3+x*pp;
|
| pp=1.04213797561761569935E-2+x*pp;
|
| pp=4.76367800457137231464E-2+x*pp;
|
| pp=2.07448227648435975150E-1+x*pp;
|
| pp=4.94214826801497100753E-1+x*pp;
|
| pp=9.99999999999999996796E-1+x*pp;
|
| qq=-2.31581873324120129819E-5;
|
| qq=5.39605580493303397842E-4+x*qq;
|
| qq=-4.45641913851797240494E-3+x*qq;
|
| qq=1.18139785222060435552E-2+x*qq;
|
| qq=3.58236398605498653373E-2+x*qq;
|
| qq=-2.34591795718243348568E-1+x*qq;
|
| qq=7.14304917030273074085E-2+x*qq;
|
| qq=1.00000000000000000320+x*qq;
|
| //--- return result
|
| return(z*pp/qq);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Natural logarithm of gamma function |
|
| //| Input parameters: |
|
| //| X - argument |
|
| //| Result: |
|
| //| logarithm of the absolute value of the Gamma(X). |
|
| //| Output parameters: |
|
| //| SgnGam - sign(Gamma(X)) |
|
| //| Domain: |
|
| //| 0 < X < 2.55e305 |
|
| //| -2.55e305 < X < 0, X is not an integer. |
|
| //| ACCURACY: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 3 28000 5.4e-16 1.1e-16 |
|
| //| IEEE 2.718, 2.556e305 40000 3.5e-16 8.3e-17 |
|
| //| The error criterion was relative when the function magnitude |
|
| //| was greater than one but absolute when it was less than one. |
|
| //| The following test used the relative error criterion, though |
|
| //| at certain points the relative error could be much higher than |
|
| //| indicated. |
|
| //| IEEE -200, -4 10000 4.8e-16 1.3e-16 |
|
| //+------------------------------------------------------------------+
|
| double CGammaFunc::LnGamma(double x,double &sgngam)
|
| {
|
| //--- create variables
|
| double a;
|
| double b;
|
| double c;
|
| double p;
|
| double q;
|
| double u;
|
| double w;
|
| double z;
|
| int i;
|
| double logpi;
|
| double ls2pi;
|
| double tmp=0;
|
| //--- initialization
|
| sgngam=1;
|
| logpi=1.14472988584940017414;
|
| ls2pi=0.91893853320467274178;
|
| //--- check
|
| if(x<-34.0)
|
| {
|
| q=-x;
|
| w=LnGamma(q,tmp);
|
| p=(int)MathFloor(q);
|
| i=(int)MathRound(p);
|
| //--- check
|
| if(i%2==0)
|
| sgngam=-1;
|
| else
|
| sgngam=1;
|
| z=q-p;
|
| //--- check
|
| if(z>0.5)
|
| {
|
| p++;
|
| z=p-q;
|
| }
|
| z=q*MathSin(M_PI*z);
|
| //--- return result
|
| return(logpi-MathLog(z)-w);
|
| }
|
| //--- x>=-34
|
| if(x<13)
|
| {
|
| z=1;
|
| p=0;
|
| u=x;
|
| //--- cycle
|
| while(u>=3)
|
| {
|
| p--;
|
| u=x+p;
|
| z*=u;
|
| }
|
| //--- cycle
|
| while(u<2)
|
| {
|
| z/=u;
|
| p++;
|
| u=x+p;
|
| }
|
| //--- check
|
| if(z<0)
|
| {
|
| sgngam=-1;
|
| z=-z;
|
| }
|
| else
|
| sgngam=1;
|
| //--- check
|
| if(u==2)
|
| return(MathLog(z));
|
| //--- calculation
|
| p-=2;
|
| x+=p;
|
| b=-1378.25152569120859100;
|
| b=-38801.6315134637840924+x*b;
|
| b=-331612.992738871184744+x*b;
|
| b=-1162370.97492762307383+x*b;
|
| b=-1721737.00820839662146+x*b;
|
| b=-853555.664245765465627+x*b;
|
| c=1;
|
| c=-351.815701436523470549+x*c;
|
| c=-17064.2106651881159223+x*c;
|
| c=-220528.590553854454839+x*c;
|
| c=-1139334.44367982507207+x*c;
|
| c=-2532523.07177582951285+x*c;
|
| c=-2018891.41433532773231+x*c;
|
| p=x*b/c;
|
| //--- return result
|
| return(MathLog(z)+p);
|
| }
|
| //--- x>=13
|
| q=(x-0.5)*MathLog(x)-x+ls2pi;
|
| //--- check
|
| if(x>100000000)
|
| return(q);
|
| //--- change value
|
| p=1/(x*x);
|
| //--- check
|
| if(x>=1000.0)
|
| q+=((7.9365079365079365079365*0.0001*p-2.7777777777777777777778*0.001)*p+0.0833333333333333333333)/x;
|
| else
|
| {
|
| a=8.11614167470508450300*0.0001;
|
| a=-(5.95061904284301438324*0.0001)+p*a;
|
| a=7.93650340457716943945*0.0001+p*a;
|
| a=-(2.77777777730099687205*0.001)+p*a;
|
| a=8.33333333333331927722*0.01+p*a;
|
| q+=a/x;
|
| }
|
| //--- return result
|
| return(q);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Stirling function |
|
| //+------------------------------------------------------------------+
|
| double CGammaFunc::GammaStirlFunc(double x)
|
| {
|
| //--- create variables
|
| double y;
|
| double w;
|
| double v;
|
| double stir;
|
| //--- initialization
|
| w=1/x;
|
| stir=7.87311395793093628397E-4;
|
| stir=-2.29549961613378126380E-4+w*stir;
|
| stir=-2.68132617805781232825E-3+w*stir;
|
| stir=3.47222221605458667310E-3+w*stir;
|
| stir=8.33333333333482257126E-2+w*stir;
|
| w=1+w*stir;
|
| y=MathExp(x);
|
| //--- check
|
| if(x>143.01608)
|
| {
|
| v=MathPow(x,0.5*x-0.25);
|
| y=v*(v/y);
|
| }
|
| else
|
| y=MathPow(x,x-0.5)/y;
|
| //--- return result
|
| return(2.50662827463100050242*y*w);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Normal distribution |
|
| //+------------------------------------------------------------------+
|
| class CNormalDistr
|
| {
|
| public:
|
| //--- methods
|
| static double ErrorFunction(double x);
|
| static double ErrorFunctionC(double x);
|
| static double NormalDistribution(const double x);
|
| static double NormalPDF(const double x);
|
| static double NormalCDF(const double x);
|
| static double InvErF(const double e);
|
| static double InvNormalDistribution(double y0);
|
| static double InvNormalCDF(const double y0);
|
| static double BivariateNormalPDF(const double x,const double y,const double rho);
|
| static double BivariateNormalCDF(double x,double y,const double rho);
|
|
|
| private:
|
| static double BVNIntegrate3(double rangea,double rangeb,double x,double y,double gw,double gx);
|
| static double BVNIntegrate6(double rangea,double rangeb,double x,double y,double s,double gw,double gx);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Error function |
|
| //| The integral is |
|
| //| x |
|
| //| - |
|
| //| 2 | | 2 |
|
| //| erf(x) = -------- | exp( - t ) dt. |
|
| //| sqrt(pi) | | |
|
| //| - |
|
| //| 0 |
|
| //| For 0 <= |x| < 1, erf(x) = x * P4(x**2)/Q5(x**2); otherwise |
|
| //| erf(x) = 1 - erfc(x). |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,1 30000 3.7e-16 1.0e-16 |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::ErrorFunction(double x)
|
| {
|
| //--- create variables
|
| double xsq=0;
|
| double s=0;
|
| double p=0;
|
| double q=0;
|
| //--- initialization
|
| s=MathSign(x);
|
| //--- change value
|
| x=MathAbs(x);
|
| //--- check
|
| if(x<0.5)
|
| {
|
| //--- calculation
|
| xsq=x*x;
|
| p=0.007547728033418631287834;
|
| p=-0.288805137207594084924010+xsq*p;
|
| p=14.3383842191748205576712+xsq*p;
|
| p=38.0140318123903008244444+xsq*p;
|
| p=3017.82788536507577809226+xsq*p;
|
| p=7404.07142710151470082064+xsq*p;
|
| p=80437.3630960840172832162+xsq*p;
|
| //---
|
| q=38.0190713951939403753468+xsq;
|
| q=658.070155459240506326937+xsq*q;
|
| q=6379.60017324428279487120+xsq*q;
|
| q=34216.5257924628539769006+xsq*q;
|
| q=80437.3630960840172826266+xsq*q;
|
| //--- return result
|
| return(s*1.1283791670955125738961589031*x*p/q);
|
| }
|
| //--- check
|
| if(x>=10)
|
| return(s);
|
| //--- return result
|
| return(s*(1-ErrorFunctionC(x)));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complementary error function |
|
| //| 1 - erf(x) = |
|
| //| inf. |
|
| //| - |
|
| //| 2 | | 2 |
|
| //| erfc(x) = -------- | exp( - t ) dt |
|
| //| sqrt(pi) | | |
|
| //| - |
|
| //| x |
|
| //| For small x, erfc(x) = 1 - erf(x); otherwise rational |
|
| //| approximations are computed. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,26.6417 30000 5.7e-14 1.5e-14 |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::ErrorFunctionC(double x)
|
| {
|
| //--- create variables
|
| double p=0;
|
| double q=0;
|
| //--- check
|
| if(x<0.0)
|
| return(2-ErrorFunctionC(-x));
|
| //--- check
|
| if(x<0.5)
|
| return(1.0-ErrorFunction(x));
|
| //--- check
|
| if(x>=10)
|
| return(0);
|
| //--- calculation
|
| p=0.5641877825507397413087057563;
|
| p=9.675807882987265400604202961+x*p;
|
| p=77.08161730368428609781633646+x*p;
|
| p=368.5196154710010637133875746+x*p;
|
| p=1143.262070703886173606073338+x*p;
|
| p=2320.439590251635247384768711+x*p;
|
| p=2898.0293292167655611275846+x*p;
|
| p=1826.3348842295112592168999+x*p;
|
|
|
| q=17.14980943627607849376131193+x;
|
| q=137.1255960500622202878443578+x*q;
|
| q=661.7361207107653469211984771+x*q;
|
| q=2094.384367789539593790281779+x*q;
|
| q=4429.612803883682726711528526+x*q;
|
| q=6089.5424232724435504633068+x*q;
|
| q=4958.82756472114071495438422+x*q;
|
| q=1826.3348842295112595576438+x*q;
|
| //--- return result
|
| return(MathExp(-CMath::Sqr(x))*p/q);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Normal distribution function |
|
| //| Returns the area under the Gaussian probability density |
|
| //| function, integrated from minus infinity to x: |
|
| //| x |
|
| //| - |
|
| //| 1 | | 2 |
|
| //| ndtr(x) = --------- | exp( - t /2 ) dt |
|
| //| sqrt(2pi) | | |
|
| //| - |
|
| //| -inf. |
|
| //| = ( 1 + erf(z) ) / 2 |
|
| //| = erfc(z) / 2 |
|
| //| where z = x/sqrt(2). Computation is via the functions |
|
| //| erf and erfc. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -13,0 30000 3.4e-14 6.7e-15 |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::NormalDistribution(const double x)
|
| {
|
| return(0.5*(ErrorFunction(x/1.41421356237309504880)+1));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Normal distribution PDF |
|
| //| Returns Gaussian probability density function: |
|
| //| 1 |
|
| //| f(x) = --------- * exp(-x^2/2) |
|
| //| sqrt(2pi) |
|
| //| Cephes Math Library Release 2.8: June, 2000 |
|
| //| Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::NormalPDF(const double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert(MathIsValidNumber(x),"NormalPDF: X is infinite"))
|
| return(0);
|
|
|
| double result=MathExp(-(x*x/2))/MathSqrt(2*M_PI);
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Normal distribution CDF |
|
| //| Returns the area under the Gaussian probability density |
|
| //| function, integrated from minus infinity to x: |
|
| //| x |
|
| //| - |
|
| //| 1 | | 2 |
|
| //| ndtr(x) = --------- | exp( - t /2 ) dt |
|
| //| sqrt(2pi) | | |
|
| //| - |
|
| //| -inf. |
|
| //| |
|
| //| = ( 1 + erf(z) ) / 2 |
|
| //| = erfc(z) / 2 |
|
| //| where z = x/sqrt(2). Computation is via the functions erf and |
|
| //| erfc. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -13,0 30000 3.4e-14 6.7e-15 |
|
| //| Cephes Math Library Release 2.8: June, 2000 |
|
| //| Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::NormalCDF(const double x)
|
| {
|
| double result=0.5*(ErrorFunction(x/1.41421356237309504880)+1);
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of the error function |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::InvErF(const double e)
|
| {
|
| return(InvNormalDistribution(0.5*(e+1))/MathSqrt(2));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of Normal distribution function |
|
| //| Returns the argument, x, for which the area under the |
|
| //| Gaussian probability density function (integrated from |
|
| //| minus infinity to x) is equal to y. |
|
| //| For small arguments 0 < y < exp(-2), the program computes |
|
| //| z = sqrt( -2.0 * log(y) ); then the approximation is |
|
| //| x = z - log(z)/z - (1/z) P(1/z) / Q(1/z). |
|
| //| There are two rational functions P/Q, one for 0 < y < exp(-32) |
|
| //| and the other for y up to exp(-2). For larger arguments, |
|
| //| w = y - 0.5, and x/sqrt(2pi) = w + w**3 R(w**2)/S(w**2)). |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0.125, 1 20000 7.2e-16 1.3e-16 |
|
| //| IEEE 3e-308, 0.135 50000 4.6e-16 9.8e-17 |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::InvNormalDistribution(double y0)
|
| {
|
| //--- check
|
| if(y0<=0)
|
| return(-CMath::m_maxrealnumber);
|
| //--- check
|
| if(y0>=1)
|
| return(CMath::m_maxrealnumber);
|
| //--- create variables
|
| double expm2=0.13533528323661269189;
|
| double s2pi=2.50662827463100050242;
|
| double x;
|
| double y=y0;
|
| double z;
|
| double y2;
|
| double x0;
|
| double x1;
|
| int code=1;
|
| double p0;
|
| double q0;
|
| double p1;
|
| double q1;
|
| double p2;
|
| double q2;
|
| //--- check
|
| if(y>1.0-expm2)
|
| {
|
| y=1.0-y;
|
| code=0;
|
| }
|
| //--- check
|
| if(y>expm2)
|
| {
|
| y-=0.5;
|
| y2=y*y;
|
| p0=-59.9633501014107895267;
|
| p0=98.0010754185999661536+y2*p0;
|
| p0=-56.6762857469070293439+y2*p0;
|
| p0=13.9312609387279679503+y2*p0;
|
| p0=-1.23916583867381258016+y2*p0;
|
| q0=1;
|
| q0=1.95448858338141759834+y2*q0;
|
| q0=4.67627912898881538453+y2*q0;
|
| q0=86.3602421390890590575+y2*q0;
|
| q0=-225.462687854119370527+y2*q0;
|
| q0=200.260212380060660359+y2*q0;
|
| q0=-82.0372256168333339912+y2*q0;
|
| q0=15.9056225126211695515+y2*q0;
|
| q0=-1.18331621121330003142+y2*q0;
|
| x=y+y*y2*p0/q0;
|
| x*=s2pi;
|
| //--- return result
|
| return(x);
|
| }
|
| //--- calculation
|
| x=MathSqrt(-(2.0*MathLog(y)));
|
| x0=x-MathLog(x)/x;
|
| z=1.0/x;
|
| //--- check
|
| if(x<8.0)
|
| {
|
| p1=4.05544892305962419923;
|
| p1=31.5251094599893866154+z*p1;
|
| p1=57.1628192246421288162+z*p1;
|
| p1=44.0805073893200834700+z*p1;
|
| p1=14.6849561928858024014+z*p1;
|
| p1=2.18663306850790267539+z*p1;
|
| p1=-(1.40256079171354495875*0.1)+z*p1;
|
| p1=-(3.50424626827848203418*0.01)+z*p1;
|
| p1=-(8.57456785154685413611*0.0001)+z*p1;
|
| q1=1;
|
| q1=15.7799883256466749731+z*q1;
|
| q1=45.3907635128879210584+z*q1;
|
| q1=41.3172038254672030440+z*q1;
|
| q1=15.0425385692907503408+z*q1;
|
| q1=2.50464946208309415979+z*q1;
|
| q1=-(1.42182922854787788574*0.1)+z*q1;
|
| q1=-(3.80806407691578277194*0.01)+z*q1;
|
| q1=-(9.33259480895457427372*0.0001)+z*q1;
|
| x1=z*p1/q1;
|
| }
|
| else
|
| {
|
| p2=3.23774891776946035970;
|
| p2=6.91522889068984211695+z*p2;
|
| p2=3.93881025292474443415+z*p2;
|
| p2=1.33303460815807542389+z*p2;
|
| p2=2.01485389549179081538*0.1+z*p2;
|
| p2=1.23716634817820021358*0.01+z*p2;
|
| p2=3.01581553508235416007*0.0001+z*p2;
|
| p2=2.65806974686737550832*0.000001+z*p2;
|
| p2=6.23974539184983293730*0.000000001+z*p2;
|
| q2=1;
|
| q2=6.02427039364742014255+z*q2;
|
| q2=3.67983563856160859403+z*q2;
|
| q2=1.37702099489081330271+z*q2;
|
| q2=2.16236993594496635890*0.1+z*q2;
|
| q2=1.34204006088543189037*0.01+z*q2;
|
| q2=3.28014464682127739104*0.0001+z*q2;
|
| q2=2.89247864745380683936*0.000001+z*q2;
|
| q2=6.79019408009981274425*0.000000001+z*q2;
|
| x1=z*p2/q2;
|
| }
|
| x=x0-x1;
|
| //--- check
|
| if(code!=0)
|
| x=-x;
|
| //--- return result
|
| return(x);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of Normal CDF |
|
| //| Returns the argument, x, for which the area under the Gaussian |
|
| //| probability density function (integrated from minus infinity to |
|
| //| x) is equal to y. |
|
| //| For small arguments 0 < y < exp(-2), the program computes |
|
| //| z = sqrt( -2.0 * log(y) ); then the approximation is |
|
| //| x = z - log(z)/z - (1/z) P(1/z) / Q(1/z). |
|
| //| There are two rational functions P/Q, one for 0 < y < exp(-32) |
|
| //| and the other for y up to exp(-2). For larger arguments, |
|
| //| w = y - 0.5, and x/sqrt(2pi) = w + w**3 R(w**2)/S(w**2)). |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0.125, 1 20000 7.2e-16 1.3e-16 |
|
| //| IEEE 3e-308, 0.135 50000 4.6e-16 9.8e-17 |
|
| //| Cephes Math Library Release 2.8: June, 2000 |
|
| //| Copyright 1984, 1987, 1988, 1992, 2000 by Stephen L. Moshier |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::InvNormalCDF(const double y0)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double expm2=0;
|
| double s2pi=0;
|
| double x=0;
|
| double y=0;
|
| double z=0;
|
| double y2=0;
|
| double x0=0;
|
| double x1=0;
|
| int code=0;
|
| double p0=0;
|
| double q0=0;
|
| double p1=0;
|
| double q1=0;
|
| double p2=0;
|
| double q2=0;
|
|
|
| expm2=0.13533528323661269189;
|
| s2pi=2.50662827463100050242;
|
|
|
| if(y0<=0.0)
|
| {
|
| result=-CMath::m_maxrealnumber;
|
| return(result);
|
| }
|
|
|
| if(y0>=1.0)
|
| {
|
| result=CMath::m_maxrealnumber;
|
| return(result);
|
| }
|
| code=1;
|
| y=y0;
|
| if(y>(1.0-expm2))
|
| {
|
| y=1.0-y;
|
| code=0;
|
| }
|
| if(y>expm2)
|
| {
|
| y=y-0.5;
|
| y2=y*y;
|
| p0=-59.9633501014107895267;
|
| p0=98.0010754185999661536+y2*p0;
|
| p0=-56.6762857469070293439+y2*p0;
|
| p0=13.9312609387279679503+y2*p0;
|
| p0=-1.23916583867381258016+y2*p0;
|
| q0=1;
|
| q0=1.95448858338141759834+y2*q0;
|
| q0=4.67627912898881538453+y2*q0;
|
| q0=86.3602421390890590575+y2*q0;
|
| q0=-225.462687854119370527+y2*q0;
|
| q0=200.260212380060660359+y2*q0;
|
| q0=-82.0372256168333339912+y2*q0;
|
| q0=15.9056225126211695515+y2*q0;
|
| q0=-1.18331621121330003142+y2*q0;
|
| x=y+y*y2*p0/q0;
|
| x=x*s2pi;
|
| result=x;
|
| return(result);
|
| }
|
| x=MathSqrt(-(2.0*MathLog(y)));
|
| x0=x-MathLog(x)/x;
|
| z=1.0/x;
|
| if(x<8.0)
|
| {
|
| p1=4.05544892305962419923;
|
| p1=31.5251094599893866154+z*p1;
|
| p1=57.1628192246421288162+z*p1;
|
| p1=44.0805073893200834700+z*p1;
|
| p1=14.6849561928858024014+z*p1;
|
| p1=2.18663306850790267539+z*p1;
|
| p1=-(1.40256079171354495875*0.1)+z*p1;
|
| p1=-(3.50424626827848203418*0.01)+z*p1;
|
| p1=-(8.57456785154685413611*0.0001)+z*p1;
|
| q1=1;
|
| q1=15.7799883256466749731+z*q1;
|
| q1=45.3907635128879210584+z*q1;
|
| q1=41.3172038254672030440+z*q1;
|
| q1=15.0425385692907503408+z*q1;
|
| q1=2.50464946208309415979+z*q1;
|
| q1=-(1.42182922854787788574*0.1)+z*q1;
|
| q1=-(3.80806407691578277194*0.01)+z*q1;
|
| q1=-(9.33259480895457427372*0.0001)+z*q1;
|
| x1=z*p1/q1;
|
| }
|
| else
|
| {
|
| p2=3.23774891776946035970;
|
| p2=6.91522889068984211695+z*p2;
|
| p2=3.93881025292474443415+z*p2;
|
| p2=1.33303460815807542389+z*p2;
|
| p2=2.01485389549179081538*0.1+z*p2;
|
| p2=1.23716634817820021358*0.01+z*p2;
|
| p2=3.01581553508235416007*0.0001+z*p2;
|
| p2=2.65806974686737550832*0.000001+z*p2;
|
| p2=6.23974539184983293730*0.000000001+z*p2;
|
| q2=1;
|
| q2=6.02427039364742014255+z*q2;
|
| q2=3.67983563856160859403+z*q2;
|
| q2=1.37702099489081330271+z*q2;
|
| q2=2.16236993594496635890*0.1+z*q2;
|
| q2=1.34204006088543189037*0.01+z*q2;
|
| q2=3.28014464682127739104*0.0001+z*q2;
|
| q2=2.89247864745380683936*0.000001+z*q2;
|
| q2=6.79019408009981274425*0.000000001+z*q2;
|
| x1=z*p2/q2;
|
| }
|
| x=x0-x1;
|
| if(code!=0)
|
| x=-x;
|
| //--- return result
|
| result=x;
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bivariate normal PDF |
|
| //| Returns probability density function of the bivariate Gaussian |
|
| //| with correlation parameter equal to Rho: |
|
| //| 1 ( x^2 - 2*rho*x*y + y^2 )|
|
| //| f(x,y,rho) = ----------------- * exp( - ----------------------- )|
|
| //| 2pi*sqrt(1-rho^2) ( 2*(1-rho^2) )|
|
| //| with -1<rho<+1 and arbitrary x, y. |
|
| //| This function won't fail as long as Rho is in (-1,+1) range. |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::BivariateNormalPDF(const double x,const double y,
|
| const double rho)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double onerho2=0;
|
| //--- check
|
| if(!CAp::Assert(MathIsValidNumber(x),"BivariateNormalCDF: X is infinite"))
|
| return(result);
|
| if(!CAp::Assert(MathIsValidNumber(y),"BivariateNormalCDF: Y is infinite"))
|
| return(result);
|
| if(!CAp::Assert(MathIsValidNumber(rho),"BivariateNormalCDF: Rho is infinite"))
|
| return(result);
|
| if(!CAp::Assert(-1<rho && rho<1.0,"BivariateNormalCDF: Rho is not in (-1,+1) range"))
|
| return(result);
|
|
|
| onerho2=(1-rho)*(1+rho);
|
| result=MathExp(-((x*x+y*y-2*rho*x*y)/(2*onerho2)))/(2*M_PI*MathSqrt(onerho2));
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bivariate normal CDF |
|
| //| Returns the area under the bivariate Gaussian PDF with |
|
| //| correlation parameter equal to Rho, integrated from minus |
|
| //| infinity to (x,y): |
|
| //| x y |
|
| //| - - |
|
| //| 1 | | | | |
|
| //| bvn(x,y,rho) = ------------------- | | f(u,v,rho)*du*dv |
|
| //| 2pi*sqrt(1-rho^2) | | | | |
|
| //| - - |
|
| //| -INF -INF |
|
| //| where |
|
| //| ( u^2 - 2*rho*u*v + v^2 ) |
|
| //| f(u,v,rho) = exp( - ----------------------- ) |
|
| //| ( 2*(1-rho^2) ) |
|
| //| with -1<rho<+1 and arbitrary x, y. |
|
| //| This subroutine uses high-precision approximation scheme proposed|
|
| //| by Alan Genz in "Numerical Computation of Rectangular Bivariate |
|
| //| and Trivariate Normal and t probabilities", which computes CDF |
|
| //| with absolute error roughly equal to 1e-14. |
|
| //| This function won't fail as long as Rho is in (-1,+1) range. |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::BivariateNormalCDF(double x,double y,
|
| const double rho)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double rangea=0;
|
| double rangeb=0;
|
| double s=0;
|
| double v=0;
|
| double v0=0;
|
| double v1=0;
|
| double fxys=0;
|
| double ta=0;
|
| double tb=0;
|
| double tc=0;
|
| //--- check
|
| if(!CAp::Assert(MathIsValidNumber(x),"BivariateNormalCDF: X is infinite"))
|
| return(result);
|
| if(!CAp::Assert(MathIsValidNumber(y),"BivariateNormalCDF: Y is infinite"))
|
| return(result);
|
| if(!CAp::Assert(MathIsValidNumber(rho),"BivariateNormalCDF: Rho is infinite"))
|
| return(result);
|
| if(!CAp::Assert(-1<rho && rho<1.0,"BivariateNormalCDF: Rho is not in (-1,+1) range"))
|
| return(result);
|
|
|
| if(rho==0.0)
|
| {
|
| result=NormalCDF(x)*NormalCDF(y);
|
| return(result);
|
| }
|
|
|
| if(MathAbs(rho)<=0.8)
|
| {
|
| // Rho is small, compute integral using using formula (3) by Alan Genz, integrated
|
| // by means of 10-point Gauss-Legendre quadrature
|
| rangea=0;
|
| rangeb=MathArcsin(rho);
|
| v=BVNIntegrate3(rangea,rangeb,x,y,0.2491470458134028,-0.1252334085114689);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.2491470458134028,0.1252334085114689);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.2334925365383548,-0.3678314989981802);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.2334925365383548,0.3678314989981802);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.2031674267230659,-0.5873179542866175);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.2031674267230659,0.5873179542866175);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.1600783285433462,-0.7699026741943047);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.1600783285433462,0.7699026741943047);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.1069393259953184,-0.9041172563704749);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.1069393259953184,0.9041172563704749);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.0471753363865118,-0.9815606342467192);
|
| v+=BVNIntegrate3(rangea,rangeb,x,y,0.0471753363865118,0.9815606342467192);
|
| v=v*0.5*(rangeb-rangea)/(2*M_PI);
|
| result=NormalCDF(x)*NormalCDF(y)+v;
|
| }
|
| else
|
| {
|
| // Rho is large, compute integral using using formula (6) by Alan Genz, integrated
|
| // by means of 20-point Gauss-Legendre quadrature.
|
| x=-x;
|
| y=-y;
|
| s=MathSign(rho);
|
| if(s>0.0)
|
| fxys=NormalCDF(-MathMax(x,y));
|
| else
|
| fxys=MathMax(0.0,NormalCDF(-x)-NormalCDF(y));
|
| rangea=0;
|
| rangeb=MathSqrt(1-rho*rho);
|
| // Compute first term (analytic integral) from formula (6)
|
| ta=rangeb;
|
| tb=MathAbs(x-s*y);
|
| tc=(4-s*x*y)/8;
|
| v0=ta*(1-tc*(tb*tb-ta*ta)/3)*MathExp(-(tb*tb/(2*ta*ta)))-tb*(1-tc*tb*tb/3)*MathSqrt(2*M_PI)*NormalCDF(-(tb/ta));
|
| v0*=MathExp(-(s*x*y/2))/(2*M_PI);
|
| // Compute second term (numerical integral, 20-point Gauss-Legendre rule) from formula (6)
|
| v1=BVNIntegrate6(rangea,rangeb,x,y,s,0.1527533871307258,-0.0765265211334973);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1527533871307258,0.0765265211334973);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1491729864726037,-0.2277858511416451);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1491729864726037,0.2277858511416451);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1420961093183820,-0.3737060887154195);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1420961093183820,0.3737060887154195);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1316886384491766,-0.5108670019508271);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1316886384491766,0.5108670019508271);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1181945319615184,-0.6360536807265150);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1181945319615184,0.6360536807265150);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1019301198172404,-0.7463319064601508);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.1019301198172404,0.7463319064601508);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0832767415767048,-0.8391169718222188);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0832767415767048,0.8391169718222188);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0626720483341091,-0.9122344282513259);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0626720483341091,0.9122344282513259);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0406014298003869,-0.9639719272779138);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0406014298003869,0.9639719272779138);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0176140071391521,-0.9931285991850949);
|
| v1+=BVNIntegrate6(rangea,rangeb,x,y,s,0.0176140071391521,0.9931285991850949);
|
| v1=v1*0.5*(rangeb-rangea)/(2*M_PI);
|
| result=fxys-s*(v0+v1);
|
| }
|
|
|
| result=MathMax(result,0);
|
| result=MathMin(result,1);
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal function which computes integrand of formula (3) by |
|
| //| Alan Genz times Gaussian weights (passed by user). |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::BVNIntegrate3(double rangea,double rangeb,
|
| double x,double y,double gw,
|
| double gx)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double r=0;
|
| double t2=0;
|
| double dd=0;
|
| double sinr=0;
|
| double cosr=0;
|
|
|
| r=(rangeb-rangea)*0.5*gx+(rangeb+rangea)*0.5;
|
| t2=MathTan(0.5*r);
|
| dd=1/(1+t2*t2);
|
| sinr=2*t2*dd;
|
| cosr=(1-t2*t2)*dd;
|
| result=gw*MathExp(-((x*x+y*y-2*x*y*sinr)/(2*cosr*cosr)));
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal function which computes integrand of formula (6) by |
|
| //| Alan Genz times Gaussian weights (passed by user). |
|
| //+------------------------------------------------------------------+
|
| double CNormalDistr::BVNIntegrate6(double rangea,double rangeb,
|
| double x,double y,double s,
|
| double gw,double gx)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double r=0;
|
| double exphsk22x2=0;
|
| double exphsk2=0;
|
| double sqrt1x2=0;
|
| double exphsk1sqrt1x2=0;
|
|
|
| r=(rangeb-rangea)*0.5*gx+(rangeb+rangea)*0.5;
|
| exphsk22x2=MathExp(-((x-s*y)*(x-s*y)/(2*r*r)));
|
| exphsk2=MathExp(-(x*s*y/2));
|
| sqrt1x2=MathSqrt((1-r)*(1+r));
|
| exphsk1sqrt1x2=MathExp(-(x*s*y/(1+sqrt1x2)));
|
| result=gw*exphsk22x2*(exphsk1sqrt1x2/sqrt1x2-exphsk2*(1+(4-x*y*s)*r*r/8));
|
| //--- create variables
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| class CIncGammaF
|
| {
|
| public:
|
| static double IncompleteGamma(const double a,const double x);
|
| static double IncompleteGammaC(const double a,const double x);
|
| static double InvIncompleteGammaC(const double a,const double y0);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Incomplete gamma integral |
|
| //| The function is defined by |
|
| //| x |
|
| //| - |
|
| //| 1 | | -t a-1 |
|
| //| igam(a,x) = ----- | e t dt. |
|
| //| - | | |
|
| //| | (a) - |
|
| //| 0 |
|
| //| In this implementation both arguments must be positive. |
|
| //| The integral is evaluated by either a power series or |
|
| //| continued fraction expansion, depending on the relative |
|
| //| values of a and x. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,30 200000 3.6e-14 2.9e-15 |
|
| //| IEEE 0,100 300000 9.9e-14 1.5e-14 |
|
| //+------------------------------------------------------------------+
|
| double CIncGammaF::IncompleteGamma(const double a,const double x)
|
| {
|
| //--- check
|
| if(x<=0 || a<=0)
|
| return(0);
|
| //--- check
|
| if(x>1 && x>a)
|
| return(1-IncompleteGammaC(a,x));
|
| //--- create variables
|
| double igammaepsilon=0.000000000000001;
|
| double ans;
|
| double ax;
|
| double c;
|
| double r;
|
| double tmp=0;
|
| //--- change value
|
| ax=a*MathLog(x)-x-CGammaFunc::LnGamma(a,tmp);
|
| //--- check
|
| if(ax<-709.78271289338399)
|
| return(0);
|
| //--- change values
|
| ax=MathExp(ax);
|
| r=a;
|
| c=1;
|
| ans=1;
|
| //--- cycle
|
| do
|
| {
|
| r++;
|
| c*=x/r;
|
| ans+=c;
|
| }
|
| while(c/ans>igammaepsilon);
|
| //--- return result
|
| return(ans*ax/a);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complemented incomplete gamma integral |
|
| //| The function is defined by |
|
| //| igamc(a,x) = 1 - igam(a,x) |
|
| //| inf. |
|
| //| - |
|
| //| 1 | | -t a-1 |
|
| //| = ----- | e t dt. |
|
| //| - | | |
|
| //| | (a) - |
|
| //| x |
|
| //| In this implementation both arguments must be positive. |
|
| //| The integral is evaluated by either a power series or |
|
| //| continued fraction expansion, depending on the relative |
|
| //| values of a and x. |
|
| //| ACCURACY: |
|
| //| Tested at random a, x. |
|
| //| a x Relative error: |
|
| //| arithmetic domain domain # trials peak rms |
|
| //| IEEE 0.5,100 0,100 200000 1.9e-14 1.7e-15|
|
| //| IEEE 0.01,0.5 0,100 200000 1.4e-13 1.6e-15|
|
| //+------------------------------------------------------------------+
|
| double CIncGammaF::IncompleteGammaC(const double a,const double x)
|
| {
|
| //--- check
|
| if(x<=0 || a<=0)
|
| return(1);
|
| //--- check
|
| if(x<1 || x<a)
|
| return(1-IncompleteGamma(a,x));
|
| //--- create variables
|
| double igammaepsilon=0.000000000000001;
|
| double igammabignumber=4503599627370496.0;
|
| double igammabignumberinv=2.22044604925031308085*0.0000000000000001;
|
| double ans=0;
|
| double ax=0;
|
| double c=0;
|
| double yc=0;
|
| double r=0;
|
| double t=0;
|
| double y=0;
|
| double z=0;
|
| double pk=0;
|
| double pkm1=0;
|
| double pkm2=0;
|
| double qk=0;
|
| double qkm1=0;
|
| double qkm2=0;
|
| double tmp=0;
|
| //--- change value
|
| ax=a*MathLog(x)-x-CGammaFunc::LnGamma(a,tmp);
|
| //--- check
|
| if(ax<-709.78271289338399)
|
| return(0);
|
| //--- change values
|
| ax=MathExp(ax);
|
| y=1-a;
|
| z=x+y+1;
|
| c=0;
|
| pkm2=1;
|
| qkm2=x;
|
| pkm1=x+1;
|
| qkm1=z*x;
|
| ans=pkm1/qkm1;
|
| //--- cycle
|
| do
|
| {
|
| c++;
|
| y++;
|
| z+=2;
|
| yc=y*c;
|
| pk=pkm1*z-pkm2*yc;
|
| qk=qkm1*z-qkm2*yc;
|
| //--- check
|
| if(qk!=0)
|
| {
|
| r=pk/qk;
|
| t=MathAbs((ans-r)/r);
|
| ans=r;
|
| }
|
| else
|
| t=1;
|
| //--- change values
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| //--- check
|
| if(MathAbs(pk)>igammabignumber)
|
| {
|
| pkm2=pkm2*igammabignumberinv;
|
| pkm1=pkm1*igammabignumberinv;
|
| qkm2=qkm2*igammabignumberinv;
|
| qkm1=qkm1*igammabignumberinv;
|
| }
|
| }
|
| while(t>igammaepsilon);
|
| //--- return result
|
| return(ans*ax);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of complemented imcomplete gamma integral |
|
| //| Given p, the function finds x such that |
|
| //| igamc( a, x ) = p. |
|
| //| Starting with the approximate value |
|
| //| 3 |
|
| //| x = a t |
|
| //| where |
|
| //| t = 1 - d - ndtri(p) sqrt(d) |
|
| //| and |
|
| //| d = 1/9a, |
|
| //| the routine performs up to 10 Newton iterations to find the |
|
| //| root of igamc(a,x) - p = 0. |
|
| //| ACCURACY: |
|
| //| Tested at random a, p in the intervals indicated. |
|
| //| a p Relative error: |
|
| //| arithmetic domain domain # trials peak rms |
|
| //| IEEE 0.5,100 0,0.5 100000 1.0e-14 1.7e-15|
|
| //| IEEE 0.01,0.5 0,0.5 100000 9.0e-14 3.4e-15|
|
| //| IEEE 0.5,10000 0,0.5 20000 2.3e-13 3.8e-14|
|
| //+------------------------------------------------------------------+
|
| double CIncGammaF::InvIncompleteGammaC(const double a,const double y0)
|
| {
|
| //--- create variables
|
| double igammaepsilon=0;
|
| double iinvgammabignumber=0;
|
| double x0=0;
|
| double x1=0;
|
| double x=0;
|
| double yl=0;
|
| double yh=0;
|
| double y=0;
|
| double d=0;
|
| double lgm=0;
|
| double dithresh=0;
|
| int i=0;
|
| int dir=0;
|
| double tmp=0;
|
| //--- initialization
|
| igammaepsilon=0.000000000000001;
|
| iinvgammabignumber=4503599627370496.0;
|
| x0=iinvgammabignumber;
|
| yl=0;
|
| x1=0;
|
| yh=1;
|
| dithresh=5*igammaepsilon;
|
| d=1/(9*a);
|
| y=1-d-CNormalDistr::InvNormalDistribution(y0)*MathSqrt(d);
|
| x=a*y*y*y;
|
| lgm=CGammaFunc::LnGamma(a,tmp);
|
| i=0;
|
| //--- cycle
|
| while(i<10)
|
| {
|
| //--- check
|
| if(x>x0 || x<x1)
|
| {
|
| d=0.0625;
|
| break;
|
| }
|
| //--- function call
|
| y=IncompleteGammaC(a,x);
|
| //--- check
|
| if(y<yl || y>yh)
|
| {
|
| d=0.0625;
|
| break;
|
| }
|
| //--- check
|
| if(y<y0)
|
| {
|
| x0=x;
|
| yl=y;
|
| }
|
| else
|
| {
|
| x1=x;
|
| yh=y;
|
| }
|
| //--- change value
|
| d=(a-1)*MathLog(x)-x-lgm;
|
| //--- check
|
| if(d<(double)(-709.78271289338399))
|
| {
|
| d=0.0625;
|
| break;
|
| }
|
| //--- change values
|
| d=-MathExp(d);
|
| d=(y-y0)/d;
|
| //--- check
|
| if(MathAbs(d/x)<igammaepsilon)
|
| return(x);
|
| //--- change values
|
| x=x-d;
|
| i=i+1;
|
| }
|
| //--- check
|
| if(x0==iinvgammabignumber)
|
| {
|
| //--- check
|
| if(x<=0.0)
|
| x=1;
|
| //--- cycle
|
| while(x0==iinvgammabignumber)
|
| {
|
| x=(1+d)*x;
|
| y=IncompleteGammaC(a,x);
|
| //--- check
|
| if(y<y0)
|
| {
|
| x0=x;
|
| yl=y;
|
| break;
|
| }
|
| d=d+d;
|
| }
|
| }
|
| //--- change values
|
| d=0.5;
|
| dir=0;
|
| i=0;
|
| //--- cycle
|
| while(i<400)
|
| {
|
| x=x1+d*(x0-x1);
|
| y=IncompleteGammaC(a,x);
|
| lgm=(x0-x1)/(x1+x0);
|
| //--- check
|
| if(MathAbs(lgm)<dithresh)
|
| break;
|
| //--- change value
|
| lgm=(y-y0)/y0;
|
| //--- check
|
| if(MathAbs(lgm)<dithresh)
|
| break;
|
| //--- check
|
| if(x<=0.0)
|
| break;
|
| //--- check
|
| if(y>=y0)
|
| {
|
| x1=x;
|
| yh=y;
|
| //--- check
|
| if(dir<0)
|
| {
|
| dir=0;
|
| d=0.5;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(dir>1)
|
| d=0.5*d+0.5;
|
| else
|
| d=(y0-yl)/(yh-yl);
|
| }
|
| dir=dir+1;
|
| }
|
| else
|
| {
|
| x0=x;
|
| yl=y;
|
| //--- check
|
| if(dir>0)
|
| {
|
| dir=0;
|
| d=0.5;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(dir<-1)
|
| d=0.5*d;
|
| else
|
| d=(y0-yl)/(yh-yl);
|
| }
|
| dir=dir-1;
|
| }
|
| i=i+1;
|
| }
|
| //--- return result
|
| return(x);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Airy function |
|
| //+------------------------------------------------------------------+
|
| class CAiryF
|
| {
|
| public:
|
| static void Airy(const double x,double &ai,double &aip,double &bi,double &bip);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Airy function |
|
| //| Solution of the differential equation |
|
| //| y"(x) = xy. |
|
| //| The function returns the two independent solutions Ai, Bi |
|
| //| and their first derivatives Ai'(x), Bi'(x). |
|
| //| Evaluation is by power series summation for small x, |
|
| //| by rational minimax approximations for large x. |
|
| //| ACCURACY: |
|
| //| Error criterion is absolute when function <= 1, relative |
|
| //| when function > 1, except * denotes relative error criterion. |
|
| //| For large negative x, the absolute error increases as x^1.5. |
|
| //| For large positive x, the relative error increases as x^1.5. |
|
| //| Arithmetic domain function # trials peak rms |
|
| //| IEEE -10, 0 Ai 10000 1.6e-15 2.7e-16 |
|
| //| IEEE 0, 10 Ai 10000 2.3e-14* 1.8e-15*|
|
| //| IEEE -10, 0 Ai' 10000 4.6e-15 7.6e-16 |
|
| //| IEEE 0, 10 Ai' 10000 1.8e-14* 1.5e-15*|
|
| //| IEEE -10, 10 Bi 30000 4.2e-15 5.3e-16 |
|
| //| IEEE -10, 10 Bi' 30000 4.9e-15 7.3e-16 |
|
| //+------------------------------------------------------------------+
|
| void CAiryF::Airy(const double x,double &ai,double &aip,double &bi,
|
| double &bip)
|
| {
|
| //--- create variables
|
| double z=0;
|
| double zz=0;
|
| double t=0;
|
| double f=0;
|
| double g=0;
|
| double uf=0;
|
| double ug=0;
|
| double k=0;
|
| double zeta=0;
|
| double theta=0;
|
| int domflg=0;
|
| double c1=0;
|
| double c2=0;
|
| double sqrt3=0;
|
| double sqpii=0;
|
| double afn=0;
|
| double afd=0;
|
| double agn=0;
|
| double agd=0;
|
| double apfn=0;
|
| double apfd=0;
|
| double apgn=0;
|
| double apgd=0;
|
| double an=0;
|
| double ad=0;
|
| double apn=0;
|
| double apd=0;
|
| double bn16=0;
|
| double bd16=0;
|
| double bppn=0;
|
| double bppd=0;
|
| //--- initialization
|
| ai=0;
|
| aip=0;
|
| bi=0;
|
| bip=0;
|
| sqpii=5.64189583547756286948E-1;
|
| c1=0.35502805388781723926;
|
| c2=0.258819403792806798405;
|
| sqrt3=1.732050807568877293527;
|
| domflg=0;
|
| //--- check
|
| if(x>25.77)
|
| {
|
| //--- initialization
|
| ai=0;
|
| aip=0;
|
| bi=CMath::m_maxrealnumber;
|
| bip=CMath::m_maxrealnumber;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x<-2.09)
|
| {
|
| //--- calculation
|
| domflg=15;
|
| t=MathSqrt(-x);
|
| zeta=-(2.0*x*t/3.0);
|
| t=MathSqrt(t);
|
| k=sqpii/t;
|
| z=1.0/zeta;
|
| zz=z*z;
|
| //--- calculation
|
| afn=-1.31696323418331795333E-1;
|
| afn=afn*zz-6.26456544431912369773E-1;
|
| afn=afn*zz-6.93158036036933542233E-1;
|
| afn=afn*zz-2.79779981545119124951E-1;
|
| afn=afn*zz-4.91900132609500318020E-2;
|
| afn=afn*zz-4.06265923594885404393E-3;
|
| afn=afn*zz-1.59276496239262096340E-4;
|
| afn=afn*zz-2.77649108155232920844E-6;
|
| afn=afn*zz-1.67787698489114633780E-8;
|
| //--- calculation
|
| afd=1.00000000000000000000E0;
|
| afd=afd*zz+1.33560420706553243746E1;
|
| afd=afd*zz+3.26825032795224613948E1;
|
| afd=afd*zz+2.67367040941499554804E1;
|
| afd=afd*zz+9.18707402907259625840E0;
|
| afd=afd*zz+1.47529146771666414581E0;
|
| afd=afd*zz+1.15687173795188044134E-1;
|
| afd=afd*zz+4.40291641615211203805E-3;
|
| afd=afd*zz+7.54720348287414296618E-5;
|
| afd=afd*zz+4.51850092970580378464E-7;
|
| uf=1.0+zz*afn/afd;
|
| //--- calculation
|
| agn=1.97339932091685679179E-2;
|
| agn=agn*zz+3.91103029615688277255E-1;
|
| agn=agn*zz+1.06579897599595591108E0;
|
| agn=agn*zz+9.39169229816650230044E-1;
|
| agn=agn*zz+3.51465656105547619242E-1;
|
| agn=agn*zz+6.33888919628925490927E-2;
|
| agn=agn*zz+5.85804113048388458567E-3;
|
| agn=agn*zz+2.82851600836737019778E-4;
|
| agn=agn*zz+6.98793669997260967291E-6;
|
| agn=agn*zz+8.11789239554389293311E-8;
|
| agn=agn*zz+3.41551784765923618484E-10;
|
| //--- calculation
|
| agd=1.00000000000000000000E0;
|
| agd=agd*zz+9.30892908077441974853E0;
|
| agd=agd*zz+1.98352928718312140417E1;
|
| agd=agd*zz+1.55646628932864612953E1;
|
| agd=agd*zz+5.47686069422975497931E0;
|
| agd=agd*zz+9.54293611618961883998E-1;
|
| agd=agd*zz+8.64580826352392193095E-2;
|
| agd=agd*zz+4.12656523824222607191E-3;
|
| agd=agd*zz+1.01259085116509135510E-4;
|
| agd=agd*zz+1.17166733214413521882E-6;
|
| agd=agd*zz+4.91834570062930015649E-9;
|
| //--- calculation
|
| ug=z*agn/agd;
|
| theta=zeta+0.25*M_PI;
|
| f=MathSin(theta);
|
| g=MathCos(theta);
|
| ai=k*(f*uf-g*ug);
|
| bi=k*(g*uf+f*ug);
|
| apfn=1.85365624022535566142E-1;
|
| apfn=apfn*zz+8.86712188052584095637E-1;
|
| apfn=apfn*zz+9.87391981747398547272E-1;
|
| apfn=apfn*zz+4.01241082318003734092E-1;
|
| apfn=apfn*zz+7.10304926289631174579E-2;
|
| apfn=apfn*zz+5.90618657995661810071E-3;
|
| apfn=apfn*zz+2.33051409401776799569E-4;
|
| apfn=apfn*zz+4.08718778289035454598E-6;
|
| apfn=apfn*zz+2.48379932900442457853E-8;
|
| //--- calculation
|
| apfd=1.00000000000000000000E0;
|
| apfd=apfd*zz+1.47345854687502542552E1;
|
| apfd=apfd*zz+3.75423933435489594466E1;
|
| apfd=apfd*zz+3.14657751203046424330E1;
|
| apfd=apfd*zz+1.09969125207298778536E1;
|
| apfd=apfd*zz+1.78885054766999417817E0;
|
| apfd=apfd*zz+1.41733275753662636873E-1;
|
| apfd=apfd*zz+5.44066067017226003627E-3;
|
| apfd=apfd*zz+9.39421290654511171663E-5;
|
| apfd=apfd*zz+5.65978713036027009243E-7;
|
| uf=1.0+zz*apfn/apfd;
|
| //--- calculation
|
| apgn=-3.55615429033082288335E-2;
|
| apgn=apgn*zz-6.37311518129435504426E-1;
|
| apgn=apgn*zz-1.70856738884312371053E0;
|
| apgn=apgn*zz-1.50221872117316635393E0;
|
| apgn=apgn*zz-5.63606665822102676611E-1;
|
| apgn=apgn*zz-1.02101031120216891789E-1;
|
| apgn=apgn*zz-9.48396695961445269093E-3;
|
| apgn=apgn*zz-4.60325307486780994357E-4;
|
| apgn=apgn*zz-1.14300836484517375919E-5;
|
| apgn=apgn*zz-1.33415518685547420648E-7;
|
| apgn=apgn*zz-5.63803833958893494476E-10;
|
| //--- calculation
|
| apgd=1.00000000000000000000E0;
|
| apgd=apgd*zz+9.85865801696130355144E0;
|
| apgd=apgd*zz+2.16401867356585941885E1;
|
| apgd=apgd*zz+1.73130776389749389525E1;
|
| apgd=apgd*zz+6.17872175280828766327E0;
|
| apgd=apgd*zz+1.08848694396321495475E0;
|
| apgd=apgd*zz+9.95005543440888479402E-2;
|
| apgd=apgd*zz+4.78468199683886610842E-3;
|
| apgd=apgd*zz+1.18159633322838625562E-4;
|
| apgd=apgd*zz+1.37480673554219441465E-6;
|
| apgd=apgd*zz+5.79912514929147598821E-9;
|
| ug=z*apgn/apgd;
|
| k=sqpii*t;
|
| aip=-(k*(g*uf+f*ug));
|
| bip=k*(f*uf-g*ug);
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x>=(double)(2.09))
|
| {
|
| domflg=5;
|
| t=MathSqrt(x);
|
| zeta=2.0*x*t/3.0;
|
| g=MathExp(zeta);
|
| t=MathSqrt(t);
|
| k=2.0*t*g;
|
| z=1.0/zeta;
|
| //--- calculation
|
| an=3.46538101525629032477E-1;
|
| an=an*z+1.20075952739645805542E1;
|
| an=an*z+7.62796053615234516538E1;
|
| an=an*z+1.68089224934630576269E2;
|
| an=an*z+1.59756391350164413639E2;
|
| an=an*z+7.05360906840444183113E1;
|
| an=an*z+1.40264691163389668864E1;
|
| an=an*z+9.99999999999999995305E-1;
|
| ad=5.67594532638770212846E-1;
|
| ad=ad*z+1.47562562584847203173E1;
|
| ad=ad*z+8.45138970141474626562E1;
|
| ad=ad*z+1.77318088145400459522E2;
|
| ad=ad*z+1.64234692871529701831E2;
|
| ad=ad*z+7.14778400825575695274E1;
|
| ad=ad*z+1.40959135607834029598E1;
|
| ad=ad*z+1.00000000000000000470E0;
|
| //--- calculation
|
| f=an/ad;
|
| ai=sqpii*f/k;
|
| k=-(0.5*sqpii*t/g);
|
| apn=6.13759184814035759225E-1;
|
| apn=apn*z+1.47454670787755323881E1;
|
| apn=apn*z+8.20584123476060982430E1;
|
| apn=apn*z+1.71184781360976385540E2;
|
| apn=apn*z+1.59317847137141783523E2;
|
| apn=apn*z+6.99778599330103016170E1;
|
| apn=apn*z+1.39470856980481566958E1;
|
| apn=apn*z+1.00000000000000000550E0;
|
| apd=3.34203677749736953049E-1;
|
| apd=apd*z+1.11810297306158156705E1;
|
| apd=apd*z+7.11727352147859965283E1;
|
| apd=apd*z+1.58778084372838313640E2;
|
| apd=apd*z+1.53206427475809220834E2;
|
| apd=apd*z+6.86752304592780337944E1;
|
| apd=apd*z+1.38498634758259442477E1;
|
| apd=apd*z+9.99999999999999994502E-1;
|
| f=apn/apd;
|
| aip=f*k;
|
| //--- check
|
| if(x>(double)(8.3203353))
|
| {
|
| //--- calculation
|
| bn16=-2.53240795869364152689E-1;
|
| bn16=bn16*z+5.75285167332467384228E-1;
|
| bn16=bn16*z-3.29907036873225371650E-1;
|
| bn16=bn16*z+6.44404068948199951727E-2;
|
| bn16=bn16*z-3.82519546641336734394E-3;
|
| bd16=1.00000000000000000000E0;
|
| bd16=bd16*z-7.15685095054035237902E0;
|
| bd16=bd16*z+1.06039580715664694291E1;
|
| bd16=bd16*z-5.23246636471251500874E0;
|
| bd16=bd16*z+9.57395864378383833152E-1;
|
| bd16=bd16*z-5.50828147163549611107E-2;
|
| //--- calculation
|
| f=z*bn16/bd16;
|
| k=sqpii*g;
|
| bi=k*(1.0+f)/t;
|
| bppn=4.65461162774651610328E-1;
|
| bppn=bppn*z-1.08992173800493920734E0;
|
| bppn=bppn*z+6.38800117371827987759E-1;
|
| bppn=bppn*z-1.26844349553102907034E-1;
|
| bppn=bppn*z+7.62487844342109852105E-3;
|
| bppd=1.00000000000000000000E0;
|
| bppd=bppd*z-8.70622787633159124240E0;
|
| bppd=bppd*z+1.38993162704553213172E1;
|
| bppd=bppd*z-7.14116144616431159572E0;
|
| bppd=bppd*z+1.34008595960680518666E0;
|
| bppd=bppd*z-7.84273211323341930448E-2;
|
| f=z*bppn/bppd;
|
| bip=k*t*(1.0+f);
|
| //--- exit the function
|
| return;
|
| }
|
| }
|
| //--- change values
|
| f=1.0;
|
| g=x;
|
| t=1.0;
|
| uf=1.0;
|
| ug=x;
|
| k=1.0;
|
| z=x*x*x;
|
| //--- cycle
|
| while(t>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| uf=uf*z;
|
| k=k+1.0;
|
| uf=uf/k;
|
| ug=ug*z;
|
| k=k+1.0;
|
| ug=ug/k;
|
| uf=uf/k;
|
| f=f+uf;
|
| k=k+1.0;
|
| ug=ug/k;
|
| g=g+ug;
|
| t=MathAbs(uf/f);
|
| }
|
| //--- change values
|
| uf=c1*f;
|
| ug=c2*g;
|
| //--- check
|
| if(domflg%2==0)
|
| ai=uf-ug;
|
| //--- check
|
| if(domflg/2%2==0)
|
| bi=sqrt3*(uf+ug);
|
| //--- change values
|
| k=4.0;
|
| uf=x*x/2.0;
|
| ug=z/3.0;
|
| f=uf;
|
| g=1.0+ug;
|
| uf=uf/3.0;
|
| t=1.0;
|
| //--- cycle
|
| while(t>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| uf=uf*z;
|
| ug=ug/k;
|
| k=k+1.0;
|
| ug=ug*z;
|
| uf=uf/k;
|
| f=f+uf;
|
| k=k+1.0;
|
| ug=ug/k;
|
| uf=uf/k;
|
| g=g+ug;
|
| k=k+1.0;
|
| t=MathAbs(ug/g);
|
| }
|
| //--- change values
|
| uf=c1*f;
|
| ug=c2*g;
|
| //--- check
|
| if(domflg/4%2==0)
|
| aip=uf-ug;
|
| //--- check
|
| if(domflg/8%2==0)
|
| bip=sqrt3*(uf+ug);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function |
|
| //+------------------------------------------------------------------+
|
| class CBessel
|
| {
|
| public:
|
| static double BesselJ0(double x);
|
| static double BesselJ1(double x);
|
| static double BesselJN(int n,double x);
|
| static double BesselY0(double x);
|
| static double BesselY1(double x);
|
| static double BesselYN(int n,double x);
|
| static double BesselI0(double x);
|
| static double BesselI1(double x);
|
| static double BesselK0(double x);
|
| static double BesselK1(double x);
|
| static double BesselKN(int nn,double x);
|
|
|
| private:
|
| static void BesselMFirstCheb(const double c,double &b0,double &b1,double &b2);
|
| static void BesselMNextCheb(const double x,const double c,double &b0,double &b1,double &b2);
|
| static void BesselM1FirstCheb(const double c,double &b0,double &b1,double &b2);
|
| static void BesselM1NextCheb(const double x,const double c,double &b0,double &b1,double &b2);
|
| static void BesselAsympt0(const double x,double &pzero,double &qzero);
|
| static void BesselAsympt1(const double x,double &pzero,double &qzero);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of order zero |
|
| //| Returns Bessel function of order zero of the argument. |
|
| //| The domain is divided into the intervals [0, 5] and |
|
| //| (5, infinity). In the first interval the following rational |
|
| //| approximation is used: |
|
| //| 2 2 |
|
| //| (w - r ) (w - r ) P (w) / Q (w) |
|
| //| 1 2 3 8 |
|
| //| 2 |
|
| //| where w = x and the two r's are zeros of the function. |
|
| //| In the second interval, the Hankel asymptotic expansion |
|
| //| is employed with two rational functions of degree 6/6 |
|
| //| and 7/7. |
|
| //| ACCURACY: |
|
| //| Absolute error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 60000 4.2e-16 1.1e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselJ0(double x)
|
| {
|
| //--- create variables
|
| double xsq=0;
|
| double nn=0;
|
| double pzero=0;
|
| double qzero=0;
|
| double p1=0;
|
| double q1=0;
|
| //--- check
|
| if(x<0.0)
|
| x=-x;
|
| //--- check
|
| if(x>8.0)
|
| {
|
| //--- function call
|
| BesselAsympt0(x,pzero,qzero);
|
| nn=x-M_PI/4;
|
| //--- return result
|
| return(MathSqrt(2/M_PI/x)*(pzero*MathCos(nn)-qzero*MathSin(nn)));
|
| }
|
| //--- calculation
|
| xsq=CMath::Sqr(x);
|
| p1=26857.86856980014981415848441;
|
| p1=-40504123.71833132706360663322+xsq*p1;
|
| p1=25071582855.36881945555156435+xsq*p1;
|
| p1=-8085222034853.793871199468171+xsq*p1;
|
| p1=1434354939140344.111664316553+xsq*p1;
|
| p1=-136762035308817138.6865416609+xsq*p1;
|
| p1=6382059341072356562.289432465+xsq*p1;
|
| p1=-117915762910761053603.8440800+xsq*p1;
|
| p1=493378725179413356181.6813446+xsq*p1;
|
| q1=1.0;
|
| q1=1363.063652328970604442810507+xsq*q1;
|
| q1=1114636.098462985378182402543+xsq*q1;
|
| q1=669998767.2982239671814028660+xsq*q1;
|
| q1=312304311494.1213172572469442+xsq*q1;
|
| q1=112775673967979.8507056031594+xsq*q1;
|
| q1=30246356167094626.98627330784+xsq*q1;
|
| q1=5428918384092285160.200195092+xsq*q1;
|
| q1=493378725179413356211.3278438+xsq*q1;
|
| //--- return result
|
| return(p1/q1);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of order one |
|
| //| Returns Bessel function of order one of the argument. |
|
| //| The domain is divided into the intervals [0, 8] and |
|
| //| (8, infinity). In the first interval a 24 term Chebyshev |
|
| //| expansion is used. In the second, the asymptotic |
|
| //| trigonometric representation is employed using two |
|
| //| rational functions of degree 5/5. |
|
| //| ACCURACY: |
|
| //| Absolute error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 2.6e-16 1.1e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselJ1(double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double s=0;
|
| double xsq=0;
|
| double nn=0;
|
| double pzero=0;
|
| double qzero=0;
|
| double p1=0;
|
| double q1=0;
|
| //--- calculation
|
| s=MathSign(x);
|
| //--- check
|
| if(x<0.0)
|
| x=-x;
|
| //--- check
|
| if(x>8.0)
|
| {
|
| //--- function call
|
| BesselAsympt1(x,pzero,qzero);
|
| nn=x-3*M_PI/4;
|
| result=MathSqrt(2/M_PI/x)*(pzero*MathCos(nn)-qzero*MathSin(nn));
|
| //--- check
|
| if(s<0.0)
|
| result=-result;
|
| //--- return result
|
| return(result);
|
| }
|
| //--- calculation
|
| xsq=CMath::Sqr(x);
|
| p1=2701.122710892323414856790990;
|
| p1=-4695753.530642995859767162166+xsq*p1;
|
| p1=3413234182.301700539091292655+xsq*p1;
|
| p1=-1322983480332.126453125473247+xsq*p1;
|
| p1=290879526383477.5409737601689+xsq*p1;
|
| p1=-35888175699101060.50743641413+xsq*p1;
|
| p1=2316433580634002297.931815435+xsq*p1;
|
| p1=-66721065689249162980.20941484+xsq*p1;
|
| p1=581199354001606143928.050809+xsq*p1;
|
| q1=1.0;
|
| q1=1606.931573481487801970916749+xsq*q1;
|
| q1=1501793.594998585505921097578+xsq*q1;
|
| q1=1013863514.358673989967045588+xsq*q1;
|
| q1=524371026216.7649715406728642+xsq*q1;
|
| q1=208166122130760.7351240184229+xsq*q1;
|
| q1=60920613989175217.46105196863+xsq*q1;
|
| q1=11857707121903209998.37113348+xsq*q1;
|
| q1=1162398708003212287858.529400+xsq*q1;
|
| //--- return result
|
| return(s*x*p1/q1);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of integer order |
|
| //| Returns Bessel function of order n, where n is a |
|
| //| (possibly negative) integer. |
|
| //| The ratio of jn(x) to j0(x) is computed by backward |
|
| //| recurrence. First the ratio jn/jn-1 is found by a |
|
| //| continued fraction expansion. Then the recurrence |
|
| //| relating successive orders is applied until j0 or j1 is |
|
| //| reached. |
|
| //| If n = 0 or 1 the routine for j0 or j1 is called |
|
| //| directly. |
|
| //| ACCURACY: |
|
| //| Absolute error: |
|
| //| arithmetic range # trials peak rms |
|
| //| IEEE 0, 30 5000 4.4e-16 7.9e-17 |
|
| //| Not suitable for large n or x. Use jv() (fractional order) |
|
| //| instead. |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselJN(int n,double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double pkm2=0;
|
| double pkm1=0;
|
| double pk=0;
|
| double xk=0;
|
| double r=0;
|
| double ans=0;
|
| int k=0;
|
| int sg=0;
|
| //--- check
|
| if(n<0)
|
| {
|
| n=-n;
|
| //--- check
|
| if(n%2==0)
|
| sg=1;
|
| else
|
| sg=-1;
|
| }
|
| else
|
| sg=1;
|
| //--- check
|
| if(x<0.0)
|
| {
|
| //--- check
|
| if(n%2!=0)
|
| sg=-sg;
|
| x=-x;
|
| }
|
| //--- check
|
| if(n==0)
|
| return(sg*BesselJ0(x));
|
| //--- check
|
| if(n==1)
|
| return(sg*BesselJ1(x));
|
| //--- check
|
| if(n==2)
|
| {
|
| //--- check
|
| if(x==0.0)
|
| return(0);
|
| else
|
| return(sg*(2.0*BesselJ1(x)/x-BesselJ0(x)));
|
| }
|
| //--- check
|
| if(x<CMath::m_machineepsilon)
|
| return(0);
|
| //--- change values
|
| k=53;
|
| pk=2*(n+k);
|
| ans=pk;
|
| xk=x*x;
|
| //--- cycle
|
| do
|
| {
|
| pk=pk-2.0;
|
| ans=pk-xk/ans;
|
| k=k-1;
|
| }
|
| while(k!=0);
|
| //--- change values
|
| ans=x/ans;
|
| pk=1.0;
|
| pkm1=1.0/ans;
|
| k=n-1;
|
| r=2*k;
|
| //--- cycle
|
| do
|
| {
|
| pkm2=(pkm1*r-pk*x)/x;
|
| pk=pkm1;
|
| pkm1=pkm2;
|
| r=r-2.0;
|
| k=k-1;
|
| }
|
| while(k!=0);
|
| //--- check
|
| if(MathAbs(pk)>MathAbs(pkm1))
|
| ans=BesselJ1(x)/pk;
|
| else
|
| ans=BesselJ0(x)/pkm1;
|
| //--- return result
|
| return(sg*ans);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of the second kind, order zero |
|
| //| Returns Bessel function of the second kind, of order |
|
| //| zero, of the argument. |
|
| //| The domain is divided into the intervals [0, 5] and |
|
| //| (5, infinity). In the first interval a rational approximation |
|
| //| R(x) is employed to compute |
|
| //| y0(x) = R(x) + 2 * log(x) * j0(x) / PI. |
|
| //| Thus a call to j0() is required. |
|
| //| In the second interval, the Hankel asymptotic expansion |
|
| //| is employed with two rational functions of degree 6/6 |
|
| //| and 7/7. |
|
| //| ACCURACY: |
|
| //| Absolute error, when y0(x) < 1; else relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 1.3e-15 1.6e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselY0(double x)
|
| {
|
| //--- create variables
|
| double nn=0;
|
| double xsq=0;
|
| double pzero=0;
|
| double qzero=0;
|
| double p4=0;
|
| double q4=0;
|
| //--- check
|
| if(x>8.0)
|
| {
|
| //--- function call
|
| BesselAsympt0(x,pzero,qzero);
|
| nn=x-M_PI/4;
|
| //--- return result
|
| return(MathSqrt(2/M_PI/x)*(pzero*MathSin(nn)+qzero*MathCos(nn)));
|
| }
|
| //--- calculation
|
| xsq=CMath::Sqr(x);
|
| p4=-41370.35497933148554125235152;
|
| p4=59152134.65686889654273830069+xsq*p4;
|
| p4=-34363712229.79040378171030138+xsq*p4;
|
| p4=10255208596863.94284509167421+xsq*p4;
|
| p4=-1648605817185729.473122082537+xsq*p4;
|
| p4=137562431639934407.8571335453+xsq*p4;
|
| p4=-5247065581112764941.297350814+xsq*p4;
|
| p4=65874732757195549259.99402049+xsq*p4;
|
| p4=-27502866786291095837.01933175+xsq*p4;
|
| q4=1.0;
|
| q4=1282.452772478993804176329391+xsq*q4;
|
| q4=1001702.641288906265666651753+xsq*q4;
|
| q4=579512264.0700729537480087915+xsq*q4;
|
| q4=261306575504.1081249568482092+xsq*q4;
|
| q4=91620380340751.85262489147968+xsq*q4;
|
| q4=23928830434997818.57439356652+xsq*q4;
|
| q4=4192417043410839973.904769661+xsq*q4;
|
| q4=372645883898616588198.9980+xsq*q4;
|
| //--- return result
|
| return(p4/q4+2/M_PI*BesselJ0(x)*MathLog(x));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of second kind of order one |
|
| //| Returns Bessel function of the second kind of order one |
|
| //| of the argument. |
|
| //| The domain is divided into the intervals [0, 8] and |
|
| //| (8, infinity). In the first interval a 25 term Chebyshev |
|
| //| expansion is used, and a call to j1() is required. |
|
| //| In the second, the asymptotic trigonometric representation |
|
| //| is employed using two rational functions of degree 5/5. |
|
| //| ACCURACY: |
|
| //| Absolute error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 1.0e-15 1.3e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselY1(double x)
|
| {
|
| //--- create variables
|
| double nn=0;
|
| double xsq=0;
|
| double pzero=0;
|
| double qzero=0;
|
| double p4=0;
|
| double q4=0;
|
| //--- check
|
| if(x>8.0)
|
| {
|
| //--- function call
|
| BesselAsympt1(x,pzero,qzero);
|
| nn=x-3*M_PI/4;
|
| //--- return result
|
| return(MathSqrt(2/M_PI/x)*(pzero*MathSin(nn)+qzero*MathCos(nn)));
|
| }
|
| //--- calculation
|
| xsq=CMath::Sqr(x);
|
| p4=-2108847.540133123652824139923;
|
| p4=3639488548.124002058278999428+xsq*p4;
|
| p4=-2580681702194.450950541426399+xsq*p4;
|
| p4=956993023992168.3481121552788+xsq*p4;
|
| p4=-196588746272214065.8820322248+xsq*p4;
|
| p4=21931073399177975921.11427556+xsq*p4;
|
| p4=-1212297555414509577913.561535+xsq*p4;
|
| p4=26554738314348543268942.48968+xsq*p4;
|
| p4=-99637534243069222259967.44354+xsq*p4;
|
| q4=1.0;
|
| q4=1612.361029677000859332072312+xsq*q4;
|
| q4=1563282.754899580604737366452+xsq*q4;
|
| q4=1128686837.169442121732366891+xsq*q4;
|
| q4=646534088126.5275571961681500+xsq*q4;
|
| q4=297663212564727.6729292742282+xsq*q4;
|
| q4=108225825940881955.2553850180+xsq*q4;
|
| q4=29549879358971486742.90758119+xsq*q4;
|
| q4=5435310377188854170800.653097+xsq*q4;
|
| q4=508206736694124324531442.4152+xsq*q4;
|
| //--- return result
|
| return(x*p4/q4+2/M_PI*(BesselJ1(x)*MathLog(x)-1/x));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Bessel function of second kind of integer order |
|
| //| Returns Bessel function of order n, where n is a |
|
| //| (possibly negative) integer. |
|
| //| The function is evaluated by forward recurrence on |
|
| //| n, starting with values computed by the routines |
|
| //| y0() and y1(). |
|
| //| If n = 0 or 1 the routine for y0 or y1 is called |
|
| //| directly. |
|
| //| ACCURACY: |
|
| //| Absolute error, except relative |
|
| //| when y > 1: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 3.4e-15 4.3e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselYN(int n,double x)
|
| {
|
| //--- create variables
|
| double a=0;
|
| double b=0;
|
| double tmp=0;
|
| double s=0;
|
| //--- initialization
|
| s=1;
|
| //--- check
|
| if(n<0)
|
| {
|
| n=-n;
|
| //--- check
|
| if(n%2!=0)
|
| s=-1;
|
| }
|
| //--- check
|
| if(n==0)
|
| return(BesselY0(x));
|
| //--- check
|
| if(n==1)
|
| return(s*BesselY1(x));
|
| //--- calculation
|
| a=BesselY0(x);
|
| b=BesselY1(x);
|
| for(int i=1; i<=n-1; i++)
|
| {
|
| tmp=b;
|
| b=2*i/x*b-a;
|
| a=tmp;
|
| }
|
| //--- return result
|
| return(s*b);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Modified Bessel function of order zero |
|
| //| Returns modified Bessel function of order zero of the |
|
| //| argument. |
|
| //| The function is defined as i0(x) = j0( ix ). |
|
| //| The range is partitioned into the two intervals [0,8] and |
|
| //| (8, infinity). Chebyshev polynomial expansions are employed |
|
| //| in each interval. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,30 30000 5.8e-16 1.4e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselI0(double x)
|
| {
|
| //--- create variables
|
| double y=0;
|
| double v=0;
|
| double z=0;
|
| double b0=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- check
|
| if(x<0.0)
|
| x=-x;
|
| //--- check
|
| if(x<=8.0)
|
| {
|
| y=x/2.0-2.0;
|
| //--- function calls
|
| BesselMFirstCheb(-4.41534164647933937950E-18,b0,b1,b2);
|
| BesselMNextCheb(y,3.33079451882223809783E-17,b0,b1,b2);
|
| BesselMNextCheb(y,-2.43127984654795469359E-16,b0,b1,b2);
|
| BesselMNextCheb(y,1.71539128555513303061E-15,b0,b1,b2);
|
| BesselMNextCheb(y,-1.16853328779934516808E-14,b0,b1,b2);
|
| BesselMNextCheb(y,7.67618549860493561688E-14,b0,b1,b2);
|
| BesselMNextCheb(y,-4.85644678311192946090E-13,b0,b1,b2);
|
| BesselMNextCheb(y,2.95505266312963983461E-12,b0,b1,b2);
|
| BesselMNextCheb(y,-1.72682629144155570723E-11,b0,b1,b2);
|
| BesselMNextCheb(y,9.67580903537323691224E-11,b0,b1,b2);
|
| BesselMNextCheb(y,-5.18979560163526290666E-10,b0,b1,b2);
|
| BesselMNextCheb(y,2.65982372468238665035E-9,b0,b1,b2);
|
| BesselMNextCheb(y,-1.30002500998624804212E-8,b0,b1,b2);
|
| BesselMNextCheb(y,6.04699502254191894932E-8,b0,b1,b2);
|
| BesselMNextCheb(y,-2.67079385394061173391E-7,b0,b1,b2);
|
| BesselMNextCheb(y,1.11738753912010371815E-6,b0,b1,b2);
|
| BesselMNextCheb(y,-4.41673835845875056359E-6,b0,b1,b2);
|
| BesselMNextCheb(y,1.64484480707288970893E-5,b0,b1,b2);
|
| BesselMNextCheb(y,-5.75419501008210370398E-5,b0,b1,b2);
|
| BesselMNextCheb(y,1.88502885095841655729E-4,b0,b1,b2);
|
| BesselMNextCheb(y,-5.76375574538582365885E-4,b0,b1,b2);
|
| BesselMNextCheb(y,1.63947561694133579842E-3,b0,b1,b2);
|
| BesselMNextCheb(y,-4.32430999505057594430E-3,b0,b1,b2);
|
| BesselMNextCheb(y,1.05464603945949983183E-2,b0,b1,b2);
|
| BesselMNextCheb(y,-2.37374148058994688156E-2,b0,b1,b2);
|
| BesselMNextCheb(y,4.93052842396707084878E-2,b0,b1,b2);
|
| BesselMNextCheb(y,-9.49010970480476444210E-2,b0,b1,b2);
|
| BesselMNextCheb(y,1.71620901522208775349E-1,b0,b1,b2);
|
| BesselMNextCheb(y,-3.04682672343198398683E-1,b0,b1,b2);
|
| BesselMNextCheb(y,6.76795274409476084995E-1,b0,b1,b2);
|
| //--- calculation
|
| v=0.5*(b0-b2);
|
| //--- return result
|
| return(MathExp(x)*v);
|
| }
|
| //--- change value
|
| z=32.0/x-2.0;
|
| //--- function calls
|
| BesselMFirstCheb(-7.23318048787475395456E-18,b0,b1,b2);
|
| BesselMNextCheb(z,-4.83050448594418207126E-18,b0,b1,b2);
|
| BesselMNextCheb(z,4.46562142029675999901E-17,b0,b1,b2);
|
| BesselMNextCheb(z,3.46122286769746109310E-17,b0,b1,b2);
|
| BesselMNextCheb(z,-2.82762398051658348494E-16,b0,b1,b2);
|
| BesselMNextCheb(z,-3.42548561967721913462E-16,b0,b1,b2);
|
| BesselMNextCheb(z,1.77256013305652638360E-15,b0,b1,b2);
|
| BesselMNextCheb(z,3.81168066935262242075E-15,b0,b1,b2);
|
| BesselMNextCheb(z,-9.55484669882830764870E-15,b0,b1,b2);
|
| BesselMNextCheb(z,-4.15056934728722208663E-14,b0,b1,b2);
|
| BesselMNextCheb(z,1.54008621752140982691E-14,b0,b1,b2);
|
| BesselMNextCheb(z,3.85277838274214270114E-13,b0,b1,b2);
|
| BesselMNextCheb(z,7.18012445138366623367E-13,b0,b1,b2);
|
| BesselMNextCheb(z,-1.79417853150680611778E-12,b0,b1,b2);
|
| BesselMNextCheb(z,-1.32158118404477131188E-11,b0,b1,b2);
|
| BesselMNextCheb(z,-3.14991652796324136454E-11,b0,b1,b2);
|
| BesselMNextCheb(z,1.18891471078464383424E-11,b0,b1,b2);
|
| BesselMNextCheb(z,4.94060238822496958910E-10,b0,b1,b2);
|
| BesselMNextCheb(z,3.39623202570838634515E-9,b0,b1,b2);
|
| BesselMNextCheb(z,2.26666899049817806459E-8,b0,b1,b2);
|
| BesselMNextCheb(z,2.04891858946906374183E-7,b0,b1,b2);
|
| BesselMNextCheb(z,2.89137052083475648297E-6,b0,b1,b2);
|
| BesselMNextCheb(z,6.88975834691682398426E-5,b0,b1,b2);
|
| BesselMNextCheb(z,3.36911647825569408990E-3,b0,b1,b2);
|
| BesselMNextCheb(z,8.04490411014108831608E-1,b0,b1,b2);
|
| //--- calculation
|
| v=0.5*(b0-b2);
|
| //--- return result
|
| return(MathExp(x)*v/MathSqrt(x));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Modified Bessel function of order one |
|
| //| Returns modified Bessel function of order one of the |
|
| //| argument. |
|
| //| The function is defined as i1(x) = -i j1( ix ). |
|
| //| The range is partitioned into the two intervals [0,8] and |
|
| //| (8, infinity). Chebyshev polynomial expansions are employed |
|
| //| in each interval. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 1.9e-15 2.1e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselI1(double x)
|
| {
|
| //--- create variables
|
| double y=0;
|
| double z=0;
|
| double v=0;
|
| double b0=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- initialization
|
| z=MathAbs(x);
|
| //--- check
|
| if(z<=8.0)
|
| {
|
| //--- initialization
|
| y=z/2.0-2.0;
|
| //--- function call
|
| BesselM1FirstCheb(2.77791411276104639959E-18,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.11142121435816608115E-17,b0,b1,b2);
|
| BesselM1NextCheb(y,1.55363195773620046921E-16,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.10559694773538630805E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,7.60068429473540693410E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,-5.04218550472791168711E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,3.22379336594557470981E-13,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.98397439776494371520E-12,b0,b1,b2);
|
| BesselM1NextCheb(y,1.17361862988909016308E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,-6.66348972350202774223E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,3.62559028155211703701E-10,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.88724975172282928790E-9,b0,b1,b2);
|
| BesselM1NextCheb(y,9.38153738649577178388E-9,b0,b1,b2);
|
| BesselM1NextCheb(y,-4.44505912879632808065E-8,b0,b1,b2);
|
| BesselM1NextCheb(y,2.00329475355213526229E-7,b0,b1,b2);
|
| BesselM1NextCheb(y,-8.56872026469545474066E-7,b0,b1,b2);
|
| BesselM1NextCheb(y,3.47025130813767847674E-6,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.32731636560394358279E-5,b0,b1,b2);
|
| BesselM1NextCheb(y,4.78156510755005422638E-5,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.61760815825896745588E-4,b0,b1,b2);
|
| BesselM1NextCheb(y,5.12285956168575772895E-4,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.51357245063125314899E-3,b0,b1,b2);
|
| BesselM1NextCheb(y,4.15642294431288815669E-3,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.05640848946261981558E-2,b0,b1,b2);
|
| BesselM1NextCheb(y,2.47264490306265168283E-2,b0,b1,b2);
|
| BesselM1NextCheb(y,-5.29459812080949914269E-2,b0,b1,b2);
|
| BesselM1NextCheb(y,1.02643658689847095384E-1,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.76416518357834055153E-1,b0,b1,b2);
|
| BesselM1NextCheb(y,2.52587186443633654823E-1,b0,b1,b2);
|
| //--- calculation
|
| v=0.5*(b0-b2);
|
| z=v*z*MathExp(z);
|
| }
|
| else
|
| {
|
| //--- initialization
|
| y=32.0/z-2.0;
|
| //--- function calls
|
| BesselM1FirstCheb(7.51729631084210481353E-18,b0,b1,b2);
|
| BesselM1NextCheb(y,4.41434832307170791151E-18,b0,b1,b2);
|
| BesselM1NextCheb(y,-4.65030536848935832153E-17,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.20952592199342395980E-17,b0,b1,b2);
|
| BesselM1NextCheb(y,2.96262899764595013876E-16,b0,b1,b2);
|
| BesselM1NextCheb(y,3.30820231092092828324E-16,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.88035477551078244854E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.81440307243700780478E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,1.04202769841288027642E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,4.27244001671195135429E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.10154184277266431302E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,-4.08355111109219731823E-13,b0,b1,b2);
|
| BesselM1NextCheb(y,-7.19855177624590851209E-13,b0,b1,b2);
|
| BesselM1NextCheb(y,2.03562854414708950722E-12,b0,b1,b2);
|
| BesselM1NextCheb(y,1.41258074366137813316E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,3.25260358301548823856E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.89749581235054123450E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,-5.58974346219658380687E-10,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.83538038596423702205E-9,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.63146884688951950684E-8,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.51223623787020892529E-7,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.88256480887769039346E-6,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.10588938762623716291E-4,b0,b1,b2);
|
| BesselM1NextCheb(y,-9.76109749136146840777E-3,b0,b1,b2);
|
| BesselM1NextCheb(y,7.78576235018280120474E-1,b0,b1,b2);
|
| //--- calculation
|
| v=0.5*(b0-b2);
|
| z=v*MathExp(z)/MathSqrt(z);
|
| }
|
| //--- check
|
| if(x<0.0)
|
| z=-z;
|
| //--- return result
|
| return(z);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Modified Bessel function, second kind, order zero |
|
| //| Returns modified Bessel function of the second kind |
|
| //| of order zero of the argument. |
|
| //| The range is partitioned into the two intervals [0,8] and |
|
| //| (8, infinity). Chebyshev polynomial expansions are employed |
|
| //| in each interval. |
|
| //| ACCURACY: |
|
| //| Tested at 2000 random points between 0 and 8. Peak absolute |
|
| //| error (relative when K0 > 1) was 1.46e-14; rms, 4.26e-15. |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 1.2e-15 1.6e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselK0(double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double y=0;
|
| double z=0;
|
| double v=0;
|
| double b0=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- check
|
| if(!CAp::Assert(x>0.0,__FUNCTION__+": x<=0"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(x<=2.0)
|
| {
|
| //--- initialization
|
| y=x*x-2.0;
|
| //--- function calls
|
| BesselMFirstCheb(1.37446543561352307156E-16,b0,b1,b2);
|
| BesselMNextCheb(y,4.25981614279661018399E-14,b0,b1,b2);
|
| BesselMNextCheb(y,1.03496952576338420167E-11,b0,b1,b2);
|
| BesselMNextCheb(y,1.90451637722020886025E-9,b0,b1,b2);
|
| BesselMNextCheb(y,2.53479107902614945675E-7,b0,b1,b2);
|
| BesselMNextCheb(y,2.28621210311945178607E-5,b0,b1,b2);
|
| BesselMNextCheb(y,1.26461541144692592338E-3,b0,b1,b2);
|
| BesselMNextCheb(y,3.59799365153615016266E-2,b0,b1,b2);
|
| BesselMNextCheb(y,3.44289899924628486886E-1,b0,b1,b2);
|
| BesselMNextCheb(y,-5.35327393233902768720E-1,b0,b1,b2);
|
| v=0.5*(b0-b2);
|
| v=v-MathLog(0.5*x)*BesselI0(x);
|
| }
|
| else
|
| {
|
| //--- initialization
|
| z=8.0/x-2.0;
|
| //--- function call
|
| BesselMFirstCheb(5.30043377268626276149E-18,b0,b1,b2);
|
| BesselMNextCheb(z,-1.64758043015242134646E-17,b0,b1,b2);
|
| BesselMNextCheb(z,5.21039150503902756861E-17,b0,b1,b2);
|
| BesselMNextCheb(z,-1.67823109680541210385E-16,b0,b1,b2);
|
| BesselMNextCheb(z,5.51205597852431940784E-16,b0,b1,b2);
|
| BesselMNextCheb(z,-1.84859337734377901440E-15,b0,b1,b2);
|
| BesselMNextCheb(z,6.34007647740507060557E-15,b0,b1,b2);
|
| BesselMNextCheb(z,-2.22751332699166985548E-14,b0,b1,b2);
|
| BesselMNextCheb(z,8.03289077536357521100E-14,b0,b1,b2);
|
| BesselMNextCheb(z,-2.98009692317273043925E-13,b0,b1,b2);
|
| BesselMNextCheb(z,1.14034058820847496303E-12,b0,b1,b2);
|
| BesselMNextCheb(z,-4.51459788337394416547E-12,b0,b1,b2);
|
| BesselMNextCheb(z,1.85594911495471785253E-11,b0,b1,b2);
|
| BesselMNextCheb(z,-7.95748924447710747776E-11,b0,b1,b2);
|
| BesselMNextCheb(z,3.57739728140030116597E-10,b0,b1,b2);
|
| BesselMNextCheb(z,-1.69753450938905987466E-9,b0,b1,b2);
|
| BesselMNextCheb(z,8.57403401741422608519E-9,b0,b1,b2);
|
| BesselMNextCheb(z,-4.66048989768794782956E-8,b0,b1,b2);
|
| BesselMNextCheb(z,2.76681363944501510342E-7,b0,b1,b2);
|
| BesselMNextCheb(z,-1.83175552271911948767E-6,b0,b1,b2);
|
| BesselMNextCheb(z,1.39498137188764993662E-5,b0,b1,b2);
|
| BesselMNextCheb(z,-1.28495495816278026384E-4,b0,b1,b2);
|
| BesselMNextCheb(z,1.56988388573005337491E-3,b0,b1,b2);
|
| BesselMNextCheb(z,-3.14481013119645005427E-2,b0,b1,b2);
|
| BesselMNextCheb(z,2.44030308206595545468E0,b0,b1,b2);
|
| //--- calculation
|
| v=0.5*(b0-b2);
|
| v=v*MathExp(-x)/MathSqrt(x);
|
| }
|
| //--- return result
|
| return(v);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Modified Bessel function, second kind, order one |
|
| //| Computes the modified Bessel function of the second kind |
|
| //| of order one of the argument. |
|
| //| The range is partitioned into the two intervals [0,2] and |
|
| //| (2, infinity). Chebyshev polynomial expansions are employed |
|
| //| in each interval. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 30000 1.2e-15 1.6e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselK1(double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double y=0;
|
| double z=0;
|
| double v=0;
|
| double b0=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- initialization
|
| z=0.5*x;
|
| //--- check
|
| if(!CAp::Assert(z>0.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(x<=2.0)
|
| {
|
| //--- initialization
|
| y=x*x-2.0;
|
| //--- function calls
|
| BesselM1FirstCheb(-7.02386347938628759343E-18,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.42744985051936593393E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,-6.66690169419932900609E-13,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.41148839263352776110E-10,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.21338763073472585583E-8,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.43340614156596823496E-6,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.73028895751305206302E-4,b0,b1,b2);
|
| BesselM1NextCheb(y,-6.97572385963986435018E-3,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.22611180822657148235E-1,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.53155960776544875667E-1,b0,b1,b2);
|
| BesselM1NextCheb(y,1.52530022733894777053E0,b0,b1,b2);
|
| v=0.5*(b0-b2);
|
| //--- get result
|
| result=MathLog(z)*BesselI1(x)+v/x;
|
| }
|
| else
|
| {
|
| //--- initialization
|
| y=8.0/x-2.0;
|
| //--- function calls
|
| BesselM1FirstCheb(-5.75674448366501715755E-18,b0,b1,b2);
|
| BesselM1NextCheb(y,1.79405087314755922667E-17,b0,b1,b2);
|
| BesselM1NextCheb(y,-5.68946255844285935196E-17,b0,b1,b2);
|
| BesselM1NextCheb(y,1.83809354436663880070E-16,b0,b1,b2);
|
| BesselM1NextCheb(y,-6.05704724837331885336E-16,b0,b1,b2);
|
| BesselM1NextCheb(y,2.03870316562433424052E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,-7.01983709041831346144E-15,b0,b1,b2);
|
| BesselM1NextCheb(y,2.47715442448130437068E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,-8.97670518232499435011E-14,b0,b1,b2);
|
| BesselM1NextCheb(y,3.34841966607842919884E-13,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.28917396095102890680E-12,b0,b1,b2);
|
| BesselM1NextCheb(y,5.13963967348173025100E-12,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.12996783842756842877E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,9.21831518760500529508E-11,b0,b1,b2);
|
| BesselM1NextCheb(y,-4.19035475934189648750E-10,b0,b1,b2);
|
| BesselM1NextCheb(y,2.01504975519703286596E-9,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.03457624656780970260E-8,b0,b1,b2);
|
| BesselM1NextCheb(y,5.74108412545004946722E-8,b0,b1,b2);
|
| BesselM1NextCheb(y,-3.50196060308781257119E-7,b0,b1,b2);
|
| BesselM1NextCheb(y,2.40648494783721712015E-6,b0,b1,b2);
|
| BesselM1NextCheb(y,-1.93619797416608296024E-5,b0,b1,b2);
|
| BesselM1NextCheb(y,1.95215518471351631108E-4,b0,b1,b2);
|
| BesselM1NextCheb(y,-2.85781685962277938680E-3,b0,b1,b2);
|
| BesselM1NextCheb(y,1.03923736576817238437E-1,b0,b1,b2);
|
| BesselM1NextCheb(y,2.72062619048444266945E0,b0,b1,b2);
|
| v=0.5*(b0-b2);
|
| //--- get result
|
| result=MathExp(-x)*v/MathSqrt(x);
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Modified Bessel function, second kind, integer order |
|
| //| Returns modified Bessel function of the second kind |
|
| //| of order n of the argument. |
|
| //| The range is partitioned into the two intervals [0,9.55] and |
|
| //| (9.55, infinity). An ascending power series is used in the |
|
| //| low range, and an asymptotic expansion in the high range. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,30 90000 1.8e-8 3.0e-10 |
|
| //| Error is high only near the crossover point x = 9.55 |
|
| //| between the two expansions used. |
|
| //+------------------------------------------------------------------+
|
| double CBessel::BesselKN(int nn,double x)
|
| {
|
| //--- create variables
|
| double k=0;
|
| double kf=0;
|
| double nk1f=0;
|
| double nkf=0;
|
| double zn=0;
|
| double t=0;
|
| double s=0;
|
| double z0=0;
|
| double z=0;
|
| double ans=0;
|
| double fn=0;
|
| double pn=0;
|
| double pk=0;
|
| double zmn=0;
|
| double tlg=0;
|
| double tox=0;
|
| int i=0;
|
| int n=0;
|
| double eul=0;
|
| //--- initialization
|
| eul=5.772156649015328606065e-1;
|
| //--- check
|
| if(nn<0)
|
| n=-nn;
|
| else
|
| n=nn;
|
| //--- check
|
| if(!CAp::Assert(n<=31,__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(x>0.0,__FUNCTION__+":domain error"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(x<=9.55)
|
| {
|
| //--- initialization
|
| ans=0.0;
|
| z0=0.25*x*x;
|
| fn=1.0;
|
| pn=0.0;
|
| zmn=1.0;
|
| tox=2.0/x;
|
| //--- check
|
| if(n>0)
|
| {
|
| //--- calculation
|
| pn=-eul;
|
| k=1.0;
|
| for(i=1; i<=n-1; i++)
|
| {
|
| pn=pn+1.0/k;
|
| k=k+1.0;
|
| fn=fn*k;
|
| }
|
| zmn=tox;
|
| //--- check
|
| if(n==1)
|
| ans=1.0/x;
|
| else
|
| {
|
| //--- change values
|
| nk1f=fn/n;
|
| kf=1.0;
|
| s=nk1f;
|
| z=-z0;
|
| zn=1.0;
|
| //--- calculation
|
| for(i=1; i<=n-1; i++)
|
| {
|
| nk1f=nk1f/(n-i);
|
| kf=kf*i;
|
| zn=zn*z;
|
| t=nk1f*zn/kf;
|
| s=s+t;
|
| //--- check
|
| if(!CAp::Assert(CMath::m_maxrealnumber-MathAbs(t)>MathAbs(s),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(!(tox>1.0 && CMath::m_maxrealnumber/tox<zmn),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| zmn=zmn*tox;
|
| }
|
| //--- change values
|
| s=s*0.5;
|
| t=MathAbs(s);
|
| //--- check
|
| if(!CAp::Assert(!(zmn>1.0 && CMath::m_maxrealnumber/zmn<t),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(!(t>1.0 && CMath::m_maxrealnumber/t<zmn),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| ans=s*zmn;
|
| }
|
| }
|
| //--- change values
|
| tlg=2.0*MathLog(0.5*x);
|
| pk=-eul;
|
| //--- check
|
| if(n==0)
|
| {
|
| pn=pk;
|
| t=1.0;
|
| }
|
| else
|
| {
|
| pn=pn+1.0/n;
|
| t=1.0/fn;
|
| }
|
| s=(pk+pn-tlg)*t;
|
| k=1.0;
|
| //--- cycle
|
| do
|
| {
|
| t=t*(z0/(k*(k+n)));
|
| pk=pk+1.0/k;
|
| pn=pn+1.0/(k+n);
|
| s=s+(pk+pn-tlg)*t;
|
| k=k+1.0;
|
| }
|
| while(MathAbs(t/s)>CMath::m_machineepsilon);
|
| s=0.5*s/zmn;
|
| //--- check
|
| if(n%2!=0)
|
| {
|
| s=-s;
|
| }
|
| ans=ans+s;
|
| //--- return result
|
| return(ans);
|
| }
|
| //--- check
|
| if(x>MathLog(CMath::m_maxrealnumber))
|
| return(0);
|
| //--- change values
|
| k=n;
|
| pn=4.0*k*k;
|
| pk=1.0;
|
| z0=8.0*x;
|
| fn=1.0;
|
| t=1.0;
|
| s=t;
|
| nkf=CMath::m_maxrealnumber;
|
| i=0;
|
| //--- cycle
|
| do
|
| {
|
| z=pn-pk*pk;
|
| t=t*z/(fn*z0);
|
| nk1f=MathAbs(t);
|
| //--- check
|
| if(i>=n && nk1f>(double)(nkf))
|
| break;
|
| //--- change values
|
| nkf=nk1f;
|
| s=s+t;
|
| fn=fn+1.0;
|
| pk=pk+2.0;
|
| i=i+1;
|
| }
|
| while(MathAbs(t/s)>CMath::m_machineepsilon);
|
| //--- return result
|
| return(MathExp(-x)*MathSqrt(M_PI/(2.0*x))*s);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselMFirstCheb(const double c,double &b0,double &b1,
|
| double &b2)
|
| {
|
| //--- change values
|
| b0=c;
|
| b1=0.0;
|
| b2=0.0;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselMNextCheb(const double x,const double c,double &b0,
|
| double &b1,double &b2)
|
| {
|
| //--- change values
|
| b2=b1;
|
| b1=b0;
|
| b0=x*b1-b2+c;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselM1FirstCheb(const double c,double &b0,double &b1,
|
| double &b2)
|
| {
|
| //--- change values
|
| b0=c;
|
| b1=0.0;
|
| b2=0.0;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselM1NextCheb(const double x,const double c,double &b0,
|
| double &b1,double &b2)
|
| {
|
| //--- change values
|
| b2=b1;
|
| b1=b0;
|
| b0=x*b1-b2+c;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselAsympt0(const double x,double &pzero,double &qzero)
|
| {
|
| //--- create variables
|
| double xsq=0;
|
| double p2=0;
|
| double q2=0;
|
| double p3=0;
|
| double q3=0;
|
| //--- initialization
|
| pzero=0;
|
| qzero=0;
|
| //--- calculation
|
| xsq=64.0/(x*x);
|
| p2=0.0;
|
| p2=2485.271928957404011288128951+xsq*p2;
|
| p2=153982.6532623911470917825993+xsq*p2;
|
| p2=2016135.283049983642487182349+xsq*p2;
|
| p2=8413041.456550439208464315611+xsq*p2;
|
| p2=12332384.76817638145232406055+xsq*p2;
|
| p2=5393485.083869438325262122897+xsq*p2;
|
| q2=1.0;
|
| q2=2615.700736920839685159081813+xsq*q2;
|
| q2=156001.7276940030940592769933+xsq*q2;
|
| q2=2025066.801570134013891035236+xsq*q2;
|
| q2=8426449.050629797331554404810+xsq*q2;
|
| q2=12338310.22786324960844856182+xsq*q2;
|
| q2=5393485.083869438325560444960+xsq*q2;
|
| p3=-0.0;
|
| p3=-4.887199395841261531199129300+xsq*p3;
|
| p3=-226.2630641933704113967255053+xsq*p3;
|
| p3=-2365.956170779108192723612816+xsq*p3;
|
| p3=-8239.066313485606568803548860+xsq*p3;
|
| p3=-10381.41698748464093880530341+xsq*p3;
|
| p3=-3984.617357595222463506790588+xsq*p3;
|
| q3=1.0;
|
| q3=408.7714673983499223402830260+xsq*q3;
|
| q3=15704.89191515395519392882766+xsq*q3;
|
| q3=156021.3206679291652539287109+xsq*q3;
|
| q3=533291.3634216897168722255057+xsq*q3;
|
| q3=666745.4239319826986004038103+xsq*q3;
|
| q3=255015.5108860942382983170882+xsq*q3;
|
| //--- get result
|
| pzero=p2/q2;
|
| qzero=8*p3/q3/x;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CBessel::BesselAsympt1(const double x,double &pzero,double &qzero)
|
| {
|
| //--- create variables
|
| double xsq=0;
|
| double p2=0;
|
| double q2=0;
|
| double p3=0;
|
| double q3=0;
|
| //--- initialization
|
| pzero=0;
|
| qzero=0;
|
| //--- calculation
|
| xsq=64.0/(x*x);
|
| p2=-1611.616644324610116477412898;
|
| p2=-109824.0554345934672737413139+xsq*p2;
|
| p2=-1523529.351181137383255105722+xsq*p2;
|
| p2=-6603373.248364939109255245434+xsq*p2;
|
| p2=-9942246.505077641195658377899+xsq*p2;
|
| p2=-4435757.816794127857114720794+xsq*p2;
|
| q2=1.0;
|
| q2=-1455.009440190496182453565068+xsq*q2;
|
| q2=-107263.8599110382011903063867+xsq*q2;
|
| q2=-1511809.506634160881644546358+xsq*q2;
|
| q2=-6585339.479723087072826915069+xsq*q2;
|
| q2=-9934124.389934585658967556309+xsq*q2;
|
| q2=-4435757.816794127856828016962+xsq*q2;
|
| p3=35.26513384663603218592175580;
|
| p3=1706.375429020768002061283546+xsq*p3;
|
| p3=18494.26287322386679652009819+xsq*p3;
|
| p3=66178.83658127083517939992166+xsq*p3;
|
| p3=85145.16067533570196555001171+xsq*p3;
|
| p3=33220.91340985722351859704442+xsq*p3;
|
| q3=1.0;
|
| q3=863.8367769604990967475517183+xsq*q3;
|
| q3=37890.22974577220264142952256+xsq*q3;
|
| q3=400294.4358226697511708610813+xsq*q3;
|
| q3=1419460.669603720892855755253+xsq*q3;
|
| q3=1819458.042243997298924553839+xsq*q3;
|
| q3=708712.8194102874357377502472+xsq*q3;
|
| //--- get result
|
| pzero=p2/q2;
|
| qzero=8*p3/q3/x;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Beta function |
|
| //+------------------------------------------------------------------+
|
| class CBetaF
|
| {
|
| public:
|
| static double Beta(const double a,const double b);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Beta function |
|
| //| - - |
|
| //| | (a) | (b) |
|
| //| beta( a, b ) = -----------. |
|
| //| - |
|
| //| | (a+b) |
|
| //| For large arguments the logarithm of the function is |
|
| //| evaluated using lgam(), then exponentiated. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,30 30000 8.1e-14 1.1e-14 |
|
| //+------------------------------------------------------------------+
|
| double CBetaF::Beta(const double a,const double b)
|
| {
|
| //--- create variables
|
| double y=0;
|
| double sg=0;
|
| double s=0;
|
| //--- initialization
|
| sg=1;
|
| //--- check
|
| if(!CAp::Assert(a>0.0 || a!=(double)((int)MathFloor(a)),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(b>0.0 || b!=(double)((int)MathFloor(b)),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
|
|
| y=a+b;
|
| //--- check
|
| if(MathAbs(y)>171.624376956302725)
|
| {
|
| //--- calculation
|
| y=CGammaFunc::LnGamma(y,s);
|
| sg=sg*s;
|
| y=CGammaFunc::LnGamma(b,s)-y;
|
| sg=sg*s;
|
| y=CGammaFunc::LnGamma(a,s)+y;
|
| sg=sg*s;
|
| //--- check
|
| if(!CAp::Assert(y<=(double)(MathLog(CMath::m_maxrealnumber)),__FUNCTION__+": overflow"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(sg*MathExp(y));
|
| }
|
| //--- function call
|
| y=CGammaFunc::GammaFunc(y);
|
| //--- check
|
| if(!CAp::Assert(y!=0.0,"Overflow in Beta"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(a>b)
|
| {
|
| //--- calculation
|
| y=CGammaFunc::GammaFunc(a)/y;
|
| y=y*CGammaFunc::GammaFunc(b);
|
| }
|
| else
|
| {
|
| //--- calculation
|
| y=CGammaFunc::GammaFunc(b)/y;
|
| y=y*CGammaFunc::GammaFunc(a);
|
| }
|
| //--- return result
|
| return(y);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Incomplete beta function |
|
| //+------------------------------------------------------------------+
|
| class CIncBetaF
|
| {
|
| public:
|
| static double IncompleteBeta(double a,double b,double x);
|
| static double InvIncompleteBeta(const double a,double b,double y);
|
|
|
| private:
|
| static double IncompleteBetaFracExpans(const double a,const double b,const double x,const double big,const double biginv);
|
| static double IncompleteBetaFracExpans2(const double a,const double b,const double x,const double big,const double biginv);
|
| static double IncompleteBetaPowSeries(const double a,const double b,const double x,const double maxgam);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Incomplete beta integral |
|
| //| Returns incomplete beta integral of the arguments, evaluated |
|
| //| from zero to x. The function is defined as |
|
| //| x |
|
| //| - - |
|
| //| | (a+b) | | a-1 b-1 |
|
| //| ----------- | t (1-t) dt. |
|
| //| - - | | |
|
| //| | (a) | (b) - |
|
| //| 0 |
|
| //| The domain of definition is 0 <= x <= 1. In this |
|
| //| implementation a and b are restricted to positive values. |
|
| //| The integral from x to 1 may be obtained by the symmetry |
|
| //| relation |
|
| //| 1 - incbet( a, b, x ) = incbet( b, a, 1-x ). |
|
| //| The integral is evaluated by a continued fraction expansion |
|
| //| or, when b*x is small, by a power series. |
|
| //| ACCURACY: |
|
| //| Tested at uniformly distributed random points (a,b,x) with a and |
|
| //| b in "domain" and x between 0 and 1. |
|
| //| Relative error |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,5 10000 6.9e-15 4.5e-16 |
|
| //| IEEE 0,85 250000 2.2e-13 1.7e-14 |
|
| //| IEEE 0,1000 30000 5.3e-12 6.3e-13 |
|
| //| IEEE 0,10000 250000 9.3e-11 7.1e-12 |
|
| //| IEEE 0,100000 10000 8.7e-10 4.8e-11 |
|
| //| Outputs smaller than the IEEE gradual underflow threshold |
|
| //| were excluded from these statistics. |
|
| //+------------------------------------------------------------------+
|
| double CIncBetaF::IncompleteBeta(double a,double b,double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert(a>0 && b>0,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(x>=0 && x<=1,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(x==0)
|
| return(0);
|
| //--- check
|
| if(x==1)
|
| return(1);
|
| //--- create variables
|
| double t;
|
| double xc;
|
| double w;
|
| double y;
|
| int flag=0;
|
| double sg=0;
|
| double big=4.503599627370496e15;
|
| double biginv=2.22044604925031308085e-16;
|
| double maxgam=171.624376956302725;
|
| double minlog=MathLog(CMath::m_minrealnumber);
|
| double maxlog=MathLog(CMath::m_maxrealnumber);
|
| //--- check
|
| if(b*x<=1.0 && x<=0.95)
|
| return(IncompleteBetaPowSeries(a,b,x,maxgam));
|
| //--- change value
|
| w=1.0-x;
|
| //--- check
|
| if(x>a/(a+b))
|
| {
|
| flag=1;
|
| t=a;
|
| a=b;
|
| b=t;
|
| xc=x;
|
| x=w;
|
| }
|
| else
|
| xc=w;
|
| //--- check
|
| if(flag==1 && b*x<=1.0 && x<=0.95)
|
| {
|
| t=IncompleteBetaPowSeries(a,b,x,maxgam);
|
| //--- check
|
| if(t<=CMath::m_machineepsilon)
|
| return(1.0-CMath::m_machineepsilon);
|
| else
|
| return(1.0-t);
|
| }
|
| //--- change value
|
| y=x*(a+b-2.0)-(a-1.0);
|
| //--- check
|
| if(y<0.0)
|
| w=IncompleteBetaFracExpans(a,b,x,big,biginv);
|
| else
|
| w=IncompleteBetaFracExpans2(a,b,x,big,biginv)/xc;
|
| //--- change values
|
| y=a*MathLog(x);
|
| t=b*MathLog(xc);
|
| //--- check
|
| if(a+b<maxgam && MathAbs(y)<maxlog && MathAbs(t)<maxlog)
|
| {
|
| t=MathPow(xc,b);
|
| t*=MathPow(x,a);
|
| t/=a;
|
| t*=w;
|
| t*=CGammaFunc::GammaFunc(a+b)/(CGammaFunc::GammaFunc(a)*CGammaFunc::GammaFunc(b));
|
| //--- check
|
| if(flag==1)
|
| {
|
| //--- check
|
| if(t<=CMath::m_machineepsilon)
|
| return(1.0-CMath::m_machineepsilon);
|
| else
|
| return(1.0-t);
|
| }
|
| else
|
| return(t);
|
| }
|
| //--- change values
|
| y+=t+CGammaFunc::LnGamma(a+b,sg)-CGammaFunc::LnGamma(a,sg)-CGammaFunc::LnGamma(b,sg);
|
| y+=MathLog(w/a);
|
| //--- check
|
| if(y<minlog)
|
| t=0.0;
|
| else
|
| t=MathExp(y);
|
| //--- check
|
| if(flag==1)
|
| {
|
| if(t<=CMath::m_machineepsilon)
|
| t=1.0-CMath::m_machineepsilon;
|
| else
|
| t=1.0-t;
|
| }
|
| //--- return result
|
| return(t);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of imcomplete beta integral |
|
| //| Given y, the function finds x such that |
|
| //| incbet( a, b, x ) = y . |
|
| //| The routine performs interval halving or Newton iterations to |
|
| //| find the root of incbet(a,b,x) - y = 0. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| x a,b |
|
| //| arithmetic domain domain # trials peak rms |
|
| //| IEEE 0,1 .5,10000 50000 5.8e-12 1.3e-13 |
|
| //| IEEE 0,1 .25,100 100000 1.8e-13 3.9e-15 |
|
| //| IEEE 0,1 0,5 50000 1.1e-12 5.5e-15 |
|
| //| With a and b constrained to half-integer or integer values: |
|
| //| IEEE 0,1 .5,10000 50000 5.8e-12 1.1e-13 |
|
| //| IEEE 0,1 .5,100 100000 1.7e-14 7.9e-16 |
|
| //| With a = .5, b constrained to half-integer or integer values: |
|
| //| IEEE 0,1 .5,10000 10000 8.3e-11 1.0e-11 |
|
| //+------------------------------------------------------------------+
|
| double CIncBetaF::InvIncompleteBeta(const double a,double b,double y)
|
| {
|
| //--- check
|
| if(!CAp::Assert(y>=0 && y<=1,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(y==0)
|
| return(0);
|
| //--- check
|
| if(y==1.0)
|
| return(1);
|
| //--- create variables
|
| double aaa=0;
|
| double bbb=0;
|
| double y0=0;
|
| double d=0;
|
| double yyy=0;
|
| double x=0;
|
| double x0=0.0;
|
| double x1=1.0;
|
| double lgm=0;
|
| double yp=0;
|
| double di=0;
|
| double dithresh=0;
|
| double yl=0.0;
|
| double yh=1.0;
|
| double xt=0;
|
| int i=0;
|
| int rflg=0;
|
| int dir=0;
|
| int nflg=0;
|
| double s=0;
|
| int mainlooppos=0;
|
| int ihalve=1;
|
| int ihalvecycle=2;
|
| int newt=3;
|
| int newtcycle=4;
|
| int breaknewtcycle=5;
|
| int breakihalvecycle=6;
|
| //--- main
|
| while(true)
|
| {
|
| //--- start
|
| if(mainlooppos==0)
|
| {
|
| if(a<=1.0 || b<=1.0)
|
| {
|
| dithresh=1.0e-6;
|
| rflg=0;
|
| aaa=a;
|
| bbb=b;
|
| y0=y;
|
| x=aaa/(aaa+bbb);
|
| yyy=IncompleteBeta(aaa,bbb,x);
|
| mainlooppos=ihalve;
|
| continue;
|
| }
|
| else
|
| dithresh=1.0e-4;
|
| //--- change value
|
| yp=-CNormalDistr::InvNormalDistribution(y);
|
| //--- check
|
| if(y>0.5)
|
| {
|
| rflg=1;
|
| aaa=b;
|
| bbb=a;
|
| y0=1.0-y;
|
| yp=-yp;
|
| }
|
| else
|
| {
|
| rflg=0;
|
| aaa=a;
|
| bbb=b;
|
| y0=y;
|
| }
|
| //--- change values
|
| lgm=(yp*yp-3.0)/6.0;
|
| x=2.0/(1.0/(2.0*aaa-1.0)+1.0/(2.0*bbb-1.0));
|
| d=yp*MathSqrt(x+lgm)/x-(1.0/(2.0*bbb-1.0)-1.0/(2.0*aaa-1.0))*(lgm+5.0/6.0-2.0/(3.0*x));
|
| d=2.0*d;
|
| //--- check
|
| if(d<MathLog(CMath::m_minrealnumber))
|
| {
|
| x=0;
|
| break;
|
| }
|
| //--- change values
|
| x=aaa/(aaa+bbb*MathExp(d));
|
| yyy=IncompleteBeta(aaa,bbb,x);
|
| yp=(yyy-y0)/y0;
|
| //--- check
|
| if(MathAbs(yp)<0.2)
|
| {
|
| mainlooppos=newt;
|
| continue;
|
| }
|
| mainlooppos=ihalve;
|
| continue;
|
| }
|
| //--- check
|
| if(mainlooppos==ihalve)
|
| {
|
| dir=0;
|
| di=0.5;
|
| i=0;
|
| mainlooppos=ihalvecycle;
|
| continue;
|
| }
|
| //--- check
|
| if(mainlooppos==ihalvecycle)
|
| {
|
| //--- check
|
| if(i<=99)
|
| {
|
| //--- check
|
| if(i!=0)
|
| {
|
| x=x0+di*(x1-x0);
|
| //--- check
|
| if(x==1.0)
|
| x=1.0-CMath::m_machineepsilon;
|
| //--- check
|
| if(x==0.0)
|
| {
|
| di=0.5;
|
| x=x0+di*(x1-x0);
|
| //--- check
|
| if(x==0.0)
|
| break;
|
| }
|
| //--- change values
|
| yyy=IncompleteBeta(aaa,bbb,x);
|
| yp=(x1-x0)/(x1+x0);
|
| //--- check
|
| if(MathAbs(yp)<dithresh)
|
| {
|
| mainlooppos=newt;
|
| continue;
|
| }
|
| yp=(yyy-y0)/y0;
|
| //--- check
|
| if(MathAbs(yp)<dithresh)
|
| {
|
| mainlooppos=newt;
|
| continue;
|
| }
|
| }
|
| //--- check
|
| if(yyy<y0)
|
| {
|
| x0=x;
|
| yl=yyy;
|
| //--- check
|
| if(dir<0)
|
| {
|
| dir=0;
|
| di=0.5;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(dir>3)
|
| di=1.0-(1.0-di)*(1.0-di);
|
| else
|
| {
|
| //--- check
|
| if(dir>1)
|
| di=0.5*di+0.5;
|
| else
|
| di=(y0-yyy)/(yh-yl);
|
| }
|
| }
|
| dir++;
|
| //--- check
|
| if(x0>0.75)
|
| {
|
| //--- check
|
| if(rflg==1)
|
| {
|
| rflg=0;
|
| aaa=a;
|
| bbb=b;
|
| y0=y;
|
| }
|
| else
|
| {
|
| rflg=1;
|
| aaa=b;
|
| bbb=a;
|
| y0=1.0-y;
|
| }
|
| //--- change values
|
| x=1.0-x;
|
| yyy=IncompleteBeta(aaa,bbb,x);
|
| x0=0.0;
|
| yl=0.0;
|
| x1=1.0;
|
| yh=1.0;
|
| mainlooppos=ihalve;
|
| continue;
|
| }
|
| }
|
| else
|
| {
|
| x1=x;
|
| //--- check
|
| if(rflg==1 && x1<CMath::m_machineepsilon)
|
| {
|
| x=0.0;
|
| break;
|
| }
|
| yh=yyy;
|
| //--- check
|
| if(dir>0)
|
| {
|
| dir=0;
|
| di=0.5;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(dir<-3)
|
| di*=di;
|
| else
|
| {
|
| //--- check
|
| if(dir<-1)
|
| di*=0.5;
|
| else
|
| di=(yyy-y0)/(yh-yl);
|
| }
|
| }
|
| dir--;
|
| }
|
| i++;
|
| mainlooppos=ihalvecycle;
|
| continue;
|
| }
|
| else
|
| {
|
| mainlooppos=breakihalvecycle;
|
| continue;
|
| }
|
| }
|
| //--- check
|
| if(mainlooppos==breakihalvecycle)
|
| {
|
| //--- check
|
| if(x0>=1.0)
|
| {
|
| x=1.0-CMath::m_machineepsilon;
|
| break;
|
| }
|
| //--- check
|
| if(x<=0.0)
|
| {
|
| x=0.0;
|
| break;
|
| }
|
| mainlooppos=newt;
|
| continue;
|
| }
|
| //--- check
|
| if(mainlooppos==newt)
|
| {
|
| if(nflg!=0)
|
| break;
|
| //--- change values
|
| nflg=1;
|
| lgm=CGammaFunc::LnGamma(aaa+bbb,s)-CGammaFunc::LnGamma(aaa,s)-CGammaFunc::LnGamma(bbb,s);
|
| i=0;
|
| mainlooppos=newtcycle;
|
| continue;
|
| }
|
| //--- check
|
| if(mainlooppos==newtcycle)
|
| {
|
| //--- check
|
| if(i<=7)
|
| {
|
| //--- check
|
| if(i!=0)
|
| yyy=IncompleteBeta(aaa,bbb,x);
|
| //--- check
|
| if(yyy<yl)
|
| {
|
| x=x0;
|
| yyy=yl;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(yyy>yh)
|
| {
|
| x=x1;
|
| yyy=yh;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(yyy<y0)
|
| {
|
| x0=x;
|
| yl=yyy;
|
| }
|
| else
|
| {
|
| x1=x;
|
| yh=yyy;
|
| }
|
| }
|
| }
|
| //--- check
|
| if(x==1.0 || x==0.0)
|
| {
|
| mainlooppos=breaknewtcycle;
|
| continue;
|
| }
|
| //--- change value
|
| d=(aaa-1.0)*MathLog(x)+(bbb-1.0)*MathLog(1.0-x)+lgm;
|
| //--- check
|
| if(d<MathLog(CMath::m_minrealnumber))
|
| break;
|
| //--- check
|
| if(d>MathLog(CMath::m_maxrealnumber))
|
| {
|
| mainlooppos=breaknewtcycle;
|
| continue;
|
| }
|
| //--- change values
|
| d=MathExp(d);
|
| d=(yyy-y0)/d;
|
| xt=x-d;
|
| //--- check
|
| if(xt<=x0)
|
| {
|
| yyy=(x-x0)/(x1-x0);
|
| xt=x0+0.5*yyy*(x-x0);
|
| //--- check
|
| if(xt<=0.0)
|
| {
|
| mainlooppos=breaknewtcycle;
|
| continue;
|
| }
|
| }
|
| //--- check
|
| if(xt>=x1)
|
| {
|
| yyy=(x1-x)/(x1-x0);
|
| xt=x1-0.5*yyy*(x1-x);
|
| //--- check
|
| if(xt>=1.0)
|
| {
|
| mainlooppos=breaknewtcycle;
|
| continue;
|
| }
|
| }
|
| x=xt;
|
| //--- check
|
| if(MathAbs(d/x)<128.0*CMath::m_machineepsilon)
|
| break;
|
| //--- change values
|
| i=i+1;
|
| mainlooppos=newtcycle;
|
| continue;
|
| }
|
| else
|
| {
|
| mainlooppos=breaknewtcycle;
|
| continue;
|
| }
|
| }
|
| //--- check
|
| if(mainlooppos==breaknewtcycle)
|
| {
|
| dithresh=256.0*CMath::m_machineepsilon;
|
| mainlooppos=ihalve;
|
| continue;
|
| }
|
| }
|
| //--- get result
|
| if(rflg!=0)
|
| {
|
| if(x<=CMath::m_machineepsilon)
|
| x=1.0-CMath::m_machineepsilon;
|
| else
|
| x=1.0-x;
|
| }
|
| //--- return result
|
| return(x);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Continued fraction expansion #1 for incomplete beta integral |
|
| //+------------------------------------------------------------------+
|
| double CIncBetaF::IncompleteBetaFracExpans(const double a,const double b,
|
| const double x,const double big,
|
| const double biginv)
|
| {
|
| //--- create variables
|
| double xk=0;
|
| double pk=0;
|
| double pkm1=1.0;
|
| double pkm2=0.0;
|
| double qk=0;
|
| double qkm1=1.0;
|
| double qkm2=1.0;
|
| double k1=a;
|
| double k2=a+b;
|
| double k3=a;
|
| double k4=a+1.0;
|
| double k5=1.0;
|
| double k6=b-1.0;
|
| double k7=k4;
|
| double k8=a+2.0;
|
| double r=1.0;
|
| double t=0;
|
| double ans=1.0;
|
| double thresh=3.0*CMath::m_machineepsilon;
|
| int n=0;
|
| //--- cycle
|
| do
|
| {
|
| xk=-(x*k1*k2/(k3*k4));
|
| pk=pkm1+pkm2*xk;
|
| qk=qkm1+qkm2*xk;
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| xk=x*k5*k6/(k7*k8);
|
| pk=pkm1+pkm2*xk;
|
| qk=qkm1+qkm2*xk;
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| //--- check
|
| if(qk!=0)
|
| r=pk/qk;
|
| //--- check
|
| if(r!=0)
|
| {
|
| t=MathAbs((ans-r)/r);
|
| ans=r;
|
| }
|
| else
|
| t=1.0;
|
| //--- check
|
| if(t<thresh)
|
| break;
|
| //--- change values
|
| k1+=1.0;
|
| k2+=1.0;
|
| k3+=2.0;
|
| k4+=2.0;
|
| k5+=1.0;
|
| k6-=1.0;
|
| k7+=2.0;
|
| k8+=2.0;
|
| //--- check
|
| if(MathAbs(qk)+MathAbs(pk)>big)
|
| {
|
| pkm2*=biginv;
|
| pkm1*=biginv;
|
| qkm2*=biginv;
|
| qkm1*=biginv;
|
| }
|
| //--- check
|
| if(MathAbs(qk)<biginv || MathAbs(pk)<biginv)
|
| {
|
| pkm2*=big;
|
| pkm1*=big;
|
| qkm2*=big;
|
| qkm1*=big;
|
| }
|
| n++;
|
| }
|
| while(n!=300);
|
| //--- return result
|
| return(ans);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Continued fraction expansion #2 |
|
| //| for incomplete beta integral |
|
| //+------------------------------------------------------------------+
|
| double CIncBetaF::IncompleteBetaFracExpans2(const double a,const double b,
|
| const double x,const double big,
|
| const double biginv)
|
| {
|
| //--- create variables
|
| double xk=0;
|
| double pk=0;
|
| double pkm1=1.0;
|
| double pkm2=0.0;
|
| double qk=0;
|
| double qkm1=1.0;
|
| double qkm2=1.0;
|
| double k1=a;
|
| double k2=b-1.0;
|
| double k3=a;
|
| double k4=a+1.0;
|
| double k5=1.0;
|
| double k6=a+b;
|
| double k7=a+1.0;
|
| double k8=a+2.0;
|
| double r=1.0;
|
| double t=0;
|
| double ans=1.0;
|
| double z=x/(1.0-x);
|
| double thresh=3.0*CMath::m_machineepsilon;
|
| int n=0;
|
| //--- cycle
|
| do
|
| {
|
| xk=-(z*k1*k2/(k3*k4));
|
| pk=pkm1+pkm2*xk;
|
| qk=qkm1+qkm2*xk;
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| xk=z*k5*k6/(k7*k8);
|
| pk=pkm1+pkm2*xk;
|
| qk=qkm1+qkm2*xk;
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| //--- check
|
| if(qk!=0)
|
| r=pk/qk;
|
| //--- check
|
| if(r!=0)
|
| {
|
| t=MathAbs((ans-r)/r);
|
| ans=r;
|
| }
|
| else
|
| t=1.0;
|
| //--- check
|
| if(t<thresh)
|
| break;
|
| //--- change values
|
| k1+=1.0;
|
| k2-=1.0;
|
| k3+=2.0;
|
| k4+=2.0;
|
| k5+=1.0;
|
| k6+=1.0;
|
| k7+=2.0;
|
| k8+=2.0;
|
| //--- check
|
| if(MathAbs(qk)+MathAbs(pk)>big)
|
| {
|
| pkm2*=biginv;
|
| pkm1*=biginv;
|
| qkm2*=biginv;
|
| qkm1*=biginv;
|
| }
|
| //--- check
|
| if(MathAbs(qk)<biginv || MathAbs(pk)<biginv)
|
| {
|
| pkm2*=big;
|
| pkm1*=big;
|
| qkm2*=big;
|
| qkm1*=big;
|
| }
|
| n++;
|
| }
|
| while(n!=300);
|
| //--- return result
|
| return(ans);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Power series for incomplete beta integral. |
|
| //| Use when b*x is small and x not too close to 1. |
|
| //+------------------------------------------------------------------+
|
| double CIncBetaF::IncompleteBetaPowSeries(const double a,const double b,
|
| const double x,const double maxgam)
|
| {
|
| //--- create variables
|
| double s=0.0;
|
| double u=(1.0-b)*x;
|
| double t=u;
|
| double v=u/(a+1.0);
|
| double n=2.0;
|
| double t1=v;
|
| double ai=1.0/a;
|
| double z=CMath::m_machineepsilon*ai;
|
| double sg=0;
|
| //--- cycle
|
| while(MathAbs(v)>z)
|
| {
|
| u=(n-b)*x/n;
|
| t=t*u;
|
| v=t/(a+n);
|
| s=s+v;
|
| n=n+1.0;
|
| }
|
| //--- change values
|
| s+=t1;
|
| s+=ai;
|
| u=a*MathLog(x);
|
| //--- check
|
| if(a+b<maxgam && MathAbs(u)<MathLog(CMath::m_maxrealnumber))
|
| {
|
| t=CGammaFunc::GammaFunc(a+b)/(CGammaFunc::GammaFunc(a)*CGammaFunc::GammaFunc(b));
|
| s*=t*MathPow(x,a);
|
| }
|
| else
|
| {
|
| t=CGammaFunc::LnGamma(a+b,sg)-CGammaFunc::LnGamma(a,sg)-CGammaFunc::LnGamma(b,sg)+u+MathLog(s);
|
| //--- check
|
| if(t<MathLog(CMath::m_minrealnumber))
|
| s=0.0;
|
| else
|
| s=MathExp(t);
|
| }
|
| //--- return result
|
| return(s);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Binomial distribution |
|
| //+------------------------------------------------------------------+
|
| class CBinomialDistr
|
| {
|
| public:
|
| static double BinomialDistribution(const int k,const int n,const double p);
|
| static double BinomialComplDistribution(const int k,const int n,const double p);
|
| static double InvBinomialDistribution(const int k,const int n,const double y);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Binomial distribution |
|
| //| Returns the sum of the terms 0 through k of the Binomial |
|
| //| probability density: |
|
| //| k |
|
| //| -- ( n ) j n-j |
|
| //| > ( ) p (1-p) |
|
| //| -- ( j ) |
|
| //| j=0 |
|
| //| The terms are not summed directly; instead the incomplete |
|
| //| beta integral is employed, according to the formula |
|
| //| y = bdtr( k, n, p ) = incbet( n-k, k+1, 1-p ). |
|
| //| The arguments must be positive, with p ranging from 0 to 1. |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,p), with p between 0 and 1. |
|
| //| a,b Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| For p between 0.001 and 1: |
|
| //| IEEE 0,100 100000 4.3e-15 2.6e-16 |
|
| //+------------------------------------------------------------------+
|
| double CBinomialDistr::BinomialDistribution(const int k,const int n,
|
| const double p)
|
| {
|
| //--- check
|
| if(!CAp::Assert(p>=0 && p<=1,__FUNCTION__+": the eroor variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(k>=-1 && k<=n,__FUNCTION__+": the eroor variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(k==-1)
|
| return(0);
|
| //--- check
|
| if(k==n)
|
| return(1);
|
| //--- create variables
|
| double dk;
|
| double dn=n-k;
|
| //--- check
|
| if(k==0)
|
| dk=MathPow(1.0-p,dn);
|
| else
|
| {
|
| dk=k+1;
|
| dk=CIncBetaF::IncompleteBeta(dn,dk,1.0-p);
|
| }
|
| //--- return result
|
| return(dk);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complemented binomial distribution |
|
| //| Returns the sum of the terms k+1 through n of the Binomial |
|
| //| probability density: |
|
| //| n |
|
| //| -- ( n ) j n-j |
|
| //| > ( ) p (1-p) |
|
| //| -- ( j ) |
|
| //| j=k+1 |
|
| //| The terms are not summed directly; instead the incomplete |
|
| //| beta integral is employed, according to the formula |
|
| //| y = bdtrc( k, n, p ) = incbet( k+1, n-k, p ). |
|
| //| The arguments must be positive, with p ranging from 0 to 1. |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,p). |
|
| //| a,b Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| For p between 0.001 and 1: |
|
| //| IEEE 0,100 100000 6.7e-15 8.2e-16 |
|
| //| For p between 0 and .001: |
|
| //| IEEE 0,100 100000 1.5e-13 2.7e-15 |
|
| //+------------------------------------------------------------------+
|
| double CBinomialDistr::BinomialComplDistribution(const int k,
|
| const int n,
|
| const double p)
|
| {
|
| //--- check
|
| if(!CAp::Assert(p>=0 && p<=1,__FUNCTION__+": the eroor variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(!CAp::Assert(k>=-1 && k<=n,__FUNCTION__+": the eroor variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(k==-1)
|
| return(1.0);
|
| //--- check
|
| if(k==n)
|
| return(0);
|
| //--- create variables
|
| double dk;
|
| double dn=n-k;
|
| //--- check
|
| if(k==0)
|
| {
|
| if(p<0.01)
|
| dk=-CNearUnitYUnit::NUExp1m(dn*CNearUnitYUnit::NULog1p(-p));
|
| else
|
| dk=1.0-MathPow(1.0-p,dn);
|
| }
|
| else
|
| {
|
| dk=k+1;
|
| dk=CIncBetaF::IncompleteBeta(dk,dn,p);
|
| }
|
| //--- return result
|
| return(dk);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse binomial distribution |
|
| //| Finds the event probability p such that the sum of the |
|
| //| terms 0 through k of the Binomial probability density |
|
| //| is equal to the given cumulative probability y. |
|
| //| This is accomplished using the inverse beta integral |
|
| //| function and the relation |
|
| //| 1 - p = incbi( n-k, k+1, y ). |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,p). |
|
| //| a,b Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| For p between 0.001 and 1: |
|
| //| IEEE 0,100 100000 2.3e-14 6.4e-16 |
|
| //| IEEE 0,10000 100000 6.6e-12 1.2e-13 |
|
| //| For p between 10^-6 and 0.001: |
|
| //| IEEE 0,100 100000 2.0e-12 1.3e-14 |
|
| //| IEEE 0,10000 100000 1.5e-12 3.2e-14 |
|
| //+------------------------------------------------------------------+
|
| double CBinomialDistr::InvBinomialDistribution(const int k,const int n,
|
| const double y)
|
| {
|
| //--- create variables
|
| double dk=0;
|
| double dn=0;
|
| double p=0;
|
| //--- check
|
| if(!CAp::Assert(k>=0 && k<n,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- initialization
|
| dn=n-k;
|
| //--- check
|
| if(k==0)
|
| {
|
| //--- check
|
| if(y>0.8)
|
| p=-CNearUnitYUnit::NUExp1m(CNearUnitYUnit::NULog1p(y-1.0)/dn);
|
| else
|
| p=1.0-MathPow(y,1.0/dn);
|
| }
|
| else
|
| {
|
| dk=k+1;
|
| //--- function call
|
| p=CIncBetaF::IncompleteBeta(dn,dk,0.5);
|
| //--- check
|
| if(p>0.5)
|
| p=CIncBetaF::InvIncompleteBeta(dk,dn,1.0-y);
|
| else
|
| p=1.0-CIncBetaF::InvIncompleteBeta(dn,dk,y);
|
| }
|
| //--- return result
|
| return(p);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Chebyshev polynomials |
|
| //+------------------------------------------------------------------+
|
| class CChebyshev
|
| {
|
| public:
|
| static double ChebyshevCalculate(const int r,const int n,const double x);
|
| static double ChebyshevSum(double &c[],const int r,const int n,const double x);
|
| static void ChebyshevCoefficients(const int n,double &c[]);
|
| static void FromChebyshev(double &a[],const int n,double &b[]);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Calculation of the value of the Chebyshev polynomials of the |
|
| //| first and second kinds. |
|
| //| Parameters: |
|
| //| r - polynomial kind, either 1 or 2. |
|
| //| n - degree, n>=0 |
|
| //| x - argument, -1 <= x <= 1 |
|
| //| Result: |
|
| //| the value of the Chebyshev polynomial at x |
|
| //+------------------------------------------------------------------+
|
| double CChebyshev::ChebyshevCalculate(const int r,const int n,
|
| const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| int i=0;
|
| double a=0;
|
| double b=0;
|
| //--- Prepare A and B
|
| if(r==1)
|
| {
|
| a=1;
|
| b=x;
|
| }
|
| else
|
| {
|
| a=1;
|
| b=2*x;
|
| }
|
| //--- Special cases: N=0 or N=1
|
| if(n==0)
|
| return(a);
|
| //--- check
|
| if(n==1)
|
| return(b);
|
| //--- General case: N>=2
|
| for(i=2; i<=n; i++)
|
| {
|
| result=2*x*b-a;
|
| a=b;
|
| b=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Summation of Chebyshev polynomials using Clenshaw?s recurrence |
|
| //| formula. |
|
| //| This routine calculates |
|
| //| c[0]*T0(x) + c[1]*T1(x) + ... + c[N]*TN(x) |
|
| //| or |
|
| //| c[0]*U0(x) + c[1]*U1(x) + ... + c[N]*UN(x) |
|
| //| depending on the R. |
|
| //| Parameters: |
|
| //| r - polynomial kind, either 1 or 2. |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Chebyshev polynomial at x |
|
| //+------------------------------------------------------------------+
|
| double CChebyshev::ChebyshevSum(double &c[],const int r,const int n,
|
| const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double b1=0;
|
| double b2=0;
|
| int i=0;
|
| //--- initialization
|
| b1=0;
|
| b2=0;
|
| //--- calculation
|
| for(i=n; i>=1; i--)
|
| {
|
| result=2*x*b1-b2+c[i];
|
| b2=b1;
|
| b1=result;
|
| }
|
| //--- check
|
| if(r==1)
|
| result=-b2+x*b1+c[0];
|
| else
|
| result=-b2+2*x*b1+c[0];
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Representation of Tn as C[0] + C[1]*X + ... + C[N]*X^N |
|
| //| Input parameters: |
|
| //| N - polynomial degree, n>=0 |
|
| //| Output parameters: |
|
| //| C - coefficients |
|
| //+------------------------------------------------------------------+
|
| void CChebyshev::ChebyshevCoefficients(const int n,double &c[])
|
| {
|
| //--- allocation
|
| ArrayResize(c,n+1);
|
| //--- initialization
|
| ArrayInitialize(c,0.0);
|
| //--- check
|
| if(n==0 || n==1)
|
| c[n]=1;
|
| else
|
| {
|
| c[n]=MathExp((n-1)*MathLog(2));
|
| //--- calculation
|
| for(int i=0; i<=n/2-1; i++)
|
| c[n-2*(i+1)]=-(c[n-2*i]*(n-2*i)*(n-2*i-1)/4/(i+1)/(n-i-1));
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Conversion of a series of Chebyshev polynomials to a power |
|
| //| series. |
|
| //| Represents A[0]*T0(x) + A[1]*T1(x) + ... + A[N]*Tn(x) as |
|
| //| B[0] + B[1]*X + ... + B[N]*X^N. |
|
| //| Input parameters: |
|
| //| A - Chebyshev series coefficients |
|
| //| N - degree, N>=0 |
|
| //| Output parameters |
|
| //| B - power series coefficients |
|
| //+------------------------------------------------------------------+
|
| void CChebyshev::FromChebyshev(double &a[],const int n,double &b[])
|
| {
|
| //--- create variables
|
| int i=0;
|
| int k=0;
|
| double e=0;
|
| double d=0;
|
| //--- allocation
|
| ArrayResize(b,n+1);
|
| //--- initialization
|
| ArrayInitialize(b,0.0);
|
| //--- calculation
|
| d=0;
|
| i=0;
|
| do
|
| {
|
| k=i;
|
| //--- cycle
|
| do
|
| {
|
| e=b[k];
|
| b[k]=0;
|
| //--- check
|
| if(i<=1 && k==i)
|
| b[k]=1;
|
| else
|
| {
|
| //--- check
|
| if(i!=0)
|
| b[k]=2*d;
|
| //--- check
|
| if(k>i+1)
|
| b[k]=b[k]-b[k-2];
|
| }
|
| //--- change values
|
| d=e;
|
| k=k+1;
|
| }
|
| while(k<=n);
|
| //--- change values
|
| d=b[i];
|
| e=0;
|
| k=i;
|
| //--- calculation
|
| while(k<=n)
|
| {
|
| e=e+b[k]*a[k];
|
| k=k+2;
|
| }
|
| //--- change values
|
| b[i]=e;
|
| i=i+1;
|
| }
|
| while(i<=n);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Chi-square distribution |
|
| //+------------------------------------------------------------------+
|
| class CChiSquareDistr
|
| {
|
| public:
|
| static double ChiSquareDistribution(const double v,const double x);
|
| static double ChiSquareComplDistribution(const double v,const double x);
|
| static double InvChiSquareDistribution(const double v,const double y);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Chi-square distribution |
|
| //| Returns the area under the left hand tail (from 0 to x) |
|
| //| of the Chi square probability density function with |
|
| //| v degrees of freedom. |
|
| //| x |
|
| //| - |
|
| //| 1 | | v/2-1 -t/2 |
|
| //| P( x | v ) = ----------- | t e dt |
|
| //| v/2 - | | |
|
| //| 2 | (v/2) - |
|
| //| 0 |
|
| //| where x is the Chi-square variable. |
|
| //| The incomplete gamma integral is used, according to the |
|
| //| formula |
|
| //| y = chdtr( v, x ) = igam( v/2.0, x/2.0 ). |
|
| //| The arguments must both be positive. |
|
| //| ACCURACY: |
|
| //| See incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CChiSquareDistr::ChiSquareDistribution(const double v,
|
| const double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert(x>=0 && v>=1,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CIncGammaF::IncompleteGamma(v/2.0,x/2.0));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complemented Chi-square distribution |
|
| //| Returns the area under the right hand tail (from x to |
|
| //| infinity) of the Chi square probability density function |
|
| //| with v degrees of freedom: |
|
| //| inf. |
|
| //| - |
|
| //| 1 | | v/2-1 -t/2 |
|
| //| P( x | v ) = ----------- | t e dt |
|
| //| v/2 - | | |
|
| //| 2 | (v/2) - |
|
| //| x |
|
| //| where x is the Chi-square variable. |
|
| //| The incomplete gamma integral is used, according to the |
|
| //| formula |
|
| //| y = chdtr( v, x ) = igamc( v/2.0, x/2.0 ). |
|
| //| The arguments must both be positive. |
|
| //| ACCURACY: |
|
| //| See incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CChiSquareDistr::ChiSquareComplDistribution(const double v,
|
| const double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert(x>=0.0 && v>=1.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CIncGammaF::IncompleteGammaC(v/2.0,x/2.0));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of complemented Chi-square distribution |
|
| //| Finds the Chi-square argument x such that the integral |
|
| //| from x to infinity of the Chi-square density is equal |
|
| //| to the given cumulative probability y. |
|
| //| This is accomplished using the inverse gamma integral |
|
| //| function and the relation |
|
| //| x/2 = igami( df/2, y ); |
|
| //| ACCURACY: |
|
| //| See inverse incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CChiSquareDistr::InvChiSquareDistribution(const double v,
|
| const double y)
|
| {
|
| //--- check
|
| if(!CAp::Assert((y>=0.0 && y<=1.0) && v>=1.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(2*CIncGammaF::InvIncompleteGammaC(0.5*v,y));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Dawson integral |
|
| //+------------------------------------------------------------------+
|
| class CDawson
|
| {
|
| public:
|
| static double DawsonIntegral(double x);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Dawson's Integral |
|
| //| Approximates the integral |
|
| //| x |
|
| //| - |
|
| //| 2 | | 2 |
|
| //| dawsn(x) = exp( -x ) | exp( t ) dt |
|
| //| | | |
|
| //| - |
|
| //| 0 |
|
| //| Three different rational approximations are employed, for |
|
| //| the intervals 0 to 3.25; 3.25 to 6.25; and 6.25 up. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,10 10000 6.9e-16 1.0e-16 |
|
| //+------------------------------------------------------------------+
|
| double CDawson::DawsonIntegral(double x)
|
| {
|
| //--- create variables
|
| int sg=1;
|
| double x2=0;
|
| double y=0;
|
| double an=0;
|
| double ad=0;
|
| double bn=0;
|
| double bd=0;
|
| double cn=0;
|
| double cd=0;
|
| //--- check
|
| if(x<0.0)
|
| {
|
| sg=-1;
|
| x=-x;
|
| }
|
| //--- check
|
| if(x<3.25)
|
| {
|
| //--- calculation
|
| x2=x*x;
|
| an=1.13681498971755972054E-11;
|
| an=an*x2+8.49262267667473811108E-10;
|
| an=an*x2+1.94434204175553054283E-8;
|
| an=an*x2+9.53151741254484363489E-7;
|
| an=an*x2+3.07828309874913200438E-6;
|
| an=an*x2+3.52513368520288738649E-4;
|
| an=an*x2+-8.50149846724410912031E-4;
|
| an=an*x2+4.22618223005546594270E-2;
|
| an=an*x2+-9.17480371773452345351E-2;
|
| an=an*x2+9.99999999999999994612E-1;
|
| ad=2.40372073066762605484E-11;
|
| ad=ad*x2+1.48864681368493396752E-9;
|
| ad=ad*x2+5.21265281010541664570E-8;
|
| ad=ad*x2+1.27258478273186970203E-6;
|
| ad=ad*x2+2.32490249820789513991E-5;
|
| ad=ad*x2+3.25524741826057911661E-4;
|
| ad=ad*x2+3.48805814657162590916E-3;
|
| ad=ad*x2+2.79448531198828973716E-2;
|
| ad=ad*x2+1.58874241960120565368E-1;
|
| ad=ad*x2+5.74918629489320327824E-1;
|
| ad=ad*x2+1.00000000000000000539E0;
|
| y=x*an/ad;
|
| //--- return result
|
| return(sg*y);
|
| }
|
| x2=1.0/(x*x);
|
| //--- check
|
| if(x<6.25)
|
| {
|
| //--- calculation
|
| bn=5.08955156417900903354E-1;
|
| bn=bn*x2-2.44754418142697847934E-1;
|
| bn=bn*x2+9.41512335303534411857E-2;
|
| bn=bn*x2-2.18711255142039025206E-2;
|
| bn=bn*x2+3.66207612329569181322E-3;
|
| bn=bn*x2-4.23209114460388756528E-4;
|
| bn=bn*x2+3.59641304793896631888E-5;
|
| bn=bn*x2-2.14640351719968974225E-6;
|
| bn=bn*x2+9.10010780076391431042E-8;
|
| bn=bn*x2-2.40274520828250956942E-9;
|
| bn=bn*x2+3.59233385440928410398E-11;
|
| bd=1.00000000000000000000E0;
|
| bd=bd*x2-6.31839869873368190192E-1;
|
| bd=bd*x2+2.36706788228248691528E-1;
|
| bd=bd*x2-5.31806367003223277662E-2;
|
| bd=bd*x2+8.48041718586295374409E-3;
|
| bd=bd*x2-9.47996768486665330168E-4;
|
| bd=bd*x2+7.81025592944552338085E-5;
|
| bd=bd*x2-4.55875153252442634831E-6;
|
| bd=bd*x2+1.89100358111421846170E-7;
|
| bd=bd*x2-4.91324691331920606875E-9;
|
| bd=bd*x2+7.18466403235734541950E-11;
|
| y=1.0/x+x2*bn/(bd*x);
|
| //--- return result
|
| return(sg*0.5*y);
|
| }
|
| //--- check
|
| if(x>1.0E9)
|
| return(sg*0.5/x);
|
| //--- calculation
|
| cn=-5.90592860534773254987E-1;
|
| cn=cn*x2+6.29235242724368800674E-1;
|
| cn=cn*x2-1.72858975380388136411E-1;
|
| cn=cn*x2+1.64837047825189632310E-2;
|
| cn=cn*x2-4.86827613020462700845E-4;
|
| cd=1.00000000000000000000E0;
|
| cd=cd*x2-2.69820057197544900361E0;
|
| cd=cd*x2+1.73270799045947845857E0;
|
| cd=cd*x2-3.93708582281939493482E-1;
|
| cd=cd*x2+3.44278924041233391079E-2;
|
| cd=cd*x2-9.73655226040941223894E-4;
|
| y=1.0/x+x2*cn/(cd*x);
|
| //--- return result
|
| return(sg*0.5*y);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Elliptic integral |
|
| //+------------------------------------------------------------------+
|
| class CElliptic
|
| {
|
| public:
|
| static double EllipticIntegralK(const double m);
|
| static double EllipticIntegralKhighPrecision(const double m1);
|
| static double IncompleteEllipticIntegralK(double phi,const double m);
|
| static double EllipticIntegralE(double m);
|
| static double IncompleteEllipticIntegralE(const double phi,const double m);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Complete elliptic integral of the first kind |
|
| //| Approximates the integral |
|
| //| pi/2 |
|
| //| - |
|
| //| | | |
|
| //| | dt |
|
| //| K(m) = | ------------------ |
|
| //| | 2 |
|
| //| | | sqrt( 1 - m sin t ) |
|
| //| - |
|
| //| 0 |
|
| //| using the approximation |
|
| //| P(x) - log x Q(x). |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,1 30000 2.5e-16 6.8e-17 |
|
| //+------------------------------------------------------------------+
|
| double CElliptic::EllipticIntegralK(const double m)
|
| {
|
| //--- return result
|
| return(EllipticIntegralKhighPrecision(1.0-m));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complete elliptic integral of the first kind |
|
| //| Approximates the integral |
|
| //| pi/2 |
|
| //| - |
|
| //| | | |
|
| //| | dt |
|
| //| K(m) = | ------------------ |
|
| //| | 2 |
|
| //| | | sqrt( 1 - m sin t ) |
|
| //| - |
|
| //| 0 |
|
| //| where m = 1 - m1, using the approximation |
|
| //| P(x) - log x Q(x). |
|
| //| The argument m1 is used rather than m so that the logarithmic |
|
| //| singularity at m = 1 will be shifted to the origin; this |
|
| //| preserves maximum accuracy. |
|
| //| K(0) = pi/2. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,1 30000 2.5e-16 6.8e-17 |
|
| //+------------------------------------------------------------------+
|
| double CElliptic::EllipticIntegralKhighPrecision(const double m1)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double p=0;
|
| double q=0;
|
| //--- check
|
| if(m1<=CMath::m_machineepsilon)
|
| result=1.3862943611198906188E0-0.5*MathLog(m1);
|
| else
|
| {
|
| //--- calculation
|
| p=1.37982864606273237150E-4;
|
| p=p*m1+2.28025724005875567385E-3;
|
| p=p*m1+7.97404013220415179367E-3;
|
| p=p*m1+9.85821379021226008714E-3;
|
| p=p*m1+6.87489687449949877925E-3;
|
| p=p*m1+6.18901033637687613229E-3;
|
| p=p*m1+8.79078273952743772254E-3;
|
| p=p*m1+1.49380448916805252718E-2;
|
| p=p*m1+3.08851465246711995998E-2;
|
| p=p*m1+9.65735902811690126535E-2;
|
| p=p*m1+1.38629436111989062502E0;
|
| q=2.94078955048598507511E-5;
|
| q=q*m1+9.14184723865917226571E-4;
|
| q=q*m1+5.94058303753167793257E-3;
|
| q=q*m1+1.54850516649762399335E-2;
|
| q=q*m1+2.39089602715924892727E-2;
|
| q=q*m1+3.01204715227604046988E-2;
|
| q=q*m1+3.73774314173823228969E-2;
|
| q=q*m1+4.88280347570998239232E-2;
|
| q=q*m1+7.03124996963957469739E-2;
|
| q=q*m1+1.24999999999870820058E-1;
|
| q=q*m1+4.99999999999999999821E-1;
|
| //--- get result
|
| result=p-q*MathLog(m1);
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Incomplete elliptic integral of the first kind F(phi|m) |
|
| //| Approximates the integral |
|
| //| phi |
|
| //| - |
|
| //| | | |
|
| //| | dt |
|
| //| F(phi_\m) = | ------------------ |
|
| //| | 2 |
|
| //| | | sqrt( 1 - m sin t ) |
|
| //| - |
|
| //| 0 |
|
| //| of amplitude phi and modulus m, using the arithmetic - |
|
| //| geometric mean algorithm. |
|
| //| ACCURACY: |
|
| //| Tested at random points with m in [0, 1] and phi as indicated. |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -10,10 200000 7.4e-16 1.0e-16 |
|
| //+------------------------------------------------------------------+
|
| double CElliptic::IncompleteEllipticIntegralK(double phi,const double m)
|
| {
|
| //--- create variables
|
| double pio2=1.57079632679489661923;
|
| double a=0;
|
| double b=0;
|
| double c=0;
|
| double e=0;
|
| double temp=0;
|
| double t=0;
|
| double k=0;
|
| int d=0;
|
| int md=0;
|
| int s=0;
|
| int npio2=0;
|
| //--- check
|
| if(m==0.0)
|
| return(phi);
|
| //--- initialization
|
| a=1.0-m;
|
| //--- check
|
| if(a==0.0)
|
| return(MathLog(MathTan(0.5*(pio2+phi))));
|
| //--- initialization
|
| npio2=(int)MathFloor(phi/pio2);
|
| //--- check
|
| if(npio2%2!=0)
|
| npio2=npio2+1;
|
| //--- check
|
| if(npio2!=0)
|
| {
|
| k=EllipticIntegralK(1-a);
|
| phi=phi-npio2*pio2;
|
| }
|
| else
|
| k=0;
|
| //--- check
|
| if(phi<0.0)
|
| {
|
| phi=-phi;
|
| s=-1;
|
| }
|
| else
|
| s=0;
|
| //--- calculation
|
| b=MathSqrt(a);
|
| t=MathTan(phi);
|
| //--- check
|
| if(MathAbs(t)>10)
|
| {
|
| e=1.0/(b*t);
|
| //--- check
|
| if(MathAbs(e)<10)
|
| {
|
| e=MathArctan(e);
|
| //--- check
|
| if(npio2==0)
|
| k=EllipticIntegralK(1-a);
|
| //--- function call
|
| temp=k-IncompleteEllipticIntegralK(e,m);
|
| //--- check
|
| if(s<0)
|
| temp=-temp;
|
| //--- return result
|
| return(temp+npio2*k);
|
| }
|
| }
|
| //--- change values
|
| a=1.0;
|
| c=MathSqrt(m);
|
| d=1;
|
| md=0;
|
| //--- cycle
|
| while(MathAbs(c/a)>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| temp=b/a;
|
| phi=phi+MathArctan(t*temp)+md*M_PI;
|
| md=(int)((phi+pio2)/M_PI);
|
| t=t*(1.0+temp)/(1.0-temp*t*t);
|
| c=0.5*(a-b);
|
| temp=MathSqrt(a*b);
|
| a=0.5*(a+b);
|
| b=temp;
|
| d=d+d;
|
| }
|
| //--- change value
|
| temp=(MathArctan(t)+md*M_PI)/(d*a);
|
| //--- check
|
| if(s<0)
|
| temp=-temp;
|
| //--- return result
|
| return(temp+npio2*k);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complete elliptic integral of the second kind |
|
| //| Approximates the integral |
|
| //| pi/2 |
|
| //| - |
|
| //| | | 2 |
|
| //| E(m) = | sqrt( 1 - m sin t ) dt |
|
| //| | | |
|
| //| - |
|
| //| 0 |
|
| //| using the approximation |
|
| //| P(x) - x log x Q(x). |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 1 10000 2.1e-16 7.3e-17 |
|
| //+------------------------------------------------------------------+
|
| double CElliptic::EllipticIntegralE(double m)
|
| {
|
| //--- create variables
|
| double p=0;
|
| double q=0;
|
| //--- check
|
| if(!CAp::Assert(m>=0.0 && m<=1.0,__FUNCTION__+": m<0 or m>1"))
|
| return(EMPTY_VALUE);
|
| //--- change value
|
| m=1.0-m;
|
| //--- check
|
| if(m==0.0)
|
| return(1.0);
|
| //--- calculation
|
| p=1.53552577301013293365E-4;
|
| p=p*m+2.50888492163602060990E-3;
|
| p=p*m+8.68786816565889628429E-3;
|
| p=p*m+1.07350949056076193403E-2;
|
| p=p*m+7.77395492516787092951E-3;
|
| p=p*m+7.58395289413514708519E-3;
|
| p=p*m+1.15688436810574127319E-2;
|
| p=p*m+2.18317996015557253103E-2;
|
| p=p*m+5.68051945617860553470E-2;
|
| p=p*m+4.43147180560990850618E-1;
|
| p=p*m+1.00000000000000000299E0;
|
| q=3.27954898576485872656E-5;
|
| q=q*m+1.00962792679356715133E-3;
|
| q=q*m+6.50609489976927491433E-3;
|
| q=q*m+1.68862163993311317300E-2;
|
| q=q*m+2.61769742454493659583E-2;
|
| q=q*m+3.34833904888224918614E-2;
|
| q=q*m+4.27180926518931511717E-2;
|
| q=q*m+5.85936634471101055642E-2;
|
| q=q*m+9.37499997197644278445E-2;
|
| q=q*m+2.49999999999888314361E-1;
|
| //--- return result
|
| return(p-q*m*MathLog(m));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Incomplete elliptic integral of the second kind |
|
| //| Approximates the integral |
|
| //| phi |
|
| //| - |
|
| //| | | |
|
| //| | 2 |
|
| //| E(phi_\m) = | sqrt( 1 - m sin t ) dt |
|
| //| | |
|
| //| | | |
|
| //| - |
|
| //| 0 |
|
| //| of amplitude phi and modulus m, using the arithmetic - |
|
| //| geometric mean algorithm. |
|
| //| ACCURACY: |
|
| //| Tested at random arguments with phi in [-10, 10] and m in |
|
| //| [0, 1]. |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -10,10 150000 3.3e-15 1.4e-16 |
|
| //+------------------------------------------------------------------+
|
| double CElliptic::IncompleteEllipticIntegralE(const double phi,
|
| const double m)
|
| {
|
| //--- create variables
|
| double pio2=1.57079632679489661923;
|
| double a=0;
|
| double b=0;
|
| double c=0;
|
| double e=0;
|
| double temp=0;
|
| double lphi=0;
|
| double t=0;
|
| double ebig=0;
|
| int d=0;
|
| int md=0;
|
| int npio2=0;
|
| int s=0;
|
| //--- check
|
| if(m==0.0)
|
| return(phi);
|
| //--- change values
|
| lphi=phi;
|
| npio2=(int)MathFloor(lphi/pio2);
|
| //--- check
|
| if(npio2%2!=0)
|
| npio2=npio2+1;
|
| lphi=lphi-npio2*pio2;
|
| //--- check
|
| if(lphi<0.0)
|
| {
|
| lphi=-lphi;
|
| s=-1;
|
| }
|
| else
|
| s=1;
|
| //--- change values
|
| a=1.0-m;
|
| ebig=EllipticIntegralE(m);
|
| //--- check
|
| if(a==0.0)
|
| {
|
| temp=MathSin(lphi);
|
| //--- check
|
| if(s<0)
|
| temp=-temp;
|
| //--- return result
|
| return(temp+npio2*ebig);
|
| }
|
| //--- calculation
|
| t=MathTan(lphi);
|
| b=MathSqrt(a);
|
| //--- Thanks to Brian Fitzgerald <fitzgb@mml0.meche.rpi.edu>
|
| //--- for pointing out an instability near odd multiples of pi/2
|
| if(MathAbs(t)>10)
|
| {
|
| //--- Transform the amplitude
|
| e=1.0/(b*t);
|
| //--- ... but avoid multiple recursions.
|
| if(MathAbs(e)<10)
|
| {
|
| e=MathArctan(e);
|
| temp=ebig+m*MathSin(lphi)*MathSin(e)-IncompleteEllipticIntegralE(e,m);
|
| //--- check
|
| if(s<0)
|
| temp=-temp;
|
| //--- return result
|
| return(temp+npio2*ebig);
|
| }
|
| }
|
| //--- change values
|
| c=MathSqrt(m);
|
| a=1.0;
|
| d=1;
|
| e=0.0;
|
| md=0;
|
| //--- cycle
|
| while(MathAbs(c/a)>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| temp=b/a;
|
| lphi=lphi+MathArctan(t*temp)+md*M_PI;
|
| md=(int)((lphi+pio2)/M_PI);
|
| t=t*(1.0+temp)/(1.0-temp*t*t);
|
| c=0.5*(a-b);
|
| temp=MathSqrt(a*b);
|
| a=0.5*(a+b);
|
| b=temp;
|
| d=d+d;
|
| e=e+c*MathSin(lphi);
|
| }
|
| //--- change value
|
| temp=ebig/EllipticIntegralK(m);
|
| temp=temp*((MathArctan(t)+md*M_PI)/(d*a));
|
| temp=temp+e;
|
| //--- check
|
| if(s<0)
|
| temp=-temp;
|
| //--- return result
|
| return(temp+npio2*ebig);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Exponential integral |
|
| //+------------------------------------------------------------------+
|
| class CExpIntegrals
|
| {
|
| public:
|
| static double ExponentialIntegralEi(const double x);
|
| static double ExponentialIntegralEn(const double x,const int n);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Exponential integral Ei(x) |
|
| //| x |
|
| //| - t |
|
| //| | | e |
|
| //| Ei(x) = -|- --- dt . |
|
| //| | | t |
|
| //| - |
|
| //| -inf |
|
| //| Not defined for x <= 0. |
|
| //| See also expn.c. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,100 50000 8.6e-16 1.3e-16 |
|
| //+------------------------------------------------------------------+
|
| double CExpIntegrals::ExponentialIntegralEi(const double x)
|
| {
|
| //--- create variables
|
| double eul=0.5772156649015328606065;
|
| double f=0;
|
| double f1=0;
|
| double f2=0;
|
| double w=0;
|
| //--- check
|
| if(x<=0.0)
|
| return(0);
|
| //--- check
|
| if(x<2.0)
|
| {
|
| //--- calculation
|
| f1=-5.350447357812542947283;
|
| f1=f1*x+218.5049168816613393830;
|
| f1=f1*x-4176.572384826693777058;
|
| f1=f1*x+55411.76756393557601232;
|
| f1=f1*x-331338.1331178144034309;
|
| f1=f1*x+1592627.163384945414220;
|
| f2=1.000000000000000000000;
|
| f2=f2*x-52.50547959112862969197;
|
| f2=f2*x+1259.616186786790571525;
|
| f2=f2*x-17565.49581973534652631;
|
| f2=f2*x+149306.2117002725991967;
|
| f2=f2*x-729494.9239640527645655;
|
| f2=f2*x+1592627.163384945429726;
|
| f=f1/f2;
|
| //--- return result
|
| return(eul+MathLog(x)+x*f);
|
| }
|
| //--- check
|
| if(x<4.0)
|
| {
|
| //--- calculation
|
| w=1/x;
|
| f1=1.981808503259689673238E-2;
|
| f1=f1*w-1.271645625984917501326;
|
| f1=f1*w-2.088160335681228318920;
|
| f1=f1*w+2.755544509187936721172;
|
| f1=f1*w-4.409507048701600257171E-1;
|
| f1=f1*w+4.665623805935891391017E-2;
|
| f1=f1*w-1.545042679673485262580E-3;
|
| f1=f1*w+7.059980605299617478514E-5;
|
| f2=1.000000000000000000000;
|
| f2=f2*w+1.476498670914921440652;
|
| f2=f2*w+5.629177174822436244827E-1;
|
| f2=f2*w+1.699017897879307263248E-1;
|
| f2=f2*w+2.291647179034212017463E-2;
|
| f2=f2*w+4.450150439728752875043E-3;
|
| f2=f2*w+1.727439612206521482874E-4;
|
| f2=f2*w+3.953167195549672482304E-5;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //--- check
|
| if(x<8.0)
|
| {
|
| //--- calculation
|
| w=1/x;
|
| f1=-1.373215375871208729803;
|
| f1=f1*w-7.084559133740838761406E-1;
|
| f1=f1*w+1.580806855547941010501;
|
| f1=f1*w-2.601500427425622944234E-1;
|
| f1=f1*w+2.994674694113713763365E-2;
|
| f1=f1*w-1.038086040188744005513E-3;
|
| f1=f1*w+4.371064420753005429514E-5;
|
| f1=f1*w+2.141783679522602903795E-6;
|
| f2=1.000000000000000000000;
|
| f2=f2*w+8.585231423622028380768E-1;
|
| f2=f2*w+4.483285822873995129957E-1;
|
| f2=f2*w+7.687932158124475434091E-2;
|
| f2=f2*w+2.449868241021887685904E-2;
|
| f2=f2*w+8.832165941927796567926E-4;
|
| f2=f2*w+4.590952299511353531215E-4;
|
| f2=f2*w+-4.729848351866523044863E-6;
|
| f2=f2*w+2.665195537390710170105E-6;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //--- check
|
| if(x<16.0)
|
| {
|
| //--- calculation
|
| w=1/x;
|
| f1=-2.106934601691916512584;
|
| f1=f1*w+1.732733869664688041885;
|
| f1=f1*w-2.423619178935841904839E-1;
|
| f1=f1*w+2.322724180937565842585E-2;
|
| f1=f1*w+2.372880440493179832059E-4;
|
| f1=f1*w-8.343219561192552752335E-5;
|
| f1=f1*w+1.363408795605250394881E-5;
|
| f1=f1*w-3.655412321999253963714E-7;
|
| f1=f1*w+1.464941733975961318456E-8;
|
| f1=f1*w+6.176407863710360207074E-10;
|
| f2=1.000000000000000000000;
|
| f2=f2*w-2.298062239901678075778E-1;
|
| f2=f2*w+1.105077041474037862347E-1;
|
| f2=f2*w-1.566542966630792353556E-2;
|
| f2=f2*w+2.761106850817352773874E-3;
|
| f2=f2*w-2.089148012284048449115E-4;
|
| f2=f2*w+1.708528938807675304186E-5;
|
| f2=f2*w-4.459311796356686423199E-7;
|
| f2=f2*w+1.394634930353847498145E-8;
|
| f2=f2*w+6.150865933977338354138E-10;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //--- check
|
| if(x<32.0)
|
| {
|
| //--- calculation
|
| w=1/x;
|
| f1=-2.458119367674020323359E-1;
|
| f1=f1*w-1.483382253322077687183E-1;
|
| f1=f1*w+7.248291795735551591813E-2;
|
| f1=f1*w-1.348315687380940523823E-2;
|
| f1=f1*w+1.342775069788636972294E-3;
|
| f1=f1*w-7.942465637159712264564E-5;
|
| f1=f1*w+2.644179518984235952241E-6;
|
| f1=f1*w-4.239473659313765177195E-8;
|
| f2=1.000000000000000000000;
|
| f2=f2*w-1.044225908443871106315E-1;
|
| f2=f2*w-2.676453128101402655055E-1;
|
| f2=f2*w+9.695000254621984627876E-2;
|
| f2=f2*w-1.601745692712991078208E-2;
|
| f2=f2*w+1.496414899205908021882E-3;
|
| f2=f2*w-8.462452563778485013756E-5;
|
| f2=f2*w+2.728938403476726394024E-6;
|
| f2=f2*w-4.239462431819542051337E-8;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //--- check
|
| if(x<64)
|
| {
|
| //--- calculation
|
| w=1/x;
|
| f1=1.212561118105456670844E-1;
|
| f1=f1*w-5.823133179043894485122E-1;
|
| f1=f1*w+2.348887314557016779211E-1;
|
| f1=f1*w-3.040034318113248237280E-2;
|
| f1=f1*w+1.510082146865190661777E-3;
|
| f1=f1*w-2.523137095499571377122E-5;
|
| f2=1.000000000000000000000;
|
| f2=f2*w-1.002252150365854016662;
|
| f2=f2*w+2.928709694872224144953E-1;
|
| f2=f2*w-3.337004338674007801307E-2;
|
| f2=f2*w+1.560544881127388842819E-3;
|
| f2=f2*w-2.523137093603234562648E-5;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //--- calculation
|
| w=1/x;
|
| f1=-7.657847078286127362028E-1;
|
| f1=f1*w+6.886192415566705051750E-1;
|
| f1=f1*w-2.132598113545206124553E-1;
|
| f1=f1*w+3.346107552384193813594E-2;
|
| f1=f1*w-3.076541477344756050249E-3;
|
| f1=f1*w+1.747119316454907477380E-4;
|
| f1=f1*w-6.103711682274170530369E-6;
|
| f1=f1*w+1.218032765428652199087E-7;
|
| f1=f1*w-1.086076102793290233007E-9;
|
| f2=1.000000000000000000000;
|
| f2=f2*w-1.888802868662308731041;
|
| f2=f2*w+1.066691687211408896850;
|
| f2=f2*w-2.751915982306380647738E-1;
|
| f2=f2*w+3.930852688233823569726E-2;
|
| f2=f2*w-3.414684558602365085394E-3;
|
| f2=f2*w+1.866844370703555398195E-4;
|
| f2=f2*w-6.345146083130515357861E-6;
|
| f2=f2*w+1.239754287483206878024E-7;
|
| f2=f2*w-1.086076102793126632978E-9;
|
| f=f1/f2;
|
| //--- return result
|
| return(MathExp(x)*w*(1+w*f));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Exponential integral En(x) |
|
| //| Evaluates the exponential integral |
|
| //| inf. |
|
| //| - |
|
| //| | | -xt |
|
| //| | e |
|
| //| E (x) = | ---- dt. |
|
| //| n | n |
|
| //| | | t |
|
| //| - |
|
| //| 1 |
|
| //| Both n and x must be nonnegative. |
|
| //| The routine employs either a power series, a continued |
|
| //| fraction, or an asymptotic formula depending on the |
|
| //| relative values of n and x. |
|
| //| ACCURACY: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0, 30 10000 1.7e-15 3.6e-16 |
|
| //+------------------------------------------------------------------+
|
| double CExpIntegrals::ExponentialIntegralEn(const double x,const int n)
|
| {
|
| //--- create variables
|
| double eul=0.57721566490153286060;
|
| double big=1.44115188075855872*MathPow(10,17);
|
| double result=0;
|
| double r=0;
|
| double t=0;
|
| double yk=0;
|
| double xk=0;
|
| double pk=0;
|
| double pkm1=0;
|
| double pkm2=0;
|
| double qk=0;
|
| double qkm1=0;
|
| double qkm2=0;
|
| double psi=0;
|
| double z=0;
|
| int i=0;
|
| int k=0;
|
| //--- check
|
| if(((n<0 || x<0.0) || x>170) || (x==0.0 && n<2))
|
| return(-1);
|
| //--- check
|
| if(x==0.0)
|
| return(1.0/(double)(n-1));
|
| //--- check
|
| if(n==0)
|
| return(MathExp(-x)/x);
|
| //--- check
|
| if(n>5000)
|
| {
|
| //--- calculation
|
| xk=x+n;
|
| yk=1/(xk*xk);
|
| t=n;
|
| result=yk*t*(6*x*x-8*t*x+t*t);
|
| result=yk*(result+t*(t-2.0*x));
|
| result=yk*(result+t);
|
| result=(result+1)*MathExp(-x)/xk;
|
| //--- return result
|
| return(result);
|
| }
|
| //--- check
|
| if(x<=1.0)
|
| {
|
| //--- calculation
|
| psi=-eul-MathLog(x);
|
| for(i=1; i<=n-1; i++)
|
| psi=psi+1.0/(double)i;
|
| z=-x;
|
| xk=0;
|
| yk=1;
|
| pk=1-n;
|
| //--- check
|
| if(n==1)
|
| result=0.0;
|
| else
|
| result=1.0/pk;
|
| do
|
| {
|
| //--- change values
|
| xk=xk+1;
|
| yk=yk*z/xk;
|
| pk=pk+1;
|
| //--- check
|
| if(pk!=0.0)
|
| result=result+yk/pk;
|
| //--- check
|
| if(result!=0.0)
|
| t=MathAbs(yk/result);
|
| else
|
| t=1;
|
| }
|
| while(t>=CMath::m_machineepsilon);
|
| t=1;
|
| for(i=1; i<=n-1; i++)
|
| t=t*z/i;
|
| //--- return result
|
| return(psi*t-result);
|
| }
|
| else
|
| {
|
| //--- calculation
|
| k=1;
|
| pkm2=1;
|
| qkm2=x;
|
| pkm1=1.0;
|
| qkm1=x+n;
|
| result=pkm1/qkm1;
|
| do
|
| {
|
| k=k+1;
|
| //--- check
|
| if(k%2==1)
|
| {
|
| yk=1;
|
| xk=n+(double)(k-1)/2.0;
|
| }
|
| else
|
| {
|
| yk=x;
|
| xk=(double)k/2.0;
|
| }
|
| //--- change values
|
| pk=pkm1*yk+pkm2*xk;
|
| qk=qkm1*yk+qkm2*xk;
|
| //--- check
|
| if(qk!=0.0)
|
| {
|
| r=pk/qk;
|
| t=MathAbs((result-r)/r);
|
| result=r;
|
| }
|
| else
|
| t=1;
|
| //--- change values
|
| pkm2=pkm1;
|
| pkm1=pk;
|
| qkm2=qkm1;
|
| qkm1=qk;
|
| //--- check
|
| if(MathAbs(pk)>big)
|
| {
|
| //--- change values
|
| pkm2=pkm2/big;
|
| pkm1=pkm1/big;
|
| qkm2=qkm2/big;
|
| qkm1=qkm1/big;
|
| }
|
| }
|
| while(t>=CMath::m_machineepsilon);
|
| result=result*MathExp(-x);
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| F distribution |
|
| //+------------------------------------------------------------------+
|
| class CFDistr
|
| {
|
| public:
|
| static double FDistribution(const int a,const int b,const double x);
|
| static double FComplDistribution(const int a,const int b,const double x);
|
| static double InvFDistribution(const int a,const int b,const double y);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| F distribution |
|
| //| Returns the area from zero to x under the F density |
|
| //| function (also known as Snedcor's density or the |
|
| //| variance ratio density). This is the density |
|
| //| of x = (u1/df1)/(u2/df2), where u1 and u2 are random |
|
| //| variables having Chi square distributions with df1 |
|
| //| and df2 degrees of freedom, respectively. |
|
| //| The incomplete beta integral is used, according to the |
|
| //| formula |
|
| //| P(x) = incbet( df1/2, df2/2, (df1*x/(df2 + df1*x) ). |
|
| //| The arguments a and b are greater than zero, and x is |
|
| //| nonnegative. |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,x). |
|
| //| x a,b Relative error: |
|
| //| arithmetic domain domain # trials peak rms |
|
| //| IEEE 0,1 0,100 100000 9.8e-15 1.7e-15 |
|
| //| IEEE 1,5 0,100 100000 6.5e-15 3.5e-16 |
|
| //| IEEE 0,1 1,10000 100000 2.2e-11 3.3e-12 |
|
| //| IEEE 1,5 1,10000 100000 1.1e-11 1.7e-13 |
|
| //+------------------------------------------------------------------+
|
| double CFDistr::FDistribution(const int a,const int b,const double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert(a>=1 && b>=1 && x>=0,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- calculation
|
| double w=a*x;
|
| w/=b+w;
|
| //--- return result
|
| return(CIncBetaF::IncompleteBeta(0.5*a,0.5*b,w));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complemented F distribution |
|
| //| Returns the area from x to infinity under the F density |
|
| //| function (also known as Snedcor's density or the |
|
| //| variance ratio density). |
|
| //| inf. |
|
| //| - |
|
| //| 1 | | a-1 b-1 |
|
| //| 1-P(x) = ------ | t (1-t) dt |
|
| //| B(a,b) | | |
|
| //| - |
|
| //| x |
|
| //| The incomplete beta integral is used, according to the |
|
| //| formula |
|
| //| P(x) = incbet( df2/2, df1/2, (df2/(df2 + df1*x) ). |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,x) in the indicated intervals. |
|
| //| x a,b Relative error: |
|
| //| arithmetic domain domain # trials peak rms |
|
| //| IEEE 0,1 1,100 100000 3.7e-14 5.9e-16 |
|
| //| IEEE 1,5 1,100 100000 8.0e-15 1.6e-15 |
|
| //| IEEE 0,1 1,10000 100000 1.8e-11 3.5e-13 |
|
| //| IEEE 1,5 1,10000 100000 2.0e-11 3.0e-12 |
|
| //+------------------------------------------------------------------+
|
| double CFDistr::FComplDistribution(const int a,const int b,const double x)
|
| {
|
| //--- check
|
| if(!CAp::Assert((a>=1 && b>=1) && x>=0.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- calculation
|
| double w=b/(b+a*x);
|
| //--- return result
|
| return(CIncBetaF::IncompleteBeta(0.5*b,0.5*a,w));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse of complemented F distribution |
|
| //| Finds the F density argument x such that the integral |
|
| //| from x to infinity of the F density is equal to the |
|
| //| given probability p. |
|
| //| This is accomplished using the inverse beta integral |
|
| //| function and the relations |
|
| //| z = incbi( df2/2, df1/2, p ) |
|
| //| x = df2 (1-z) / (df1 z). |
|
| //| Note: the following relations hold for the inverse of |
|
| //| the uncomplemented F distribution: |
|
| //| z = incbi( df1/2, df2/2, p ) |
|
| //| x = df2 z / (df1 (1-z)). |
|
| //| ACCURACY: |
|
| //| Tested at random points (a,b,p). |
|
| //| a,b Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| For p between .001 and 1: |
|
| //| IEEE 1,100 100000 8.3e-15 4.7e-16 |
|
| //| IEEE 1,10000 100000 2.1e-11 1.4e-13 |
|
| //| For p between 10^-6 and 10^-3: |
|
| //| IEEE 1,100 50000 1.3e-12 8.4e-15 |
|
| //| IEEE 1,10000 50000 3.0e-12 4.8e-14 |
|
| //+------------------------------------------------------------------+
|
| double CFDistr::InvFDistribution(const int a,const int b,const double y)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double w=0;
|
| //--- check
|
| if(!CAp::Assert(((a>=1 && b>=1) && y>0.0) && y<=1.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- Compute probability for x=0.5
|
| w=CIncBetaF::IncompleteBeta(0.5*b,0.5*a,0.5);
|
| //--- If that is greater than y,then the solution w < .5
|
| //--- Otherwise,solve at 1-y to remove cancellation in (b - b*w)
|
| if(w>y || y<0.001)
|
| {
|
| //--- calculation
|
| w=CIncBetaF::InvIncompleteBeta(0.5*b,0.5*a,y);
|
| result=(b-b*w)/(a*w);
|
| }
|
| else
|
| {
|
| //--- calculation
|
| w=CIncBetaF::InvIncompleteBeta(0.5*a,0.5*b,1.0-y);
|
| result=b*w/(a*(1.0-w));
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Fresnel integral |
|
| //+------------------------------------------------------------------+
|
| class CFresnel
|
| {
|
| public:
|
| static void FresnelIntegral(double x,double &c,double &s);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Fresnel integral |
|
| //| Evaluates the Fresnel integrals |
|
| //| x |
|
| //| - |
|
| //| | | |
|
| //| C(x) = | cos(pi/2 t**2) dt, |
|
| //| | | |
|
| //| - |
|
| //| 0 |
|
| //| x |
|
| //| - |
|
| //| | | |
|
| //| S(x) = | sin(pi/2 t**2) dt. |
|
| //| | | |
|
| //| - |
|
| //| 0 |
|
| //| The integrals are evaluated by a power series for x < 1. |
|
| //| For x >= 1 auxiliary functions f(x) and g(x) are employed |
|
| //| such that |
|
| //| C(x) = 0.5 + f(x) sin( pi/2 x**2 ) - g(x) cos( pi/2 x**2 ) |
|
| //| S(x) = 0.5 - f(x) cos( pi/2 x**2 ) - g(x) sin( pi/2 x**2 ) |
|
| //| ACCURACY: |
|
| //| Relative error. |
|
| //| Arithmetic function domain # trials peak rms |
|
| //| IEEE S(x) 0, 10 10000 2.0e-15 3.2e-16|
|
| //| IEEE C(x) 0, 10 10000 1.8e-15 3.3e-16|
|
| //+------------------------------------------------------------------+
|
| void CFresnel::FresnelIntegral(double x,double &c,double &s)
|
| {
|
| //--- create variables
|
| double xxa=0;
|
| double f=0;
|
| double g=0;
|
| double cc=0;
|
| double ss=0;
|
| double t=0;
|
| double u=0;
|
| double x2=0;
|
| double sn=0;
|
| double sd=0;
|
| double cn=0;
|
| double cd=0;
|
| double fn=0;
|
| double fd=0;
|
| double gn=0;
|
| double gd=0;
|
| double mpi=0;
|
| double mpio2=0;
|
| //--- initialization
|
| mpi=3.14159265358979323846;
|
| mpio2=1.57079632679489661923;
|
| xxa=x;
|
| x=MathAbs(xxa);
|
| x2=x*x;
|
| //--- check
|
| if(x2<2.5625)
|
| {
|
| //--- calculation
|
| t=x2*x2;
|
| sn=-2.99181919401019853726E3;
|
| sn=sn*t+7.08840045257738576863E5;
|
| sn=sn*t-6.29741486205862506537E7;
|
| sn=sn*t+2.54890880573376359104E9;
|
| sn=sn*t-4.42979518059697779103E10;
|
| sn=sn*t+3.18016297876567817986E11;
|
| sd=1.00000000000000000000E0;
|
| sd=sd*t+2.81376268889994315696E2;
|
| sd=sd*t+4.55847810806532581675E4;
|
| sd=sd*t+5.17343888770096400730E6;
|
| sd=sd*t+4.19320245898111231129E8;
|
| sd=sd*t+2.24411795645340920940E10;
|
| sd=sd*t+6.07366389490084639049E11;
|
| cn=-4.98843114573573548651E-8;
|
| cn=cn*t+9.50428062829859605134E-6;
|
| cn=cn*t-6.45191435683965050962E-4;
|
| cn=cn*t+1.88843319396703850064E-2;
|
| cn=cn*t-2.05525900955013891793E-1;
|
| cn=cn*t+9.99999999999999998822E-1;
|
| cd=3.99982968972495980367E-12;
|
| cd=cd*t+9.15439215774657478799E-10;
|
| cd=cd*t+1.25001862479598821474E-7;
|
| cd=cd*t+1.22262789024179030997E-5;
|
| cd=cd*t+8.68029542941784300606E-4;
|
| cd=cd*t+4.12142090722199792936E-2;
|
| cd=cd*t+1.00000000000000000118E0;
|
| s=MathSign(xxa)*x*x2*sn/sd;
|
| c=MathSign(xxa)*x*cn/cd;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x>36974.0)
|
| {
|
| //--- calculation
|
| c=MathSign(xxa)*0.5;
|
| s=MathSign(xxa)*0.5;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- calculation
|
| x2=x*x;
|
| t=mpi*x2;
|
| u=1/(t*t);
|
| t=1/t;
|
| fn=4.21543555043677546506E-1;
|
| fn=fn*u+1.43407919780758885261E-1;
|
| fn=fn*u+1.15220955073585758835E-2;
|
| fn=fn*u+3.45017939782574027900E-4;
|
| fn=fn*u+4.63613749287867322088E-6;
|
| fn=fn*u+3.05568983790257605827E-8;
|
| fn=fn*u+1.02304514164907233465E-10;
|
| fn=fn*u+1.72010743268161828879E-13;
|
| fn=fn*u+1.34283276233062758925E-16;
|
| fn=fn*u+3.76329711269987889006E-20;
|
| fd=1.00000000000000000000E0;
|
| fd=fd*u+7.51586398353378947175E-1;
|
| fd=fd*u+1.16888925859191382142E-1;
|
| fd=fd*u+6.44051526508858611005E-3;
|
| fd=fd*u+1.55934409164153020873E-4;
|
| fd=fd*u+1.84627567348930545870E-6;
|
| fd=fd*u+1.12699224763999035261E-8;
|
| fd=fd*u+3.60140029589371370404E-11;
|
| fd=fd*u+5.88754533621578410010E-14;
|
| fd=fd*u+4.52001434074129701496E-17;
|
| fd=fd*u+1.25443237090011264384E-20;
|
| gn=5.04442073643383265887E-1;
|
| gn=gn*u+1.97102833525523411709E-1;
|
| gn=gn*u+1.87648584092575249293E-2;
|
| gn=gn*u+6.84079380915393090172E-4;
|
| gn=gn*u+1.15138826111884280931E-5;
|
| gn=gn*u+9.82852443688422223854E-8;
|
| gn=gn*u+4.45344415861750144738E-10;
|
| gn=gn*u+1.08268041139020870318E-12;
|
| gn=gn*u+1.37555460633261799868E-15;
|
| gn=gn*u+8.36354435630677421531E-19;
|
| gn=gn*u+1.86958710162783235106E-22;
|
| gd=1.00000000000000000000E0;
|
| gd=gd*u+1.47495759925128324529E0;
|
| gd=gd*u+3.37748989120019970451E-1;
|
| gd=gd*u+2.53603741420338795122E-2;
|
| gd=gd*u+8.14679107184306179049E-4;
|
| gd=gd*u+1.27545075667729118702E-5;
|
| gd=gd*u+1.04314589657571990585E-7;
|
| gd=gd*u+4.60680728146520428211E-10;
|
| gd=gd*u+1.10273215066240270757E-12;
|
| gd=gd*u+1.38796531259578871258E-15;
|
| gd=gd*u+8.39158816283118707363E-19;
|
| gd=gd*u+1.86958710162783236342E-22;
|
| //--- get result
|
| f=1-u*fn/fd;
|
| g=t*gn/gd;
|
| t=mpio2*x2;
|
| cc=MathCos(t);
|
| ss=MathSin(t);
|
| t=mpi*x;
|
| c=0.5+(f*ss-g*cc)/t;
|
| s=0.5-(f*cc+g*ss)/t;
|
| c=c*MathSign(xxa);
|
| s=s*MathSign(xxa);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Hermite polynomial |
|
| //+------------------------------------------------------------------+
|
| class CHermite
|
| {
|
| public:
|
| static double HermiteCalculate(const int n,const double x);
|
| static double HermiteSum(double &c[],const int n,const double x);
|
| static void HermiteCoefficients(const int n,double &c[]);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Calculation of the value of the Hermite polynomial. |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Hermite polynomial Hn at x |
|
| //+------------------------------------------------------------------+
|
| double CHermite::HermiteCalculate(const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double a=1.0;
|
| double b=2.0*x;
|
| //--- Special cases: N=0 or N=1
|
| if(n==0)
|
| return(a);
|
| //--- check
|
| if(n==1)
|
| return(b);
|
| //--- General case: N>=2
|
| for(int i=2; i<=n; i++)
|
| {
|
| result=2*x*b-2*(i-1)*a;
|
| a=b;
|
| b=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Summation of Hermite polynomials using Clenshaw?s recurrence |
|
| //| formula. |
|
| //| This routine calculates |
|
| //| c[0]*H0(x) + c[1]*H1(x) + ... + c[N]*HN(x) |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Hermite polynomial at x |
|
| //+------------------------------------------------------------------+
|
| double CHermite::HermiteSum(double &c[],const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- calculation
|
| for(int i=n; i>=0; i--)
|
| {
|
| result=2*(x*b1-(i+1)*b2)+c[i];
|
| b2=b1;
|
| b1=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Representation of Hn as C[0] + C[1]*X + ... + C[N]*X^N |
|
| //| Input parameters: |
|
| //| N - polynomial degree, n>=0 |
|
| //| Output parameters: |
|
| //| C - coefficients |
|
| //+------------------------------------------------------------------+
|
| void CHermite::HermiteCoefficients(const int n,double &c[])
|
| {
|
| //--- allocation
|
| ArrayResize(c,n+1);
|
| //--- initialization
|
| ArrayInitialize(c,0.0);
|
| //--- calculation
|
| c[n]=MathExp(n*MathLog(2));
|
| for(int i=0; i<=n/2-1; i++)
|
| c[n-2*(i+1)]=-(c[n-2*i]*(n-2*i)*(n-2*i-1)/4/(i+1));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Jacobian elliptic functions |
|
| //+------------------------------------------------------------------+
|
| class CJacobianElliptic
|
| {
|
| public:
|
| static void JacobianEllipticFunctions(const double u,const double m,double &sn,double &cn,double &dn,double &ph);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Jacobian Elliptic Functions |
|
| //| Evaluates the Jacobian elliptic functions sn(u|m), cn(u|m), |
|
| //| and dn(u|m) of parameter m between 0 and 1, and real |
|
| //| argument u. |
|
| //| These functions are periodic, with quarter-period on the |
|
| //| real axis equal to the complete elliptic integral |
|
| //| ellpk(1.0-m). |
|
| //| Relation to incomplete elliptic integral: |
|
| //| If u = ellik(phi,m), then sn(u|m) = sin(phi), |
|
| //| and cn(u|m) = cos(phi). Phi is called the amplitude of u. |
|
| //| Computation is by means of the arithmetic-geometric mean |
|
| //| algorithm, except when m is within 1e-9 of 0 or 1. In the |
|
| //| latter case with m close to 1, the approximation applies |
|
| //| only for phi < pi/2. |
|
| //| ACCURACY: |
|
| //| Tested at random points with u between 0 and 10, m between |
|
| //| 0 and 1. |
|
| //| Absolute error (* = relative error): |
|
| //| arithmetic function # trials peak rms |
|
| //| IEEE phi 10000 9.2e-16* 1.4e-16* |
|
| //| IEEE sn 50000 4.1e-15 4.6e-16 |
|
| //| IEEE cn 40000 3.6e-15 4.4e-16 |
|
| //| IEEE dn 10000 1.3e-12 1.8e-14 |
|
| //| Peak error observed in consistency check using addition |
|
| //| theorem for sn(u+v) was 4e-16 (absolute). Also tested by |
|
| //| the above relation to the incomplete elliptic integral. |
|
| //| Accuracy deteriorates when u is large. |
|
| //+------------------------------------------------------------------+
|
| void CJacobianElliptic::JacobianEllipticFunctions(const double u,
|
| const double m,
|
| double &sn,double &cn,
|
| double &dn,double &ph)
|
| {
|
| //--- create variables
|
| double ai=0;
|
| double b=0;
|
| double phi=0;
|
| double t=0;
|
| double twon=0;
|
| int i=0;
|
| //--- create arrays
|
| double a[];
|
| double c[];
|
| //--- initialization
|
| sn=0;
|
| cn=0;
|
| dn=0;
|
| ph=0;
|
| //--- check
|
| if(!CAp::Assert(m>=0.0 && m<=1.0,__FUNCTION__+": m<0 or m>1"))
|
| return;
|
| //--- allocation
|
| ArrayResize(a,9);
|
| ArrayResize(c,9);
|
| //--- check
|
| if(m<1.0e-9)
|
| {
|
| //--- calculation
|
| t=MathSin(u);
|
| b=MathCos(u);
|
| ai=0.25*m*(u-t*b);
|
| sn=t-ai*b;
|
| cn=b+ai*t;
|
| ph=u-ai;
|
| dn=1.0-0.5*m*t*t;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(m>=0.9999999999)
|
| {
|
| //--- calculation
|
| ai=0.25*(1.0-m);
|
| b=MathCosh(u);
|
| t=MathTanh(u);
|
| phi=1.0/b;
|
| twon=b*MathSinh(u);
|
| sn=t+ai*(twon-u)/(b*b);
|
| ph=2.0*MathArctan(MathExp(u))-1.57079632679489661923+ai*(twon-u)/b;
|
| ai=ai*t*phi;
|
| cn=phi-ai*(twon-u);
|
| dn=phi+ai*(twon+u);
|
| //--- exit the function
|
| return;
|
| }
|
| //--- change values
|
| a[0]=1.0;
|
| b=MathSqrt(1.0-m);
|
| c[0]=MathSqrt(m);
|
| twon=1.0;
|
| i=0;
|
| //--- cycle
|
| while(MathAbs(c[i]/a[i])>CMath::m_machineepsilon)
|
| {
|
| //--- check
|
| if(i>7)
|
| {
|
| //--- check
|
| if(!CAp::Assert(false,__FUNCTION__+": overflow"))
|
| return;
|
| //--- break the cycle
|
| break;
|
| }
|
| //--- calculation
|
| ai=a[i];
|
| i=i+1;
|
| c[i]=0.5*(ai-b);
|
| t=MathSqrt(ai*b);
|
| a[i]=0.5*(ai+b);
|
| b=t;
|
| twon=twon*2.0;
|
| }
|
| phi=twon*a[i]*u;
|
| //--- cycle
|
| do
|
| {
|
| //--- calculation
|
| t=c[i]*MathSin(phi)/a[i];
|
| b=phi;
|
| phi=(MathArcsin(t)+phi)/2.0;
|
| i=i-1;
|
| }
|
| while(i!=0);
|
| //--- get result
|
| sn=MathSin(phi);
|
| t=MathCos(phi);
|
| cn=t;
|
| dn=t/MathCos(phi-b);
|
| ph=phi;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Laguerre polynomial |
|
| //+------------------------------------------------------------------+
|
| class CLaguerre
|
| {
|
| public:
|
| static double LaguerreCalculate(const int n,const double x);
|
| static double LaguerreSum(double &c[],const int n,const double x);
|
| static void LaguerreCoefficients(const int n,double &c[]);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Calculation of the value of the Laguerre polynomial. |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Laguerre polynomial Ln at x |
|
| //+------------------------------------------------------------------+
|
| double CLaguerre::LaguerreCalculate(const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double a=0;
|
| double b=0;
|
| double i=0;
|
| //--- initialization
|
| result=1;
|
| a=1;
|
| b=1-x;
|
| //--- check
|
| if(n==1)
|
| result=b;
|
| //--- initialization
|
| i=2;
|
| //--- cycle
|
| while(i<=n)
|
| {
|
| //--- calculation
|
| result=((2*i-1-x)*b-(i-1)*a)/i;
|
| a=b;
|
| b=result;
|
| i=i+1;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Summation of Laguerre polynomials using Clenshaw?s recurrence |
|
| //| formula. |
|
| //| This routine calculates c[0]*L0(x) + c[1]*L1(x) + ... + |
|
| //| + c[N]*LN(x) |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Laguerre polynomial at x |
|
| //+------------------------------------------------------------------+
|
| double CLaguerre::LaguerreSum(double &c[],const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- calculation
|
| for(int i=n; i>=0; i--)
|
| {
|
| result=(2*i+1-x)*b1/(i+1)-(i+1)*b2/(i+2)+c[i];
|
| b2=b1;
|
| b1=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Representation of Ln as C[0] + C[1]*X + ... + C[N]*X^N |
|
| //| Input parameters: |
|
| //| N - polynomial degree, n>=0 |
|
| //| Output parameters: |
|
| //| C - coefficients |
|
| //+------------------------------------------------------------------+
|
| void CLaguerre::LaguerreCoefficients(const int n,double &c[])
|
| {
|
| //--- allocation
|
| ArrayResize(c,n+1);
|
| //--- initialization
|
| c[0]=1;
|
| //--- calculation
|
| for(int i=0; i<n; i++)
|
| c[i+1]=-(c[i]*(n-i)/(i+1)/(i+1));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Legendre polynomial |
|
| //+------------------------------------------------------------------+
|
| class CLegendre
|
| {
|
| public:
|
| static double LegendreCalculate(const int n,const double x);
|
| static double LegendreSum(double &c[],const int n,const double x);
|
| static void LegendreCoefficients(const int n,double &c[]);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Calculation of the value of the Legendre polynomial Pn. |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Legendre polynomial Pn at x |
|
| //+------------------------------------------------------------------+
|
| double CLegendre::LegendreCalculate(const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=1.0;
|
| double a=1.0;
|
| double b=x;
|
| //--- check
|
| if(n==0)
|
| return(a);
|
| //--- check
|
| if(n==1)
|
| return(b);
|
| //--- calculation
|
| for(int i=2; i<=n; i++)
|
| {
|
| result=((2*i-1)*x*b-(i-1)*a)/i;
|
| a=b;
|
| b=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Summation of Legendre polynomials using Clenshaw?s recurrence |
|
| //| formula. |
|
| //| This routine calculates |
|
| //| c[0]*P0(x) + c[1]*P1(x) + ... + c[N]*PN(x) |
|
| //| Parameters: |
|
| //| n - degree, n>=0 |
|
| //| x - argument |
|
| //| Result: |
|
| //| the value of the Legendre polynomial at x |
|
| //+------------------------------------------------------------------+
|
| double CLegendre::LegendreSum(double &c[],const int n,const double x)
|
| {
|
| //--- create variables
|
| double result=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- calculation
|
| for(int i=n; i>=0; i--)
|
| {
|
| result=(2*i+1)*x*b1/(i+1)-(i+1)*b2/(i+2)+c[i];
|
| b2=b1;
|
| b1=result;
|
| }
|
| //--- return result
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Representation of Pn as C[0] + C[1]*X + ... + C[N]*X^N |
|
| //| Input parameters: |
|
| //| N - polynomial degree, n>=0 |
|
| //| Output parameters: |
|
| //| C - coefficients |
|
| //+------------------------------------------------------------------+
|
| void CLegendre::LegendreCoefficients(const int n,double &c[])
|
| {
|
| //--- allocation
|
| ArrayResize(c,n+1);
|
| //--- initialization
|
| ArrayInitialize(c,0.0);
|
| c[n]=1;
|
| //--- calculation
|
| for(int i=1; i<=n; i++)
|
| c[n]=c[n]*(n+i)/2/i;
|
| for(int i=0; i<=n/2-1; i++)
|
| c[n-2*(i+1)]=-(c[n-2*i]*(n-2*i)*(n-2*i-1)/2/(i+1)/(2*(n-i)-1));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Poisson distribution |
|
| //+------------------------------------------------------------------+
|
| class CPoissonDistr
|
| {
|
| public:
|
| static double PoissonDistribution(const int k,const double m);
|
| static double PoissonComplDistribution(const int k,const double m);
|
| static double InvPoissonDistribution(const int k,const double y);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Poisson distribution |
|
| //| Returns the sum of the first k+1 terms of the Poisson |
|
| //| distribution: |
|
| //| k j |
|
| //| -- -m m |
|
| //| > e -- |
|
| //| -- j! |
|
| //| j=0 |
|
| //| The terms are not summed directly; instead the incomplete |
|
| //| gamma integral is employed, according to the relation |
|
| //| y = pdtr( k, m ) = igamc( k+1, m ). |
|
| //| The arguments must both be positive. |
|
| //| ACCURACY: |
|
| //| See incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CPoissonDistr::PoissonDistribution(const int k,const double m)
|
| {
|
| //--- check
|
| if(!CAp::Assert(k>=0 && m>0.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CIncGammaF::IncompleteGammaC(k+1,m));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Complemented Poisson distribution |
|
| //| Returns the sum of the terms k+1 to infinity of the Poisson |
|
| //| distribution: |
|
| //| inf. j |
|
| //| -- -m m |
|
| //| > e -- |
|
| //| -- j! |
|
| //| j=k+1 |
|
| //| The terms are not summed directly; instead the incomplete |
|
| //| gamma integral is employed, according to the formula |
|
| //| y = pdtrc( k, m ) = igam( k+1, m ). |
|
| //| The arguments must both be positive. |
|
| //| ACCURACY: |
|
| //| See incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CPoissonDistr::PoissonComplDistribution(const int k,const double m)
|
| {
|
| //--- check
|
| if(!CAp::Assert(k>=0 && m>0.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CIncGammaF::IncompleteGamma(k+1,m));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Inverse Poisson distribution |
|
| //| Finds the Poisson variable x such that the integral |
|
| //| from 0 to x of the Poisson density is equal to the |
|
| //| given probability y. |
|
| //| This is accomplished using the inverse gamma integral |
|
| //| function and the relation |
|
| //| m = igami( k+1, y ). |
|
| //| ACCURACY: |
|
| //| See inverse incomplete gamma function |
|
| //+------------------------------------------------------------------+
|
| double CPoissonDistr::InvPoissonDistribution(const int k,const double y)
|
| {
|
| //--- check
|
| if(!CAp::Assert((k>=0 && y>=0.0) && y<1.0,__FUNCTION__+": domain error"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CIncGammaF::InvIncompleteGammaC(k+1,y));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Psi function |
|
| //+------------------------------------------------------------------+
|
| class CPsiF
|
| {
|
| public:
|
| static double Psi(double x);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Psi (digamma) function |
|
| //| d - |
|
| //| psi(x) = -- ln | (x) |
|
| //| dx |
|
| //| is the logarithmic derivative of the gamma function. |
|
| //| For integer x, |
|
| //| n-1 |
|
| //| - |
|
| //| psi(n) = -EUL + > 1/k. |
|
| //| - |
|
| //| k=1 |
|
| //| This formula is used for 0 < n <= 10. If x is negative, it |
|
| //| is transformed to a positive argument by the reflection |
|
| //| formula psi(1-x) = psi(x) + pi cot(pi x). |
|
| //| For general positive x, the argument is made greater than 10 |
|
| //| using the recurrence psi(x+1) = psi(x) + 1/x. |
|
| //| Then the following asymptotic expansion is applied: |
|
| //| inf. B |
|
| //| - 2k |
|
| //| psi(x) = log(x) - 1/2x - > ------- |
|
| //| - 2k |
|
| //| k=1 2k x |
|
| //| where the B2k are Bernoulli numbers. |
|
| //| ACCURACY: |
|
| //| Relative error (except absolute when |psi| < 1): |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE 0,30 30000 1.3e-15 1.4e-16 |
|
| //| IEEE -30,0 40000 1.5e-15 2.2e-16 |
|
| //+------------------------------------------------------------------+
|
| double CPsiF::Psi(double x)
|
| {
|
| //--- create variables
|
| double p=0;
|
| double q=0;
|
| double nz=0;
|
| double s=0;
|
| double w=0;
|
| double y=0;
|
| double z=0;
|
| double polv=0;
|
| int i=0;
|
| int n=0;
|
| int negative=0;
|
| //--- check
|
| if(x<=0.0)
|
| {
|
| negative=1;
|
| q=x;
|
| p=(int)MathFloor(q);
|
| //--- check
|
| if(p==q)
|
| {
|
| //--- check
|
| if(!CAp::Assert(false,__FUNCTION__+": singularity in Psi(x)"))
|
| return(EMPTY_VALUE);
|
| //--- return result
|
| return(CMath::m_maxrealnumber);
|
| }
|
| nz=q-p;
|
| //--- check
|
| if(nz!=0.5)
|
| {
|
| //--- check
|
| if(nz>0.5)
|
| {
|
| p=p+1.0;
|
| nz=q-p;
|
| }
|
| //--- change value
|
| nz=M_PI/MathTan(M_PI*nz);
|
| }
|
| else
|
| nz=0.0;
|
| x=1.0-x;
|
| }
|
| //--- check
|
| if(x<=10.0 && x==(double)((int)MathFloor(x)))
|
| {
|
| y=0.0;
|
| n=(int)MathFloor(x);
|
| //--- calculation
|
| for(i=1; i<=n-1; i++)
|
| {
|
| w=i;
|
| y=y+1.0/w;
|
| }
|
| y=y-0.57721566490153286061;
|
| }
|
| else
|
| {
|
| //--- change values
|
| s=x;
|
| w=0.0;
|
| //--- cycle
|
| while(s<10.0)
|
| {
|
| w=w+1.0/s;
|
| s=s+1.0;
|
| }
|
| //--- check
|
| if(s<1.0E17)
|
| {
|
| //--- calculation
|
| z=1.0/(s*s);
|
| polv=8.33333333333333333333E-2;
|
| polv=polv*z-2.10927960927960927961E-2;
|
| polv=polv*z+7.57575757575757575758E-3;
|
| polv=polv*z-4.16666666666666666667E-3;
|
| polv=polv*z+3.96825396825396825397E-3;
|
| polv=polv*z-8.33333333333333333333E-3;
|
| polv=polv*z+8.33333333333333333333E-2;
|
| y=z*polv;
|
| }
|
| else
|
| y=0.0;
|
| //--- change value
|
| y=MathLog(s)-0.5/s-y-w;
|
| }
|
| //--- check
|
| if(negative!=0)
|
| y=y-nz;
|
| //--- return result
|
| return(y);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Student's t distribution |
|
| //+------------------------------------------------------------------+
|
| class CStudenttDistr
|
| {
|
| public:
|
| static double StudenttDistribution(const int k,const double t);
|
| static double InvStudenttDistribution(const int k,double p);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Student's t distribution |
|
| //| Computes the integral from minus infinity to t of the Student |
|
| //| t distribution with integer k > 0 degrees of freedom: |
|
| //| t |
|
| //| - |
|
| //| | | |
|
| //| - | 2 -(k+1)/2 |
|
| //| | ( (k+1)/2 ) | ( x ) |
|
| //| ---------------------- | ( 1 + --- ) dx |
|
| //| - | ( k ) |
|
| //| sqrt( k pi ) | ( k/2 ) | |
|
| //| | | |
|
| //| - |
|
| //| -inf. |
|
| //| Relation to incomplete beta integral: |
|
| //| 1 - stdtr(k,t) = 0.5 * incbet( k/2, 1/2, z ) |
|
| //| where |
|
| //| z = k/(k + t**2). |
|
| //| For t < -2, this is the method of computation. For higher t, |
|
| //| a direct method is derived from integration by parts. |
|
| //| Since the function is symmetric about t=0, the area under the |
|
| //| right tail of the density is found by calling the function |
|
| //| with -t instead of t. |
|
| //| ACCURACY: |
|
| //| Tested at random 1<=k<=25. The "domain" refers to t. |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE -100,-2 50000 5.9e-15 1.4e-15 |
|
| //| IEEE -2,100 500000 2.7e-15 4.9e-17 |
|
| //+------------------------------------------------------------------+
|
| double CStudenttDistr::StudenttDistribution(const int k,const double t)
|
| {
|
| //--- check
|
| if(!CAp::Assert(k>0,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(t==0)
|
| return(0.5);
|
| //--- create variables
|
| double x=0;
|
| double rk=0;
|
| double z=0;
|
| double f=0;
|
| double tz=0;
|
| double p=0;
|
| double xsqk=0;
|
| int j=0;
|
| //--- check
|
| if(t<-2.0)
|
| {
|
| rk=k;
|
| z=rk/(rk+t*t);
|
| //--- return result
|
| return(0.5*CIncBetaF::IncompleteBeta(0.5*rk,0.5,z));
|
| }
|
| //--- check
|
| if(t<0)
|
| x=-t;
|
| else
|
| x=t;
|
| //--- change values
|
| rk=k;
|
| z=1.0+x*x/rk;
|
| //--- check
|
| if(k%2!=0)
|
| {
|
| xsqk=x/MathSqrt(rk);
|
| p=MathArctan(xsqk);
|
| //--- check
|
| if(k>1)
|
| {
|
| f=1.0;
|
| tz=1.0;
|
| j=3;
|
| //--- cycle
|
| while(j<k-1 && tz/f>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| tz*=(j-1)/(z*j);
|
| f+=tz;
|
| j+=2;
|
| }
|
| p+=f*xsqk/z;
|
| }
|
| p*=2.0/M_PI;
|
| }
|
| else
|
| {
|
| f=1.0;
|
| tz=1.0;
|
| j=2;
|
| //--- cycle
|
| while(j<k-1 && tz/f>CMath::m_machineepsilon)
|
| {
|
| //--- calculation
|
| tz*=(j-1)/(z*j);
|
| f+=tz;
|
| j+=2;
|
| }
|
| p=f*x/MathSqrt(z*rk);
|
| }
|
| //--- check
|
| if(t<0)
|
| p=-p;
|
| //--- return result
|
| return(0.5+0.5*p);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Functional inverse of Student's t distribution |
|
| //| Given probability p, finds the argument t such that stdtr(k,t) |
|
| //| is equal to p. |
|
| //| ACCURACY: |
|
| //| Tested at random 1<=k<=100. The "domain" refers to p: |
|
| //| Relative error: |
|
| //| arithmetic domain # trials peak rms |
|
| //| IEEE .001,.999 25000 5.7e-15 8.0e-16 |
|
| //| IEEE 10^-6,.001 25000 2.0e-12 2.9e-14 |
|
| //+------------------------------------------------------------------+
|
| double CStudenttDistr::InvStudenttDistribution(const int k,double p)
|
| {
|
| //--- create variables
|
| double t=0;
|
| double rk=k;
|
| double z=0;
|
| int rflg=0;
|
| CIncBetaF ibetaf;
|
| //--- check
|
| if(!CAp::Assert(k>0 && p>0 && p<1,__FUNCTION__+": the error variable"))
|
| return(EMPTY_VALUE);
|
| //--- check
|
| if(p>0.25 && p<0.75)
|
| {
|
| //--- check
|
| if(p==0.5)
|
| return(0);
|
| //--- change values
|
| z=1.0-2.0*p;
|
| z=CIncBetaF::InvIncompleteBeta(0.5,0.5*rk,MathAbs(z));
|
| t=MathSqrt(rk*z/(1.0-z));
|
| //--- check
|
| if(p<0.5)
|
| t=-t;
|
| //--- return result
|
| return(t);
|
| }
|
| //--- 0<p<=0.25 or 0.75<=p<1
|
| rflg=-1;
|
| //--- check
|
| if(p>=0.5)
|
| {
|
| p=1.0-p;
|
| rflg=1;
|
| }
|
| //--- change value
|
| z=CIncBetaF::InvIncompleteBeta(0.5*rk,0.5,2.0*p);
|
| //--- check
|
| if(CMath::m_maxrealnumber*z<rk)
|
| return(rflg*CMath::m_maxrealnumber);
|
|
|
| t=MathSqrt(rk/z-rk);
|
| //--- return result
|
| return(rflg*t);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Trigonometric integrals |
|
| //+------------------------------------------------------------------+
|
| class CTrigIntegrals
|
| {
|
| public:
|
| static void SineCosineIntegrals(double x,double &si,double &ci);
|
| static void HyperbolicSineCosineIntegrals(double x,double &shi,double &chi);
|
|
|
| private:
|
| static void ChebIterationShiChi(const double x,const double c,double &b0,double &b1,double &b2);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Sine and cosine integrals |
|
| //| Evaluates the integrals |
|
| //| x |
|
| //| - |
|
| //| | cos t - 1 |
|
| //| Ci(x) = eul + ln x + | --------- dt, |
|
| //| | t |
|
| //| - |
|
| //| 0 |
|
| //| x |
|
| //| - |
|
| //| | sin t |
|
| //| Si(x) = | ----- dt |
|
| //| | t |
|
| //| - |
|
| //| 0 |
|
| //| where eul = 0.57721566490153286061 is Euler's constant. |
|
| //| The integrals are approximated by rational functions. |
|
| //| For x > 8 auxiliary functions f(x) and g(x) are employed |
|
| //| such that |
|
| //| Ci(x) = f(x) sin(x) - g(x) cos(x) |
|
| //| Si(x) = pi/2 - f(x) cos(x) - g(x) sin(x) |
|
| //| ACCURACY: |
|
| //| Test interval = [0,50]. |
|
| //| Absolute error, except relative when > 1: |
|
| //| arithmetic function # trials peak rms |
|
| //| IEEE Si 30000 4.4e-16 7.3e-17 |
|
| //| IEEE Ci 30000 6.9e-16 5.1e-17 |
|
| //+------------------------------------------------------------------+
|
| void CTrigIntegrals::SineCosineIntegrals(double x,double &si,double &ci)
|
| {
|
| //--- create variables
|
| double z=0;
|
| double c=0;
|
| double s=0;
|
| double f=0;
|
| double g=0;
|
| int sg=0;
|
| double sn=0;
|
| double sd=0;
|
| double cn=0;
|
| double cd=0;
|
| double fn=0;
|
| double fd=0;
|
| double gn=0;
|
| double gd=0;
|
| //--- initialization
|
| si=0;
|
| ci=0;
|
| //--- check
|
| if(x<0.0)
|
| {
|
| sg=-1;
|
| x=-x;
|
| }
|
| else
|
| sg=0;
|
| //--- check
|
| if(x==0.0)
|
| {
|
| si=0;
|
| ci=-CMath::m_maxrealnumber;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x>1.0E9)
|
| {
|
| si=1.570796326794896619-MathCos(x)/x;
|
| ci=MathSin(x)/x;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x<=4.0)
|
| {
|
| //--- calculation
|
| z=x*x;
|
| sn=-8.39167827910303881427E-11;
|
| sn=sn*z+4.62591714427012837309E-8;
|
| sn=sn*z-9.75759303843632795789E-6;
|
| sn=sn*z+9.76945438170435310816E-4;
|
| sn=sn*z-4.13470316229406538752E-2;
|
| sn=sn*z+1.00000000000000000302E0;
|
| sd=2.03269266195951942049E-12;
|
| sd=sd*z+1.27997891179943299903E-9;
|
| sd=sd*z+4.41827842801218905784E-7;
|
| sd=sd*z+9.96412122043875552487E-5;
|
| sd=sd*z+1.42085239326149893930E-2;
|
| sd=sd*z+9.99999999999999996984E-1;
|
| s=x*sn/sd;
|
| cn=2.02524002389102268789E-11;
|
| cn=cn*z-1.35249504915790756375E-8;
|
| cn=cn*z+3.59325051419993077021E-6;
|
| cn=cn*z-4.74007206873407909465E-4;
|
| cn=cn*z+2.89159652607555242092E-2;
|
| cn=cn*z-1.00000000000000000080E0;
|
| cd=4.07746040061880559506E-12;
|
| cd=cd*z+3.06780997581887812692E-9;
|
| cd=cd*z+1.23210355685883423679E-6;
|
| cd=cd*z+3.17442024775032769882E-4;
|
| cd=cd*z+5.10028056236446052392E-2;
|
| cd=cd*z+4.00000000000000000080E0;
|
| c=z*cn/cd;
|
| //--- check
|
| if(sg!=0)
|
| s=-s;
|
| //--- calculation
|
| si=s;
|
| ci=0.57721566490153286061+MathLog(x)+c;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- change values
|
| s=MathSin(x);
|
| c=MathCos(x);
|
| z=1.0/(x*x);
|
| //--- check
|
| if(x<8.0)
|
| {
|
| //--- calculation
|
| fn=4.23612862892216586994E0;
|
| fn=fn*z+5.45937717161812843388E0;
|
| fn=fn*z+1.62083287701538329132E0;
|
| fn=fn*z+1.67006611831323023771E-1;
|
| fn=fn*z+6.81020132472518137426E-3;
|
| fn=fn*z+1.08936580650328664411E-4;
|
| fn=fn*z+5.48900223421373614008E-7;
|
| fd=1.00000000000000000000E0;
|
| fd=fd*z+8.16496634205391016773E0;
|
| fd=fd*z+7.30828822505564552187E0;
|
| fd=fd*z+1.86792257950184183883E0;
|
| fd=fd*z+1.78792052963149907262E-1;
|
| fd=fd*z+7.01710668322789753610E-3;
|
| fd=fd*z+1.10034357153915731354E-4;
|
| fd=fd*z+5.48900252756255700982E-7;
|
| f=fn/(x*fd);
|
| gn=8.71001698973114191777E-2;
|
| gn=gn*z+6.11379109952219284151E-1;
|
| gn=gn*z+3.97180296392337498885E-1;
|
| gn=gn*z+7.48527737628469092119E-2;
|
| gn=gn*z+5.38868681462177273157E-3;
|
| gn=gn*z+1.61999794598934024525E-4;
|
| gn=gn*z+1.97963874140963632189E-6;
|
| gn=gn*z+7.82579040744090311069E-9;
|
| gd=1.00000000000000000000E0;
|
| gd=gd*z+1.64402202413355338886E0;
|
| gd=gd*z+6.66296701268987968381E-1;
|
| gd=gd*z+9.88771761277688796203E-2;
|
| gd=gd*z+6.22396345441768420760E-3;
|
| gd=gd*z+1.73221081474177119497E-4;
|
| gd=gd*z+2.02659182086343991969E-6;
|
| gd=gd*z+7.82579218933534490868E-9;
|
| g=z*gn/gd;
|
| }
|
| else
|
| {
|
| //--- calculation
|
| fn=4.55880873470465315206E-1;
|
| fn=fn*z+7.13715274100146711374E-1;
|
| fn=fn*z+1.60300158222319456320E-1;
|
| fn=fn*z+1.16064229408124407915E-2;
|
| fn=fn*z+3.49556442447859055605E-4;
|
| fn=fn*z+4.86215430826454749482E-6;
|
| fn=fn*z+3.20092790091004902806E-8;
|
| fn=fn*z+9.41779576128512936592E-11;
|
| fn=fn*z+9.70507110881952024631E-14;
|
| fd=1.00000000000000000000E0;
|
| fd=fd*z+9.17463611873684053703E-1;
|
| fd=fd*z+1.78685545332074536321E-1;
|
| fd=fd*z+1.22253594771971293032E-2;
|
| fd=fd*z+3.58696481881851580297E-4;
|
| fd=fd*z+4.92435064317881464393E-6;
|
| fd=fd*z+3.21956939101046018377E-8;
|
| fd=fd*z+9.43720590350276732376E-11;
|
| fd=fd*z+9.70507110881952025725E-14;
|
| f=fn/(x*fd);
|
| gn=6.97359953443276214934E-1;
|
| gn=gn*z+3.30410979305632063225E-1;
|
| gn=gn*z+3.84878767649974295920E-2;
|
| gn=gn*z+1.71718239052347903558E-3;
|
| gn=gn*z+3.48941165502279436777E-5;
|
| gn=gn*z+3.47131167084116673800E-7;
|
| gn=gn*z+1.70404452782044526189E-9;
|
| gn=gn*z+3.85945925430276600453E-12;
|
| gn=gn*z+3.14040098946363334640E-15;
|
| gd=1.00000000000000000000E0;
|
| gd=gd*z+1.68548898811011640017E0;
|
| gd=gd*z+4.87852258695304967486E-1;
|
| gd=gd*z+4.67913194259625806320E-2;
|
| gd=gd*z+1.90284426674399523638E-3;
|
| gd=gd*z+3.68475504442561108162E-5;
|
| gd=gd*z+3.57043223443740838771E-7;
|
| gd=gd*z+1.72693748966316146736E-9;
|
| gd=gd*z+3.87830166023954706752E-12;
|
| gd=gd*z+3.14040098946363335242E-15;
|
| g=z*gn/gd;
|
| }
|
| si=1.570796326794896619-f*c-g*s;
|
| //--- check
|
| if(sg!=0)
|
| si=-si;
|
| //--- get result
|
| ci=f*s-g*c;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Hyperbolic sine and cosine integrals |
|
| //| Approximates the integrals |
|
| //| x |
|
| //| - |
|
| //| | | cosh t - 1 |
|
| //| Chi(x) = eul + ln x + | ----------- dt, |
|
| //| | | t |
|
| //| - |
|
| //| 0 |
|
| //| x |
|
| //| - |
|
| //| | | sinh t |
|
| //| Shi(x) = | ------ dt |
|
| //| | | t |
|
| //| - |
|
| //| 0 |
|
| //| where eul = 0.57721566490153286061 is Euler's constant. |
|
| //| The integrals are evaluated by power series for x < 8 |
|
| //| and by Chebyshev expansions for x between 8 and 88. |
|
| //| For large x, both functions approach exp(x)/2x. |
|
| //| Arguments greater than 88 in magnitude return MAXNUM. |
|
| //| ACCURACY: |
|
| //| Test interval 0 to 88. |
|
| //| Relative error: |
|
| //| arithmetic function # trials peak rms |
|
| //| IEEE Shi 30000 6.9e-16 1.6e-16 |
|
| //| Absolute error, except relative when |Chi| > 1: |
|
| //| IEEE Chi 30000 8.4e-16 1.4e-16 |
|
| //+------------------------------------------------------------------+
|
| void CTrigIntegrals::HyperbolicSineCosineIntegrals(double x,
|
| double &shi,
|
| double &chi)
|
| {
|
| //--- create variables
|
| double k=0;
|
| double z=0;
|
| double c=0;
|
| double s=0;
|
| double a=0;
|
| int sg=0;
|
| double b0=0;
|
| double b1=0;
|
| double b2=0;
|
| //--- initialization
|
| shi=0;
|
| chi=0;
|
| //--- check
|
| if(x<0.0)
|
| {
|
| sg=-1;
|
| x=-x;
|
| }
|
| else
|
| sg=0;
|
| //--- check
|
| if(x==0.0)
|
| {
|
| shi=0;
|
| chi=-CMath::m_maxrealnumber;
|
| //--- exit the function
|
| return;
|
| }
|
| //--- check
|
| if(x<8.0)
|
| {
|
| //--- initialization
|
| z=x*x;
|
| a=1.0;
|
| s=1.0;
|
| c=0.0;
|
| k=2.0;
|
| do
|
| {
|
| //--- calculation
|
| a=a*z/k;
|
| c=c+a/k;
|
| k=k+1.0;
|
| a=a/k;
|
| s=s+a/k;
|
| k=k+1.0;
|
| }
|
| while(MathAbs(a/s)>=CMath::m_machineepsilon);
|
| s=s*x;
|
| }
|
| else
|
| {
|
| //--- check
|
| if(x<18.0)
|
| {
|
| //--- calculation
|
| a=(576.0/x-52.0)/10.0;
|
| k=MathExp(x)/x;
|
| b0=1.83889230173399459482E-17;
|
| b1=0.0;
|
| //--- function calls
|
| ChebIterationShiChi(a,-9.55485532279655569575E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,2.04326105980879882648E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,1.09896949074905343022E-15,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.31313534344092599234E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,5.93976226264314278932E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.47197010497749154755E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.40059764613117131000E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,9.49044626224223543299E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.61596181145435454033E-11,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.77899784436430310321E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,1.35455469767246947469E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.03257121792819495123E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.56699611114982536845E-8,b0,b1,b2);
|
| ChebIterationShiChi(a,1.44818877384267342057E-7,b0,b1,b2);
|
| ChebIterationShiChi(a,7.82018215184051295296E-7,b0,b1,b2);
|
| ChebIterationShiChi(a,-5.39919118403805073710E-6,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.12458202168959833422E-5,b0,b1,b2);
|
| ChebIterationShiChi(a,8.90136741950727517826E-5,b0,b1,b2);
|
| ChebIterationShiChi(a,2.02558474743846862168E-3,b0,b1,b2);
|
| ChebIterationShiChi(a,2.96064440855633256972E-2,b0,b1,b2);
|
| ChebIterationShiChi(a,1.11847751047257036625E0,b0,b1,b2);
|
| //--- calculation
|
| s=k*0.5*(b0-b2);
|
| b0=-8.12435385225864036372E-18;
|
| b1=0.0;
|
| //--- function calls
|
| ChebIterationShiChi(a,2.17586413290339214377E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,5.22624394924072204667E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,-9.48812110591690559363E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,5.35546311647465209166E-15,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.21009970113732918701E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-6.00865178553447437951E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,7.16339649156028587775E-13,b0,b1,b2);
|
| ChebIterationShiChi(a,-2.93496072607599856104E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.40359438136491256904E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,8.76302288609054966081E-11,b0,b1,b2);
|
| ChebIterationShiChi(a,-4.40092476213282340617E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.87992075640569295479E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,1.31458150989474594064E-8,b0,b1,b2);
|
| ChebIterationShiChi(a,-4.75513930924765465590E-8,b0,b1,b2);
|
| ChebIterationShiChi(a,-2.21775018801848880741E-7,b0,b1,b2);
|
| ChebIterationShiChi(a,1.94635531373272490962E-6,b0,b1,b2);
|
| ChebIterationShiChi(a,4.33505889257316408893E-6,b0,b1,b2);
|
| ChebIterationShiChi(a,-6.13387001076494349496E-5,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.13085477492997465138E-4,b0,b1,b2);
|
| ChebIterationShiChi(a,4.97164789823116062801E-4,b0,b1,b2);
|
| ChebIterationShiChi(a,2.64347496031374526641E-2,b0,b1,b2);
|
| ChebIterationShiChi(a,1.11446150876699213025E0,b0,b1,b2);
|
| c=k*0.5*(b0-b2);
|
| }
|
| else
|
| {
|
| //--- check
|
| if(x<=88.0)
|
| {
|
| //--- calculation
|
| a=(6336.0/x-212.0)/70.0;
|
| k=MathExp(x)/x;
|
| b0=-1.05311574154850938805E-17;
|
| b1=0.0;
|
| //--- function calls
|
| ChebIterationShiChi(a,2.62446095596355225821E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,8.82090135625368160657E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.38459811878103047136E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,-8.30608026366935789136E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,3.93397875437050071776E-15,b0,b1,b2);
|
| ChebIterationShiChi(a,1.01765565969729044505E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-4.21128170307640802703E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.60818204519802480035E-13,b0,b1,b2);
|
| ChebIterationShiChi(a,3.34714954175994481761E-13,b0,b1,b2);
|
| ChebIterationShiChi(a,2.72600352129153073807E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,1.66894954752839083608E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.49278141024730899554E-11,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.58580661666482709598E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.79289437183355633342E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,1.76281629144264523277E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,1.69050228879421288846E-8,b0,b1,b2);
|
| ChebIterationShiChi(a,1.25391771228487041649E-7,b0,b1,b2);
|
| ChebIterationShiChi(a,1.16229947068677338732E-6,b0,b1,b2);
|
| ChebIterationShiChi(a,1.61038260117376323993E-5,b0,b1,b2);
|
| ChebIterationShiChi(a,3.49810375601053973070E-4,b0,b1,b2);
|
| ChebIterationShiChi(a,1.28478065259647610779E-2,b0,b1,b2);
|
| ChebIterationShiChi(a,1.03665722588798326712E0,b0,b1,b2);
|
| //--- calculation
|
| s=k*0.5*(b0-b2);
|
| b0=8.06913408255155572081E-18;
|
| b1=0.0;
|
| //--- function calls
|
| ChebIterationShiChi(a,-2.08074168180148170312E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,-5.98111329658272336816E-17,b0,b1,b2);
|
| ChebIterationShiChi(a,2.68533951085945765591E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,4.52313941698904694774E-16,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.10734917335299464535E-15,b0,b1,b2);
|
| ChebIterationShiChi(a,-4.42823207332531972288E-15,b0,b1,b2);
|
| ChebIterationShiChi(a,3.49639695410806959872E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,6.63406731718911586609E-14,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.71902448093119218395E-13,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.27135418132338309016E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,2.74851141935315395333E-12,b0,b1,b2);
|
| ChebIterationShiChi(a,2.33781843985453438400E-11,b0,b1,b2);
|
| ChebIterationShiChi(a,2.71436006377612442764E-11,b0,b1,b2);
|
| ChebIterationShiChi(a,-2.56600180000355990529E-10,b0,b1,b2);
|
| ChebIterationShiChi(a,-1.61021375163803438552E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,-4.72543064876271773512E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,-3.00095178028681682282E-9,b0,b1,b2);
|
| ChebIterationShiChi(a,7.79387474390914922337E-8,b0,b1,b2);
|
| ChebIterationShiChi(a,1.06942765566401507066E-6,b0,b1,b2);
|
| ChebIterationShiChi(a,1.59503164802313196374E-5,b0,b1,b2);
|
| ChebIterationShiChi(a,3.49592575153777996871E-4,b0,b1,b2);
|
| ChebIterationShiChi(a,1.28475387530065247392E-2,b0,b1,b2);
|
| ChebIterationShiChi(a,1.03665693917934275131E0,b0,b1,b2);
|
| c=k*0.5*(b0-b2);
|
| }
|
| else
|
| {
|
| //--- check
|
| if(sg!=0)
|
| shi=-CMath::m_maxrealnumber;
|
| else
|
| shi=CMath::m_maxrealnumber;
|
| chi=CMath::m_maxrealnumber;
|
| //--- exit the function
|
| return;
|
| }
|
| }
|
| }
|
| //--- check
|
| if(sg!=0)
|
| s=-s;
|
| //--- get result
|
| shi=s;
|
| chi=0.57721566490153286061+MathLog(x)+c;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Internal subroutine |
|
| //+------------------------------------------------------------------+
|
| void CTrigIntegrals::ChebIterationShiChi(const double x,const double c,
|
| double &b0,double &b1,double &b2)
|
| {
|
| //--- change values
|
| b2=b1;
|
| b1=b0;
|
| b0=x*b1-b2+c;
|
| }
|
| //+------------------------------------------------------------------+
|