1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::cmp::{Ordering, PartialOrd};
#[cfg(feature = "decimal")]
use decimal::d128;

/// A set where every two elements have an infimum (i.e. greatest lower bound).
pub trait MeetSemilattice: Sized {
    /// Returns the meet (aka. infimum) of two values.
    fn meet(&self, other: &Self) -> Self;
}

/// A set where every two elements have a suppremum (i.e. smallest upper bound).
pub trait JoinSemilattice: Sized {
    /// Returns the join (aka. supremum) of two values.
    fn join(&self, other: &Self) -> Self;
}

/// Partially orderable sets where every two elements have a suppremum and infimum.
pub trait Lattice: MeetSemilattice + JoinSemilattice + PartialOrd {
    /// Returns the infimum and the supremum simultaneously.
    #[inline]
    fn meet_join(&self, other: &Self) -> (Self, Self) {
        (self.meet(other), self.join(other))
    }

    /// Return the minimum of `self` and `other` if they are comparable.
    #[inline]
    fn partial_min<'a>(&'a self, other: &'a Self) -> Option<&'a Self> {
        if let Some(ord) = self.partial_cmp(other) {
            match ord {
                Ordering::Greater => Some(other),
                _ => Some(self),
            }
        } else {
            None
        }
    }

    /// Return the maximum of `self` and `other` if they are comparable.
    #[inline]
    fn partial_max<'a>(&'a self, other: &'a Self) -> Option<&'a Self> {
        if let Some(ord) = self.partial_cmp(other) {
            match ord {
                Ordering::Less => Some(other),
                _ => Some(self),
            }
        } else {
            None
        }
    }

    /// Sorts two values in increasing order using a partial ordering.
    #[inline]
    fn partial_sort2<'a>(&'a self, other: &'a Self) -> Option<(&'a Self, &'a Self)> {
        if let Some(ord) = self.partial_cmp(other) {
            match ord {
                Ordering::Less => Some((self, other)),
                _ => Some((other, self)),
            }
        } else {
            None
        }
    }

    /// Clamp `value` between `min` and `max`. Returns `None` if `value` is not comparable to
    /// `min` or `max`.
    #[inline]
    fn partial_clamp<'a>(&'a self, min: &'a Self, max: &'a Self) -> Option<&'a Self> {
        if let (Some(cmp_min), Some(cmp_max)) = (self.partial_cmp(min), self.partial_cmp(max)) {
            if cmp_min == Ordering::Less {
                Some(min)
            } else if cmp_max == Ordering::Greater {
                Some(max)
            } else {
                Some(self)
            }
        } else {
            None
        }
    }
}

macro_rules! impl_lattice(
    ($($T:ident),*) => {$(
        impl MeetSemilattice for $T {
            #[inline]
            fn meet(&self, other: &Self) -> Self {
                if *self <= *other {
                    *self
                }
                else {
                    *other
                }
            }
        }

        impl JoinSemilattice for $T {
            #[inline]
            fn join(&self, other: &Self) -> Self {
                if *self >= *other {
                    *self
                }
                else {
                    *other
                }
            }
        }

        impl Lattice for $T {
            #[inline]
            fn meet_join(&self, other: &Self) -> (Self, Self) {
                if *self >= *other {
                    (*other, *self)
                }
                else {
                    (*self, *other)
                }
            }
        }
    )*}
);

impl_lattice!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64);
#[cfg(feature = "decimal")]
impl_lattice!(d128);