1 module rmathd.t;
2 
3 public import rmathd.common;
4 public import rmathd.beta;
5 public import rmathd.normal;
6 public import rmathd.chisq;
7 
8 /*
9 ** dmd t.d common.d normal.d beta.d chisq.d && ./t
10 */
11 
12 
13 T dt(T: double)(T x, T n, int give_log)
14 {
15     
16     mixin R_D__0!give_log;
17     if(isNaN(x) || isNaN(n))
18 	    return x + n;
19     if (n <= 0)
20     	return T.nan;
21     if(!isFinite(x))
22 	    return R_D__0;
23     if(!isFinite(n))
24 	    return dnorm!T(x, 0., 1., give_log);
25 
26     T u, t = -bd0!T(n/2., (n + 1)/2.) + stirlerr!T((n + 1)/2.) - stirlerr!T(n/2.),
27 	x2n = x*x/n, // in  [0, Inf]
28 	ax = 0., // <- -Wpedantic
29 	l_x2n; // := log(sqrt(1 + x2n)) = log(1 + x2n)/2
30     int lrg_x2n =  (x2n > 1./DBL_EPSILON);
31     if (lrg_x2n) { // large x^2/n :
32 	    ax = fabs(x);
33 	    l_x2n = log(ax) - log(n)/2.; // = log(x2n)/2 = 1/2 * log(x^2 / n)
34 	    u = //  log(1 + x2n) * n/2 =  n * log(1 + x2n)/2 =
35 	        n*l_x2n;
36     } else if (x2n > 0.2) {
37 	    l_x2n = log(1 + x2n)/2.;
38 	    u = n * l_x2n;
39     } else {
40 	    l_x2n = log1p(x2n)/2.;
41 	    u = -bd0(n/2.,(n+x*x)/2.) + x*x/2.;
42     }
43 
44     //old: return  R_D_fexp(M_2PI*(1+x2n), t-u);
45 
46     // R_D_fexp(f,x) :=  (give_log ? -0.5*log(f)+(x) : exp(x)/sqrt(f))
47     // f = 2pi*(1+x2n)
48     //  ==> 0.5*log(f) = log(2pi)/2 + log(1+x2n)/2 = log(2pi)/2 + l_x2n
49     //	     1/sqrt(f) = 1/sqrt(2pi * (1+ x^2 / n))
50     //		       = 1/sqrt(2pi)/(|x|/sqrt(n)*sqrt(1+1/x2n))
51     //		       = M_1_SQRT_2PI * sqrt(n)/ (|x|*sqrt(1+1/x2n))
52     if(give_log)
53 	    return t - u - (M_LN_SQRT_2PI + l_x2n);
54 
55     // else :  if(lrg_x2n) : sqrt(1 + 1/x2n) ='= sqrt(1) = 1
56     T I_sqrt_ = (lrg_x2n ? sqrt(n)/ax : exp(-l_x2n));
57     return exp(t - u)*M_1_SQRT_2PI*I_sqrt_;
58 }
59 
60 
61 T pt(T: double)(T x, T n, int lower_tail, int log_p)
62 {
63     /* return  P[ T <= x ]	where
64      * T ~ t_{n}  (t distrib. with n degrees of freedom).
65     
66      *	--> ./pnt.c for NON-central
67      */
68     T val, nx;
69     
70     if (isNaN(x) || isNaN(n))
71 	    return x + n;
72     
73     if (n <= 0.0)
74     	return T.nan;
75 
76     if(!isFinite(x))
77 	    return (x < 0) ? R_DT_0!T(lower_tail, log_p) : R_DT_1!T(lower_tail, log_p);
78     if(!isFinite(n))
79 	    return pnorm!T(x, 0.0, 1.0, lower_tail, log_p);
80 
81     //#ifdef R_version_le_260
82     //if (n > 4e5) { /*-- Fixme(?): test should depend on `n' AND `x' ! */
83 	//    /* Approx. from	 Abramowitz & Stegun 26.7.8 (p.949) */
84 	//    val = 1./(4.*n);
85 	//    return pnorm!T(x*(1. - val)/sqrt(1. + x*x*2.*val), 0.0, 1.0, lower_tail, log_p);
86     //}
87     //#endif
88 
89     nx = 1 + (x/n)*x;
90     /* FIXME: This test is probably losing rather than gaining precision,
91      * now that pbeta(*, log_p = TRUE) is much better.
92      * Note however that a version of this test *is* needed for x*x > D_MAX */
93     if(nx > 1e100) { /* <==>  x*x > 1e100 * n  */
94 	    /* Danger of underflow. So use Abramowitz & Stegun 26.5.4
95 	       pbeta(z, a, b) ~ z^a(1-z)^b / aB(a,b) ~ z^a / aB(a,b),
96 	       with z = 1/nx,  a = n/2,  b= 1/2 :
97 	    */
98 	    T lval;
99 	    lval = -0.5*n*(2*log(fabs(x)) - log(n)) - lbeta!T(0.5*n, 0.5) - log(0.5*n);
100 	    val = log_p ? lval : exp(lval);
101     } else {
102 	    val = (n > x * x)
103 	        ? pbeta!T(x * x / (n + x * x), 0.5, n / 2., /*lower_tail*/0, log_p)
104 	        : pbeta!T(1. / nx,             n / 2., 0.5, /*lower_tail*/1, log_p);
105     }
106 
107     /* Use "1 - v"  if	lower_tail  and	 x > 0 (but not both):*/
108     if(x <= 0.)
109 	    lower_tail = !lower_tail;
110 
111     if(log_p) {
112 	    if(lower_tail)
113 	    	return log1p(-0.5*exp(val));
114 	    else
115 	    	return val - M_LN2; /* = log(.5* pbeta(....)) */
116     } else {
117 	    val /= 2.;
118 	    mixin R_D_Cval!(val);
119 	    return R_D_Cval;
120     }
121 }
122 
123 
124 T qt(T: double)(T p, T ndf, int lower_tail, int log_p)
125 {
126     const static T eps = 1.0e-12;
127     
128     T P, q;
129     
130     if (isNaN(p) || isNaN(ndf))
131 	    return p + ndf;
132 	immutable(T) NINF = -T.infinity, INF = T.infinity;
133     mixin (R_Q_P01_boundaries!(p, NINF, INF)());
134 
135     if (ndf <= 0)
136     	return T.nan;
137 
138     if (ndf < 1) { /* based on qnt */
139 	    const static T accu = 1e-13;
140 	    const static T Eps = 1e-11; /* must be > accu */
141         
142 	    T ux, lx, nx, pp;
143         
144 	    int iter = 0;
145         
146         mixin R_DT_qIv!p;
147 	    p = R_DT_qIv;
148         
149 	    /* Invert pt(.) :
150 	     * 1. finding an upper and lower bound */
151 	    if(p > 1 - DBL_EPSILON)
152 	    	return T.infinity;
153 	    pp = fmin2!T(1 - DBL_EPSILON, p * (1 + Eps));
154 	    for(ux = 1.; ux < DBL_MAX && pt!T(ux, ndf, 1, 0) < pp; ux *= 2){}
155 	    pp = p * (1 - Eps);
156 	    for(lx =-1.; lx > -DBL_MAX && pt!T(lx, ndf, 1, 0) > pp; lx *= 2){}
157         
158 	    /* 2. interval (lx,ux)  halving
159 	       regula falsi failed on qt(0.1, 0.1)
160 	     */
161 	    do {
162 	        nx = 0.5 * (lx + ux);
163 	        if (pt!T(nx, ndf, 1, 0) > p) ux = nx; else lx = nx;
164 	    } while ((ux - lx) / fabs(nx) > accu && ++iter < 1000);
165         
166 	    if(iter >= 1000){
167 	    	doNothing();
168 	    	//ML_ERROR(ME_PRECISION, "qt");
169 	    }
170         
171 	    return 0.5*(lx + ux);
172     }
173 
174     /* Old comment:
175      * FIXME: "This test should depend on  ndf  AND p  !!
176      * -----  and in fact should be replaced by
177      * something like Abramowitz & Stegun 26.7.5 (p.949)"
178      *
179      * That would say that if the qnorm value is x then
180      * the result is about x + (x^3+x)/4df + (5x^5+16x^3+3x)/96df^2
181      * The differences are tiny even if x ~ 1e5, and qnorm is not
182      * that accurate in the extreme tails.
183      */
184     if (ndf > 1e20)
185     	return qnorm!T(p, 0., 1., lower_tail, log_p);
186 
187     P = R_D_qIv!T(p, log_p); /* if exp(p) underflows, we fix below */
188 
189     //Rboolean
190     int neg = (!lower_tail || P < 0.5) && (lower_tail || P > 0.5),
191 	is_neg_lower = (lower_tail == neg); /* both TRUE or FALSE == !xor */
192 
193 	//mixin R_D_Lval!p;
194 	mixin R_D_Cval!p;
195 
196     if(neg)
197 	    P = 2*(log_p ? (lower_tail ? P : -expm1(p)) : R_D_Lval!T(p, lower_tail));
198     else
199 	    P = 2*(log_p ? (lower_tail ? -expm1(p) : P) : R_D_Cval);
200 
201     /* 0 <= P <= 1 ; P = 2*min(P', 1 - P')  in all cases */
202 
203     if (fabs(ndf - 2) < eps) {	/* df ~= 2 */
204 	    if(P > DBL_MIN) {
205 	        if(3* P < DBL_EPSILON) /* P ~= 0 */
206 	    	q = 1 / sqrt(P);
207 	        else if (P > 0.9)	   /* P ~= 1 */
208 	    	q = (1 - P) * sqrt(2 /(P * (2 - P)));
209 	        else /* eps/3 <= P <= 0.9 */
210 	    	q = sqrt(2 / (P * (2 - P)) - 2);
211 	    } else { /* P << 1, q = 1/sqrt(P) = ... */
212 	        if(log_p)
213 	    	    q = is_neg_lower ? exp(- p/2) / M_SQRT2 : 1/sqrt(-expm1(p));
214 	        else
215 	    	    q = T.infinity;
216 	    }
217     } else if (ndf < 1 + eps) { /* df ~= 1  (df < 1 excluded above): Cauchy */
218 	    if(P == 1.) q = 0; // some versions of tanpi give Inf, some NaN
219 	    else if(P > 0)
220 	        q = 1/tanpi!T(P/2.);/* == - tan((P+1) * M_PI_2) -- suffers for P ~= 0 */
221         
222 	    else { /* P = 0, but maybe = 2*exp(p) ! */
223 	        if(log_p) /* 1/tan(e) ~ 1/e */
224 	    	q = is_neg_lower ? M_1_PI * exp(-p) : -1./(M_PI * expm1(p));
225 	        else
226 	    	q = T.infinity;
227 	    }
228     }
229     else {		/*-- usual case;  including, e.g.,  df = 1.1 */
230 	T x = 0., y, log_P2 = 0./* -Wall */,
231 	    a = 1 / (ndf - 0.5),
232 	    b = 48 / (a * a),
233 	    c = ((20700 * a / b - 98) * a - 16) * a + 96.36,
234 	    d = ((94.5 / (b + c) - 3) / b + 1) * sqrt(a*M_PI_2)*ndf;
235 
236     //Rboolean
237 	int P_ok1 = P > DBL_MIN || !log_p,  P_ok = P_ok1;
238 	if(P_ok1) {
239 	    y = pow(d * P, 2.0 / ndf);
240 	    P_ok = (y >= DBL_EPSILON);
241 	}
242 	if(!P_ok) {// log.p && P very.small  ||  (d*P)^(2/df) =: y < eps_c
243 	    log_P2 = is_neg_lower ? R_D_log!T(p, log_p) : R_D_LExp!T(p, log_p); /* == log(P / 2) */
244 	    x = (log(d) + M_LN2 + log_P2) / ndf;
245 	    y = exp(2*x);
246 	}
247 
248 	if ((ndf < 2.1 && P > 0.5) || y > 0.05 + a) { /* P > P0(df) */
249 	    /* Asymptotic inverse expansion about normal */
250 	    if(P_ok)
251 		    x = qnorm!T(0.5 * P, 0., 1., /*lower_tail*/1, /*log_p*/ 0);
252 	    else /* log_p && P underflowed */
253 		    x = qnorm!T(log_P2,  0., 1., lower_tail, /*log_p*/ 1);
254 
255 	    y = x * x;
256 	    if (ndf < 5)
257 		    c += 0.3 * (ndf - 4.5) * (x + 0.6);
258 	    c = (((0.05 * d * x - 5) * x - 7) * x - 2) * x + b + c;
259 	    y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c
260 		  - y - 3) / b + 1) * x;
261 	    y = expm1(a * y * y);
262 	    q = sqrt(ndf * y);
263 	} else if(!P_ok && x < - M_LN2 * DBL_MANT_DIG) {/* 0.5* log(DBL_EPSILON) */
264 	    /* y above might have underflown */
265 	    q = sqrt(ndf)*exp(-x);
266 	} else { /* re-use 'y' from above */
267 	    y = ((1 / (((ndf + 6) / (ndf * y) - 0.089 * d - 0.822)
268 		       * (ndf + 2) * 3) + 0.5 / (ndf + 4))
269 		 * y - 1) * (ndf + 1) / (ndf + 2) + 1 / y;
270 	    q = sqrt(ndf * y);
271 	}
272 
273 
274 	/* Now apply 2-term Taylor expansion improvement (1-term = Newton):
275 	 * as by Hill (1981) [ref.above] */
276 
277 	/* FIXME: This can be far from optimal when log_p = TRUE
278 	 *      but is still needed, e.g. for qt(-2, df=1.01, log=TRUE).
279 	 *	Probably also improvable when  lower_tail = FALSE */
280 
281 	if(P_ok1) {
282 	    int it = 0;
283 	    while(it++ < 10 && (y = dt(q, ndf, 0)) > 0 &&
284 		  isFinite(x = (pt!T(q, ndf, 0, 0) - P/2) / y) &&
285 		  fabs(x) > 1e-14*fabs(q))
286 		/* Newton (=Taylor 1 term):
287 		 *  q += x;
288 		 * Taylor 2-term : */
289 		q += x * (1. + x * q * (ndf + 1) / (2 * (q * q + ndf)));
290 	}
291     }
292     if(neg) q = -q;
293     return q;
294 }
295 
296 
297 T rt(T: double)(T df)
298 {
299     if (isNaN(df) || df <= 0.0)
300     	return T.nan;
301 
302     if(!isFinite(df))
303 	    return norm_rand!T();
304     else {
305     /* Some compilers (including MW6) evaluated this from right to left
306     	return norm_rand() / sqrt(rchisq(df) / df); */
307     	T num = norm_rand!T();
308     	return num / sqrt(rchisq!T(df) / df);
309     }
310 }
311 
312 
313 void test_t()
314 {
315 	import std.stdio: writeln;
316 	writeln("dt: ", dt(1., 20., 0));
317 	writeln("pt: ", pt(1., 20., 1, 0));
318 	writeln("qt: ", qt(0.7, 20., 1, 0));
319 	writeln("rt: ", rt(20.), ", rt: ", rt(20.), ", rt: ", rt(20.), ", rt: ", rt(20.));
320 }
321