Skip to main content

acls_rs/algebra/
semilattice.rs

1//! Semilattice and lattice algebraic structures.
2//!
3//! Semilattices extend monoids with meet (∧) and join (∨) operations that form
4//! partial orders. Lattices combine both meet and join semilattices.
5
6use super::Monoid;
7
8/// A meet-semilattice: a partially ordered set with a greatest lower bound operation.
9///
10/// # Algebraic Laws
11///
12/// For a type to implement `MeetSemilattice`, it must satisfy:
13///
14/// - **Associativity**: `(a ∧ b) ∧ c = a ∧ (b ∧ c)`
15/// - **Commutativity**: `a ∧ b = b ∧ a`
16/// - **Idempotence**: `a ∧ a = a`
17/// - **Partial Order**: `a ≤ b` iff `a ∧ b = a`
18///
19/// # Examples
20///
21/// ```
22/// use acls_rs::algebra::MeetSemilattice;
23/// use acls_rs::permission::{AtomicPermission, PermissionSet};
24///
25/// let perms1 = PermissionSet::from([
26///     AtomicPermission::new("file", "read"),
27///     AtomicPermission::new("file", "write"),
28/// ]);
29/// let perms2 = PermissionSet::from([
30///     AtomicPermission::new("file", "read"),
31/// ]);
32///
33/// // Meet is intersection (most restrictive)
34/// let intersection = perms1.meet(perms2);
35/// assert_eq!(intersection.len(), 1);
36/// ```
37pub trait MeetSemilattice: Monoid + PartialOrd {
38    /// Compute the greatest lower bound (meet) of two elements.
39    ///
40    /// The meet operation `a ∧ b` returns the greatest element that is less than
41    /// or equal to both `a` and `b`.
42    ///
43    /// For permission sets, meet is intersection (most restrictive combination).
44    ///
45    /// # Laws
46    ///
47    /// - **Associativity**: `(a ∧ b) ∧ c = a ∧ (b ∧ c)`
48    /// - **Commutativity**: `a ∧ b = b ∧ a`
49    /// - **Idempotence**: `a ∧ a = a`
50    fn meet(self, other: Self) -> Self;
51}
52
53/// A join-semilattice: a partially ordered set with a least upper bound operation.
54///
55/// # Algebraic Laws
56///
57/// For a type to implement `JoinSemilattice`, it must satisfy:
58///
59/// - **Associativity**: `(a ∨ b) ∨ c = a ∨ (b ∨ c)`
60/// - **Commutativity**: `a ∨ b = b ∨ a`
61/// - **Idempotence**: `a ∨ a = a`
62/// - **Partial Order**: `a ≤ b` iff `a ∨ b = b`
63///
64/// # Examples
65///
66/// ```
67/// use acls_rs::algebra::JoinSemilattice;
68/// use acls_rs::permission::{AtomicPermission, PermissionSet};
69///
70/// let perms1 = PermissionSet::from([
71///     AtomicPermission::new("file", "read"),
72/// ]);
73/// let perms2 = PermissionSet::from([
74///     AtomicPermission::new("file", "write"),
75/// ]);
76///
77/// // Join is union (least restrictive)
78/// let union = perms1.join(perms2);
79/// assert_eq!(union.len(), 2);
80/// ```
81pub trait JoinSemilattice: Monoid + PartialOrd {
82    /// Compute the least upper bound (join) of two elements.
83    ///
84    /// The join operation `a ∨ b` returns the smallest element that is greater than
85    /// or equal to both `a` and `b`.
86    ///
87    /// For permission sets, join is union (least restrictive combination).
88    ///
89    /// # Laws
90    ///
91    /// - **Associativity**: `(a ∨ b) ∨ c = a ∨ (b ∨ c)`
92    /// - **Commutativity**: `a ∨ b = b ∨ a`
93    /// - **Idempotence**: `a ∨ a = a`
94    fn join(self, other: Self) -> Self;
95}
96
97/// A bounded meet-semilattice with a top element.
98///
99/// The top element `⊤` is the greatest element in the partial order:
100/// `a ∧ ⊤ = a` for all `a`.
101pub trait BoundedMeetSemilattice: MeetSemilattice {
102    /// Return the top element (greatest element).
103    ///
104    /// The top element must satisfy: `a.meet(Self::top()) == a` for all `a`.
105    fn top() -> Self;
106}
107
108/// A bounded join-semilattice with a bottom element.
109///
110/// The bottom element `⊥` is the least element in the partial order:
111/// `a ∨ ⊥ = a` for all `a`.
112pub trait BoundedJoinSemilattice: JoinSemilattice {
113    /// Return the bottom element (least element).
114    ///
115    /// The bottom element must satisfy: `a.join(Self::bottom()) == a` for all `a`.
116    fn bottom() -> Self;
117}
118
119/// A lattice: a partially ordered set with both meet and join operations.
120///
121/// # Algebraic Laws
122///
123/// In addition to meet and join semilattice laws, a lattice must satisfy:
124///
125/// - **Absorption**: `a ∧ (a ∨ b) = a` and `a ∨ (a ∧ b) = a`
126///
127/// # Examples
128///
129/// ```
130/// use acls_rs::algebra::Lattice;
131/// use acls_rs::permission::{AtomicPermission, PermissionSet};
132///
133/// // PermissionSet is a Lattice
134/// let perms: PermissionSet = PermissionSet::from([
135///     AtomicPermission::new("file", "read"),
136/// ]);
137///
138/// // Lattice operations are available via MeetSemilattice and JoinSemilattice traits
139/// ```
140pub trait Lattice: MeetSemilattice + JoinSemilattice {}
141
142// Blanket implementation: any type with both meet and join is a lattice
143impl<T: MeetSemilattice + JoinSemilattice> Lattice for T {}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::algebra::Semigroup;
149    use std::collections::BTreeSet;
150
151    // Implement semilattices for BTreeSet for testing
152    impl<T: Ord + Clone> MeetSemilattice for BTreeSet<T> {
153        fn meet(self, other: Self) -> Self {
154            self.intersection(&other).cloned().collect()
155        }
156    }
157
158    impl<T: Ord + Clone> JoinSemilattice for BTreeSet<T> {
159        fn join(self, other: Self) -> Self {
160            self.combine(other) // Reuse semigroup union
161        }
162    }
163
164    #[test]
165    fn test_meet_commutativity() {
166        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
167        let b: BTreeSet<i32> = [2, 3, 4].iter().cloned().collect();
168
169        assert_eq!(a.clone().meet(b.clone()), b.meet(a));
170    }
171
172    #[test]
173    fn test_meet_associativity() {
174        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
175        let b: BTreeSet<i32> = [2, 3, 4].iter().cloned().collect();
176        let c: BTreeSet<i32> = [3, 4, 5].iter().cloned().collect();
177
178        let left = a.clone().meet(b.clone()).meet(c.clone());
179        let right = a.meet(b.meet(c));
180
181        assert_eq!(left, right);
182    }
183
184    #[test]
185    fn test_meet_idempotence() {
186        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
187
188        assert_eq!(a.clone().meet(a.clone()), a);
189    }
190
191    #[test]
192    fn test_join_commutativity() {
193        let a: BTreeSet<i32> = [1, 2].iter().cloned().collect();
194        let b: BTreeSet<i32> = [3, 4].iter().cloned().collect();
195
196        assert_eq!(a.clone().join(b.clone()), b.join(a));
197    }
198
199    #[test]
200    fn test_join_idempotence() {
201        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
202
203        assert_eq!(a.clone().join(a.clone()), a);
204    }
205}