/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 | } |