use super::{ObservationLocation, PosteriorDistribution};
use num_traits::Float;
impl<T> PosteriorDistribution<T> {
#[allow(dead_code)]
pub fn cumulative_mass(&self, x: T) -> T
where
T: Float,
{
let _first = self.knots[0];
let _last = self.knots[self.knots.len() - 1];
match self.locate(x) {
Ok(ObservationLocation::Boundary) => unreachable!("interior bounds already checked"),
Ok(ObservationLocation::ExistingKnot(i)) => self
.log_interval_mass
.iter()
.take(i)
.map(|lp| lp.exp())
.fold(T::zero(), |acc, p| acc + p),
Ok(ObservationLocation::Interior(i)) => {
let left_mass = self
.log_interval_mass
.iter()
.take(i)
.map(|lp| lp.exp())
.fold(T::zero(), |acc, p| acc + p);
let interval_mass = self.log_interval_mass[i].exp();
let left = self.knots[i];
let right = self.knots[i + 1];
let frac = (x - left) / (right - left);
left_mass + frac * interval_mass
}
Err(_) => {
if x < self.knots[0] {
T::zero()
} else {
T::one()
}
}
}
}
pub fn quantile(&self, p: T) -> T
where
T: Float,
{
debug_assert!(p >= T::zero() && p <= T::one());
if p <= T::zero() {
return self.knots[0];
}
if p >= T::one() {
return self.knots[self.knots.len() - 1];
}
let mut acc = T::zero();
for i in 0..self.log_interval_mass.len() {
let mass = self.log_interval_mass[i].exp();
let next = acc + mass;
if p <= next {
let local = (p - acc) / mass;
let left = self.knots[i];
let right = self.knots[i + 1];
return left + local * (right - left);
}
acc = next;
}
self.knots[self.knots.len() - 1]
}
pub fn median(&self) -> T
where
T: Float,
{
self.quantile(T::from(0.5).unwrap())
}
}