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