Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/nmath/qt.c
Line
Count
Source
1
/*
2
 *  Mathlib : A C Library of Special Functions
3
 *  Copyright (C) 2000-2022 The R Core Team
4
 *  Copyright (C) 2003-2022 The R Foundation
5
 *  Copyright (C) 1998 Ross Ihaka
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, a copy is available at
19
 *  https://www.R-project.org/Licenses/
20
 *
21
 *  DESCRIPTION
22
 *
23
 *  The "Student" t distribution quantile function.
24
 *
25
 *  NOTES
26
 *
27
 *  This is a C translation of the Fortran routine given in:
28
 *  Hill, G.W (1970) "Algorithm 396: Student's t-quantiles"
29
 *  CACM 13(10), 619-620.
30
 *
31
 *  Supplemented by inversion for 0 < ndf < 1.
32
 *
33
 *  ADDITIONS:
34
 *  - lower_tail, log_p
35
 *  - using  expm1() : takes care of  Lozy (1979) "Remark on Algo.", TOMS
36
 *  - Apply 2-term Taylor expansion as in
37
 *    Hill, G.W (1981) "Remark on Algo.396", ACM TOMS 7, 250-1
38
 *  - Improve the formula decision for 1 < df < 2
39
 */
40
41
#include "nmath.h"
42
#include "dpq.h"
43
44
double qt(double p, double ndf, int lower_tail, int log_p)
45
0
{
46
0
    const static double eps = 1.e-12;
47
48
0
    double P, q;
49
50
0
#ifdef IEEE_754
51
0
    if (ISNAN(p) || ISNAN(ndf))
52
0
  return p + ndf;
53
0
#endif
54
55
0
    R_Q_P01_boundaries(p, ML_NEGINF, ML_POSINF);
56
57
0
    if (ndf <= 0) ML_WARN_return_NAN;
58
59
0
    if (ndf < 1) { /* based on qnt */
60
0
  const static double accu = 1e-13;
61
0
  const static double Eps = 1e-11; /* must be > accu */
62
63
0
  double ux, lx, nx, pp;
64
65
0
  int iter = 0;
66
67
0
  p = R_DT_qIv(p);
68
69
  /* Invert pt(.) :
70
   * 1. finding an upper and lower bound */
71
0
  if(p > 1 - DBL_EPSILON) return ML_POSINF;
72
0
  pp = fmin2(1 - DBL_EPSILON, p * (1 + Eps));
73
0
  for(ux = 1.; ux < DBL_MAX && pt(ux, ndf, TRUE, FALSE) < pp; ux *= 2);
74
0
  pp = p * (1 - Eps);
75
0
  for(lx =-1.; lx > -DBL_MAX && pt(lx, ndf, TRUE, FALSE) > pp; lx *= 2);
76
77
  /* 2. interval (lx,ux)  halving
78
     regula falsi failed on qt(0.1, 0.1)
79
   */
80
0
  do {
81
0
      nx = 0.5 * (lx + ux);
82
0
      if (pt(nx, ndf, TRUE, FALSE) > p) ux = nx; else lx = nx;
83
0
  } while ((ux - lx) / fabs(nx) > accu && ++iter < 1000);
84
85
0
  if(iter >= 1000) ML_WARNING(ME_PRECISION, "qt");
86
87
0
  return 0.5 * (lx + ux);
88
0
    }
89
90
    /* Old comment:
91
     * FIXME: "This test should depend on  ndf  AND p  !!
92
     * -----  and in fact should be replaced by
93
     * something like Abramowitz & Stegun 26.7.5 (p.949)"
94
     *
95
     * That would say that if the qnorm value is x then
96
     * the result is about x + (x^3+x)/4df + (5x^5+16x^3+3x)/96df^2
97
     * The differences are tiny even if x ~ 1e5, and qnorm is not
98
     * that accurate in the extreme tails.
99
     */
100
0
    if (ndf > 1e20) return qnorm(p, 0., 1., lower_tail, log_p);
101
102
0
    P = R_D_qIv(p); /* if exp(p) underflows, we fix below */
103
104
0
    bool neg = (!lower_tail || P < 0.5) && (lower_tail || P > 0.5),
105
0
  is_neg_lower = (lower_tail == neg); /* both TRUE or FALSE == !xor */
106
0
    if(neg)
107
0
  P = 2 * (log_p ? (lower_tail ? P : -expm1(p)) : R_D_Lval(p));
108
0
    else
109
0
  P = 2 * (log_p ? (lower_tail ? -expm1(p) : P) : R_D_Cval(p));
110
    /* 0 <= P <= 1 ; P = 2*min(P', 1 - P')  in all cases */
111
112
0
     if (fabs(ndf - 2) < eps) { /* df ~= 2 */
113
0
  if(P > DBL_MIN) {
114
0
      if(3* P < DBL_EPSILON) /* P ~= 0 */
115
0
    q = 1 / sqrt(P);
116
0
      else if (P > 0.9)    /* P ~= 1 */
117
0
    q = (1 - P) * sqrt(2 /(P * (2 - P)));
118
0
      else /* eps/3 <= P <= 0.9 */
119
0
    q = sqrt(2 / (P * (2 - P)) - 2);
120
0
  }
121
0
  else { /* P << 1, q = 1/sqrt(P) = ... */
122
0
      if(log_p)
123
0
    q = is_neg_lower ? exp(- p/2) / M_SQRT2 : 1/sqrt(-expm1(p));
124
0
      else
125
0
    q = ML_POSINF;
126
0
  }
127
0
    }
128
0
    else if (ndf < 1 + eps) { /* df ~= 1  (df < 1 excluded above): Cauchy */
129
0
  if(P == 1.) q = 0; // some versions of tanpi give Inf, some NaN
130
0
  else if(P > 0)
131
0
      q = 1/tanpi(P/2.);/* == - tan((P+1) * M_PI_2) -- suffers for P ~= 0 */
132
133
0
  else { /* P = 0, but maybe = 2*exp(p) ! */
134
0
      if(log_p) /* 1/tan(e) ~ 1/e */
135
0
    q = is_neg_lower ? M_1_PI * exp(-p) : -1./(M_PI * expm1(p));
136
0
      else
137
0
    q = ML_POSINF;
138
0
  }
139
0
    }
140
0
    else {   /*-- usual case;  including, e.g.,  df = 1.1 */
141
0
  double x = 0., y, log_P2 = 0./* -Wall */,
142
0
      a = 1 / (ndf - 0.5),
143
0
      b = 48 / (a * a),
144
0
      c = ((20700 * a / b - 98) * a - 16) * a + 96.36,
145
0
      d = ((94.5 / (b + c) - 3) / b + 1) * sqrt(a * M_PI_2) * ndf;
146
0
  bool
147
0
      P_ok1 = P > DBL_MIN || !log_p,
148
0
      P_ok  = P_ok1; // when true (after check below), use "normal scale": log_p=FALSE
149
0
  if(P_ok1) {
150
0
      y = pow(d * P, 2.0 / ndf);
151
0
      P_ok = (y >= DBL_EPSILON);
152
0
  }
153
0
  if(!P_ok) {// log.p && P very.small  ||  (d*P)^(2/df) =: y < eps_c
154
0
      log_P2 = is_neg_lower ? R_D_log(p) : R_D_LExp(p); /* == log(P / 2) */
155
0
      x = (log(d) + M_LN2 + log_P2) / ndf;
156
0
      y = exp(2 * x);
157
0
  }
158
159
0
  if ((ndf < 2.1 && P > 0.5) || y > 0.05 + a) { /* P > P0(df) */
160
      /* Asymptotic inverse expansion about normal */
161
0
      if(P_ok)
162
0
    x = qnorm(0.5 * P, 0., 1., /*lower_tail*/TRUE,  /*log_p*/FALSE);
163
0
      else /* log_p && P underflowed */
164
0
    x = qnorm(log_P2,  0., 1., lower_tail,         /*log_p*/ TRUE);
165
166
0
      y = x * x;
167
0
      if (ndf < 5)
168
0
    c += 0.3 * (ndf - 4.5) * (x + 0.6);
169
0
      c = (((0.05 * d * x - 5) * x - 7) * x - 2) * x + b + c;
170
0
      y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c
171
0
      - y - 3) / b + 1) * x;
172
0
      y = expm1(a * y * y);
173
0
      q = sqrt(ndf * y);
174
0
  } else if(!P_ok && x < - M_LN2 * DBL_MANT_DIG) {/* 0.5* log(DBL_EPSILON) */
175
      /* y above might have underflown */
176
0
      q = sqrt(ndf) * exp(-x);
177
0
  }
178
0
  else { /* re-use 'y' from above */
179
0
      y = ((1 / (((ndf + 6) / (ndf * y) - 0.089 * d - 0.822)
180
0
           * (ndf + 2) * 3) + 0.5 / (ndf + 4))
181
0
     * y - 1) * (ndf + 1) / (ndf + 2) + 1 / y;
182
0
      q = sqrt(ndf * y);
183
0
  }
184
185
186
  /* Now apply 2-term Taylor expansion improvement (1-term = Newton):
187
   * as by Hill (1981) [ref.above] */
188
189
  /* FIXME: This can be far from optimal when log_p = TRUE
190
   *      but is still needed, e.g. for qt(-2, df=1.01, log=TRUE).
191
   *  Probably also improvable when  lower_tail = FALSE */
192
193
0
  if(P_ok1) {
194
0
      double M = fabs(sqrt(DBL_MAX/2.) - ndf);
195
0
      int it=0;
196
0
      while(it++ < 10 && (y = dt(q, ndf, FALSE)) > 0 &&
197
0
      R_FINITE(x = (pt(q, ndf, FALSE, FALSE) - P/2) / y) &&
198
0
      fabs(x) > 1e-14*fabs(q)) {
199
    /* Newton (=Taylor 1 term):
200
     *  q += x;
201
     * Taylor 2-term : */
202
0
    double F = (fabs(q) < M) ?
203
0
        q * (ndf + 1) / (2 * (q * q + ndf)) :
204
0
            (ndf + 1) / (2 * (q     + ndf/q)),
205
0
        del_q = x * (1. + x * F);
206
0
    if(R_FINITE(del_q) && R_FINITE(q + del_q))
207
0
        q += del_q;
208
0
    else if(R_FINITE(x) && R_FINITE(q + x))
209
0
        q += x;
210
0
    else // FIXME??  if  q+x = +/-Inf is *better* than q should use it
211
0
        break; // cannot improve  q  with a Newton/Taylor step
212
0
      }
213
0
  }
214
0
    }
215
0
     return neg ? -q : q;
216
0
}