1use std::cmp::Ordering;
2use std::collections::HashSet;
3use std::hash::Hash;
4
5use dfir_rs::lattices::{IsBot, IsTop, LatticeFrom, LatticeOrd, Merge};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
13
14pub struct BoundedSetLattice<T, const N: usize>
15where
16 T: Eq + Hash,
17{
18 items: HashSet<T>,
21 is_top: bool,
22}
23
24impl<T, const N: usize> LatticeFrom<BoundedSetLattice<T, N>> for BoundedSetLattice<T, N>
25where
26 T: Eq + Hash,
27{
28 fn lattice_from(other: BoundedSetLattice<T, N>) -> Self {
29 other
30 }
31}
32
33impl<T, const N: usize> Default for BoundedSetLattice<T, N>
34where
35 T: Eq + Hash,
36{
37 fn default() -> Self {
38 Self {
39 items: HashSet::new(),
40 is_top: N == 0, }
42 }
43}
44
45impl<T> From<()> for BoundedSetLattice<T, 0>
46where
47 T: Eq + Hash,
48{
49 fn from(_: ()) -> Self {
50 Default::default()
51 }
52}
53
54impl<T> From<BoundedSetLattice<T, 0>> for ()
55where
56 T: Eq + Hash,
57{
58 fn from(_: BoundedSetLattice<T, 0>) -> Self {}
59}
60
61impl<T, const N: usize> BoundedSetLattice<T, N>
62where
63 T: Eq + Hash,
64{
65 pub fn new() -> Self {
66 Default::default()
67 }
68
69 pub fn new_from<U>(items: U) -> Self
70 where
71 U: IntoIterator<Item = T>,
72 {
73 let mut lattice = Self::new();
74 lattice.merge(items);
75 lattice
76 }
77}
78
79impl<T, const N: usize> IsBot for BoundedSetLattice<T, N>
80where
81 T: Eq + Hash,
82{
83 fn is_bot(&self) -> bool {
84 match N {
85 0 => true,
86 _ => self.items.is_empty() && !self.is_top,
87 }
88 }
89}
90
91impl<T, const N: usize> IsTop for BoundedSetLattice<T, N>
92where
93 T: Eq + Hash,
94{
95 fn is_top(&self) -> bool {
96 self.is_top
97 }
98}
99
100impl<T, const N: usize, U> Merge<U> for BoundedSetLattice<T, N>
101where
102 U: IntoIterator<Item = T>,
103 T: Eq + Hash,
104{
105 fn merge(&mut self, other: U) -> bool {
106 if self.is_top {
107 return false;
108 }
109
110 let old_len = self.items.len();
111 self.items.extend(other);
112 let new_len = self.items.len();
113
114 if new_len >= N {
115 self.is_top = true;
116 self.items.clear();
117 }
118
119 new_len != old_len
120 }
121}
122
123impl<T, const N: usize> PartialOrd<Self> for BoundedSetLattice<T, N>
124where
125 T: Eq + Hash,
126{
127 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
128 match (self.is_top, other.is_top) {
129 (true, true) => Some(Ordering::Equal),
130 (true, false) => Some(Ordering::Greater),
131 (false, true) => Some(Ordering::Less),
132 (false, false) => match self.items.len().cmp(&other.items.len()) {
133 Ordering::Greater => {
134 if other.items.iter().all(|key| self.items.contains(key)) {
135 Some(Ordering::Greater)
136 } else {
137 None
138 }
139 }
140 Ordering::Less => {
141 if self.items.iter().all(|key| other.items.contains(key)) {
142 Some(Ordering::Less)
143 } else {
144 None
145 }
146 }
147 Ordering::Equal => {
148 if self.items.iter().all(|key| other.items.contains(key)) {
149 Some(Ordering::Equal)
150 } else {
151 None
152 }
153 }
154 },
155 }
156 }
157}
158
159impl<T, const N: usize> PartialEq<Self> for BoundedSetLattice<T, N>
160where
161 T: Eq + Hash,
162{
163 fn eq(&self, other: &Self) -> bool {
164 match (self.is_top, other.is_top) {
165 (true, true) => true,
166 (true, false) => false,
167 (false, true) => false,
168 (false, false) => self.items == other.items,
169 }
170 }
171}
172
173impl<T, const N: usize> LatticeOrd for BoundedSetLattice<T, N> where T: Eq + Hash {}
174
175impl<T, const N: usize> Merge<BoundedSetLattice<T, N>> for BoundedSetLattice<T, N>
176where
177 T: Eq + Hash,
178{
179 fn merge(&mut self, other: BoundedSetLattice<T, N>) -> bool {
180 match (self.is_top, other.is_top) {
181 (true, _) => false,
182 (false, true) => {
183 self.is_top = true;
184 self.items.clear();
185 true
186 }
187 (false, false) => self.merge(other.items),
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use dfir_rs::lattices::test::check_all;
195
196 use super::*;
197
198 #[test]
199 fn test_0_bounded_set_lattice() {
200 let mut lat: BoundedSetLattice<i32, 0> = ().into();
201 assert!(lat.is_bot() && lat.is_top());
202
203 assert!(!lat.merge([1]));
205
206 assert!(lat.is_bot() && lat.is_top());
208 }
209
210 #[test]
211 fn test_1_bounded_set_lattice() {
212 let mut lat = BoundedSetLattice::<i32, 1>::new();
214 assert!(lat.is_bot() && !lat.is_top());
215 assert!(lat.items.is_empty());
216
217 assert!(lat.merge([1]));
218 assert!(!lat.is_bot() && lat.is_top());
219 assert!(lat.items.is_empty()); assert!(!lat.merge([2]));
222 }
223
224 #[test]
225 fn test_2_bounded_set_lattice() {
226 let mut a = BoundedSetLattice::<i32, 2>::new();
227 let b: BoundedSetLattice<i32, 2> = BoundedSetLattice::new_from([1, 2]);
228
229 assert!(a.is_bot() && !a.is_top());
230 assert!(!b.is_bot() && b.is_top());
231
232 assert!(a.merge(b));
233 assert!(!a.is_bot() && a.is_top());
234
235 assert!(!a.merge([3]));
236 }
237
238 #[test]
239 fn test_lattice_properties() {
240 check_all(&[
241 Default::default(),
242 BoundedSetLattice::<i32, 3>::new_from([1]),
243 BoundedSetLattice::<i32, 3>::new_from([1, 2]),
244 BoundedSetLattice::<i32, 3>::new_from([1, 2, 3]),
245 BoundedSetLattice::<i32, 3>::new_from([1, 2, 3, 4]),
246 ]);
247 }
248}