package umontreal.iro.lecuyer.util;
 import cern.jet.math.Bessel;

/**
 * This class provides a few constants and some methods to compute numerical
 * quantities such as factorials, combinations, gamma functions, and so on.
 * 
 */
public class Num {
   protected static final double XBIG = 40.0;
   private static final double SQPI_2 = 0.88622692545275801; // Sqrt(Pi)/2
   private static final double LOG_SQPI_2 = -0.1207822376352453; // Ln(Sqrt(Pi)/2)
   private static final double LOG4 = 1.3862943611198906;   // Ln(4)
   private static final double LOG_PI = 1.14472988584940017413; // Ln(Pi)
   private static final double PIsur2 = Math.PI/2.0;

   private static final double UNSIX = 1.0/6.0;
   private static final double QUARAN = 1.0/42.0;
   private static final double UNTRENTE = 1.0 / 30.0;
   private static final double DTIERS = 2.0 / 3.0;
   private static final double CTIERS = 5.0 / 3.0;
   private static final double STIERS = 7.0 / 3.0;
   private static final double QTIERS = 14.0 / 3.0;

   private static final double[] AERF = {
      // used in erf(x)
      1.4831105640848036E0,
      -3.0107107338659494E-1,
      6.8994830689831566E-2,
      -1.3916271264722188E-2,
      2.4207995224334637E-3,
      -3.6586396858480864E-4,
      4.8620984432319048E-5,
      -5.7492565580356848E-6,
      6.1132435784347647E-7,
      -5.8991015312958434E-8,
      5.2070090920686482E-9,
      -4.2329758799655433E-10,
      3.188113506649174974E-11,
      -2.2361550188326843E-12,
      1.46732984799108492E-13,
      -9.044001985381747E-15,
      5.25481371547092E-16
   };

   private static final double[] AERFC = {
      // used in erfc(x)
       6.10143081923200418E-1,
      -4.34841272712577472E-1,
      1.76351193643605501E-1,
      -6.07107956092494149E-2,
      1.77120689956941145E-2,
      -4.32111938556729382E-3,
      8.54216676887098679E-4,
      -1.27155090609162743E-4,
      1.12481672436711895E-5,
      3.13063885421820973E-7,
      -2.70988068537762022E-7,
      3.07376227014076884E-8,
      2.51562038481762294E-9,
      -1.02892992132031913E-9,
      2.99440521199499394E-11,
      2.60517896872669363E-11,
      -2.63483992417196939E-12,
      -6.43404509890636443E-13,
      1.12457401801663447E-13,
      1.7281533389986098E-14,
      -4.2641016949424E-15,
      -5.4537197788E-16,
      1.5869760776E-16,
      2.08998378E-17,
      -0.5900E-17
      };


   private static final double[] AlnGamma = {
      /* Chebyshev coefficients for lnGamma (x + 3), 0 <= x <= 1 In Yudell
         Luke: The special functions and their approximations, Vol. II,
         Academic Press, p. 301, 1969. There is an error in the additive
         constant in the formula: (Ln (2)). */
      0.52854303698223459887,
      0.54987644612141411418,
      0.02073980061613665136,
     -0.00056916770421543842,
      0.00002324587210400169,
     -0.00000113060758570393,
      0.00000006065653098948,
     -0.00000000346284357770,
      0.00000000020624998806,
     -0.00000000001266351116,
      0.00000000000079531007,
     -0.00000000000005082077,
      0.00000000000000329187,
     -0.00000000000000021556,
      0.00000000000000001424,
     -0.00000000000000000095
   };

   private Num() {}


   /**
    * Difference between 1.0 and the smallest double greater than 1.0.
    * 
    */
   public static final double DBL_EPSILON = 2.2204460492503131e-16;


   /**
    * Largest int x such that 2x-1 is representable
    *   (approximately) as a double.
    * 
    */
   public static final int DBL_MAX_EXP = 1024;


   /**
    * Smallest int x such that 2x-1 is representable
    *   (approximately) as a normalised double.
    * 
    */
   public static final int DBL_MIN_EXP = -1021;


   /**
    * Largest int x such that 10x is representable
    *    (approximately) as a double.
    * 
    */
   public static final int DBL_MAX_10_EXP = 308;


   /**
    * Smallest normalized positive floating-point double.
    * 
    */
   public static final double DBL_MIN = 2.2250738585072014e-308;


   /**
    * Natural logarithm of DBL_MIN.
    * 
    */
   public static final double LN_DBL_MIN = -708.3964185322641;


   /**
    * Number of decimal digits of precision in a double.
    * 
    */
   public static final int DBL_DIG = 15;


   /**
    * The constant e.
    * 
    */
   public static final double EBASE = 2.7182818284590452354;


   /**
    * The Euler-Mascheroni constant.
    * 
    */
   public static final double EULER = 0.57721566490153286;


   /**
    * The value of (2)1/2.
    * 
    */
   public static final double RAC2 = 1.41421356237309504880;


   /**
    * The value of 
    * 1/(2)1/2.
    * 
    */
   public static final double IRAC2 = 0.70710678118654752440;


   /**
    * The values of ln 2.
    * 
    */
   public static final double LN2 = 0.69314718055994530941;


   /**
    * The values of 1/ln 2.
    * 
    */
   public static final double ILN2 = 1.44269504088896340737;


   /**
    * Largest integer 
    * n0 = 253 such that any integer
    *   n <= n0 is represented  exactly as a double.
    * 
    */
   public static final double MAXINTDOUBLE = 9007199254740992.0;


   /**
    * Powers of 2 up to MAXTWOEXP are stored exactly
    *     in the array TWOEXP.
    * 
    */
   public static final double MAXTWOEXP = 64;


   /**
    * Contains the precomputed positive powers of 2.
    *    One has TWOEXP[j]= 2j, for 
    * j = 0,..., 64.
    * 
    */
   public static final double TWOEXP[] = {
      1.0, 2.0, 4.0, 8.0, 1.6e1, 3.2e1,
      6.4e1, 1.28e2, 2.56e2, 5.12e2, 1.024e3,
      2.048e3, 4.096e3, 8.192e3, 1.6384e4, 3.2768e4,
      6.5536e4, 1.31072e5, 2.62144e5, 5.24288e5,
      1.048576e6, 2.097152e6, 4.194304e6, 8.388608e6,
      1.6777216e7, 3.3554432e7, 6.7108864e7,
      1.34217728e8, 2.68435456e8, 5.36870912e8,
      1.073741824e9, 2.147483648e9, 4.294967296e9,
      8.589934592e9, 1.7179869184e10, 3.4359738368e10,
      6.8719476736e10, 1.37438953472e11, 2.74877906944e11,
      5.49755813888e11, 1.099511627776e12, 2.199023255552e12,
      4.398046511104e12, 8.796093022208e12,
      1.7592186044416e13, 3.5184372088832e13,
      7.0368744177664e13, 1.40737488355328e14,
      2.81474976710656e14, 5.62949953421312e14,
      1.125899906842624e15, 2.251799813685248e15,
      4.503599627370496e15, 9.007199254740992e15,
      1.8014398509481984e16, 3.6028797018963968e16,
      7.2057594037927936e16, 1.44115188075855872e17,
      2.88230376151711744e17, 5.76460752303423488e17,
      1.152921504606846976e18, 2.305843009213693952e18,
      4.611686018427387904e18, 9.223372036854775808e18,
      1.8446744073709551616e19
     };



   /**
    * Contains the precomputed negative powers of 10.
    *    One has TEN_NEG_POW[j]= 10-j, for 
    * j = 0,…, 16.
    * 
    */
   public static final double TEN_NEG_POW[] = {
      1.0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8,
      1.0e-9, 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16
     };



   /**
    * Returns the greatest common divisor (gcd) of x and y.
    *  
    *  @param x integer
    * 
    *        @param y integer
    * 
    *        @return the GCD of x and y
    *  
    */
   public static int gcd (int x, int y) {
      if (x < 0) x = -x;
      if (y < 0) y = -y;
      int r;
      while (y != 0) {
         r = x % y;
         x = y;
         y = r;
      }
      return x;
   }


   /**
    * Returns the greatest common divisor (gcd) of x and y.
    *  
    *  @param x integer
    * 
    *        @param y integer
    * 
    *        @return the GCD of x and y
    *  
    */
   public static long gcd (long x, long y) {
      if (x < 0) x = -x;
      if (y < 0) y = -y;
      long r;
      while (y != 0) {
         r = x % y;
         x = y;
         y = r;
      }
      return x;
   }


   /**
    * Returns the number of different combinations
    *    of s objects amongst n.  Uses an algorithm that prevents overflows
    *    (when computing factorials), if possible.
    *  
    *  @param n total number of objects
    * 
    *       @param s number of chosen objects on a combination
    * 
    *       @return the number of different combinations of s objects amongst n
    *  
    */
   public static double combination (int n, int s) {
      final int NLIM = 100;      // pour eviter les debordements
      int i;
      if (s == 0 || s == n)
         return 1;
      if (s < 0) {
         System.err.println ("combination:   s < 0");
         return 0;
      }
      if (s > n) {
         System.err.println ("combination:   s > n");
         return 0;
      }
      if (s > (n/2))
         s = n - s;
      if (n <= NLIM) {
         double Res = 1.0;
         int Diff = n - s;
         for (i = 1; i <= s; i++) {
            Res *= (double)(Diff + i)/(double)i;
         }
         return Res;
      }
      else {
         double Res = (lnFactorial (n) - lnFactorial (s))
            - lnFactorial (n - s);
         return Math.exp (Res);
      }
   }



   /**
    * Returns the value of factorial n.
    *  
    *  @param n the integer for which the factorial must be computed
    * 
    *        @return the value of n!
    *  
    */
   public static double factorial (int n) {
      if (n < 0)
        throw new IllegalArgumentException ("factorial:   n < 0");
      double T = 1;
      for (int j = 2; j <= n; j++)
         T *= j;
      return T;
   }


   /**
    * Returns the value of the natural logarithm of
    *   factorial n. Gives 16 decimals of precision
    *   (relative error 
    * < 0.5×10-15).
    *  
    *  @param n the integer for which the log-factorial has to be computed
    * 
    *        @return the natural logarithm of n factorial
    *  
    */
   public static double lnFactorial (int n) {
      final int NLIM = 14;

      if (n < 0)
        throw new IllegalArgumentException ("lnFactorial:   n < 0");

      if (n == 0 || n == 1)
         return 0.0;
      if (n <= NLIM) {
         long z = 1;
         long x = 1;
         for (int i = 2; i <= n; i++) {
            ++x;
            z *= x;
         }
         return Math.log (z);
      }
      else {
         double x = (double)(n + 1);
         double y = 1.0/(x*x);
         double z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
            2.7777777777778E-3)*y + 8.3333333333333E-2;
         z = ((x - 0.5)*Math.log (x) - x) + 9.1893853320467E-1 + z/x;
         return z;
      }
   }


   /**
    * Computes and returns the Stirling numbers of the second kind
    * 
    * @param m number of rows of the allocated matrix
    * 
    *        @param n number of columns of the allocated matrix
    * 
    *        @return the matrix of Stirling numbers
    *  
    */
   public static double[][] calcMatStirling (int m, int n) {
      int i, j, k;
      double[][] M = new double[m+1][n+1];

      for (i = 0; i <= m; i++)
         for (j = 0; j <= n; j++)
            M[i][j] = 0.0;

      M[0][0] = 1.0;
      for (j = 1; j <= n; j++) {
         M[0][j] = 0.0;
         if (j <= m) {
            k = j - 1;
            M[j][j] = 1.0;
         }
         else
            k = m;
         for (i = 1; i <= k; i++)
            M[i][j] = (double)i*M[i][j - 1] + M[i - 1][j - 1];
      }
      return M;
   }


   /**
    * Returns log2(x).
    *  
    *  @param x the value for which the logarithm must be computed
    * 
    *        @return the value of log2(x)
    *  
    */
   public static double log2 (double x) {
     return ILN2*Math.log (x);
   }


   /**
    * Returns the natural logarithm of the gamma function Γ(x)
    *    evaluated at x.
    *    Gives 16 decimals of precision, but is implemented only for x > 0.
    *   
    *   @param x the value for which the lnGamma function must be computed
    * 
    *        @return the natural logarithm of the gamma function
    *  
    */
   public static double lnGamma (double x) {
      if (x <= 0.0)
         throw new IllegalArgumentException ("lnGamma:   x <= 0");

      final double XLIMBIG = 1.0/DBL_EPSILON;
      final double XLIM1 = 18.0;
      final double DK2 = 0.91893853320467274177;     // Ln (sqrt (2 Pi))
      final double DK1 = 0.9574186990510627;
      final int N = 15;              // Degree of Chebyshev polynomial
      double y, z;
      int i, k;

/*
      if (x <= 0.0) {
         double f = (1.0 - x) - Math.floor (1.0 - x);
         return LOG_PI - lnGamma (1.0 - x) - Math.log (Math.sin (Math.PI * f));
      }
*/
      if (x > XLIM1) {
         if (x > XLIMBIG)
            y = 0.0;
         else
            y = 1.0/(x*x);
         z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
            2.7777777777778E-3)*y + 8.3333333333333E-2;
         z = ((x - 0.5)*Math.log (x) - x) + DK2 + z/x;
         return z;

      } else if (x > 4.0) {
         k = (int)x;
         z = x - k;
         y = 1.0;
         for (i = 3; i < k; i++)
            y *= z + i;
         y = Math.log (y);

      } else if (x <= 0.0)
         return Double.MAX_VALUE;

      else if (x < 3.0) {
         k = (int)x;
         z = x - k;
         y = 1.0;
         for (i = 2; i >= k; i--)
            y *= z + i;
         y = -Math.log (y);
      }
      else {           // 3 <= x <= 4
         z = x - 3.0;
         y = 0.0;
      }
      z = evalCheby (AlnGamma, N, 2.0*z - 1.0);
      return z + DK1 + y;
   }


   /**
    * Computes the natural logarithm of the Beta function
    *  
    * B(λ, ν).  It is defined in terms of the Gamma function as
    *  

*

*
* B(λ, ν) = $\displaystyle {\frac{{\Gamma(\lambda)\Gamma(\nu)}}{{\Gamma(\lambda + \nu)}}}$ *

* with lam = λ and nu = ν. * */ public static double lnBeta (double lam, double nu) { return lnGamma (lam) + lnGamma (nu) - lnGamma (lam + nu); } /** * Returns the value of the logarithmic derivative of the Gamma function * * ψ(x) = Γ'(x)/Γ(x). * */ public static double digamma (double x) { final double C7[][] = { {1.3524999667726346383e4, 4.5285601699547289655e4, 4.5135168469736662555e4, 1.8529011818582610168e4, 3.3291525149406935532e3, 2.4068032474357201831e2, 5.1577892000139084710, 6.2283506918984745826e-3}, {6.9389111753763444376e-7, 1.9768574263046736421e4, 4.1255160835353832333e4, 2.9390287119932681918e4, 9.0819666074855170271e3, 1.2447477785670856039e3, 6.7429129516378593773e1, 1.0} }; final double C4[][] = { {-2.728175751315296783e-15, -6.481571237661965099e-1, -4.486165439180193579, -7.016772277667586642, -2.129404451310105168}, {7.777885485229616042, 5.461177381032150702e1, 8.929207004818613702e1, 3.227034937911433614e1, 1.0} }; double prodPj = 0.0; double prodQj = 0.0; double digX = 0.0; if (x >= 3.0) { double x2 = 1.0 / (x * x); for (int j = 4; j >= 0; j--) { prodPj = prodPj * x2 + C4[0][j]; prodQj = prodQj * x2 + C4[1][j]; } digX = Math.log (x) - (0.5 / x) + (prodPj / prodQj); } else if (x >= 0.5) { final double X0 = 1.46163214496836234126; for (int j = 7; j >= 0; j--) { prodPj = x * prodPj + C7[0][j]; prodQj = x * prodQj + C7[1][j]; } digX = (x - X0) * (prodPj / prodQj); } else { double f = (1.0 - x) - Math.floor (1.0 - x); digX = digamma (1.0 - x) + Math.PI / Math.tan (Math.PI * f); } return digX; } /** * Returns the value of the trigamma function * dψ(x)/dx, the derivative of * the digamma function, evaluated at x. * */ public static double trigamma (double x) { double y, sum; if (x < 0.5) { y = (1.0 - x) - Math.floor (1.0 - x); sum = Math.PI / Math.sin (Math.PI * y); return sum * sum - trigamma (1.0 - x); } if (x >= 40.0) { // Asymptotic series y = 1.0/(x*x); sum = 1.0 + y*(1.0/6.0 - y*(1.0/30.0 - y*(1.0/42.0 - 1.0/30.0*y))); sum += 0.5/x; return sum/x; } int i; int p = (int) x; y = x - p; sum = 0.0; if (p > 3) { for (i = 3; i < p; i++) sum -= 1.0/((y + i)*(y + i)); } else if (p < 3) { for (i = 2; i >= p; i--) sum += 1.0/((y + i)*(y + i)); } /* Chebyshev coefficients for trigamma (x + 3), 0 <= x <= 1. In Yudell Luke: The special functions and their approximations, Vol. II, Academic Press, p. 301, 1969. */ final int N = 15; final double A[] = { 2.0*0.33483869791094938576, -0.05518748204873009463, 0.00451019073601150186, -0.00036570588830372083, 2.943462746822336e-5, -2.35277681515061e-6, 1.8685317663281e-7, -1.475072018379e-8, 1.15799333714e-9, -9.043917904e-11, 7.029627e-12, -5.4398873e-13, 0.4192525e-13, -3.21903e-15, 0.2463e-15, -1.878e-17, 0., 0. }; return sum + evalChebyStar (A, N, y); } /** * Returns the value of the tetragamma function * d2ψ(x)/d2x, the second * derivative of the digamma function, evaluated at x. * */ public static double tetragamma (double x) { double y, sum; if (x < 0.5) { y = (1.0 - x) - Math.floor (1.0 - x); sum = Math.PI / Math.sin (Math.PI * y); return 2.0 * Math.cos (Math.PI * y) * sum * sum * sum + tetragamma (1.0 - x); } if (x >= 20.0) { // Asymptotic series y = 1.0/(x*x); sum = y*(0.5 - y*(1.0/6.0 - y*(1.0/6.0 - y*(0.3 - 5.0/6.0*y)))); sum += 1.0 + 1.0/x; return -sum*y; } int i; int p = (int) x; y = x - p; sum = 0.0; if (p > 3) { for (i = 3; i < p; i++) sum += 1.0 / ((y + i) * (y + i) * (y + i)); } else if (p < 3) { for (i = 2; i >= p; i--) sum -= 1.0 / ((y + i) * (y + i) * (y + i)); } /* Chebyshev coefficients for tetragamma (x + 3), 0 <= x <= 1. In Yudell Luke: The special functions and their approximations, Vol. II, Academic Press, p. 301, 1969. */ final int N = 16; final double A[] = { -0.11259293534547383037*2.0, 0.03655700174282094137, -0.00443594249602728223, 0.00047547585472892648, -4.747183638263232e-5, 4.52181523735268e-6, -4.1630007962011e-7, 3.733899816535e-8, -3.27991447410e-9, 2.8321137682e-10, -2.410402848e-11, 2.02629690e-12, -1.6852418e-13, 1.388481e-14, -1.13451e-15, 9.201e-17, -7.41e-18, 5.9e-19, -5.0e-20 }; return 2.0 * sum + evalChebyStar (A, N, y); } /** * Returns the value of the ratio * Γ(x + 1/2)/Γ(x) of two gamma * functions, evaluated in a numerically stable way. Restriction: x > 0. * */ public static double gammaRatioHalf (double x) { if (x <= 0.0) throw new IllegalArgumentException ("gammaRatioHalf: x <= 0"); if (x <= 10.0) { double y = lnGamma (x + 0.5) - lnGamma (x); return Math.exp(y); } double sum; if (x <= 300.0) { // The sum converges very slowly for small x, but faster as x increases final double EPSILON = 1.0e-15; double term = 1.0; sum = 1.0; int i = 1; while (term > EPSILON*sum) { term *= (i - 1.5)*(i - 1.5) /(i*(x + i - 1.5)); sum += term; i++; } return Math.sqrt ((x - 0.5)*sum); } // Asymptotic series for Gamma(x + 0.5) / (Gamma(x) * Sqrt(x)) // Comparer la vitesse de l'asymptotique avec la somme ci-dessus !!! double y = 1.0 / (8.0*x); sum = 1.0 + y*(-1.0 + y*(0.5 + y*(2.5 - y*(2.625 + 49.875*y)))); return sum*Math.sqrt(x); } /** * Computes the n-th harmonic number * Hn = ∑j=1n1/j. * */ public static double harmonic (long n) { if (n < 1) throw new IllegalArgumentException ("n < 1"); return digamma(n + 1) + EULER; } /** * . * * Computes the sum *

*

*
* $\displaystyle \sideset{}{'}\htsum_{{-n/2<j\le n/2}}^{}$  $\displaystyle {\frac{1}{{\vert j\vert}}}$, *

* where the symbol * ∑′ means that the term with j = 0 is excluded * from the sum. * */ public static double harmonic2 (long n) { if (n <= 0) throw new IllegalArgumentException ("n <= 0"); if (1 == n) return 0.0; if (2 == n) return 1.0; long k = n / 2; if ((n & 1) == 1) return 2.0*harmonic(k); // n odd return 1.0/k + 2.0*harmonic(k-1); // n even } /** * Returns the volume V of a sphere of radius 1 in t dimensions * using the norm Lp. It is given by the formula *

*

*
* V = ([2Γ(1 + 1/p)]t)/Γ(1 + t/p),        p > 0, *

* where Γ is the gamma function. * The case of the sup norm L∞ is obtained by choosing p = 0. * Restrictions: p >=  0 and t >= 1. * * @param p index of the used norm * * @param t number of dimensions * * @return the volume of a sphere * */ public static double volumeSphere (double p, int t) { final double EPS = 2.0*DBL_EPSILON; int pLR = (int)p; double kLR = (double)t; double Vol; int s; if (p < 0) throw new IllegalArgumentException ("volumeSphere: p < 0"); if (Math.abs (p - pLR) <= EPS) { switch (pLR) { case 0: return TWOEXP[t]; case 1: return TWOEXP[t]/(double)factorial (t); case 2: if ((t % 2) == 0) return Math.pow (Math.PI, kLR/2.0)/(double)factorial (t/2); else { s = (t + 1)/2; return Math.pow (Math.PI, (double)s - 1.0)*factorial (s)* TWOEXP[2*s]/(double)factorial (2*s); } default: } } Vol = kLR*(LN2 + lnGamma (1.0 + 1.0/p)) - lnGamma (1.0 + kLR/p); return Math.exp (Vol); } /** * Evaluates the Bernoulli polynomial Bn(x) of degree n * at x. Only degrees n <= 8 are programmed for now. * The first Bernoulli polynomials of even degree are: *
*
* * * * * * * * * * * * * * * * * * * * * *
B0(x)=1 *  
B2(x)=x2 - x + 1/6 *  
B4(x)=x4 -2x3 + x2 - 1/30 *  
B6(x)=x6 -3x5 +5x4/2 - x2/2 + 1/42 *  
B8(x)=x8 -4x7 +14x6/3 - 7x4/3 + 2x2/3 - 1/30. *  
*
* */ public static double bernoulliPoly (int n, double x) { switch (n) { case 0: return 1.0; case 1: return x - 0.5; case 2: return x*(x - 1.0) + UNSIX; case 3: return ((2.0*x - 3.0) * x + 1.0)*x*0.5; case 4: return ((x - 2.0) * x + 1.0)*x*x - UNTRENTE; case 5: return (((x - 2.5) * x + CTIERS) *x*x - UNSIX) * x; case 6: return (((x - 3.0) * x + 2.5) * x*x - 0.5) * x*x + QUARAN; case 7: return ((((x - 3.5) * x + 3.5) *x*x - 7.0/6.0) * x*x + UNSIX) * x; case 8: return ((((x - 4.0) * x + QTIERS) * x*x - STIERS) * x*x + DTIERS) * x*x - UNTRENTE; default: throw new IllegalArgumentException("n must be <= 8"); } // return 0; } /** * Evaluates a series of Chebyshev polynomials Tj at * x over the basic interval [- 1,  1]. It uses * the method of Clenshaw, i.e., computes and returns *

*

*
* y = $\displaystyle {\frac{{a_0}}{2}}$ + ∑j=1najTj(x). *

* @param a coefficients of the polynomials * * @param n largest degree of polynomials * * @param x the parameter of the Tj functions * * @return the value of a series of Chebyshev polynomials Tj. * */ public static double evalCheby (double a[], int n, double x) { if (Math.abs (x) > 1.0) System.err.println ("Chebychev polynomial evaluated "+ "at x outside [-1, 1]"); final double xx = 2.0*x; double b0 = 0.0, b1 = 0.0, b2 = 0.0; for (int j = n; j >= 0; j--) { b2 = b1; b1 = b0; b0 = (xx*b1 - b2) + a[j]; } return (b0 - b2)/2.0; } /** * Evaluates a series of shifted Chebyshev polynomials Tj* * at x over the basic interval [0,  1]. It uses * the method of Clenshaw, i.e., computes and returns *

*

*
* y = [tex2html_wrap_indisplay679] + ∑j=1najTj*(x). *

* @param a coefficients of the polynomials * * @param n largest degree of polynomials * * @param x the parameter of the Tj* functions * * @return the value of a series of Chebyshev polynomials Tj*. * */ public static double evalChebyStar (double a[], int n, double x) { if (x > 1.0 || x < 0.0) System.err.println ("Shifted Chebychev polynomial evaluated " + "at x outside [0, 1]"); final double xx = 2.0*(2.0*x - 1.0); double b0 = 0.0, b1 = 0.0, b2 = 0.0; for (int j = n; j >= 0; j--) { b2 = b1; b1 = b0; b0 = xx*b1 - b2 + a[j]; } return (b0 - b2)/2.0; } /** * Returns the value of * K1/4(x), where Ka is the modified * Bessel's function of the second kind. * The relative error on the returned value is less than * * 0.5×10-6 for * x > 10-300. * * @param x value at which the function is calculated * * @return the value of * K1/4(x) * */ public static double besselK025 (double x) { final int DEG = 6; double rac; double xx; double temp; double Res; double C; double B; int j; final double c[] = { 32177591145.0, 2099336339520.0, 16281990144000.0, 34611957596160.0, 26640289628160.0, 7901666082816.0, 755914244096.0 }; final double b[] = { 75293843625.0, 2891283595200.0, 18691126272000.0, 36807140966400.0, 27348959232000.0, 7972533043200.0, 755914244096.0 }; if (x < 1.E-300) return Double.MIN_VALUE; /*------------------------------------------------------------------ * x > 0.6 => approximation asymptotique rationnelle dans Luke: * Yudell L.Luke "Mathematical functions and their approximations", * Academic Press Inc. New York, 1975, p.371 *------------------------------------------------------------------*/ if (x >= 0.6) { B = b[DEG]; C = c[DEG]; for (j = DEG; j >= 1; j--) { B = B * x + b[j - 1]; C = C * x + c[j - 1]; } Res = Math.sqrt (Math.PI / (2.0 * x)) * Math.exp (-x) * (C / B); return Res; } /*------------------------------------------------------------------ * x < 0.6 => la serie de K_{1/4} = Pi/Sqrt (2) [I_{-1/4} - I_{1/4}] *------------------------------------------------------------------*/ xx = x * x; rac = Math.pow (x/2.0, 0.25); Res = (((xx/1386.0 + 1.0/42.0)*xx + 1.0/3.0)*xx + 1.0)/ (1.225416702465177*rac); temp = (((xx/3510.0 + 1.0/90.0)*xx + 0.2)*xx + 1.0)*rac/ 0.906402477055477; Res = Math.PI*(Res - temp)/RAC2; return Res; } /** * Returns the value of * exK1(y), where K1 is the modified Bessel * function of the second kind of order 1. Restriction: y > 0. * */ public static double expBesselK1 (double x, double y) { if (y > 500.0) { double sum = 1 + 3.0/(8.0*y) - 15.0 / (128.0*y*y) + 105.0 /(1024.0*y*y*y); return Math.sqrt(PIsur2/ y) * sum * Math.exp(x - y); } else if (Math.abs(x) > 500.0) { double b = Bessel.k1(y); return Math.exp(x + Math.log(b)); } else { return Math.exp(x) * Bessel.k1(y); } } /** * Returns the value of erf(x), the error function. It is defined as *

*

*
* erf(x) = 2/[(π)1/2]∫0xdt e-t2. *

* @param x value at which the function is calculated * * @return the value of erf(x) * */ public static double erf (double x) { if (x < 0.0) return -erf(-x); if (x >= 6.0) return 1.0; if (x >= 2.0) return 1.0 - erfc(x); double t = 0.5*x*x - 1.0; double y = Num.evalCheby (AERF, 16, t); return x*y; } /** * Returns the value of erfc(x), the complementary error function. * It is defined as *

*

*
* erfc(x) = 2/[(π)1/2]∫x∞dt e-t2. *

* * @param x value at which the function is calculated * * @return the value of erfc(x) * */ public static double erfc (double x) { if (x < 0.0) return 2.0 - erfc(-x); if (x >= XBIG) return 0.0; double t = (x - 3.75)/(x + 3.75); double y = Num.evalCheby (AERFC, 24, t); y *= Math.exp(-x*x); return y; } private static final double[] InvP1 = { 0.160304955844066229311e2, -0.90784959262960326650e2, 0.18644914861620987391e3, -0.16900142734642382420e3, 0.6545466284794487048e2, -0.864213011587247794e1, 0.1760587821390590 }; private static final double[] InvQ1 = { 0.147806470715138316110e2, -0.91374167024260313396e2, 0.21015790486205317714e3, -0.22210254121855132366e3, 0.10760453916055123830e3, -0.206010730328265443e2, 0.1e1 }; private static final double[] InvP2 = { -0.152389263440726128e-1, 0.3444556924136125216, -0.29344398672542478687e1, 0.11763505705217827302e2, -0.22655292823101104193e2, 0.19121334396580330163e2, -0.5478927619598318769e1, 0.237516689024448000 }; private static final double[] InvQ2 = { -0.108465169602059954e-1, 0.2610628885843078511, -0.24068318104393757995e1, 0.10695129973387014469e2, -0.23716715521596581025e2, 0.24640158943917284883e2, -0.10014376349783070835e2, 0.1e1 }; private static final double[] InvP3 = { 0.56451977709864482298e-4, 0.53504147487893013765e-2, 0.12969550099727352403, 0.10426158549298266122e1, 0.28302677901754489974e1, 0.26255672879448072726e1, 0.20789742630174917228e1, 0.72718806231556811306, 0.66816807711804989575e-1, -0.17791004575111759979e-1, 0.22419563223346345828e-2 }; private static final double[] InvQ3 = { 0.56451699862760651514e-4, 0.53505587067930653953e-2, 0.12986615416911646934, 0.10542932232626491195e1, 0.30379331173522206237e1, 0.37631168536405028901e1, 0.38782858277042011263e1, 0.20372431817412177929e1, 0.1e1 }; /** * Returns the value of * y = erf-1(x), the inverse of the error * function such that * x = erf(y). * * @param x value at which the function is calculated * * @return the value of erfInv(x) * */ public static double erfInv (double x) { if (x < 0.0) return -erfInv (-x); if (x > 1.0) throw new IllegalArgumentException ("x is not in [-1, 1]"); if (x >= 1.0) return Double.POSITIVE_INFINITY; int i; double y, z, v, w; if (x <= 0.75) { y = x * x - 0.5625; v = Misc.evalPoly (InvP1, 6, y); w = Misc.evalPoly (InvQ1, 6, y); z = (v / w) * x; } else if (x <= 0.9375) { y = x * x - 0.87890625; v = Misc.evalPoly (InvP2, 7, y); w = Misc.evalPoly (InvQ2, 7, y); z = (v / w) * x; } else { y = 1.0 / Math.sqrt (-Math.log (1.0 - x)); v = Misc.evalPoly (InvP3, 10, y); w = Misc.evalPoly (InvQ3, 8, y); z = (v / w) / y; } return z; } }

Ads help maintain this website.