package umontreal.iro.lecuyer.probdist;

import umontreal.iro.lecuyer.util.Num;
// import umontreal.iro.lecuyer.functions.MathFunction;


/**
 * Extends the class {@link StudentDist} for
 * the Student t-distribution. Uses methods that are faster but less precise
 * than {@link StudentDist}.
 * 
 */
public class StudentDistQuick extends StudentDist {
    private static final int STUDENT_N1 = 20;
    private static final double STUDENT_X1 = 8.01;
    private static final int STUDENT_KMAX = 200;
    private static final double STUDENT_EPS = 0.5E-16;


   /**
    * Constructs a StudentDistQuick object with n degrees of freedom.
    * 
    */
   public StudentDistQuick (int n) {
      super (n);
   }

  /*  These public methods are necessary so that the methods cdf,
   *  barF and inverseF used are those of the present
   *  class and not those of the mother class.
   */

   public double cdf (double x) {
      return cdf (n, x);
   }

   public double barF (double x) {
      return barF (n, x);
   }

   public double inverseF (double u) {
      return inverseF (n, u);
   }

   /**
    * Returns an approximation
    *   of the Student t-distribution function with n degrees of freedom.
    *   Is very poor in the tails but good in the central part of the range.
    * 
    */
   public static double cdf (int n, double x) {
      if (n <= 2)
         return StudentDist.cdf(n, x);
      if (x == Double.NEGATIVE_INFINITY)
           return 0.0;
      if (x == Double.POSITIVE_INFINITY)
           return 1.0;
      double b, y, z, z2, prec, v;
      int k;

      // first case: small n and small x
      if (n <= STUDENT_N1 && x <= STUDENT_X1) {
         b = 1.0 + x*x/n;
         y = x/Math.sqrt ((double)n);
         z = 1.0;
         for (k = n - 2; k >= 2; k -= 2)
            z = 1.0 + z*(k - 1)/(k*b);
         if (n % 2 == 0) {
               v = (1.0 + z*y/Math.sqrt (b))/2.0;
         } else {
            if (y > -1.0)
               v = (0.5 + (Math.atan (y) + z*y/b)/Math.PI);
            else
               v = (Math.atan(-1.0/y) + z * y / b) / Math.PI;
         }
         if (v > 1.0e-18)
            return v;
         else
            return 0.0;
      }

      // second case: large n and small x
      else if (x < STUDENT_X1) {
         double a = n - 0.5;
         b = 48.0*a*a;
         z2 = a*Math.log1p (x*x/n);
         z = Math.sqrt (z2);
         y = (((((64.0*z2 + 788.0)*z2 + 9801.0)*z2 + 89775.0)*z2 +
               543375.0)*z2 + 1788885.0)*z/(210.0*b*b*b);
         y -= (((4.0*z2 + 33.0)*z2 + 240.0)*z2 +  855.0)*z/(10.0*b*b);
         y += z + (z2 + 3.0)*z/b;
         if (x >= 0.0)
            return NormalDist.barF01 (-y);
         else
            return NormalDist.barF01 (y);
      }

      // third case: large x
      else {
         // Compute the Student probability density
         b = 1.0 + x*x/n;
         y = Num.gammaRatioHalf (n/2.0);
         y *= 1.0/(Math.sqrt (Math.PI*n)*Math.pow (b, (n + 1)/2.0));

         y *= 2.0*Math.sqrt (n*b);
         z = y/n;
         k = 2;
         z2 = prec = 10.0;
         while (k < STUDENT_KMAX && prec > STUDENT_EPS) {
            y *= (k - 1)/(k*b);
            z += y/(n + k);
            prec = Math.abs (z - z2);
            z2 = z;
            k += 2;
         }
         if (k >= STUDENT_KMAX)
           System.err.println ("student: k >= STUDENT_KMAX");
         if (x >= 0.0)
            return 1.0 - z/2.0;
         else
            return z/2.0;
      }
   }


   /**
    * Computes the complementary distribution function 
    * bar(F)(x).
    * 
    */
   public static double barF (int n, double x) {
        if (n <= 2)
           return StudentDist.barF(n, x);
      return cdf (n, -x);
   }


   /**
    * Returns an approximation of F-1(u), where F is the
    *   Student t-distribution function with n degrees of freedom.
    *   Gives at least  5 decimal digits of precision when n >= 3
    *   .
    *   Uses exact formulae for n = 1 and n = 2.
    * 
    */
   public static double inverseF (int n, double u) {
        if (n <= 2)
           return StudentDist.inverseF(n, u);
      final double PI = Math.PI;
      double a, b, c, d, e, p, t, x, y;

      e = (double) n;
      if (u > 0.5)
         p = 2.0 * (1.0 - u);
      else
         p = 2.0 * u;

      a = 1. / (e - 0.5);
      b = 48. / (a * a);
      c = ((20700. / b * a - 98.) * a - 16.) * a + 96.36;
      d = e * Math.sqrt (a * PI / 2.) * ((94.5 / (b + c) - 3.) / b + 1.);
      y = Math.pow ((d * p), (2.0 / e));
      if (y > (a + 0.05)) {
         if (p == 1.0)
            x = 0.0;
         else
            x = NormalDist.inverseF01 (p * 0.5);
         y = x * x;
         if (n < 5)
            c = c + 0.3 * (e - 4.5) * (x + 0.6);

         c = (((0.05 * d * x - 5.) * x - 7.) * x - 2.) * x + b + c;
         y = (((((0.4 * y + 6.3) * y + 36.) * y + 94.5) /
               c - y - 3.) / b + 1.) * x;
         y = a * (y * y);
         y = Math.expm1 (y);

      } else {
         y = ((1. / (((e + 6.) / (e * y) - 0.089 * d - 0.822) *
                     (e + 2.) * 3.) + 0.5 / (e + 4.)) * y - 1.) *
               (e + 1.) / (e + 2.) + 1. / y;
      }

      t = Math.sqrt (e * y);
      if (u < 0.5)
         return -t;
      else
         return t;
    }

}

 

Ads help maintain this website.