File: qtukey.c

package info (click to toggle)
pspp 2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 66,676 kB
  • sloc: ansic: 267,210; xml: 18,446; sh: 5,534; python: 2,881; makefile: 125; perl: 64
file content (256 lines) | stat: -rw-r--r-- 8,293 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/* PSPP - a program for statistical analysis.
   Copyright (C) 2011 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* This file is taken from the R project source code, and modified.
   The original copyright notice is reproduced below: */

/*
 *  Mathlib : A C Library of Special Functions
 *  Copyright (C) 1998              Ross Ihaka
 *  Copyright (C) 2000--2005 The R Development Core Team
 *  based in part on AS70 (C) 1974 Royal Statistical Society
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, a copy is available at
 *  http://www.r-project.org/Licenses/
 *
 *  SYNOPSIS
 *
 *        #include <Rmath.h>
 *        double qtukey(p, rr, cc, df, lower_tail, log_p);
 *
 *  DESCRIPTION
 *
 *        Computes the quantiles of the maximum of rr studentized
 *        ranges, each based on cc means and with df degrees of freedom
 *        for the standard error, is less than q.
 *
 *        The algorithm is based on that of the reference.
 *
 *  REFERENCE
 *
 *        Copenhaver, Margaret Diponzio & Holland, Burt S.
 *        Multiple comparisons of simple effects in
 *        the two-way analysis of variance with fixed effects.
 *        Journal of Statistical Computation and Simulation,
 *        Vol.30, pp.1-15, 1988.
 */

#include <config.h>

#include "tukey.h"

#include <assert.h>
#include <math.h>

#define TRUE (1)
#define FALSE (0)

#define ML_POSINF        (1.0 / 0.0)
#define ML_NEGINF        (-1.0 / 0.0)

#define R_D_Lval(p)        (lower_tail ? (p) : (0.5 - (p) + 0.5))        /*  p  */

#define R_DT_qIv(p)        (log_p ? (lower_tail ? exp(p) : - expm1(p)) \
                               : R_D_Lval(p))


static double fmax2(double x, double y)
{
        if (isnan(x) || isnan(y))
                return x + y;
        return (x < y) ? y : x;
}


#define R_Q_P01_boundaries(p, _LEFT_, _RIGHT_)                \
    if (log_p) {                                        \
      assert (p <= 0);                                        \
        if(p == 0) /* upper bound*/                        \
            return lower_tail ? _RIGHT_ : _LEFT_;        \
        if(p == ML_NEGINF)                                \
            return lower_tail ? _LEFT_ : _RIGHT_;        \
    }                                                        \
    else { /* !log_p */                                        \
      assert (p >= 0 && p <= 1);                        \
        if(p == 0)                                        \
            return lower_tail ? _LEFT_ : _RIGHT_;        \
        if(p == 1)                                        \
            return lower_tail ? _RIGHT_ : _LEFT_;        \
    }


/* qinv() :
 *        this function finds percentage point of the studentized range
 *        which is used as initial estimate for the secant method.
 *        function is adapted from portion of algorithm as 70
 *        from applied statistics (1974) ,vol. 23, no. 1
 *        by odeh, r. e. and evans, j. o.
 *
 *          p = percentage point
 *          c = no. of columns or treatments
 *          v = degrees of freedom
 *          qinv = returned initial estimate
 *
 *        vmax is cutoff above which degrees of freedom
 *        is treated as infinity.
 */

static double qinv(double p, double c, double v)
{
    static const double p0 = 0.322232421088;
    static const double q0 = 0.993484626060e-01;
    static const double p1 = -1.0;
    static const double q1 = 0.588581570495;
    static const double p2 = -0.342242088547;
    static const double q2 = 0.531103462366;
    static const double p3 = -0.204231210125;
    static const double q3 = 0.103537752850;
    static const double p4 = -0.453642210148e-04;
    static const double q4 = 0.38560700634e-02;
    static const double c1 = 0.8832;
    static const double c2 = 0.2368;
    static const double c3 = 1.214;
    static const double c4 = 1.208;
    static const double c5 = 1.4142;
    static const double vmax = 120.0;

    double ps, q, t, yi;

    ps = 0.5 - 0.5 * p;
    yi = sqrt (log (1.0 / (ps * ps)));
    t = yi + ((((yi * p4 + p3) * yi + p2) * yi + p1) * yi + p0)
           / ((((yi * q4 + q3) * yi + q2) * yi + q1) * yi + q0);
    if (v < vmax) t += (t * t * t + t) / v / 4.0;
    q = c1 - c2 * t;
    if (v < vmax) q += -c3 / v + c4 * t / v;
    return t * (q * log (c - 1.0) + c5);
}

/*
 *  Copenhaver, Margaret Diponzio & Holland, Burt S.
 *  Multiple comparisons of simple effects in
 *  the two-way analysis of variance with fixed effects.
 *  Journal of Statistical Computation and Simulation,
 *  Vol.30, pp.1-15, 1988.
 *
 *  Uses the secant method to find critical values.
 *
 *  p = confidence level (1 - alpha)
 *  rr = no. of rows or groups
 *  cc = no. of columns or treatments
 *  df = degrees of freedom of error term
 *
 *  ir(1) = error flag = 1 if wprob probability > 1
 *  ir(2) = error flag = 1 if ptukey probability > 1
 *  ir(3) = error flag = 1 if convergence not reached in 50 iterations
 *                       = 2 if df < 2
 *
 *  qtukey = returned critical value
 *
 *  If the difference between successive iterates is less than eps,
 *  the search is terminated
 */


double qtukey(double p, double rr, double cc, double df,
              int lower_tail, int log_p)
{
    static const double eps = 0.0001;
    const int maxiter = 50;

    double ans = 0.0, valx0, valx1, x0, x1, xabs;
    int iter;

    if (isnan(p) || isnan(rr) || isnan(cc) || isnan(df)) {
      /*        ML_ERROR(ME_DOMAIN, "qtukey"); */
        return p + rr + cc + df;
    }

    /* df must be > 1 ; there must be at least two values */
    /*              ^^
       JMD: The comment says 1 but the code says 2.
       Which is correct?
    */
    assert (df >= 2);
    assert (rr >= 1);
    assert (cc >= 2);


    R_Q_P01_boundaries (p, 0, ML_POSINF);

    p = R_DT_qIv(p); /* lower_tail,non-log "p" */

    /* Initial value */

    x0 = qinv(p, cc, df);

    /* Find prob(value < x0) */

    valx0 = ptukey(x0, rr, cc, df, /*LOWER*/TRUE, /*LOG_P*/FALSE) - p;

    /* Find the second iterate and prob(value < x1). */
    /* If the first iterate has probability value */
    /* exceeding p then second iterate is 1 less than */
    /* first iterate; otherwise it is 1 greater. */

    if (valx0 > 0.0)
        x1 = fmax2(0.0, x0 - 1.0);
    else
        x1 = x0 + 1.0;
    valx1 = ptukey(x1, rr, cc, df, /*LOWER*/TRUE, /*LOG_P*/FALSE) - p;

    /* Find new iterate */

    for(iter=1 ; iter < maxiter ; iter++) {
        ans = x1 - ((valx1 * (x1 - x0)) / (valx1 - valx0));
        valx0 = valx1;

        /* New iterate must be >= 0 */

        x0 = x1;
        if (ans < 0.0) {
            ans = 0.0;
            valx1 = -p;
        }
        /* Find prob(value < new iterate) */

        valx1 = ptukey(ans, rr, cc, df, /*LOWER*/TRUE, /*LOG_P*/FALSE) - p;
        x1 = ans;

        /* If the difference between two successive */
        /* iterates is less than eps, stop */

        xabs = fabs(x1 - x0);
        if (xabs < eps)
            return ans;
    }

    /* The process did not converge in 'maxiter' iterations */
    assert (0);
    return ans;
}