uncertain_rs/operations/
comparison.rs1#![allow(clippy::cast_precision_loss)]
2
3use crate::Uncertain;
4use crate::traits::Shareable;
5
6pub trait Comparison<T> {
15 #[must_use]
17 fn gt(&self, threshold: T) -> Uncertain<bool>;
18
19 #[must_use]
21 fn lt(&self, threshold: T) -> Uncertain<bool>;
22
23 #[must_use]
25 fn ge(&self, threshold: T) -> Uncertain<bool>;
26
27 #[must_use]
29 fn le(&self, threshold: T) -> Uncertain<bool>;
30
31 #[must_use]
33 fn eq(&self, threshold: T) -> Uncertain<bool>;
34
35 #[must_use]
37 fn ne(&self, threshold: T) -> Uncertain<bool>;
38}
39
40impl<T> Comparison<T> for Uncertain<T>
41where
42 T: PartialOrd + PartialEq + Shareable,
43{
44 fn gt(&self, threshold: T) -> Uncertain<bool> {
58 let sample_fn = self.sample_fn.clone();
59 Uncertain::new(move || sample_fn() > threshold)
60 }
61
62 fn lt(&self, threshold: T) -> Uncertain<bool> {
76 let sample_fn = self.sample_fn.clone();
77 Uncertain::new(move || sample_fn() < threshold)
78 }
79
80 fn ge(&self, threshold: T) -> Uncertain<bool> {
82 let sample_fn = self.sample_fn.clone();
83 Uncertain::new(move || sample_fn() >= threshold)
84 }
85
86 fn le(&self, threshold: T) -> Uncertain<bool> {
88 let sample_fn = self.sample_fn.clone();
89 Uncertain::new(move || sample_fn() <= threshold)
90 }
91
92 fn eq(&self, threshold: T) -> Uncertain<bool> {
97 let sample_fn = self.sample_fn.clone();
98 Uncertain::new(move || sample_fn() == threshold)
99 }
100
101 fn ne(&self, threshold: T) -> Uncertain<bool> {
103 let sample_fn = self.sample_fn.clone();
104 Uncertain::new(move || sample_fn() != threshold)
105 }
106}
107
108impl<T> Uncertain<T>
110where
111 T: PartialOrd + PartialEq + Shareable,
112{
113 #[must_use]
124 pub fn gt_uncertain(&self, other: &Self) -> Uncertain<bool> {
125 let sample_fn1 = self.sample_fn.clone();
126 let sample_fn2 = other.sample_fn.clone();
127 Uncertain::new(move || sample_fn1() > sample_fn2())
128 }
129
130 #[must_use]
132 pub fn lt_uncertain(&self, other: &Self) -> Uncertain<bool> {
133 let sample_fn1 = self.sample_fn.clone();
134 let sample_fn2 = other.sample_fn.clone();
135 Uncertain::new(move || sample_fn1() < sample_fn2())
136 }
137
138 #[must_use]
140 pub fn eq_uncertain(&self, other: &Self) -> Uncertain<bool> {
141 let sample_fn1 = self.sample_fn.clone();
142 let sample_fn2 = other.sample_fn.clone();
143 Uncertain::new(move || sample_fn1() == sample_fn2())
144 }
145}
146
147impl Uncertain<f64> {
149 #[must_use]
162 pub fn approx_eq(&self, target: f64, tolerance: f64) -> Uncertain<bool> {
163 self.map(move |x| (x - target).abs() <= tolerance)
164 }
165
166 #[must_use]
176 pub fn within_range(&self, min: f64, max: f64) -> Uncertain<bool> {
177 self.map(move |x| x >= min && x <= max)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_comparison_returns_uncertain_bool() {
187 let value = Uncertain::point(5.0);
188 let evidence = Comparison::gt(&value, 3.0);
189
190 assert!(evidence.sample()); }
193
194 #[test]
195 fn test_comparison_with_uncertainty() {
196 let value = Uncertain::normal(5.0, 1.0);
197 let evidence = Comparison::gt(&value, 4.0);
198
199 let samples: Vec<bool> = evidence.take_samples(1000);
201 let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
202 assert!(true_ratio > 0.8); }
204
205 #[test]
206 fn test_approximate_equality() {
207 let measurement = Uncertain::normal(10.0, 0.1);
208 let close = measurement.approx_eq(10.0, 0.5);
209
210 let samples: Vec<bool> = close.take_samples(100);
212 let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
213 assert!(true_ratio > 0.95);
214 }
215
216 #[test]
217 fn test_within_range() {
218 let value = Uncertain::uniform(0.0, 10.0);
219 let in_range = value.within_range(2.0, 8.0);
220
221 let samples: Vec<bool> = in_range.take_samples(1000);
223 let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
224 assert!((true_ratio - 0.6).abs() < 0.1);
225 }
226
227 #[test]
228 fn test_uncertain_vs_uncertain_comparison() {
229 let x = Uncertain::normal(5.0, 1.0);
230 let y = Uncertain::normal(3.0, 1.0);
231 let evidence = x.gt_uncertain(&y);
232
233 let samples: Vec<bool> = evidence.take_samples(1000);
235 let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
236 assert!(true_ratio > 0.8);
237 }
238}