#![allow(clippy::cast_precision_loss)]
use crate::Uncertain;
use crate::traits::Shareable;
pub trait Comparison<T> {
#[must_use]
fn gt(&self, threshold: T) -> Uncertain<bool>;
#[must_use]
fn lt(&self, threshold: T) -> Uncertain<bool>;
#[must_use]
fn ge(&self, threshold: T) -> Uncertain<bool>;
#[must_use]
fn le(&self, threshold: T) -> Uncertain<bool>;
#[must_use]
fn eq(&self, threshold: T) -> Uncertain<bool>;
#[must_use]
fn ne(&self, threshold: T) -> Uncertain<bool>;
}
impl<T> Comparison<T> for Uncertain<T>
where
T: PartialOrd + PartialEq + Shareable,
{
fn gt(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() > threshold)
}
fn lt(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() < threshold)
}
fn ge(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() >= threshold)
}
fn le(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() <= threshold)
}
fn eq(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() == threshold)
}
fn ne(&self, threshold: T) -> Uncertain<bool> {
let sample_fn = self.sample_fn.clone();
Uncertain::new(move || sample_fn() != threshold)
}
}
impl<T> Uncertain<T>
where
T: PartialOrd + PartialEq + Shareable,
{
#[must_use]
pub fn gt_uncertain(&self, other: &Self) -> Uncertain<bool> {
let sample_fn1 = self.sample_fn.clone();
let sample_fn2 = other.sample_fn.clone();
Uncertain::new(move || sample_fn1() > sample_fn2())
}
#[must_use]
pub fn lt_uncertain(&self, other: &Self) -> Uncertain<bool> {
let sample_fn1 = self.sample_fn.clone();
let sample_fn2 = other.sample_fn.clone();
Uncertain::new(move || sample_fn1() < sample_fn2())
}
#[must_use]
pub fn eq_uncertain(&self, other: &Self) -> Uncertain<bool> {
let sample_fn1 = self.sample_fn.clone();
let sample_fn2 = other.sample_fn.clone();
Uncertain::new(move || sample_fn1() == sample_fn2())
}
}
impl Uncertain<f64> {
#[must_use]
pub fn approx_eq(&self, target: f64, tolerance: f64) -> Uncertain<bool> {
self.map(move |x| (x - target).abs() <= tolerance)
}
#[must_use]
pub fn within_range(&self, min: f64, max: f64) -> Uncertain<bool> {
self.map(move |x| x >= min && x <= max)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_comparison_returns_uncertain_bool() {
let value = Uncertain::point(5.0);
let evidence = Comparison::gt(&value, 3.0);
assert!(evidence.sample()); }
#[test]
fn test_comparison_with_uncertainty() {
let value = Uncertain::normal(5.0, 1.0);
let evidence = Comparison::gt(&value, 4.0);
let samples: Vec<bool> = evidence.take_samples(1000);
let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
assert!(true_ratio > 0.8); }
#[test]
fn test_approximate_equality() {
let measurement = Uncertain::normal(10.0, 0.1);
let close = measurement.approx_eq(10.0, 0.5);
let samples: Vec<bool> = close.take_samples(100);
let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
assert!(true_ratio > 0.95);
}
#[test]
fn test_within_range() {
let value = Uncertain::uniform(0.0, 10.0);
let in_range = value.within_range(2.0, 8.0);
let samples: Vec<bool> = in_range.take_samples(1000);
let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
assert!((true_ratio - 0.6).abs() < 0.1);
}
#[test]
fn test_uncertain_vs_uncertain_comparison() {
let x = Uncertain::normal(5.0, 1.0);
let y = Uncertain::normal(3.0, 1.0);
let evidence = x.gt_uncertain(&y);
let samples: Vec<bool> = evidence.take_samples(1000);
let true_ratio = samples.iter().filter(|&&x| x).count() as f64 / samples.len() as f64;
assert!(true_ratio > 0.8);
}
}