use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Natural64, Probability64, Real64};
use crate::{distribution::wilcox::cwilcox, func::choose, traits::DPQ};
pub fn pwilcox<R: Into<Real64>, N1: Into<Natural64>, N2: Into<Natural64>>(
q: R,
m: N1,
n: N2,
lower_tail: bool,
) -> Probability64 {
pwilcox_inner(q, m, n, lower_tail, false).into()
}
pub fn log_pwilcox<R: Into<Real64>, N1: Into<Natural64>, N2: Into<Natural64>>(
q: R,
m: N1,
n: N2,
lower_tail: bool,
) -> LogProbability64 {
pwilcox_inner(q, m, n, lower_tail, true).into()
}
pub fn pwilcox_inner<R: Into<Real64>, N1: Into<Natural64>, N2: Into<Natural64>>(
q: R,
m: N1,
n: N2,
mut lower_tail: bool,
log: bool,
) -> f64 {
let mut q = q.into().unwrap();
let mut m = m.into().unwrap();
let mut n = n.into().unwrap();
let mut i = 0;
let mut c = 0.0;
let mut p = 0.0;
if !m.is_finite() || !n.is_finite() {
return f64::nan();
}
m = m.round();
n = n.round();
q = (q + 1e-7).floor();
if q < 0.0 {
return f64::dt_0(lower_tail, log);
}
if q >= m * n {
return f64::dt_1(lower_tail, log);
}
let mm = m;
let nn = n;
c = choose(m + n, n).unwrap();
p = 0.0;
if q <= m * n / 2.0 {
i = 0;
while i <= q as i32 {
p += cwilcox(i, mm as usize, nn as usize) / c;
i += 1
}
} else {
q = m * n - q;
i = 0;
while (i as f64) < q {
p += cwilcox(i, mm as usize, nn as usize) / c;
i += 1
}
lower_tail = !lower_tail
}
p.dt_val(lower_tail, log)
}