use r2rs_base::func::{order, pmin};
use crate::traits::StatArray;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PAdjustMethod {
FDR,
Bonferroni,
Hommel,
Hochberg,
BH,
BY,
}
pub fn p_adjust(p: &[f64], mut method: Option<PAdjustMethod>) -> Vec<f64> {
if p.len() <= 1 {
p.to_vec()
} else {
if p.len() == 2 && method == Some(PAdjustMethod::Hommel) {
method = Some(PAdjustMethod::Hochberg);
}
match method {
None => p.to_vec(),
Some(PAdjustMethod::FDR) | Some(PAdjustMethod::BH) => pmin(
&[1.0],
&p.iter()
.map(|&p_i| p.len() as f64 * p_i)
.collect::<Vec<_>>(),
),
Some(PAdjustMethod::Bonferroni) => {
let o = order(p);
let ro = order(&o);
let intermediate = o
.into_iter()
.enumerate()
.map(|(i, o)| (p.len() + 1 - i) as f64 * p[o])
.collect::<Vec<_>>()
.cummax();
let mins = pmin(&[1.0], &intermediate);
ro.into_iter().map(|ro| mins[ro]).collect()
}
Some(PAdjustMethod::Hommel) => {
let o = order(p);
let p = o.iter().map(|&o| p[o]).collect::<Vec<_>>();
let ro = order(&o);
let mut q = vec![
p.iter()
.enumerate()
.map(|(i, &p_i)| p.len() as f64 * p_i / i as f64)
.min_by(|f1, f2| f1.partial_cmp(f2).unwrap())
.unwrap();
p.len()
];
let mut pa = q.clone();
for j in (2..(p.len() - 1)).rev() {
let ij = (0..(p.len() - j + 1)).collect::<Vec<_>>();
let i2 = ((p.len() - j + 2)..p.len()).collect::<Vec<_>>();
let q1 = i2
.iter()
.zip(2..j)
.map(|(&i2, _2j)| j as f64 * p[i2] / _2j as f64)
.min_by(|f1, f2| f1.partial_cmp(f2).unwrap())
.unwrap();
for ij in ij {
q[ij] = q1.min(j as f64 * p[ij]);
}
for i2 in i2 {
q[i2] = q[p.len() - j - 1];
}
for i in 0..pa.len() {
pa[i] = pa[i].max(q[i]);
}
}
let mut ret = Vec::new();
for i in 0..p.len() {
let j = ro[i];
ret.push(pa[j].max(p[j]));
}
ret
}
Some(PAdjustMethod::Hochberg) => {
let o = order(p).into_iter().rev().collect::<Vec<_>>();
let ro = order(&o);
let intermediate = o
.into_iter()
.enumerate()
.map(|(i, o)| (p.len() + 1 - i) as f64 * p[o])
.collect::<Vec<_>>()
.cummin();
let mins = pmin(&[1.0], &intermediate);
ro.into_iter().map(|ro| mins[ro]).collect()
}
Some(PAdjustMethod::BY) => {
let o = order(p).into_iter().rev().collect::<Vec<_>>();
let ro = order(&o);
let q = (0..p.len()).map(|n| 1.0 / n as f64).sum::<f64>();
let intermediate = o
.into_iter()
.enumerate()
.map(|(i, o)| q * (p.len() as f64 / i as f64) * p[o])
.collect::<Vec<_>>()
.cummin();
let mins = pmin(&[1.0], &intermediate);
ro.into_iter().map(|ro| mins[ro]).collect()
}
}
}
}