use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Natural64, Probability64, Real64};
use crate::{distribution::signrank::csignrank, traits::DPQ};
pub fn psignrank<R: Into<Real64>, N: Into<Natural64>>(
x: R,
n: N,
lower_tail: bool,
) -> Probability64 {
psignrank_inner(x, n, lower_tail, false).into()
}
pub fn log_psignrank<R: Into<Real64>, N: Into<Natural64>>(
x: R,
n: N,
lower_tail: bool,
) -> LogProbability64 {
psignrank_inner(x, n, lower_tail, true).into()
}
fn psignrank_inner<R: Into<Real64>, N: Into<Natural64>>(
x: R,
n: N,
mut lower_tail: bool,
log: bool,
) -> f64 {
let mut x = x.into().unwrap();
let mut n = n.into().unwrap();
let mut i = 0;
let mut f = 0.0;
let mut p = 0.0;
if !n.is_finite() {
return f64::nan();
}
n = n.round();
x = (x + 1e-7).round();
if x < 0.0 {
return f64::dt_0(lower_tail, log);
}
if x >= n * (n + 1.0) / 2.0 {
return f64::dt_1(lower_tail, log);
}
let nn = n;
f = (-n * std::f64::consts::LN_2).exp();
p = 0.0;
if x <= n * (n + 1.0) / 4.0 {
i = 0;
while i as f64 <= x {
p += csignrank(i, nn as i32) * f;
i += 1
}
} else {
x = n * (n + 1.0) / 2.0 - x;
i = 0;
while (i as f64) < x {
p += csignrank(i, nn as i32) * f;
i += 1
}
lower_tail = !lower_tail
}
return p.dt_val(lower_tail, log);
}