use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Natural64, Probability64, Real64};
use crate::{distribution::wilcox::cwilcox, func::choose, traits::DPQ};
pub fn qwilcox<P: Into<Probability64>, N1: Into<Natural64>, N2: Into<Natural64>>(
x: P,
m: N1,
n: N2,
lower_tail: bool,
) -> Real64 {
let x = x.into().unwrap();
qwilcox_inner(x, m, n, lower_tail, false)
}
pub fn log_qwilcox<LP: Into<LogProbability64>, N1: Into<Natural64>, N2: Into<Natural64>>(
x: LP,
m: N1,
n: N2,
lower_tail: bool,
) -> Real64 {
let x = x.into().unwrap();
qwilcox_inner(x, m, n, lower_tail, true)
}
fn qwilcox_inner<N1: Into<Natural64>, N2: Into<Natural64>>(
mut x: f64,
m: N1,
n: N2,
lower_tail: bool,
log: bool,
) -> Real64 {
let mut m = m.into().unwrap();
let mut n = n.into().unwrap();
let mut c = 0.0;
let mut p = 0.0;
if !x.is_finite() || !m.is_finite() || !n.is_finite() {
return f64::nan().into();
}
m = m.round();
n = n.round();
if x == f64::dt_0(lower_tail, log) {
return 0.0.into();
}
if x == f64::dt_1(lower_tail, log) {
return (m * n).into();
}
if log || !lower_tail {
x = x.dt_qiv(lower_tail, log)
}
let mm = m;
let nn = n;
c = choose(m + n, n).into();
p = 0.0;
let mut q = 0;
if x <= 0.5 {
x = x - 10.0 * 2.2204460492503131e-16;
loop {
p += cwilcox(q, mm as usize, nn as usize) / c;
if p >= x {
break;
}
q += 1
}
} else {
x = 1.0 - x + 10.0 * 2.2204460492503131e-16;
loop {
p += cwilcox(q, mm as usize, nn as usize) / c;
if p > x {
q = (m * n - q as f64) as i32;
break;
} else {
q += 1
}
}
}
q.into()
}