use crate::errors::{QlError, QlResult};
use crate::fail;
use crate::types::Real;
pub mod binomial;
pub mod bivariatenormal;
pub mod bivariatestudentt;
pub mod chisquare;
pub mod gamma;
pub mod noncentralchisquare;
pub mod normal;
pub mod poisson;
pub mod studentt;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Probability(Real);
impl Probability {
pub fn value(self) -> Real {
self.0
}
}
impl TryFrom<Real> for Probability {
type Error = QlError;
fn try_from(value: Real) -> QlResult<Self> {
if !(0.0..=1.0).contains(&value) {
fail!("invalid probability: {value}");
}
Ok(Self(value))
}
}
pub trait Density {
fn pdf(&self, x: Real) -> Real;
fn ln_pdf(&self, x: Real) -> Real {
self.pdf(x).ln()
}
}
pub trait Cdf {
fn cdf(&self, x: Real) -> Real;
fn survival(&self, x: Real) -> Real {
1.0 - self.cdf(x)
}
}
pub trait Quantile {
fn quantile(&self, p: Probability) -> QlResult<Real>;
}
pub trait Support {
fn lower_bound(&self) -> Real;
fn upper_bound(&self) -> Real;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probability_accepts_unit_interval() {
for p in [0.0, 0.25, 0.5, 1.0] {
assert_eq!(Probability::try_from(p).unwrap().value(), p);
}
}
#[test]
fn probability_rejects_out_of_range_and_nonfinite() {
for p in [-0.1, 1.1, Real::NAN, Real::INFINITY, Real::NEG_INFINITY] {
assert!(Probability::try_from(p).is_err(), "should reject {p}");
}
}
struct Unit;
impl Density for Unit {
fn pdf(&self, _x: Real) -> Real {
0.5
}
}
impl Cdf for Unit {
fn cdf(&self, x: Real) -> Real {
x
}
}
#[test]
fn ln_pdf_default_is_ln_of_pdf() {
assert_eq!(Unit.ln_pdf(0.0), 0.5_f64.ln());
}
#[test]
fn survival_default_is_complement() {
assert_eq!(Unit.survival(0.3), 0.7);
}
}