1 /++ 2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution). 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 6 Authors: John Michael Hall 7 8 Copyright: 2022-3 Mir Stat Authors. 9 10 +/ 11 12 module mir.stat.distribution.students_t; 13 14 import mir.internal.utility: isFloatingPoint; 15 16 /++ 17 Computes the Student's t probability density function (PDF). 18 19 Params: 20 x = value to evaluate PDF 21 nu = degrees of freedom 22 23 See_also: 24 $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution) 25 +/ 26 @safe pure nothrow @nogc 27 T studentsTPDF(T)(const T x, const T nu) 28 if (isFloatingPoint!T) 29 in (nu > 0, "nu must be greater than zero") 30 { 31 import mir.math.common: pow, sqrt; 32 import mir.stat.constant: M_SQRTPI; 33 import mir.stat.distribution.normal: normalPDF; 34 import std.mathspecial: gamma; 35 36 if (nu != T.infinity) { 37 return (gamma((nu + 1) * 0.5) / gamma(nu * 0.5)) / sqrt(nu) * T(M_SQRTPI) * pow(1 + (x * x) / nu, -(nu + 1) * 0.5); 38 } else { 39 return normalPDF(x); 40 } 41 } 42 43 /++ 44 Ditto, with location and scale parameters (by standardizing `x`). 45 46 Params: 47 x = value to evaluate PDF 48 nu = degrees of freedom 49 mean = location parameter 50 stdDev = scale parameter 51 +/ 52 @safe pure nothrow @nogc 53 T studentsTPDF(T)(const T x, const T nu, const T mean, const T stdDev) 54 if (isFloatingPoint!T) 55 in (nu > 0, "nu must be greater than zero") 56 in (stdDev > 0, "stdDev must be greater than zero") 57 { 58 return studentsTPDF((x - mean) / stdDev, nu); 59 } 60 61 /// 62 version(mir_stat_test) 63 @safe pure nothrow @nogc 64 unittest { 65 import mir.test: shouldApprox; 66 67 studentsTPDF(-3.0, 5).shouldApprox == 0.01729258; 68 studentsTPDF(-2.0, 5).shouldApprox == 0.06509031; 69 studentsTPDF(-1.0, 5).shouldApprox == 0.2196798; 70 studentsTPDF(0.0, 5).shouldApprox == 0.3796067; 71 studentsTPDF(1.0, 5).shouldApprox == 0.2196798; 72 studentsTPDF(2.0, 5).shouldApprox == 0.06509031; 73 studentsTPDF(3.0, 5).shouldApprox == 0.01729258; 74 75 // Can include location/scale 76 studentsTPDF(-3.0, 5, 1, 2).shouldApprox == 0.06509031; 77 studentsTPDF(-2.0, 5, 1, 2).shouldApprox == 0.1245173; 78 studentsTPDF(-1.0, 5, 1, 2).shouldApprox == 0.2196798; 79 studentsTPDF(0.0, 5, 1, 2).shouldApprox == 0.3279185; 80 studentsTPDF(1.0, 5, 1, 2).shouldApprox == 0.3796067; 81 studentsTPDF(2.0, 5, 1, 2).shouldApprox == 0.3279185; 82 studentsTPDF(3.0, 5, 1, 2).shouldApprox == 0.2196798; 83 } 84 85 // Checking other DoF 86 version(mir_stat_test) 87 @safe pure nothrow @nogc 88 unittest { 89 import mir.test: shouldApprox; 90 91 studentsTPDF(-3.0, 25).shouldApprox == 0.007253748; 92 studentsTPDF(-2.0, 25).shouldApprox == 0.0573607; 93 studentsTPDF(-1.0, 25).shouldApprox == 0.237211; 94 studentsTPDF(0.0, 25).shouldApprox == 0.3949738; 95 studentsTPDF(1.0, 25).shouldApprox == 0.237211; 96 studentsTPDF(2.0, 25).shouldApprox == 0.0573607; 97 studentsTPDF(3.0, 25).shouldApprox == 0.007253748; 98 99 studentsTPDF(-3.0, double.infinity).shouldApprox == 0.004431848; 100 studentsTPDF(-2.0, double.infinity).shouldApprox == 0.05399097; 101 studentsTPDF(-1.0, double.infinity).shouldApprox == 0.2419707; 102 studentsTPDF(0.0, double.infinity).shouldApprox == 0.3989423; 103 studentsTPDF(1.0, double.infinity).shouldApprox == 0.2419707; 104 studentsTPDF(2.0, double.infinity).shouldApprox == 0.05399097; 105 studentsTPDF(3.0, double.infinity).shouldApprox == 0.004431848; 106 } 107 108 // Checking negative location parameter 109 version(mir_stat_test) 110 @safe pure nothrow @nogc 111 unittest { 112 import mir.test: shouldApprox; 113 114 studentsTPDF(-3.0, 5, -1, 2).shouldApprox == 0.2196798; 115 studentsTPDF(-2.0, 5, -1, 2).shouldApprox == 0.3279185; 116 studentsTPDF(-1.0, 5, -1, 2).shouldApprox == 0.3796067; 117 studentsTPDF(0.0, 5, -1, 2).shouldApprox == 0.3279185; 118 studentsTPDF(1.0, 5, -1, 2).shouldApprox == 0.2196798; 119 studentsTPDF(2.0, 5, -1, 2).shouldApprox == 0.1245173; 120 studentsTPDF(3.0, 5, -1, 2).shouldApprox == 0.06509031; 121 } 122 123 /++ 124 Computes the Student's t cumulative distribution function (CDF). 125 126 Params: 127 x = value to evaluate CDF 128 nu = degrees of freedom 129 130 See_also: 131 $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution) 132 +/ 133 @safe pure nothrow @nogc 134 T studentsTCDF(T)(const T x, const T nu) 135 if (isFloatingPoint!T) 136 in (nu > 0, "nu must be greater than zero") 137 { 138 import mir.stat.distribution.beta: betaCDF, betaCCDF; 139 import mir.stat.distribution.normal: normalCDF; 140 141 if (nu != T.infinity) { 142 T output; 143 if (nu > x * x) { 144 output = betaCCDF(x * x / (nu + x * x), 0.5, 0.5 * nu); 145 } else { 146 output = betaCDF((nu / (nu + x * x)), 0.5 * nu, 0.5); 147 } 148 output *= 0.5; 149 if (x > 0) { 150 output = 1 - output; 151 } 152 return output; 153 } else { 154 return normalCDF(x); 155 } 156 } 157 158 /++ 159 Ditto, with location and scale parameters (by standardizing `x`). 160 161 Params: 162 x = value to evaluate CDF 163 nu = degrees of freedom 164 mean = location parameter 165 stdDev = scale parameter 166 +/ 167 @safe pure nothrow @nogc 168 T studentsTCDF(T)(const T x, const T nu, const T mean, const T stdDev) 169 if (isFloatingPoint!T) 170 in (nu > 0, "nu must be greater than zero") 171 in (stdDev > 0, "stdDev must be greater than zero") 172 { 173 return studentsTCDF((x - mean) / stdDev, nu); 174 } 175 176 /// 177 version(mir_stat_test) 178 @safe pure nothrow @nogc 179 unittest { 180 import mir.test: shouldApprox; 181 182 studentsTCDF(-3.0, 5).shouldApprox == 0.01504962; 183 studentsTCDF(-2.0, 5).shouldApprox == 0.05096974; 184 studentsTCDF(-1.0, 5).shouldApprox == 0.1816087; 185 studentsTCDF(0.0, 5).shouldApprox == 0.5; 186 studentsTCDF(1.0, 5).shouldApprox == 0.8183913; 187 studentsTCDF(2.0, 5).shouldApprox == 0.9490303; 188 studentsTCDF(3.0, 5).shouldApprox == 0.9849504; 189 190 // Can include location/scale 191 studentsTCDF(-3.0, 5, 1, 2).shouldApprox == 0.05096974; 192 studentsTCDF(-2.0, 5, 1, 2).shouldApprox == 0.09695184; 193 studentsTCDF(-1.0, 5, 1, 2).shouldApprox == 0.1816087; 194 studentsTCDF(0.0, 5, 1, 2).shouldApprox == 0.3191494; 195 studentsTCDF(1.0, 5, 1, 2).shouldApprox == 0.5; 196 studentsTCDF(2.0, 5, 1, 2).shouldApprox == 0.6808506; 197 studentsTCDF(3.0, 5, 1, 2).shouldApprox == 0.8183913; 198 } 199 200 // Checking other DoF 201 version(mir_stat_test) 202 @safe pure nothrow @nogc 203 unittest { 204 import mir.test: shouldApprox; 205 206 studentsTCDF(-3.0, 25).shouldApprox == 0.00301909; 207 studentsTCDF(-2.0, 25).shouldApprox == 0.02823799; 208 studentsTCDF(-1.0, 25).shouldApprox == 0.163446; 209 studentsTCDF(0.0, 25).shouldApprox == 0.5; 210 studentsTCDF(1.0, 25).shouldApprox == 0.836554; 211 studentsTCDF(2.0, 25).shouldApprox == 0.971762; 212 studentsTCDF(3.0, 25).shouldApprox == 0.9969809; 213 214 studentsTCDF(-3.0, double.infinity).shouldApprox == 0.001349898; 215 studentsTCDF(-2.0, double.infinity).shouldApprox == 0.02275013; 216 studentsTCDF(-1.0, double.infinity).shouldApprox == 0.1586553; 217 studentsTCDF(0.0, double.infinity).shouldApprox == 0.5; 218 studentsTCDF(1.0, double.infinity).shouldApprox == 0.8413447; 219 studentsTCDF(2.0, double.infinity).shouldApprox == 0.9772499; 220 studentsTCDF(3.0, double.infinity).shouldApprox == 0.9986501; 221 } 222 223 // Checking negative location parameter 224 version(mir_stat_test) 225 @safe pure nothrow @nogc 226 unittest { 227 import mir.test: shouldApprox; 228 229 studentsTCDF(-3.0, 5, -1, 2).shouldApprox == 0.1816087; 230 studentsTCDF(-2.0, 5, -1, 2).shouldApprox == 0.3191494; 231 studentsTCDF(-1.0, 5, -1, 2).shouldApprox == 0.5; 232 studentsTCDF(0.0, 5, -1, 2).shouldApprox == 0.6808506; 233 studentsTCDF(1.0, 5, -1, 2).shouldApprox == 0.8183913; 234 studentsTCDF(2.0, 5, -1, 2).shouldApprox == 0.9030482; 235 studentsTCDF(3.0, 5, -1, 2).shouldApprox == 0.9490303; 236 } 237 238 /++ 239 Computes the Student's t complementary cumulative distribution function (CCDF). 240 241 Params: 242 x = value to evaluate CCDF 243 nu = degrees of freedom 244 245 See_also: 246 $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution) 247 +/ 248 @safe pure nothrow @nogc 249 T studentsTCCDF(T)(const T x, const T nu) 250 if (isFloatingPoint!T) 251 in (nu > 0, "nu must be greater than zero") 252 { 253 import mir.stat.distribution.normal: normalCCDF; 254 255 return studentsTCDF(-x, nu); 256 } 257 258 /++ 259 Ditto, with location and scale parameters (by standardizing `x`). 260 261 Params: 262 x = value to evaluate CCDF 263 nu = degrees of freedom 264 mean = location parameter 265 stdDev = scale parameter 266 +/ 267 @safe pure nothrow @nogc 268 T studentsTCCDF(T)(const T x, const T nu, const T mean, const T stdDev) 269 if (isFloatingPoint!T) 270 in (nu > 0, "nu must be greater than zero") 271 in (stdDev > 0, "stdDev must be greater than zero") 272 { 273 return studentsTCCDF((x - mean) / stdDev, nu); 274 } 275 276 /// 277 version(mir_stat_test) 278 @safe pure nothrow @nogc 279 unittest { 280 import mir.test: shouldApprox; 281 282 studentsTCCDF(-3.0, 5).shouldApprox == 0.9849504; 283 studentsTCCDF(-2.0, 5).shouldApprox == 0.9490303; 284 studentsTCCDF(-1.0, 5).shouldApprox == 0.8183913; 285 studentsTCCDF(0.0, 5).shouldApprox == 0.5; 286 studentsTCCDF(1.0, 5).shouldApprox == 0.1816087; 287 studentsTCCDF(2.0, 5).shouldApprox == 0.05096974; 288 studentsTCCDF(3.0, 5).shouldApprox == 0.01504962; 289 290 // Can include location/scale 291 studentsTCCDF(-3.0, 5, 1, 2).shouldApprox == 0.9490303; 292 studentsTCCDF(-2.0, 5, 1, 2).shouldApprox == 0.9030482; 293 studentsTCCDF(-1.0, 5, 1, 2).shouldApprox == 0.8183913; 294 studentsTCCDF(0.0, 5, 1, 2).shouldApprox == 0.6808506; 295 studentsTCCDF(1.0, 5, 1, 2).shouldApprox == 0.5; 296 studentsTCCDF(2.0, 5, 1, 2).shouldApprox == 0.3191494; 297 studentsTCCDF(3.0, 5, 1, 2).shouldApprox == 0.1816087; 298 } 299 300 // Checking other DoF 301 version(mir_stat_test) 302 @safe pure nothrow @nogc 303 unittest { 304 import mir.test: shouldApprox; 305 306 studentsTCCDF(-3.0, 25).shouldApprox == 0.9969809; 307 studentsTCCDF(-2.0, 25).shouldApprox == 0.971762; 308 studentsTCCDF(-1.0, 25).shouldApprox == 0.836554; 309 studentsTCCDF(0.0, 25).shouldApprox == 0.5; 310 studentsTCCDF(1.0, 25).shouldApprox == 0.163446; 311 studentsTCCDF(2.0, 25).shouldApprox == 0.02823799; 312 studentsTCCDF(3.0, 25).shouldApprox == 0.00301909; 313 314 studentsTCCDF(-3.0, double.infinity).shouldApprox == 0.9986501; 315 studentsTCCDF(-2.0, double.infinity).shouldApprox == 0.9772499; 316 studentsTCCDF(-1.0, double.infinity).shouldApprox == 0.8413447; 317 studentsTCCDF(0.0, double.infinity).shouldApprox == 0.5; 318 studentsTCCDF(1.0, double.infinity).shouldApprox == 0.1586553; 319 studentsTCCDF(2.0, double.infinity).shouldApprox == 0.02275013; 320 studentsTCCDF(3.0, double.infinity).shouldApprox == 0.001349898; 321 } 322 323 // Checking negative location parameter 324 version(mir_stat_test) 325 @safe pure nothrow @nogc 326 unittest { 327 import mir.test: shouldApprox; 328 329 studentsTCCDF(-3.0, 5, -1, 2).shouldApprox == 0.8183913; 330 studentsTCCDF(-2.0, 5, -1, 2).shouldApprox == 0.6808506; 331 studentsTCCDF(-1.0, 5, -1, 2).shouldApprox == 0.5; 332 studentsTCCDF(0.0, 5, -1, 2).shouldApprox == 0.3191494; 333 studentsTCCDF(1.0, 5, -1, 2).shouldApprox == 0.1816087; 334 studentsTCCDF(2.0, 5, -1, 2).shouldApprox == 0.09695184; 335 studentsTCCDF(3.0, 5, -1, 2).shouldApprox == 0.05096974; 336 } 337 338 /++ 339 Computes the Student's t inverse cumulative distribution function (InvCDF). 340 341 Params: 342 p = value to evaluate InvCDF 343 nu = degrees of freedom 344 345 See_also: 346 $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution) 347 +/ 348 @safe pure nothrow @nogc 349 T studentsTInvCDF(T)(const T p, const T nu) 350 if (isFloatingPoint!T) 351 in (p >= 0, "p must be greater than or equal to 0") 352 in (p <= 1, "p must be less than or equal to 1") 353 in (nu > 0, "nu must be greater than zero") 354 { 355 import mir.math.common: sqrt; 356 import mir.stat.distribution.beta: betaInvCDF; 357 import mir.stat.distribution.normal: normalInvCDF; 358 359 if (p == 0) { 360 return -T.infinity; 361 } else if (p == 1) { 362 return T.infinity; 363 } else if (nu != T.infinity) { 364 byte output_sign = void; 365 T p_new = void; 366 T output = void; 367 if (p > 0.25 && p < 0.75) { 368 if (p == 0.5) { 369 return 0; 370 } 371 output_sign = -1; 372 p_new = 1 - 2 * p; 373 if (p > 0.5) { 374 output_sign = 1; 375 p_new *= -1; 376 } 377 output = betaInvCDF(p_new, 0.5, 0.5 * nu); 378 output = sqrt(nu * output / (1 - output)); 379 } else { 380 output_sign = -1; 381 p_new = p; 382 if (p_new > 0.5) { 383 output_sign = 1; 384 p_new = 1 - p_new; 385 } 386 p_new *= 2; 387 output = betaInvCDF(p_new, 0.5 * nu, 0.5); 388 output = sqrt(nu / output - nu); 389 } 390 return output_sign * output; 391 } else { 392 return normalInvCDF(p); 393 } 394 } 395 396 /++ 397 Ditto, with location and scale parameters (by standardizing `x`). 398 399 Params: 400 p = value to evaluate InvCDF 401 nu = degrees of freedom 402 mean = location parameter 403 stdDev = scale parameter 404 +/ 405 @safe pure nothrow @nogc 406 T studentsTInvCDF(T)(const T p, const T nu, const T mean, const T stdDev) 407 if (isFloatingPoint!T) 408 in (p >= 0, "p must be greater than or equal to 0") 409 in (p <= 1, "p must be less than or equal to 1") 410 in (nu > 0, "nu must be greater than zero") 411 in (stdDev > 0, "stdDev must be greater than zero") 412 { 413 return mean + stdDev * studentsTInvCDF(p, nu); 414 } 415 416 /// 417 version(mir_stat_test) 418 @safe pure nothrow @nogc 419 unittest { 420 import mir.test: shouldApprox; 421 422 studentsTInvCDF(0.0, 5).shouldApprox == -double.infinity; 423 studentsTInvCDF(0.1, 5).shouldApprox == -1.475884; 424 studentsTInvCDF(0.2, 5).shouldApprox == -0.9195438; 425 studentsTInvCDF(0.3, 5).shouldApprox == -0.5594296; 426 studentsTInvCDF(0.4, 5).shouldApprox == -0.2671809; 427 studentsTInvCDF(0.5, 5).shouldApprox == 0.0; 428 studentsTInvCDF(0.6, 5).shouldApprox == 0.2671809; 429 studentsTInvCDF(0.7, 5).shouldApprox == 0.5594296; 430 studentsTInvCDF(0.8, 5).shouldApprox == 0.9195438; 431 studentsTInvCDF(0.9, 5).shouldApprox == 1.475884; 432 studentsTInvCDF(1.0, 5).shouldApprox == double.infinity; 433 434 // Can include location/scale 435 studentsTInvCDF(0.2, 5, 1, 2).shouldApprox == -0.8390876; 436 studentsTInvCDF(0.4, 5, 1, 2).shouldApprox == 0.4656382; 437 studentsTInvCDF(0.6, 5, 1, 2).shouldApprox == 1.534362; 438 studentsTInvCDF(0.8, 5, 1, 2).shouldApprox == 2.839088; 439 } 440 441 // Checking other DoF 442 version(mir_stat_test) 443 @safe pure nothrow @nogc 444 unittest { 445 import mir.test: shouldApprox; 446 447 studentsTInvCDF(0.1, 25).shouldApprox == -1.316345; 448 studentsTInvCDF(0.2, 25).shouldApprox == -0.8562362; 449 studentsTInvCDF(0.3, 25).shouldApprox == -0.5311538; 450 studentsTInvCDF(0.7, 25).shouldApprox == 0.5311538; 451 studentsTInvCDF(0.8, 25).shouldApprox == 0.8562362; 452 studentsTInvCDF(0.9, 25).shouldApprox == 1.316345; 453 454 studentsTInvCDF(0.2, double.infinity).shouldApprox == -0.8416212; 455 studentsTInvCDF(0.4, double.infinity).shouldApprox == -0.2533471; 456 studentsTInvCDF(0.6, double.infinity).shouldApprox == 0.2533471; 457 studentsTInvCDF(0.8, double.infinity).shouldApprox == 0.8416212; 458 } 459 460 // Checking negative location parameter 461 version(mir_stat_test) 462 @safe pure nothrow @nogc 463 unittest { 464 import mir.test: shouldApprox; 465 466 studentsTInvCDF(0.2, 5, -1, 2).shouldApprox == -2.839088; 467 studentsTInvCDF(0.4, 5, -1, 2).shouldApprox == -1.534362; 468 studentsTInvCDF(0.6, 5, -1, 2).shouldApprox == -0.4656383; 469 studentsTInvCDF(0.8, 5, -1, 2).shouldApprox == 0.8390876; 470 } 471 472 473 /++ 474 Computes the Student's t log probability density function (LPDF). 475 476 Params: 477 x = value to evaluate LPDF 478 nu = degrees of freedom 479 480 See_also: 481 $(LINK2 https://en.wikipedia.org/wiki/Student%27s_t-distribution, Student's t Distribution) 482 +/ 483 @safe pure nothrow @nogc 484 T studentsTLPDF(T)(const T x, const T nu) 485 if (isFloatingPoint!T) 486 in (nu > 0, "nu must be greater than zero") 487 { 488 import mir.math.common: log; 489 import mir.math.internal.log1p: log1p; 490 import mir.stat.constant: LOGPI; 491 import mir.stat.distribution.normal: normalLPDF; 492 import std.mathspecial: logGamma; 493 494 if (nu != T.infinity) { 495 return logGamma((nu + 1) * 0.5) - logGamma(nu * 0.5) - 0.5 * (log(nu) + T(LOGPI) + (nu + 1) * log1p((x * x) / nu)); 496 } else { 497 return normalLPDF(x); 498 } 499 } 500 501 /++ 502 Ditto, with location and scale parameters (by standardizing `x`). 503 504 Params: 505 x = value to evaluate LPDF 506 nu = degrees of freedom 507 mean = location parameter 508 stdDev = scale parameter 509 +/ 510 @safe pure nothrow @nogc 511 T studentsTLPDF(T)(const T x, const T nu, const T mean, const T stdDev) 512 if (isFloatingPoint!T) 513 in (nu > 0, "nu must be greater than zero") 514 in (stdDev > 0, "stdDev must be greater than zero") 515 { 516 return studentsTLPDF((x - mean) / stdDev, nu); 517 } 518 519 /// 520 version(mir_stat_test) 521 @safe pure nothrow @nogc 522 unittest { 523 import mir.test: shouldApprox; 524 import mir.math.common: log; 525 526 studentsTLPDF(-3.0, 5).shouldApprox == log(0.01729258); 527 studentsTLPDF(-2.0, 5).shouldApprox == log(0.06509031); 528 studentsTLPDF(-1.0, 5).shouldApprox == log(0.2196798); 529 studentsTLPDF(0.0, 5).shouldApprox == log(0.3796067); 530 studentsTLPDF(1.0, 5).shouldApprox == log(0.2196798); 531 studentsTLPDF(2.0, 5).shouldApprox == log(0.06509031); 532 studentsTLPDF(3.0, 5).shouldApprox == log(0.01729258); 533 534 // Can include location/scale 535 studentsTLPDF(-3.0, 5, 1, 2).shouldApprox == log(0.06509031); 536 studentsTLPDF(-2.0, 5, 1, 2).shouldApprox == log(0.1245173); 537 studentsTLPDF(-1.0, 5, 1, 2).shouldApprox == log(0.2196798); 538 studentsTLPDF(0.0, 5, 1, 2).shouldApprox == log(0.3279185); 539 studentsTLPDF(1.0, 5, 1, 2).shouldApprox == log(0.3796067); 540 studentsTLPDF(2.0, 5, 1, 2).shouldApprox == log(0.3279185); 541 studentsTLPDF(3.0, 5, 1, 2).shouldApprox == log(0.2196798); 542 } 543 544 // Checking other DoF 545 version(mir_stat_test) 546 @safe pure nothrow @nogc 547 unittest { 548 import mir.test: shouldApprox; 549 import mir.math.common: log; 550 551 studentsTLPDF(-3.0, 25).shouldApprox == log(0.007253748); 552 studentsTLPDF(-2.0, 25).shouldApprox == log(0.0573607); 553 studentsTLPDF(-1.0, 25).shouldApprox == log(0.237211); 554 studentsTLPDF(0.0, 25).shouldApprox == log(0.3949738); 555 studentsTLPDF(1.0, 25).shouldApprox == log(0.237211); 556 studentsTLPDF(2.0, 25).shouldApprox == log(0.0573607); 557 studentsTLPDF(3.0, 25).shouldApprox == log(0.007253748); 558 559 studentsTLPDF(-3.0, double.infinity).shouldApprox == log(0.004431848); 560 studentsTLPDF(-2.0, double.infinity).shouldApprox == log(0.05399097); 561 studentsTLPDF(-1.0, double.infinity).shouldApprox == log(0.2419707); 562 studentsTLPDF(0.0, double.infinity).shouldApprox == log(0.3989423); 563 studentsTLPDF(1.0, double.infinity).shouldApprox == log(0.2419707); 564 studentsTLPDF(2.0, double.infinity).shouldApprox == log(0.05399097); 565 studentsTLPDF(3.0, double.infinity).shouldApprox == log(0.004431848); 566 } 567 568 // Checking negative location parameter 569 version(mir_stat_test) 570 @safe pure nothrow @nogc 571 unittest { 572 import mir.test: shouldApprox; 573 import mir.math.common: log; 574 575 studentsTLPDF(-3.0, 5, -1, 2).shouldApprox == log(0.2196798); 576 studentsTLPDF(-2.0, 5, -1, 2).shouldApprox == log(0.3279185); 577 studentsTLPDF(-1.0, 5, -1, 2).shouldApprox == log(0.3796067); 578 studentsTLPDF(0.0, 5, -1, 2).shouldApprox == log(0.3279185); 579 studentsTLPDF(1.0, 5, -1, 2).shouldApprox == log(0.2196798); 580 studentsTLPDF(2.0, 5, -1, 2).shouldApprox == log(0.1245173); 581 studentsTLPDF(3.0, 5, -1, 2).shouldApprox == log(0.06509031); 582 }