use super::{InverseCDF, ODPRound};
use dashu::{rational::RBig, rbig};
use opendp_derive::proven;
#[cfg(all(feature = "contrib", test))]
mod test;
#[derive(Clone)]
pub struct CanonicalRV<'a> {
pub shift: RBig,
pub scale: &'a RBig,
pub tradeoff: &'a dyn Fn(RBig) -> RBig,
pub fixed_point: &'a RBig,
}
#[proven(proof_path = "traits/samplers/psrn/canonical/InverseCDF_for_CanonicalRV.tex")]
impl<'a> InverseCDF for CanonicalRV<'a> {
type Edge = RBig;
fn inverse_cdf<R: ODPRound>(&self, uniform: RBig, _refinements: usize) -> Option<RBig> {
Some(quantile_cnd(uniform, self.tradeoff, self.fixed_point)? * self.scale + &self.shift)
}
}
fn quantile_cnd(uniform: RBig, f: &dyn Fn(RBig) -> RBig, c: &RBig) -> Option<RBig> {
Some(if &uniform < c {
quantile_cnd(rbig!(1) - f(uniform), f, c)? - rbig!(1)
} else if uniform <= rbig!(1) - c {
let num = &uniform - rbig!(1 / 2);
let den = rbig!(1) - rbig!(2) * c;
if den.is_zero() {
return None;
}
num / den
} else {
quantile_cnd(f(rbig!(1) - uniform), f, c)? + rbig!(1)
})
}