use std::fmt::Debug;
pub struct GaloisConnection<A, B> {
lower: Box<dyn Fn(&A) -> B>,
upper: Box<dyn Fn(&B) -> A>,
}
impl<A: 'static + Clone + Debug + PartialOrd, B: 'static + Clone + Debug + PartialOrd>
GaloisConnection<A, B>
{
pub fn new(lower: impl Fn(&A) -> B + 'static, upper: impl Fn(&B) -> A + 'static) -> Self {
Self {
lower: Box::new(lower),
upper: Box::new(upper),
}
}
pub fn lower(&self, a: &A) -> B {
(self.lower)(a)
}
pub fn upper(&self, b: &B) -> A {
(self.upper)(b)
}
pub fn check_unit(&self, a: &A) -> bool {
*a <= (self.upper)(&(self.lower)(a))
}
pub fn check_counit(&self, b: &B) -> bool {
(self.lower)(&(self.upper)(b)) <= *b
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn galois_floor_ceil() {
let gc = GaloisConnection::new(
|x: &f64| *x as i64, |n: &i64| *n as f64, );
assert_eq!(gc.lower(&3.7), 3);
assert_eq!(gc.upper(&3), 3.0);
let gc2 = GaloisConnection::new(
|n: &i64| *n as f64, |x: &f64| *x as i64, );
assert!(gc2.check_unit(&3)); assert!(gc2.check_counit(&3.7)); }
#[test]
fn galois_taxonomy_abstraction() {
let gc = GaloisConnection::new(
|level: &i32| level + 1, |level: &i32| level - 1, );
assert!(gc.check_unit(&0));
assert!(gc.check_unit(&5));
assert!(gc.check_counit(&1));
assert!(gc.check_counit(&5));
}
mod prop {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_galois_unit(n in -1000..1000i64) {
let gc = GaloisConnection::new(
|n: &i64| *n as f64,
|x: &f64| *x as i64,
);
prop_assert!(gc.check_unit(&n));
}
#[test]
fn prop_galois_counit(x in 0.0..1000.0f64) {
let gc = GaloisConnection::new(
|n: &i64| *n as f64,
|x: &f64| *x as i64,
);
prop_assert!(gc.check_counit(&x));
}
}
}
}