uncertain_numerics/
posterior.rs1use crate::PosteriorError;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct ScalarNormalPosterior {
10 mean: f64,
11 variance: f64,
12}
13
14impl ScalarNormalPosterior {
15 pub fn new(mean: f64, variance: f64) -> Result<Self, PosteriorError> {
22 if !mean.is_finite() {
23 return Err(PosteriorError::NonFiniteMean);
24 }
25 if !variance.is_finite() {
26 return Err(PosteriorError::NonFiniteVariance);
27 }
28 if variance < 0.0 {
29 return Err(PosteriorError::NegativeVariance);
30 }
31
32 Ok(Self { mean, variance })
33 }
34
35 #[must_use]
37 pub const fn mean(self) -> f64 {
38 self.mean
39 }
40
41 #[must_use]
43 pub const fn variance(self) -> f64 {
44 self.variance
45 }
46
47 #[must_use]
49 pub fn standard_deviation(self) -> f64 {
50 self.variance.sqrt()
51 }
52}
53
54#[cfg(test)]
55#[allow(clippy::float_cmp)] mod tests {
57 use super::*;
58
59 #[test]
60 fn constructs_valid_posterior() {
61 let posterior = ScalarNormalPosterior::new(1.25, 0.09).expect("valid posterior");
62
63 assert_eq!(posterior.mean(), 1.25);
64 assert_eq!(posterior.variance(), 0.09);
65 assert!((posterior.standard_deviation() - 0.3).abs() < 1.0e-12);
66 }
67
68 #[test]
69 fn accepts_zero_variance() {
70 let posterior = ScalarNormalPosterior::new(2.0, 0.0).expect("zero variance is valid");
71
72 assert_eq!(posterior.standard_deviation(), 0.0);
73 }
74
75 #[test]
76 fn rejects_negative_variance() {
77 assert_eq!(
78 ScalarNormalPosterior::new(0.0, -1.0),
79 Err(PosteriorError::NegativeVariance)
80 );
81 }
82
83 #[test]
84 fn rejects_non_finite_parameters() {
85 assert_eq!(
86 ScalarNormalPosterior::new(f64::NAN, 1.0),
87 Err(PosteriorError::NonFiniteMean)
88 );
89 assert_eq!(
90 ScalarNormalPosterior::new(0.0, f64::INFINITY),
91 Err(PosteriorError::NonFiniteVariance)
92 );
93 }
94}