reference_interval/
reference_interval_with_sample.rs

1use num_traits::{Float, NumCast};
2use rand::Rng;
3use rand_distr::{Distribution, Normal, StandardNormal};
4use crate::reference_interval::ReferenceInterval;
5use crate::mean::mean;
6
7
8/// ReferenceIntervalWithSample decorates a ReferenceInterval with a random sample
9/// function, suitable for generating plausible ReferenceInterval normal values.
10pub struct ReferenceIntervalWithSample<T, F> 
11where
12    T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + Copy,
13    F: Float,
14    StandardNormal: Distribution<F>
15{    
16    // The reference interval, which may use an integer or float or real etc.
17    #[allow(dead_code)]
18    pub reference_interval: ReferenceInterval<T>,
19
20    // Memoize as (lower + upper) / 2 as a heuristic.
21    #[allow(dead_code)]
22    pub mean: F,
23
24    // Memoize as (mean - lower) / 2 as a heuristic because of standard interval of 95%.
25    #[allow(dead_code)]
26    pub standard_deviation: F,
27
28    // Memoize as (upper - lower) / 2 as a heuristic.
29    #[allow(dead_code)]
30    pub normal: Normal<F>,
31}
32
33impl<T, F> ReferenceIntervalWithSample<T, F>
34where
35    T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + Copy + Into<F>,
36    F: Float + NumCast,
37    StandardNormal: Distribution<F>
38{
39    /// The new function is a builder that calculates the resource interval
40    /// mean and standard deviation, then creates a normal distribution that is
41    /// ready for the random sample function.
42    #[allow(dead_code)]
43    pub fn new(reference_interval: ReferenceInterval<T>) -> Self {
44        let lower: F = reference_interval.lower.into();
45        let upper: F = reference_interval.upper.into();
46        let mean: F = mean(lower, upper);
47        let standard_deviation = (mean - lower) / F::from(2.0).unwrap();
48        let normal = Normal::new(mean, standard_deviation).expect("Normal::new");
49        Self { 
50            reference_interval: reference_interval,
51            mean: mean,
52            standard_deviation: standard_deviation,
53            normal: normal,
54        }
55    }
56}
57
58/// Implement the typical rand sample function.
59impl<T, F> Distribution<F> for ReferenceIntervalWithSample<T, F> 
60where
61    T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + Copy + Into<F>,
62    F: Float + NumCast,
63    StandardNormal: Distribution<F>
64{
65    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> F {
66        self.normal.sample(rng)
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    fn subject() -> ReferenceIntervalWithSample<i32, f64> {
75        let reference_interval = ReferenceInterval{
76            units: "units".to_string(),
77            lower: 11,
78            upper: 97,
79        };
80        ReferenceIntervalWithSample::new(reference_interval)
81    }
82
83    #[test]
84    fn test_new() {
85        let x = subject();
86        assert_eq!(x.mean as i32, 54);
87        assert_eq!(x.standard_deviation as i32, 21);
88    }
89
90    #[test]
91    fn test_sample() {
92        let subject = subject();
93        let mut rng = rand::rng();
94        let x: f64 = subject.normal.sample(&mut rng); 
95        assert!(x >= 0.0);
96    }
97
98}