1use std::collections::HashMap;
47use std::hash::Hash;
48
49use serde::Deserialize;
50use serde::Serialize;
51
52use super::BoundedJoinSemilattice;
53use super::JoinSemilattice;
54
55#[derive(
72 Clone,
73 Copy,
74 Debug,
75 PartialEq,
76 Eq,
77 PartialOrd,
78 Ord,
79 Hash,
80 Serialize,
81 Deserialize,
82 typeuri::Named
83)]
84pub struct Max<T>(pub T);
85
86impl<T: Ord + Clone> JoinSemilattice for Max<T> {
87 fn join(&self, other: &Self) -> Self {
88 if self.0 >= other.0 {
89 self.clone()
90 } else {
91 other.clone()
92 }
93 }
94}
95
96impl<T: Ord + Clone + num_traits::Bounded> BoundedJoinSemilattice for Max<T> {
97 fn bottom() -> Self {
98 Max(num_traits::Bounded::min_value())
99 }
100}
101
102impl<T> From<T> for Max<T> {
103 fn from(value: T) -> Self {
104 Max(value)
105 }
106}
107
108impl<T: Ord + Clone + num_traits::Bounded> Default for Max<T> {
109 fn default() -> Self {
110 Self::bottom()
111 }
112}
113
114impl<T> Max<T> {
115 pub fn get(&self) -> &T {
117 &self.0
118 }
119}
120
121#[derive(
138 Clone,
139 Copy,
140 Debug,
141 PartialEq,
142 Eq,
143 PartialOrd,
144 Ord,
145 Hash,
146 Serialize,
147 Deserialize,
148 typeuri::Named
149)]
150pub struct Min<T>(pub T);
151
152impl<T: Ord + Clone> JoinSemilattice for Min<T> {
153 fn join(&self, other: &Self) -> Self {
154 if self.0 <= other.0 {
155 self.clone()
156 } else {
157 other.clone()
158 }
159 }
160}
161
162impl<T: Ord + Clone + num_traits::Bounded> BoundedJoinSemilattice for Min<T> {
163 fn bottom() -> Self {
164 Min(num_traits::Bounded::max_value())
165 }
166}
167
168impl<T> From<T> for Min<T> {
169 fn from(value: T) -> Self {
170 Min(value)
171 }
172}
173
174impl<T: Ord + Clone + num_traits::Bounded> Default for Min<T> {
175 fn default() -> Self {
176 Self::bottom()
177 }
178}
179
180impl<T> Min<T> {
181 pub fn get(&self) -> &T {
183 &self.0
184 }
185}
186
187#[derive(
204 Clone,
205 Copy,
206 Debug,
207 PartialEq,
208 Eq,
209 PartialOrd,
210 Ord,
211 Hash,
212 Serialize,
213 Deserialize,
214 typeuri::Named
215)]
216pub struct Any(pub bool);
217
218impl JoinSemilattice for Any {
219 fn join(&self, other: &Self) -> Self {
220 Any(self.0 || other.0)
221 }
222}
223
224impl BoundedJoinSemilattice for Any {
225 fn bottom() -> Self {
226 Any(false)
227 }
228}
229
230impl From<bool> for Any {
231 fn from(value: bool) -> Self {
232 Any(value)
233 }
234}
235
236#[derive(
264 Clone,
265 Copy,
266 Debug,
267 PartialEq,
268 Eq,
269 Hash,
270 Serialize,
271 Deserialize,
272 typeuri::Named
273)]
274pub struct All(pub bool);
275
276impl JoinSemilattice for All {
277 fn join(&self, other: &Self) -> Self {
278 All(self.0 && other.0)
279 }
280}
281
282impl BoundedJoinSemilattice for All {
283 fn bottom() -> Self {
284 All(true)
285 }
286}
287
288impl From<bool> for All {
289 fn from(value: bool) -> Self {
290 All(value)
291 }
292}
293
294#[derive(Clone, Debug, Serialize, Deserialize)]
389#[serde(bound(
390 serialize = "K: Eq + Hash + Serialize, V: Serialize",
391 deserialize = "K: Eq + Hash + Deserialize<'de>, V: Deserialize<'de>"
392))]
393pub struct LatticeMap<K, V> {
394 inner: HashMap<K, V>,
395}
396
397impl<K, V> PartialEq for LatticeMap<K, V>
401where
402 K: Eq + Hash,
403 V: PartialEq,
404{
405 fn eq(&self, other: &Self) -> bool {
406 self.inner == other.inner
407 }
408}
409
410impl<K, V> Eq for LatticeMap<K, V>
411where
412 K: Eq + Hash,
413 V: Eq,
414{
415}
416
417impl<K, V> LatticeMap<K, V>
418where
419 K: Eq + Hash,
420{
421 pub fn new() -> Self {
423 Self {
424 inner: HashMap::new(),
425 }
426 }
427
428 pub fn insert(&mut self, k: K, v: V) {
430 self.inner.insert(k, v);
431 }
432
433 pub fn get(&self, k: &K) -> Option<&V> {
435 self.inner.get(k)
436 }
437
438 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
440 self.inner.iter()
441 }
442
443 pub fn as_inner(&self) -> &HashMap<K, V> {
445 &self.inner
446 }
447
448 pub fn into_inner(self) -> HashMap<K, V> {
450 self.inner
451 }
452
453 pub fn len(&self) -> usize {
455 self.inner.len()
456 }
457
458 pub fn is_empty(&self) -> bool {
460 self.inner.is_empty()
461 }
462}
463
464impl<K, V> Default for LatticeMap<K, V>
465where
466 K: Eq + Hash,
467{
468 fn default() -> Self {
469 Self::new()
470 }
471}
472
473impl<K, V> JoinSemilattice for LatticeMap<K, V>
474where
475 K: Eq + Hash + Clone,
476 V: JoinSemilattice + Clone,
477{
478 fn join(&self, other: &Self) -> Self {
479 let mut out = self.inner.clone();
480
481 for (k, v_other) in &other.inner {
482 out.entry(k.clone())
483 .and_modify(|v_here| {
484 *v_here = v_here.join(v_other);
485 })
486 .or_insert_with(|| v_other.clone());
487 }
488
489 LatticeMap { inner: out }
490 }
491}
492
493impl<K, V> BoundedJoinSemilattice for LatticeMap<K, V>
494where
495 K: Eq + Hash + Clone,
496 V: BoundedJoinSemilattice + Clone,
497{
498 fn bottom() -> Self {
499 LatticeMap {
500 inner: HashMap::new(),
501 }
502 }
503}
504
505use std::collections::HashSet;
506
507impl<T: Eq + Hash + Clone> JoinSemilattice for HashSet<T> {
517 fn join(&self, other: &Self) -> Self {
518 self.union(other).cloned().collect()
519 }
520}
521
522impl<T: Eq + Hash + Clone> BoundedJoinSemilattice for HashSet<T> {
523 fn bottom() -> Self {
524 HashSet::new()
525 }
526}
527
528use std::collections::BTreeSet;
529
530impl<T: Ord + Clone> JoinSemilattice for BTreeSet<T> {
534 fn join(&self, other: &Self) -> Self {
535 self.union(other).cloned().collect()
536 }
537}
538
539impl<T: Ord + Clone> BoundedJoinSemilattice for BTreeSet<T> {
540 fn bottom() -> Self {
541 BTreeSet::new()
542 }
543}
544
545impl<L: JoinSemilattice + Clone> JoinSemilattice for Option<L> {
555 fn join(&self, other: &Self) -> Self {
556 match (self, other) {
557 (None, x) | (x, None) => x.clone(),
558 (Some(a), Some(b)) => Some(a.join(b)),
559 }
560 }
561}
562
563impl<L: JoinSemilattice + Clone> BoundedJoinSemilattice for Option<L> {
564 fn bottom() -> Self {
565 None
566 }
567}
568
569impl JoinSemilattice for () {
573 fn join(&self, _other: &Self) -> Self {}
574}
575
576impl BoundedJoinSemilattice for () {
577 fn bottom() -> Self {}
578}
579
580#[cfg(test)]
583mod tests {
584 use super::*;
585
586 #[test]
589 fn max_join_is_maximum() {
590 let a = Max(5);
591 let b = Max(10);
592 assert_eq!(a.join(&b), Max(10));
593 assert_eq!(b.join(&a), Max(10));
594 }
595
596 #[test]
597 fn max_bottom_is_min_value() {
598 assert_eq!(Max::<i32>::bottom(), Max(i32::MIN));
599 assert_eq!(Max::<u64>::bottom(), Max(u64::MIN));
600 }
601
602 #[test]
603 fn max_is_idempotent() {
604 let x = Max(42);
605 assert_eq!(x.join(&x), x);
606 }
607
608 #[test]
609 fn max_is_commutative() {
610 let a = Max(3);
611 let b = Max(7);
612 assert_eq!(a.join(&b), b.join(&a));
613 }
614
615 #[test]
616 fn max_is_associative() {
617 let a = Max(1);
618 let b = Max(5);
619 let c = Max(3);
620 assert_eq!(a.join(&b).join(&c), a.join(&b.join(&c)));
621 }
622
623 #[test]
624 fn max_bottom_is_identity() {
625 let x = Max(100);
626 assert_eq!(Max::bottom().join(&x), x);
627 assert_eq!(x.join(&Max::bottom()), x);
628 }
629
630 #[test]
633 fn min_join_is_minimum() {
634 let a = Min(5);
635 let b = Min(10);
636 assert_eq!(a.join(&b), Min(5));
637 assert_eq!(b.join(&a), Min(5));
638 }
639
640 #[test]
641 fn min_bottom_is_max_value() {
642 assert_eq!(Min::<i32>::bottom(), Min(i32::MAX));
643 assert_eq!(Min::<u64>::bottom(), Min(u64::MAX));
644 }
645
646 #[test]
647 fn min_is_idempotent() {
648 let x = Min(42);
649 assert_eq!(x.join(&x), x);
650 }
651
652 #[test]
653 fn min_is_commutative() {
654 let a = Min(3);
655 let b = Min(7);
656 assert_eq!(a.join(&b), b.join(&a));
657 }
658
659 #[test]
660 fn min_is_associative() {
661 let a = Min(1);
662 let b = Min(5);
663 let c = Min(3);
664 assert_eq!(a.join(&b).join(&c), a.join(&b.join(&c)));
665 }
666
667 #[test]
668 fn min_bottom_is_identity() {
669 let x = Min(100);
670 assert_eq!(Min::bottom().join(&x), x);
671 assert_eq!(x.join(&Min::bottom()), x);
672 }
673
674 #[test]
677 fn any_join_is_or() {
678 assert_eq!(Any(false).join(&Any(false)), Any(false));
679 assert_eq!(Any(false).join(&Any(true)), Any(true));
680 assert_eq!(Any(true).join(&Any(false)), Any(true));
681 assert_eq!(Any(true).join(&Any(true)), Any(true));
682 }
683
684 #[test]
685 fn any_bottom_is_false() {
686 assert_eq!(Any::bottom(), Any(false));
687 assert_eq!(Any(false).join(&Any(true)), Any(true));
688 assert_eq!(Any(true).join(&Any(false)), Any(true));
689 }
690
691 #[test]
692 fn any_is_idempotent() {
693 assert_eq!(Any(false).join(&Any(false)), Any(false));
694 assert_eq!(Any(true).join(&Any(true)), Any(true));
695 }
696
697 #[test]
700 fn all_join_is_and() {
701 assert_eq!(All(false).join(&All(false)), All(false));
702 assert_eq!(All(false).join(&All(true)), All(false));
703 assert_eq!(All(true).join(&All(false)), All(false));
704 assert_eq!(All(true).join(&All(true)), All(true));
705 }
706
707 #[test]
708 fn all_bottom_is_true() {
709 assert_eq!(All::bottom(), All(true));
710 assert_eq!(All(true).join(&All(false)), All(false));
711 assert_eq!(All(false).join(&All(true)), All(false));
712 }
713
714 #[test]
715 fn all_is_idempotent() {
716 assert_eq!(All(false).join(&All(false)), All(false));
717 assert_eq!(All(true).join(&All(true)), All(true));
718 }
719
720 #[test]
723 fn max_from_value() {
724 let x: Max<i32> = 42.into();
725 assert_eq!(x, Max(42));
726 assert_eq!(Max::from(100u64), Max(100u64));
727 }
728
729 #[test]
730 fn min_from_value() {
731 let x: Min<i32> = 42.into();
732 assert_eq!(x, Min(42));
733 assert_eq!(Min::from(100u64), Min(100u64));
734 }
735
736 #[test]
737 fn any_from_bool() {
738 let t: Any = true.into();
739 let f: Any = false.into();
740 assert_eq!(t, Any(true));
741 assert_eq!(f, Any(false));
742 }
743
744 #[test]
745 fn all_from_bool() {
746 let t: All = true.into();
747 let f: All = false.into();
748 assert_eq!(t, All(true));
749 assert_eq!(f, All(false));
750 }
751
752 #[test]
755 fn max_default_is_bottom() {
756 assert_eq!(Max::<i32>::default(), Max::<i32>::bottom());
757 assert_eq!(Max::<u64>::default(), Max(u64::MIN));
758 }
759
760 #[test]
761 fn min_default_is_bottom() {
762 assert_eq!(Min::<i32>::default(), Min::<i32>::bottom());
763 assert_eq!(Min::<u64>::default(), Min(u64::MAX));
764 }
765
766 #[test]
769 fn max_serde_roundtrip() {
770 let original = Max(42i64);
771 let encoded = bincode::serialize(&original).unwrap();
772 let decoded: Max<i64> = bincode::deserialize(&encoded).unwrap();
773 assert_eq!(original, decoded);
774
775 let bottom = Max::<i64>::bottom();
777 let encoded = bincode::serialize(&bottom).unwrap();
778 let decoded: Max<i64> = bincode::deserialize(&encoded).unwrap();
779 assert_eq!(bottom, decoded);
780 }
781
782 #[test]
783 fn min_serde_roundtrip() {
784 let original = Min(42i64);
785 let encoded = bincode::serialize(&original).unwrap();
786 let decoded: Min<i64> = bincode::deserialize(&encoded).unwrap();
787 assert_eq!(original, decoded);
788
789 let bottom = Min::<i64>::bottom();
791 let encoded = bincode::serialize(&bottom).unwrap();
792 let decoded: Min<i64> = bincode::deserialize(&encoded).unwrap();
793 assert_eq!(bottom, decoded);
794 }
795
796 #[test]
797 fn any_serde_roundtrip() {
798 for value in [true, false] {
799 let original = Any(value);
800 let encoded = bincode::serialize(&original).unwrap();
801 let decoded: Any = bincode::deserialize(&encoded).unwrap();
802 assert_eq!(original, decoded);
803 }
804
805 let bottom = Any::bottom();
807 let encoded = bincode::serialize(&bottom).unwrap();
808 let decoded: Any = bincode::deserialize(&encoded).unwrap();
809 assert_eq!(bottom, decoded);
810 }
811
812 #[test]
813 fn all_serde_roundtrip() {
814 for value in [true, false] {
815 let original = All(value);
816 let encoded = bincode::serialize(&original).unwrap();
817 let decoded: All = bincode::deserialize(&encoded).unwrap();
818 assert_eq!(original, decoded);
819 }
820
821 let bottom = All::bottom();
823 let encoded = bincode::serialize(&bottom).unwrap();
824 let decoded: All = bincode::deserialize(&encoded).unwrap();
825 assert_eq!(bottom, decoded);
826 }
827
828 #[test]
831 fn lattice_map_new_is_empty() {
832 let m: LatticeMap<i32, Max<i32>> = LatticeMap::new();
833 assert!(m.is_empty());
834 assert_eq!(m.len(), 0);
835 }
836
837 #[test]
838 fn lattice_map_bottom_is_empty() {
839 let m: LatticeMap<i32, Max<i32>> = BoundedJoinSemilattice::bottom();
840 assert!(m.is_empty());
841 assert_eq!(m.len(), 0);
842 }
843
844 #[test]
845 fn lattice_map_insert_and_get() {
846 let mut m: LatticeMap<&str, Max<i32>> = LatticeMap::new();
847 m.insert("foo", Max(42));
848 assert_eq!(m.get(&"foo"), Some(&Max(42)));
849 assert_eq!(m.get(&"bar"), None);
850 assert_eq!(m.len(), 1);
851 assert!(!m.is_empty());
852 }
853
854 #[test]
855 fn lattice_map_join_is_pointwise() {
856 let mut m1: LatticeMap<&str, Max<i32>> = LatticeMap::new();
858 m1.insert("a", Max(1));
859 m1.insert("b", Max(10));
860
861 let mut m2: LatticeMap<&str, Max<i32>> = LatticeMap::new();
862 m2.insert("b", Max(7));
863 m2.insert("c", Max(3));
864
865 let j = m1.join(&m2);
866
867 assert_eq!(j.get(&"a"), Some(&Max(1)));
869 assert_eq!(j.get(&"b"), Some(&Max(10))); assert_eq!(j.get(&"c"), Some(&Max(3)));
871 }
872
873 #[test]
874 fn lattice_map_join_is_commutative() {
875 let mut m1: LatticeMap<u32, Min<i64>> = LatticeMap::new();
876 m1.insert(0, Min(10));
877
878 let mut m2: LatticeMap<u32, Min<i64>> = LatticeMap::new();
879 m2.insert(0, Min(20));
880
881 assert_eq!(m1.join(&m2), m2.join(&m1));
882 }
883
884 #[test]
885 fn lattice_map_join_is_idempotent() {
886 let mut m: LatticeMap<u32, Max<i64>> = LatticeMap::new();
887 m.insert(1, Max(100));
888 m.insert(2, Max(200));
889
890 assert_eq!(m.join(&m), m);
891 }
892
893 #[test]
894 fn lattice_map_watermark_example() {
895 let mut state1: LatticeMap<u32, Min<u64>> = LatticeMap::new();
897 state1.insert(0, Min(100)); state1.insert(1, Min(200)); let mut state2: LatticeMap<u32, Min<u64>> = LatticeMap::new();
901 state2.insert(0, Min(150)); state2.insert(2, Min(50)); let merged = state1.join(&state2);
906
907 assert_eq!(merged.get(&0), Some(&Min(100))); assert_eq!(merged.get(&1), Some(&Min(200))); assert_eq!(merged.get(&2), Some(&Min(50))); let watermark = merged.iter().map(|(_, v)| v.0).min().unwrap();
914 assert_eq!(watermark, 50);
915 }
916
917 #[test]
918 fn lattice_map_iter_works() {
919 let mut m: LatticeMap<&str, Max<i32>> = LatticeMap::new();
920 m.insert("a", Max(1));
921 m.insert("b", Max(2));
922
923 let items: HashMap<_, _> = m.iter().map(|(k, v)| (*k, v.0)).collect();
924 assert_eq!(items.get(&"a"), Some(&1));
925 assert_eq!(items.get(&"b"), Some(&2));
926 assert_eq!(items.len(), 2);
927 }
928
929 #[test]
930 fn lattice_map_serde_roundtrip() {
931 let mut original: LatticeMap<u32, Min<i64>> = LatticeMap::new();
932 original.insert(1, Min(10));
933 original.insert(2, Min(20));
934
935 let encoded = bincode::serialize(&original).unwrap();
936 let decoded: LatticeMap<u32, Min<i64>> = bincode::deserialize(&encoded).unwrap();
937
938 assert_eq!(original, decoded);
939 }
940
941 #[test]
944 fn hashset_join_is_union() {
945 let mut a = HashSet::new();
946 a.insert(1);
947 a.insert(2);
948
949 let mut b = HashSet::new();
950 b.insert(2);
951 b.insert(3);
952
953 let c = a.join(&b);
954 assert_eq!(c.len(), 3);
955 assert!(c.contains(&1));
956 assert!(c.contains(&2));
957 assert!(c.contains(&3));
958 }
959
960 #[test]
961 fn hashset_bottom_is_empty() {
962 let bottom: HashSet<i32> = HashSet::bottom();
963 assert!(bottom.is_empty());
964
965 let mut a = HashSet::new();
966 a.insert(42);
967
968 assert_eq!(bottom.join(&a), a);
969 assert_eq!(a.join(&bottom), a);
970 }
971
972 #[test]
973 fn hashset_is_idempotent() {
974 let mut a = HashSet::new();
975 a.insert(1);
976 a.insert(2);
977
978 assert_eq!(a.join(&a), a);
979 }
980
981 #[test]
982 fn hashset_is_commutative() {
983 let mut a = HashSet::new();
984 a.insert(1);
985 a.insert(2);
986
987 let mut b = HashSet::new();
988 b.insert(2);
989 b.insert(3);
990
991 assert_eq!(a.join(&b), b.join(&a));
992 }
993
994 #[test]
995 fn hashset_is_associative() {
996 let mut a = HashSet::new();
997 a.insert(1);
998
999 let mut b = HashSet::new();
1000 b.insert(2);
1001
1002 let mut c = HashSet::new();
1003 c.insert(3);
1004
1005 assert_eq!(a.join(&b).join(&c), a.join(&b.join(&c)));
1006 }
1007
1008 #[test]
1009 fn hashset_semigroup_blanket_impl() {
1010 let mut a: HashSet<&str> = HashSet::new();
1013 a.insert("foo");
1014
1015 let mut b: HashSet<&str> = HashSet::new();
1016 b.insert("bar");
1017
1018 let c = a.join(&b);
1020 assert_eq!(c.len(), 2);
1021 assert!(c.contains(&"foo"));
1022 assert!(c.contains(&"bar"));
1023 }
1024
1025 #[test]
1026 fn hashset_monoid_blanket_impl() {
1027 let empty: HashSet<i32> = HashSet::bottom();
1030 assert!(empty.is_empty());
1031
1032 let mut a = HashSet::new();
1033 a.insert(42);
1034
1035 assert_eq!(empty.join(&a), a);
1037 assert_eq!(a.join(&empty), a);
1038 }
1039
1040 #[test]
1043 fn btreeset_join_is_union() {
1044 let a: BTreeSet<i32> = [1, 2].into_iter().collect();
1045 let b: BTreeSet<i32> = [2, 3].into_iter().collect();
1046
1047 let c = a.join(&b);
1048 assert_eq!(c.len(), 3);
1049 assert!(c.contains(&1));
1050 assert!(c.contains(&2));
1051 assert!(c.contains(&3));
1052 }
1053
1054 #[test]
1055 fn btreeset_bottom_is_empty() {
1056 let bottom: BTreeSet<i32> = BTreeSet::bottom();
1057 assert!(bottom.is_empty());
1058
1059 let a: BTreeSet<i32> = [42].into_iter().collect();
1060 assert_eq!(bottom.join(&a), a);
1061 assert_eq!(a.join(&bottom), a);
1062 }
1063
1064 #[test]
1065 fn btreeset_is_idempotent() {
1066 let a: BTreeSet<i32> = [1, 2].into_iter().collect();
1067 assert_eq!(a.join(&a), a);
1068 }
1069
1070 #[test]
1071 fn btreeset_is_commutative() {
1072 let a: BTreeSet<i32> = [1, 2].into_iter().collect();
1073 let b: BTreeSet<i32> = [2, 3].into_iter().collect();
1074 assert_eq!(a.join(&b), b.join(&a));
1075 }
1076
1077 #[test]
1080 fn option_join_lifts_inner() {
1081 let a: Option<Max<i32>> = Some(Max(5));
1082 let b: Option<Max<i32>> = Some(Max(10));
1083
1084 assert_eq!(a.join(&b), Some(Max(10)));
1085 }
1086
1087 #[test]
1088 fn option_join_with_none() {
1089 let a: Option<Max<i32>> = Some(Max(5));
1090 let none: Option<Max<i32>> = None;
1091
1092 assert_eq!(a.join(&none), a);
1094 assert_eq!(none.join(&a), a);
1095 assert_eq!(none.join(&none), None);
1096 }
1097
1098 #[test]
1099 fn option_bottom_is_none() {
1100 let bottom: Option<Max<i32>> = Option::bottom();
1101 assert_eq!(bottom, None);
1102 }
1103
1104 #[test]
1105 fn option_with_hashset() {
1106 let a: Option<HashSet<i32>> = Some([1, 2].into_iter().collect());
1107 let b: Option<HashSet<i32>> = Some([2, 3].into_iter().collect());
1108
1109 let c = a.join(&b);
1110 let expected: HashSet<i32> = [1, 2, 3].into_iter().collect();
1111 assert_eq!(c, Some(expected));
1112 }
1113
1114 #[test]
1115 fn option_is_idempotent() {
1116 let a: Option<Max<i32>> = Some(Max(5));
1117 assert_eq!(a.join(&a), a);
1118
1119 let none: Option<Max<i32>> = None;
1120 assert_eq!(none.join(&none), none);
1121 }
1122
1123 #[test]
1124 fn option_is_commutative() {
1125 let a: Option<Max<i32>> = Some(Max(5));
1126 let b: Option<Max<i32>> = Some(Max(10));
1127 assert_eq!(a.join(&b), b.join(&a));
1128 }
1129
1130 #[test]
1133 fn unit_join_is_trivial() {
1134 assert_eq!(().join(&()), ());
1135 }
1136
1137 #[test]
1138 fn unit_bottom_is_unit() {
1139 assert_eq!(<()>::bottom(), ());
1140 }
1141
1142 #[test]
1143 fn unit_is_idempotent() {
1144 assert_eq!(().join(&()), ());
1145 }
1146}