//+------------------------------------------------------------------+
//|                                             specialfunctions.mqh |
//|            Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) |
//|                             Copyright 2012-2026, MetaQuotes Ltd. |
//|                                                     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;
  }
//+------------------------------------------------------------------+
