pub const CL_1_SIGMA: f64 = 68.268_949_213_708_58;
pub const CL_2_SIGMA: f64 = 95.449_973_610_364_2;
pub const CL_3_SIGMA: f64 = 99.730_020_393_673_97;
pub const CL_90: f64 = 90.0;
pub const CL_95: f64 = 95.0;
fn map_prob(p: f64) -> f64 {
let c = [2.515_517_f64, 0.802_853, 0.010_328];
let d = [1.432_788_f64, 0.189_269, 0.001_308];
let (sign, q) = if p >= 0.5 { (1.0, 1.0 - p) } else { (-1.0, p) };
let t = (-2.0 * q.ln()).sqrt();
let num = c[0] + c[1] * t + c[2] * t * t;
let den = 1.0 + d[0] * t + d[1] * t * t + d[2] * t * t * t;
sign * (t - num / den)
}
fn cl_to_sigma(cl: f64) -> f64 {
map_prob(0.5 * (1.0 + cl / 100.0))
}
#[derive(Clone, Debug)]
pub struct Uncertainty {
pub central: f64,
pub errminus: f64,
pub errplus: f64,
}
pub fn uncertainty(
values: &[f64],
error_type: &str,
error_conf_level: f64,
cl: f64,
alternative: bool,
) -> Result<Uncertainty, String> {
if values.is_empty() {
return Err("values slice must not be empty".to_string());
}
let err_type = error_type.to_lowercase();
let native_cl = if error_conf_level > 0.0 {
error_conf_level
} else {
CL_1_SIGMA
};
let cl_scale = cl_to_sigma(cl) / cl_to_sigma(native_cl);
let (central, errminus, errplus) = if err_type.contains("replicas")
|| err_type.contains("monte carlo")
|| err_type.contains("mc_stat")
{
let n_err = (values.len() - 1) as f64;
let mean: f64 = values.iter().skip(1).sum::<f64>() / n_err;
if alternative {
let mut sorted: Vec<f64> = values[1..].to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let n = sorted.len();
let p_lo = (1.0 - cl / 100.0) / 2.0;
let p_hi = (1.0 + cl / 100.0) / 2.0;
let i_lo = (p_lo * n as f64).floor() as usize;
let i_hi = ((p_hi * n as f64).ceil() as usize)
.saturating_sub(1)
.min(n - 1);
let lo = sorted[i_lo];
let hi = sorted[i_hi];
(mean, (mean - lo).abs(), (hi - mean).abs())
} else {
let variance: f64 = values
.iter()
.skip(1)
.map(|&v| (v - mean).powi(2))
.sum::<f64>()
/ (n_err - 1.0);
let err = variance.sqrt() * cl_scale;
(mean, err, err)
}
} else if err_type.contains("asymhessian")
|| (err_type.contains("hessian") && !err_type.contains("symm"))
{
let c = values[0];
let mut sum_plus_sq = 0.0;
let mut sum_minus_sq = 0.0;
for pair in values[1..].chunks(2) {
if pair.len() == 2 {
let (t_plus, t_minus) = (pair[0], pair[1]);
sum_plus_sq +=
f64::max(0.0, t_plus - c).powi(2) + f64::max(0.0, t_minus - c).powi(2);
sum_minus_sq +=
f64::max(0.0, c - t_plus).powi(2) + f64::max(0.0, c - t_minus).powi(2);
}
}
(
c,
sum_minus_sq.sqrt() * cl_scale,
sum_plus_sq.sqrt() * cl_scale,
)
} else {
let c = values[0];
let mut sum_sq = 0.0;
for pair in values[1..].chunks(2) {
if pair.len() == 2 {
let diff = (pair[0] - pair[1]).abs() / 2.0;
sum_sq += diff.powi(2);
}
}
let err = sum_sq.sqrt() * cl_scale;
(c, err, err)
};
Ok(Uncertainty {
central,
errminus,
errplus,
})
}