1use std::fmt::Debug;
4
5use crate::{
6 Atomize, IsBot, IsTop, Lattice, LatticeBimorphism, LatticeMorphism, LatticeOrd, Merge,
7 NaiveLatticeOrd,
8};
9
10pub fn check_all<T: Lattice + Clone + PartialEq + Debug + Default>(items: &[T]) {
13 check_lattice_ord(items);
14 check_partial_ord_properties(items);
15 check_lattice_properties(items);
16 check_lattice_is_bot(items);
17 check_lattice_is_top(items);
18 check_lattice_default_is_bot::<T>();
19}
20
21pub fn check_lattice_ord<T: LatticeOrd + NaiveLatticeOrd + Debug>(items: &[T]) {
24 for [a, b] in cartesian_power(items) {
28 assert_eq!(a.naive_cmp(b), a.partial_cmp(b), "`{:?}`, `{:?}`", a, b);
29 }
30}
31
32#[expect(
34 clippy::eq_op,
35 clippy::double_comparisons,
36 reason = "testing comparison properties"
37)]
38pub fn check_partial_ord_properties<T: PartialOrd + PartialEq + Debug>(items: &[T]) {
39 use std::cmp::Ordering::*;
40
41 for a in items {
44 assert!(a == a, "Reflexivity: `{:?}` should equal itself.", a);
45 }
46 for [a, b] in cartesian_power(items) {
48 assert_eq!(a == b, b == a, "`{:?}`, `{:?}`", a, b);
49 }
50 for [a, b, c] in cartesian_power(items) {
52 if a == b && b == c {
53 assert_eq!(a == b && b == c, a == c, "`{:?}`, `{:?}`, `{:?}`", a, b, c);
54 }
55 }
56
57 for [a, b] in cartesian_power(items) {
59 assert_eq!(
61 a == b,
62 a.partial_cmp(b) == Some(Equal),
63 "`{:?}`, `{:?}`",
64 a,
65 b,
66 );
67 assert_eq!(
69 a < b,
70 a.partial_cmp(b) == Some(Less),
71 "`{:?}`, `{:?}`",
72 a,
73 b,
74 );
75 assert_eq!(
77 a > b,
78 a.partial_cmp(b) == Some(Greater),
79 "`{:?}`, `{:?}`",
80 a,
81 b,
82 );
83 assert_eq!(a <= b, a < b || a == b, "`{:?}`, `{:?}`", a, b);
85 assert_eq!(a >= b, a > b || a == b, "`{:?}`, `{:?}`", a, b);
87 {
91 assert_eq!(a != b, !(a == b), "`{:?}`, `{:?}`", a, b);
92 }
93 }
94 for [a, b, c] in cartesian_power(items) {
96 if a < b && b < c {
97 assert!(a < c, "`{:?}`, `{:?}`, `{:?}`", a, b, c);
98 }
99 if a == b && b == c {
100 assert!(a == c, "`{:?}`, `{:?}`, `{:?}`", a, b, c);
101 }
102 if a > b && b > c {
103 assert!(a > c, "`{:?}`, `{:?}`, `{:?}`", a, b, c);
104 }
105 }
106 for [a, b] in cartesian_power(items) {
108 assert_eq!(a < b, b > a, "`{:?}`, `{:?}`", a, b);
109 }
110}
111
112pub fn check_lattice_properties<T: Merge<T> + Clone + PartialEq + Debug>(items: &[T]) {
114 for x in items {
117 assert_eq!(
118 Merge::merge_owned(x.clone(), x.clone()),
119 x.clone(),
120 "`{:?}`",
121 x,
122 );
123 }
124
125 for [x, y] in cartesian_power(items) {
128 assert_eq!(
129 Merge::merge_owned(x.clone(), y.clone()),
130 Merge::merge_owned(y.clone(), x.clone()),
131 "`{:?}`, `{:?}`",
132 x,
133 y,
134 );
135 }
136
137 for [x, y, z] in cartesian_power(items) {
140 assert_eq!(
141 Merge::merge_owned(x.clone(), Merge::merge_owned(y.clone(), z.clone())),
142 Merge::merge_owned(Merge::merge_owned(x.clone(), y.clone()), z.clone()),
143 "`{:?}`, `{:?}`, `{:?}`",
144 x,
145 y,
146 z,
147 );
148 }
149}
150
151pub fn check_lattice_is_bot<T: IsBot + LatticeOrd + Debug>(items: &[T]) {
153 let Some(bot) = items.iter().find(|&x| IsBot::is_bot(x)) else {
154 return;
155 };
156 for x in items {
157 assert!(bot <= x);
158 assert_eq!(bot == x, x.is_bot(), "{:?}", x);
159 }
160}
161
162pub fn check_lattice_is_top<T: IsTop + LatticeOrd + Debug>(items: &[T]) {
164 let Some(top) = items.iter().find(|&x| IsTop::is_top(x)) else {
165 return;
166 };
167 for x in items {
168 assert!(x <= top);
169 assert_eq!(top == x, x.is_top(), "{:?}", x);
170 }
171}
172
173pub fn check_lattice_default_is_bot<T: IsBot + Default>() {
175 assert!(T::is_bot(&T::default()));
176}
177
178pub fn check_atomize_each<
180 T: Atomize + Merge<T::Atom> + LatticeOrd + IsBot + Default + Clone + Debug,
181>(
182 items: &[T],
183) where
184 T::Atom: Debug,
185{
186 for item in items {
187 let mut reformed = T::default();
188 let mut atoms = item.clone().atomize().peekable();
189 assert_eq!(
190 atoms.peek().is_none(),
191 item.is_bot(),
192 "`{:?}` atomize should return empty iterator ({}) if and only if item is bot ({}).",
193 item,
194 atoms.peek().is_none(),
195 item.is_bot()
196 );
197 for atom in atoms {
198 assert!(
199 !atom.is_bot(),
200 "`{:?}` atomize illegally returned a bottom atom `{:?}`.",
201 item,
202 atom,
203 );
204 reformed.merge(atom);
205 }
206 assert_eq!(item, &reformed, "`{:?}` atomize failed to reform", item);
207 }
208}
209
210pub fn check_lattice_morphism<LatIn, Func>(mut func: Func, items: &[LatIn])
212where
213 Func: LatticeMorphism<LatIn>,
214 LatIn: Merge<LatIn> + Clone + PartialEq + Debug,
215 Func::Output: Merge<Func::Output> + Clone + PartialEq + Debug,
216{
217 for [a, b] in cartesian_power(items) {
218 assert_eq!(
219 func.call(Merge::merge_owned(a.clone(), b.clone())),
220 Merge::merge_owned(func.call(a.clone()), func.call(b.clone())),
221 "Func not a morphism: `f(a ⊔ b) != f(a) ⊔ f(b)`
222 \n`a = {:?}`, `b = {:?}`",
223 a,
224 b
225 )
226 }
227}
228
229pub fn check_lattice_bimorphism<LatA, LatB, Func>(
231 mut func: Func,
232 items_a: &[LatA],
233 items_b: &[LatB],
234) where
235 Func: LatticeBimorphism<LatA, LatB>,
236 LatA: Merge<LatA> + Clone + PartialEq + Debug,
237 LatB: Merge<LatB> + Clone + PartialEq + Debug,
238 Func::Output: Merge<Func::Output> + Clone + PartialEq + Debug,
239{
240 for b in items_b {
242 for [a, da] in cartesian_power(items_a) {
243 assert_eq!(
244 func.call(Merge::merge_owned(a.clone(), da.clone()), b.clone()),
245 Merge::merge_owned(
246 func.call(a.clone(), b.clone()),
247 func.call(da.clone(), b.clone())
248 ),
249 "Left arg not a morphism: `f(a ⊔ da, b) != f(a, b) ⊔ f(da, b)`
250 \n`a = {:?}`, `da = {:?}`, `b = {:?}`",
251 a,
252 da,
253 b,
254 );
255 }
256 }
257 for a in items_a {
259 for [b, db] in cartesian_power(items_b) {
260 assert_eq!(
261 func.call(a.clone(), Merge::merge_owned(b.clone(), db.clone())),
262 Merge::merge_owned(
263 func.call(a.clone(), b.clone()),
264 func.call(a.clone(), db.clone())
265 ),
266 "Right arg not a morphism: `f(a, b ⊔ db) != f(a, b) ⊔ f(a, db)`
267 \n`a = {:?}`, `b = {:?}`, `db = {:?}`",
268 a,
269 b,
270 db,
271 );
272 }
273 }
274}
275
276pub fn cartesian_power<T, const N: usize>(
282 items: &[T],
283) -> impl ExactSizeIterator<Item = [&T; N]> + Clone {
284 struct CartesianPower<'a, T, const N: usize> {
285 items: &'a [T],
286 iters: [std::iter::Peekable<std::slice::Iter<'a, T>>; N],
287 }
288 impl<'a, T, const N: usize> Iterator for CartesianPower<'a, T, N> {
289 type Item = [&'a T; N];
290
291 fn next(&mut self) -> Option<Self::Item> {
292 if self.items.is_empty() {
293 return None;
294 }
295
296 let mut go_next = true;
297 let out = std::array::from_fn::<_, N, _>(|i| {
298 let iter = &mut self.iters[i];
299 let &item = iter.peek().unwrap();
300 if go_next {
301 iter.next();
302 if iter.peek().is_none() {
303 *iter = self.items.iter().peekable();
305 } else {
306 go_next = false;
307 }
308 }
309 item
310 });
311 if go_next {
312 self.items = &[];
314 };
315 Some(out)
316 }
317
318 fn size_hint(&self) -> (usize, Option<usize>) {
319 if self.items.is_empty() {
320 return (0, Some(0));
321 }
322 let mut pow = 1;
323 let mut passed = 0;
324 for iter in self.iters.iter() {
325 passed += pow * (self.items.len() - iter.len());
326 pow *= self.items.len();
327 }
328 let size = pow - passed;
329 (size, Some(size))
330 }
331 }
332 impl<T, const N: usize> ExactSizeIterator for CartesianPower<'_, T, N> {}
333 impl<T, const N: usize> Clone for CartesianPower<'_, T, N> {
334 fn clone(&self) -> Self {
335 Self {
336 items: self.items,
337 iters: self.iters.clone(),
338 }
339 }
340 }
341 let iters = std::array::from_fn::<_, N, _>(|_| items.iter().peekable());
342 CartesianPower { items, iters }
343}
344
345#[test]
346fn test_cartesian_power() {
347 let items = &[1, 2, 3];
348 let mut iter = cartesian_power(items);
349 let mut len = 27;
350 assert_eq!(len, iter.len());
351 for x in items {
352 for y in items {
353 for z in items {
354 len -= 1;
355 assert_eq!(Some([z, y, x]), iter.next());
356 assert_eq!(len, iter.len());
357 }
358 }
359 }
360}
361
362#[test]
363fn test_cartesian_power_zero() {
364 let mut iter = cartesian_power::<_, 0>(&[0, 1, 2]);
365 assert_eq!(1, iter.len());
366 assert_eq!(Some([]), iter.next());
367 assert_eq!(None, iter.next());
368}
369
370#[test]
371fn test_cartesian_power_empty() {
372 let mut iter = cartesian_power::<_, 4>(&[] as &[usize]);
373 assert_eq!(0, iter.len());
374 assert_eq!(None, iter.next());
375}