use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Natural64, Probability64, Real64};
use crate::{distribution::signrank::csignrank, traits::DPQ};
pub fn qsignrank<P: Into<Probability64>, N: Into<Natural64>>(
x: P,
n: N,
lower_tail: bool,
) -> Real64 {
let x = x.into().unwrap();
qsignrank_inner(x, n, lower_tail, false)
}
pub fn log_qsignrank<LP: Into<LogProbability64>, N: Into<Natural64>>(
x: LP,
n: N,
lower_tail: bool,
) -> Real64 {
let x = x.into().unwrap();
qsignrank_inner(x, n, lower_tail, true)
}
fn qsignrank_inner<N: Into<Natural64>>(mut x: f64, n: N, lower_tail: bool, log: bool) -> Real64 {
let mut n = n.into().unwrap();
let mut f = 0.0;
let mut p = 0.0;
if !x.is_finite() || !n.is_finite() {
return f64::nan().into();
}
n = n.round();
if x == f64::dt_0(lower_tail, log) {
return 0.0.into();
}
if x == f64::dt_1(lower_tail, log) {
return (n * (n + 1.0) / 2.0).into();
}
if log || !lower_tail {
x = x.dt_qiv(lower_tail, log);
}
let nn = n as i32;
f = (-n * std::f64::consts::LN_2).exp();
p = 0.0;
let mut q = 0;
if x <= 0.5 {
x = x - 10.0 * 2.2204460492503131e-16;
loop {
p += csignrank(q, nn) * f;
if p >= x {
break;
}
q += 1
}
} else {
x = 1.0 - x + 10.0 * 2.2204460492503131e-16;
loop {
p += csignrank(q, nn) * f;
if p > x {
q = (n * (n + 1.0) / 2.0 - q as f64) as i32;
break;
} else {
q += 1
}
}
}
q.into()
}