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}