use std::collections::HashSet;
use std::hash::Hash;
pub trait Lattice: Clone + Send + Sync {
fn join(&self, other: &Self) -> Self;
fn meet(&self, other: &Self) -> Self;
}
macro_rules! impl_lattice_for_numeric {
($($t:ty),+) => {
$(
impl Lattice for $t {
#[inline]
fn join(&self, other: &Self) -> Self {
(*self).max(*other)
}
#[inline]
fn meet(&self, other: &Self) -> Self {
(*self).min(*other)
}
}
)+
};
}
impl_lattice_for_numeric!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl Lattice for f32 {
#[inline]
fn join(&self, other: &Self) -> Self {
self.max(*other)
}
#[inline]
fn meet(&self, other: &Self) -> Self {
self.min(*other)
}
}
impl Lattice for f64 {
#[inline]
fn join(&self, other: &Self) -> Self {
self.max(*other)
}
#[inline]
fn meet(&self, other: &Self) -> Self {
self.min(*other)
}
}
impl Lattice for bool {
#[inline]
fn join(&self, other: &Self) -> Self {
*self || *other
}
#[inline]
fn meet(&self, other: &Self) -> Self {
*self && *other
}
}
impl<T: Lattice> Lattice for Option<T> {
#[inline]
fn join(&self, other: &Self) -> Self {
match (self, other) {
(Some(a), Some(b)) => Some(a.join(b)),
(Some(a), None) => Some(a.clone()),
(None, Some(b)) => Some(b.clone()),
(None, None) => None,
}
}
#[inline]
fn meet(&self, other: &Self) -> Self {
match (self, other) {
(Some(a), Some(b)) => Some(a.meet(b)),
_ => None,
}
}
}
impl<T: Clone + Eq + Hash + Send + Sync> Lattice for HashSet<T> {
fn join(&self, other: &Self) -> Self {
self.union(other).cloned().collect()
}
fn meet(&self, other: &Self) -> Self {
self.intersection(other).cloned().collect()
}
}
impl<T: Clone + Eq + Send + Sync> Lattice for Vec<T> {
fn join(&self, other: &Self) -> Self {
let mut result = self.clone();
for item in other {
if !result.contains(item) {
result.push(item.clone());
}
}
result
}
fn meet(&self, other: &Self) -> Self {
self.iter()
.filter(|item| other.contains(item))
.cloned()
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn numeric_u32() {
assert_eq!(5u32.join(&3), 5);
assert_eq!(3u32.join(&5), 5);
assert_eq!(5u32.meet(&3), 3);
assert_eq!(3u32.meet(&5), 3);
assert_eq!(5u32.join(&5), 5);
assert_eq!(5u32.meet(&5), 5);
}
#[test]
fn numeric_i32() {
assert_eq!((-5i32).join(&3), 3);
assert_eq!((-5i32).meet(&3), -5);
}
#[test]
fn numeric_f64() {
assert_eq!(5.0f64.join(&3.0), 5.0);
assert_eq!(5.0f64.meet(&3.0), 3.0);
}
#[test]
fn boolean() {
assert!(true.join(&false));
assert!(!false.join(&false));
assert!(true.meet(&true));
assert!(!true.meet(&false));
}
#[test]
fn option() {
let some_5 = Some(5u32);
let some_3 = Some(3u32);
let none: Option<u32> = None;
assert_eq!(some_5.join(&some_3), Some(5)); assert_eq!(some_5.join(&none), Some(5));
assert_eq!(none.join(&some_3), Some(3));
assert_eq!(none.join(&none), None);
assert_eq!(some_5.meet(&some_3), Some(3)); assert_eq!(some_5.meet(&none), None);
assert_eq!(none.meet(&none), None);
}
#[test]
fn hashset() {
let set1: HashSet<i32> = [1, 2, 3].into_iter().collect();
let set2: HashSet<i32> = [2, 3, 4].into_iter().collect();
assert_eq!(set1.join(&set2), [1, 2, 3, 4].into_iter().collect());
assert_eq!(set1.meet(&set2), [2, 3].into_iter().collect());
}
#[test]
fn hashset_disjoint() {
let set1: HashSet<i32> = [1, 2].into_iter().collect();
let set2: HashSet<i32> = [3, 4].into_iter().collect();
assert_eq!(set1.join(&set2), [1, 2, 3, 4].into_iter().collect());
assert!(set1.meet(&set2).is_empty());
}
#[test]
fn vec() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![2, 3, 4];
assert_eq!(vec1.join(&vec2), vec![1, 2, 3, 4]);
assert_eq!(vec1.meet(&vec2), vec![2, 3]);
}
#[test]
fn vec_preserves_order() {
let vec1 = vec![3, 1, 2];
let vec2 = vec![4, 2, 1];
assert_eq!(vec1.join(&vec2), vec![3, 1, 2, 4]);
assert_eq!(vec1.meet(&vec2), vec![1, 2]);
}
}