use crate::{betainc, normcdf};
use std::f64::consts::PI;
pub fn tcdf(x: f64, v: f64, upper: bool) -> f64 {
if x.is_nan() || v.is_nan() || v <= 0.0 {
return f64::NAN;
}
let effective_x = if upper { -x } else { x };
if v == 1.0 {
return 0.5 + effective_x.atan() / PI;
}
const NORM_CUTOFF: f64 = 1e7;
if v > NORM_CUTOFF || v.is_infinite() {
return normcdf(effective_x, 0.0, 1.0, false);
}
if effective_x == 0.0 {
return 0.5;
}
let abs_x = effective_x.abs();
let xsq = abs_x * abs_x;
let p_low = if v < xsq {
betainc(v / (v + xsq), v / 2.0, 0.5, true) / 2.0
} else {
betainc(xsq / (v + xsq), 0.5, v / 2.0, false) / 2.0
};
if effective_x > 0.0 {
1.0 - p_low
} else {
p_low
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcdf_cauchy() {
let tol = 1e-15;
assert!((tcdf(1.0, 1.0, false) - 0.75).abs() < tol);
assert!((tcdf(-1.0, 1.0, false) - 0.25).abs() < tol);
assert!((tcdf(0.0, 1.0, false) - 0.5).abs() < tol);
}
#[test]
fn test_tcdf_symmetry_and_upper() {
let x = 1.5;
let v = 5.0;
let p_lower = tcdf(x, v, false);
let p_upper = tcdf(x, v, true);
assert!((p_lower + p_upper - 1.0).abs() < 1e-15);
assert_eq!(p_upper, tcdf(-x, v, false));
}
#[test]
fn test_tcdf_limits() {
assert_eq!(tcdf(f64::INFINITY, 10.0, false), 1.0);
assert_eq!(tcdf(f64::NEG_INFINITY, 10.0, false), 0.0);
assert!(tcdf(1.0, 0.0, false).is_nan());
assert!(tcdf(1.0, -1.0, false).is_nan());
}
#[test]
fn test_tcdf_large_v() {
let x = 1.96;
let t = tcdf(x, 1e8, false);
let n = normcdf(x, 0.0, 1.0, false);
assert!((t - n).abs() < 1e-12);
}
}