use dashu::{integer::UBig, rational::RBig};
use num::Zero;
use statrs::function::erf::erfc;
use crate::{
error::Fallible,
traits::{InfAdd, InfCast, InfDiv, InfExp, NextFloat},
};
#[cfg(all(test, feature = "contrib"))]
mod test;
pub fn conservative_continuous_laplacian_tail_to_alpha(scale: RBig, tail: RBig) -> Fallible<f64> {
check_tail_arguments(&scale, &tail)?;
f64::inf_cast(-tail / scale)?.inf_exp()?.inf_div(&2.0)
}
fn check_tail_arguments(scale: &RBig, tail: &RBig) -> Fallible<()> {
if scale <= &RBig::ZERO {
return fallible!(FailedFunction, "scale ({scale}) must be positive");
}
if tail < &RBig::ZERO {
return fallible!(FailedFunction, "tail ({tail}) must be nonnegative");
}
Ok(())
}
pub fn conservative_discrete_laplacian_tail_to_alpha(scale: RBig, tail: UBig) -> Fallible<f64> {
check_tail_arguments(&scale, &RBig::from(tail.clone()))?;
let numer = f64::inf_cast(-RBig::from(tail) / scale.clone())?.inf_exp()?;
let denom = f64::neg_inf_cast(RBig::ONE / scale)?
.neg_inf_exp()?
.neg_inf_add(&1.)?;
numer.inf_div(&denom)
}
pub fn conservative_discrete_gaussian_tail_to_alpha(scale: RBig, tail: UBig) -> Fallible<f64> {
conservative_continuous_gaussian_tail_to_alpha(scale, RBig::from(tail))
}
pub fn conservative_continuous_gaussian_tail_to_alpha(scale: RBig, tail: RBig) -> Fallible<f64> {
check_tail_arguments(&scale, &tail)?;
let sqrt_2_ceil: f64 = std::f64::consts::SQRT_2.next_up_();
let t = f64::neg_inf_cast(tail / scale)?.neg_inf_div(&sqrt_2_ceil)?;
let t = f32::neg_inf_cast(t)? as f64;
let t = f32::inf_cast(erfc(t))?.next_up_();
(t as f64).inf_div(&2.0)
}
pub(super) fn dg_pdf(x: i32, scale: f64) -> f64 {
(-(x as f64 / scale).powi(2) / 2.).exp()
}
pub(super) fn dg_normalization_term(scale: f64) -> f64 {
let mut i = 0;
let mut total = dg_pdf(i, scale);
loop {
i += 1;
let density_i = 2. * dg_pdf(i, scale);
if density_i.is_zero() {
return total;
}
total += density_i;
}
}