1
use std::collections::HashMap;
2
use std::fmt;
3
use std::hash::Hash;
4

            
5
use merc_collections::VecSet;
6

            
7
/// An antichain is a structure (<, S) such that < is a preorder on S, such that
8
/// for any s, t in S neither s < t nor t < s holds. In other words, all
9
/// elements of S are incomparable under the preorder <. This is dual to the
10
/// notion of a chain.
11
///
12
/// # Details
13
///
14
/// This implementation stores pairs (s, T) in S.
15
pub struct Antichain<K, V> {
16
    storage: HashMap<K, VecSet<VecSet<V>>>,
17

            
18
    /// The largest number of keys stored in the antichain.
19
    max_antichain: usize,
20
    /// Number of times a pair was inserted into the antichain.
21
    antichain_misses: usize,
22
    /// Number of times antichain_insert was called.
23
    antichain_inserts: usize,
24
}
25

            
26
impl<K: Eq + Hash, V: Clone + Ord> Antichain<K, V> {
27
    /// Creates a new empty antichain.
28
8741
    pub fn new() -> Self {
29
8741
        Antichain {
30
8741
            storage: HashMap::new(),
31
8741
            max_antichain: 0,
32
8741
            antichain_misses: 0,
33
8741
            antichain_inserts: 0,
34
8741
        }
35
8741
    }
36

            
37
    /// Checks whether the antichain contains a pair (s, T') such that T ⊆ T',
38
    /// i.e., a stored set that is a superset of `value`.
39
808
    pub fn contains_superset(&self, key: &K, value: &VecSet<V>) -> bool {
40
808
        self.storage
41
808
            .get(key)
42
808
            .is_some_and(|entry| entry.iter().any(|inner_value| value.is_subset(inner_value)))
43
808
    }
44

            
45
    /// Checks whether the antichain contains a pair (s, T') such that T' ⊆ T,
46
    /// i.e., a stored set that is a subset of `value`.
47
4176
    pub fn contains_subset(&self, key: &K, value: &VecSet<V>) -> bool {
48
4176
        self.storage
49
4176
            .get(key)
50
4176
            .is_some_and(|entry| entry.iter().any(|inner_value| inner_value.is_subset(value)))
51
4176
    }
52

            
53
    /// Returns true iff the antichain is empty.
54
    pub fn is_empty(&self) -> bool {
55
        self.storage.is_empty()
56
    }
57

            
58
    /// Returns the number of pairs in the antichain, i.e., the number of items
59
    /// yielded by [`Antichain::iter`].
60
2
    pub fn len(&self) -> usize {
61
4
        self.storage.values().map(|values| values.len()).sum()
62
2
    }
63

            
64
    /// Returns the metrics of this antichain
65
    pub fn metrics(&self) -> (usize, usize, usize) {
66
        (self.max_antichain, self.antichain_misses, self.antichain_inserts)
67
    }
68

            
69
    /// Returns an iterator over the pairs in the antichain.
70
140
    pub fn iter(&self) -> impl Iterator<Item = (&K, &VecSet<V>)> {
71
140
        self.storage
72
140
            .iter()
73
223
            .flat_map(|(key, values)| values.iter().map(move |value| (key, value)))
74
140
    }
75
}
76

            
77
impl<K: Eq + Hash, V: Clone + Ord> Default for Antichain<K, V> {
78
    fn default() -> Self {
79
        Self::new()
80
    }
81
}
82

            
83
impl<K, V: fmt::Debug + Ord> Antichain<K, V> {
84
    /// Checks the internal consistency of the antichain invariant.
85
    #[cfg(test)]
86
100
    fn check_consistency(&self) {
87
995
        for values in self.storage.values() {
88
4269
            for i in values.iter() {
89
21349
                for j in values.iter() {
90
21349
                    if i == j {
91
                        // Ignore identical entries
92
4269
                        continue;
93
17080
                    }
94

            
95
17080
                    assert!(
96
17080
                        !i.is_subset(j) && !j.is_subset(i),
97
                        "Antichain invariant violated: {:?} and {:?} are comparable.",
98
                        i,
99
                        j
100
                    );
101
                }
102
            }
103
        }
104
100
    }
105
}
106

            
107
/// Represents the antichain data structure used in the refinement checks.
108
pub trait AC<K: Eq + Hash, V: Clone + Ord> {
109
    /// Inserts the given (s, T) pair into the antichain and returns true iff it was
110
    /// not already present.
111
    ///
112
    /// # Details
113
    ///
114
    /// A pair (s, T) is `present` in `S` iff there exists a pair (s, T') in S such that T < T'.
115
    fn insert(&mut self, key: K, value: VecSet<V>) -> bool;
116

            
117
    /// Clears the antichain.
118
    fn clear(&mut self);
119
}
120

            
121
impl<K: Eq + Hash, V: Clone + Ord> AC<K, V> for Antichain<K, V> {
122
22704
    fn insert(&mut self, key: K, value: VecSet<V>) -> bool {
123
22704
        let mut inserted = false;
124
22704
        self.storage
125
22704
            .entry(key)
126
22704
            .and_modify(|entry| {
127
7760
                let mut contains = false;
128
18687
                entry.retain(|inner_value| {
129
18687
                    if inner_value.is_subset(&value) {
130
                        // The new value is a superset of an existing entry
131
1238
                        contains = true;
132
1238
                        true
133
17449
                    } else if value.is_subset(inner_value) {
134
                        // Remove any entry that is a superset of the new value
135
646
                        false
136
                    } else {
137
                        // Leave incomparable entries unchanged
138
16803
                        true
139
                    }
140
18687
                });
141

            
142
7760
                if !contains {
143
6550
                    self.antichain_misses += 1; // Was not present
144
6550
                    entry.insert(value.clone());
145
6550
                    inserted = true;
146
6550
                }
147
7760
            })
148
22704
            .or_insert_with(|| {
149
14944
                self.antichain_misses += 1; // Was not present
150
14944
                inserted = true;
151
14944
                VecSet::singleton(value)
152
14944
            });
153

            
154
22704
        self.antichain_inserts += 1;
155
22704
        self.max_antichain = self.max_antichain.max(self.storage.len());
156

            
157
22704
        inserted
158
22704
    }
159

            
160
1410
    fn clear(&mut self) {
161
1410
        self.storage.clear();
162
1410
    }
163
}
164

            
165
impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Antichain<T, U> {
166
1
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167
1
        writeln!(f, "Antichain {{")?;
168
1
        for (key, values) in &self.storage {
169
1
            writeln!(f, "  {:?}: {:?}", key, values)?;
170
        }
171
1
        writeln!(f, "}}")
172
1
    }
173
}
174

            
175
#[cfg(test)]
176
mod tests {
177
    use merc_collections::vecset;
178
    use merc_utilities::random_test;
179
    use rand::RngExt;
180

            
181
    use crate::AC;
182
    use crate::Antichain;
183

            
184
    #[test]
185
1
    fn test_antichain() {
186
1
        let mut antichain: Antichain<u32, u32> = Antichain::new();
187

            
188
1
        let inserted = antichain.insert(1, vecset![2, 3]);
189
1
        assert!(inserted);
190

            
191
1
        println!("{:?}", antichain);
192

            
193
1
        let inserted = antichain.insert(1, vecset![2, 3, 6]);
194
1
        assert!(
195
1
            !inserted,
196
            "The pair (1, {{2,3,6}}) should not be inserted in {:?}.",
197
            antichain
198
        );
199

            
200
1
        let inserted = antichain.insert(1, vecset![2]);
201
1
        assert!(
202
1
            inserted,
203
            "The pair (1, {{2}}) should overwrite (1, {{2, 3}}) in {:?}.",
204
            antichain
205
        );
206

            
207
1
        let inserted = antichain.insert(1, vecset![5, 6]);
208
1
        assert!(
209
1
            inserted,
210
            "The pair (1, {{5, 6}}) should be inserted since it is incomparable to existing pairs in {:?}.",
211
            antichain
212
        );
213
1
    }
214

            
215
    #[test]
216
1
    fn test_random_antichain() {
217
100
        random_test(100, |rng| {
218
100
            let mut antichain: Antichain<u32, u32> = Antichain::new();
219

            
220
            // Insert random pairs into the antichain.
221
100
            for _ in 0..50 {
222
5000
                let key = rng.random_range(0..10);
223
5000
                let set_size = rng.random_range(1..5);
224
5000
                let mut value = vecset![];
225

            
226
12613
                for _ in 0..set_size {
227
12613
                    value.insert(rng.random_range(0..20));
228
12613
                }
229

            
230
5000
                antichain.insert(key, value);
231
            }
232

            
233
100
            antichain.check_consistency();
234
100
        })
235
1
    }
236

            
237
    /// `Antichain::len` used to return `storage.len()` (the number of distinct keys)
238
    /// rather than the total number of (key, value) pairs. When a key maps to multiple
239
    /// incomparable sets, the old implementation under-counted.
240
    #[test]
241
1
    fn test_antichain_len_counts_pairs_not_keys() {
242
1
        let mut antichain: Antichain<u32, u32> = Antichain::new();
243

            
244
        // Insert two incomparable sets under key 1: {2} and {5, 6}.
245
1
        antichain.insert(1, vecset![2]);
246
1
        antichain.insert(1, vecset![5, 6]);
247
        // Insert one set under key 2.
248
1
        antichain.insert(2, vecset![10]);
249

            
250
        // The antichain has 2 pairs for key 1 and 1 pair for key 2 → 3 total.
251
        // Before the fix, `len()` returned `storage.len()` = 2 (two keys).
252
1
        assert_eq!(
253
1
            antichain.len(),
254
            3,
255
            "len() must count pairs, not keys; got {:?}",
256
            antichain
257
        );
258
        // Verify that iter yields exactly as many items as len() reports.
259
1
        assert_eq!(antichain.iter().count(), antichain.len());
260
1
    }
261
}