Coverage Report

Created: 2026-10-28 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/nmath/ptukey.c
Line
Count
Source
1
/*
2
 *  Mathlib : A C Library of Special Functions
3
 *  Copyright (C) 1998       Ross Ihaka
4
 *  Copyright (C) 2000--2007 The R Core Team
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, a copy is available at
18
 *  https://www.R-project.org/Licenses/
19
 *
20
 *  SYNOPSIS
21
 *
22
 *    #include <Rmath.h>
23
 *    double ptukey(q, rr, cc, df, lower_tail, log_p);
24
 *
25
 *  DESCRIPTION
26
 *
27
 *    Computes the probability that the maximum of rr studentized
28
 *    ranges, each based on cc means and with df degrees of freedom
29
 *    for the standard error, is less than q.
30
 *
31
 *    The algorithm is based on that of the reference.
32
 *
33
 *  REFERENCE
34
 *
35
 *    Copenhaver, Margaret Diponzio & Holland, Burt S.
36
 *    Multiple comparisons of simple effects in
37
 *    the two-way analysis of variance with fixed effects.
38
 *    Journal of Statistical Computation and Simulation,
39
 *    Vol.30, pp.1-15, 1988.
40
 */
41
42
#include "nmath.h"
43
#include "dpq.h"
44
45
static double wprob(double w, double rr, double cc)
46
0
{
47
/*  wprob() :
48
49
  This function calculates probability integral of Hartley's
50
  form of the range.
51
52
  w     = value of range
53
  rr    = no. of rows or groups
54
  cc    = no. of columns or treatments
55
  ir    = error flag = 1 if pr_w probability > 1
56
  pr_w = returned probability integral from (0, w)
57
58
  program will not terminate if ir is raised.
59
60
  bb = upper limit of legendre integration
61
  iMax = maximum acceptable value of integral
62
  nleg = order of legendre quadrature
63
  ihalf = int ((nleg + 1) / 2)
64
  wlar = value of range above which wincr1 intervals are used to
65
         calculate second part of integral,
66
         else wincr2 intervals are used.
67
  C1, C2, C3 = values which are used as cutoffs for terminating
68
  or modifying a calculation.
69
70
  M_1_SQRT_2PI = 1 / sqrt(2 * pi);  from abramowitz & stegun, p. 3.
71
  M_SQRT2 = sqrt(2)
72
  xleg = legendre 12-point nodes
73
  aleg = legendre 12-point coefficients
74
 */
75
0
#define nleg  12
76
0
#define ihalf 6
77
78
    /* looks like this is suboptimal for double precision.
79
       (see how C1-C3 are used) <MM>
80
    */
81
    /* const double iMax  = 1.; not used if = 1*/
82
0
    const static double C1 = -30.;
83
0
    const static double C2 = -50.;
84
0
    const static double C3 = 60.;
85
0
    const static double bb   = 8.;
86
0
    const static double wlar = 3.;
87
0
    const static double wincr1 = 2.;
88
0
    const static double wincr2 = 3.;
89
0
    const static double xleg[ihalf] = {
90
0
  0.981560634246719250690549090149,
91
0
  0.904117256370474856678465866119,
92
0
  0.769902674194304687036893833213,
93
0
  0.587317954286617447296702418941,
94
0
  0.367831498998180193752691536644,
95
0
  0.125233408511468915472441369464
96
0
    };
97
0
    const static double aleg[ihalf] = {
98
0
  0.047175336386511827194615961485,
99
0
  0.106939325995318430960254718194,
100
0
  0.160078328543346226334652529543,
101
0
  0.203167426723065921749064455810,
102
0
  0.233492536538354808760849898925,
103
0
  0.249147045813402785000562436043
104
0
    };
105
0
    double a, ac, pr_w, b, binc, c, cc1,
106
0
  pminus, pplus, qexpo, qsqz, rinsum, wi, wincr, xx;
107
0
    LDOUBLE blb, bub, einsum, elsum;
108
0
    int j, jj;
109
110
111
0
    qsqz = w * 0.5;
112
113
    /* if w >= 16 then the integral lower bound (occurs for c=20) */
114
    /* is 0.99999999999995 so return a value of 1. */
115
116
0
    if (qsqz >= bb)
117
0
  return 1.0;
118
119
    /* find (f(w/2) - 1) ^ cc */
120
    /* (first term in integral of hartley's form). */
121
122
0
    pr_w = 2 * pnorm(qsqz, 0.,1., 1,0) - 1.; /* erf(qsqz / M_SQRT2) */
123
    /* if pr_w ^ cc < 2e-22 then set pr_w = 0 */
124
0
    if (pr_w >= exp(C2 / cc))
125
0
  pr_w = pow(pr_w, cc);
126
0
    else
127
0
  pr_w = 0.0;
128
129
    /* if w is large then the second component of the */
130
    /* integral is small, so fewer intervals are needed. */
131
132
0
    if (w > wlar)
133
0
  wincr = wincr1;
134
0
    else
135
0
  wincr = wincr2;
136
137
    /* find the integral of second term of hartley's form */
138
    /* for the integral of the range for equal-length */
139
    /* intervals using legendre quadrature.  limits of */
140
    /* integration are from (w/2, 8).  two or three */
141
    /* equal-length intervals are used. */
142
143
    /* blb and bub are lower and upper limits of integration. */
144
145
0
    blb = qsqz;
146
0
    binc = (bb - qsqz) / wincr;
147
0
    bub = blb + binc;
148
0
    einsum = 0.0;
149
150
    /* integrate over each interval */
151
152
0
    cc1 = cc - 1.0;
153
0
    for (wi = 1; wi <= wincr; wi++) {
154
0
  elsum = 0.0;
155
0
  a = (double)(0.5 * (bub + blb));
156
157
  /* legendre quadrature with order = nleg */
158
159
0
  b = (double)(0.5 * (bub - blb));
160
161
0
  for (jj = 1; jj <= nleg; jj++) {
162
0
      if (ihalf < jj) {
163
0
    j = (nleg - jj) + 1;
164
0
    xx = xleg[j-1];
165
0
      } else {
166
0
    j = jj;
167
0
    xx = -xleg[j-1];
168
0
      }
169
0
      c = b * xx;
170
0
      ac = a + c;
171
172
      /* if exp(-qexpo/2) < 9e-14, */
173
      /* then doesn't contribute to integral */
174
175
0
      qexpo = ac * ac;
176
0
      if (qexpo > C3)
177
0
    break;
178
179
0
      pplus = 2 * pnorm(ac, 0., 1., 1,0);
180
0
      pminus= 2 * pnorm(ac, w,  1., 1,0);
181
182
      /* if rinsum ^ (cc-1) < 9e-14, */
183
      /* then doesn't contribute to integral */
184
185
0
      rinsum = (pplus * 0.5) - (pminus * 0.5);
186
0
      if (rinsum >= exp(C1 / cc1)) {
187
0
    rinsum = (aleg[j-1] * exp(-(0.5 * qexpo))) * pow(rinsum, cc1);
188
0
    elsum += rinsum;
189
0
      }
190
0
  }
191
0
  elsum *= (((2.0 * b) * cc) * M_1_SQRT_2PI);
192
0
  einsum += elsum;
193
0
  blb = bub;
194
0
  bub += binc;
195
0
    }
196
197
    /* if pr_w ^ rr < 9e-14, then return 0 */
198
0
    pr_w += (double) einsum;
199
0
    if (pr_w <= exp(C1 / rr))
200
0
  return 0.;
201
202
0
    pr_w = pow(pr_w, rr);
203
0
    if (pr_w >= 1.)/* 1 was iMax was eps */
204
0
  return 1.;
205
0
    return pr_w;
206
0
} /* wprob() */
207
208
209
double ptukey(double q, double rr, double cc, double df,
210
        int lower_tail, int log_p)
211
0
{
212
/*  function ptukey() [was qprob() ]:
213
214
  q = value of studentized range
215
  rr = no. of rows or groups
216
  cc = no. of columns or treatments
217
  df = degrees of freedom of error term
218
  ir[0] = error flag = 1 if wprob probability > 1
219
  ir[1] = error flag = 1 if qprob probability > 1
220
221
  qprob = returned probability integral over [0, q]
222
223
  The program will not terminate if ir[0] or ir[1] are raised.
224
225
  All references in wprob to Abramowitz and Stegun
226
  are from the following reference:
227
228
  Abramowitz, Milton and Stegun, Irene A.
229
  Handbook of Mathematical Functions.
230
  New York:  Dover publications, Inc. (1970).
231
232
  All constants taken from this text are
233
  given to 25 significant digits.
234
235
  nlegq = order of legendre quadrature
236
  ihalfq = int ((nlegq + 1) / 2)
237
  eps = max. allowable value of integral
238
  eps1 & eps2 = values below which there is
239
          no contribution to integral.
240
241
  d.f. <= dhaf: integral is divided into ulen1 length intervals.  else
242
  d.f. <= dquar:  integral is divided into ulen2 length intervals.  else
243
  d.f. <= deigh:  integral is divided into ulen3 length intervals.  else
244
  d.f. <= dlarg:  integral is divided into ulen4 length intervals.
245
246
  d.f. > dlarg: the range is used to calculate integral.
247
248
  M_LN2 = log(2)
249
250
  xlegq = legendre 16-point nodes
251
  alegq = legendre 16-point coefficients
252
253
  The coefficients and nodes for the legendre quadrature used in
254
  qprob and wprob were calculated using the algorithms found in:
255
256
  Stroud, A. H. and Secrest, D.
257
  Gaussian Quadrature Formulas.
258
  Englewood Cliffs,
259
  New Jersey:  Prentice-Hall, Inc, 1966.
260
261
  All values matched the tables (provided in same reference)
262
  to 30 significant digits.
263
264
  f(x) = .5 + erf(x / sqrt(2)) / 2      for x > 0
265
266
  f(x) = erfc( -x / sqrt(2)) / 2        for x < 0
267
268
  where f(x) is standard normal c. d. f.
269
270
  if degrees of freedom large, approximate integral
271
  with range distribution.
272
 */
273
0
#define nlegq 16
274
0
#define ihalfq  8
275
276
/*  const double eps = 1.0; not used if = 1 */
277
0
    const static double eps1 = -30.0;
278
0
    const static double eps2 = 1.0e-14;
279
0
    const static double dhaf  = 100.0;
280
0
    const static double dquar = 800.0;
281
0
    const static double deigh = 5000.0;
282
0
    const static double dlarg = 25000.0;
283
0
    const static double ulen1 = 1.0;
284
0
    const static double ulen2 = 0.5;
285
0
    const static double ulen3 = 0.25;
286
0
    const static double ulen4 = 0.125;
287
0
    const static double xlegq[ihalfq] = {
288
0
  0.989400934991649932596154173450,
289
0
  0.944575023073232576077988415535,
290
0
  0.865631202387831743880467897712,
291
0
  0.755404408355003033895101194847,
292
0
  0.617876244402643748446671764049,
293
0
  0.458016777657227386342419442984,
294
0
  0.281603550779258913230460501460,
295
0
  0.950125098376374401853193354250e-1
296
0
    };
297
0
    const static double alegq[ihalfq] = {
298
0
  0.271524594117540948517805724560e-1,
299
0
  0.622535239386478928628438369944e-1,
300
0
  0.951585116824927848099251076022e-1,
301
0
  0.124628971255533872052476282192,
302
0
  0.149595988816576732081501730547,
303
0
  0.169156519395002538189312079030,
304
0
  0.182603415044923588866763667969,
305
0
  0.189450610455068496285396723208
306
0
    };
307
0
    double ans, f2, f21, f2lf, ff4, otsum, qsqz, rotsum, t1, twa1, ulen, wprb;
308
0
    int i, j, jj;
309
310
0
#ifdef IEEE_754
311
0
    if (ISNAN(q) || ISNAN(rr) || ISNAN(cc) || ISNAN(df))
312
0
  ML_WARN_return_NAN;
313
0
#endif
314
315
0
    if (q <= 0)
316
0
  return R_DT_0;
317
318
    /* df must be > 1 */
319
    /* there must be at least two values */
320
321
0
    if (df < 2 || rr < 1 || cc < 2) ML_WARN_return_NAN;
322
323
0
    if(!R_FINITE(q))
324
0
  return R_DT_1;
325
326
0
    if (df > dlarg)
327
0
  return R_DT_val(wprob(q, rr, cc));
328
329
    /* calculate leading constant */
330
331
0
    f2 = df * 0.5;
332
    /* lgammafn(u) = log(gamma(u)) */
333
0
    f2lf = ((f2 * log(df)) - (df * M_LN2)) - lgammafn(f2);
334
0
    f21 = f2 - 1.0;
335
336
    /* integral is divided into unit, half-unit, quarter-unit, or */
337
    /* eighth-unit length intervals depending on the value of the */
338
    /* degrees of freedom. */
339
340
0
    ff4 = df * 0.25;
341
0
    if      (df <= dhaf) ulen = ulen1;
342
0
    else if (df <= dquar) ulen = ulen2;
343
0
    else if (df <= deigh) ulen = ulen3;
344
0
    else      ulen = ulen4;
345
346
0
    f2lf += log(ulen);
347
348
    /* integrate over each subinterval */
349
350
0
    ans = 0.0;
351
352
0
    for (i = 1; i <= 50; i++) {
353
0
  otsum = 0.0;
354
355
  /* legendre quadrature with order = nlegq */
356
  /* nodes (stored in xlegq) are symmetric around zero. */
357
358
0
  twa1 = (2 * i - 1) * ulen;
359
360
0
  for (jj = 1; jj <= nlegq; jj++) {
361
0
      if (ihalfq < jj) {
362
0
    j = jj - ihalfq - 1;
363
0
    t1 = (f2lf + (f21 * log(twa1 + (xlegq[j] * ulen))))
364
0
        - (((xlegq[j] * ulen) + twa1) * ff4);
365
0
      } else {
366
0
    j = jj - 1;
367
0
    t1 = (f2lf + (f21 * log(twa1 - (xlegq[j] * ulen))))
368
0
        + (((xlegq[j] * ulen) - twa1) * ff4);
369
370
0
      }
371
372
      /* if exp(t1) < 9e-14, then doesn't contribute to integral */
373
0
      if (t1 >= eps1) {
374
0
    if (ihalfq < jj) {
375
0
        qsqz = q * sqrt(((xlegq[j] * ulen) + twa1) * 0.5);
376
0
    } else {
377
0
        qsqz = q * sqrt(((-(xlegq[j] * ulen)) + twa1) * 0.5);
378
0
    }
379
380
    /* call wprob to find integral of range portion */
381
382
0
    wprb = wprob(qsqz, rr, cc);
383
0
    rotsum = (wprb * alegq[j]) * exp(t1);
384
0
    otsum += rotsum;
385
0
      }
386
      /* end legendre integral for interval i */
387
      /* L200: */
388
0
  }
389
390
  /* if integral for interval i < 1e-14, then stop.
391
   * However, in order to avoid small area under left tail,
392
   * at least  1 / ulen  intervals are calculated.
393
   */
394
0
  if (i * ulen >= 1.0 && otsum <= eps2)
395
0
      break;
396
397
  /* end of interval i */
398
  /* L330: */
399
400
0
  ans += otsum;
401
0
    }
402
403
0
    if(otsum > eps2) { /* not converged */
404
0
  ML_WARNING(ME_PRECISION, "ptukey");
405
0
    }
406
0
    if (ans > 1.)
407
0
  ans = 1.;
408
0
    return R_DT_val(ans);
409
0
}