Skip to main content

slotted_egraphs/
slotmap.rs

1use smallvec::SmallVec;
2
3use crate::*;
4use std::ops::Index;
5
6// Permutations are a special kind of Bijections.
7// Their key & value sets agree!
8pub(crate) type Perm = Bijection;
9
10// Bijections are bijective SlotMaps.
11pub(crate) type Bijection = SlotMap;
12
13#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
14/// A mapping between the parameter-slots of an e-class, and some "invocation" slots that you want to put into them.
15///
16/// This type is relevant for [AppliedId]s.
17pub struct SlotMap {
18    // if (l, r) in map, then there is no (l, r') in map. Each key is uniquely contained.
19    // Also: map is sorted by their keys.
20    map: SmallVec<[(Slot, Slot); 10]>,
21}
22
23impl SlotMap {
24    pub fn new() -> Self {
25        SlotMap {
26            map: Default::default(),
27        }
28    }
29
30    pub fn len(&self) -> usize {
31        self.map.len()
32    }
33
34    pub fn is_empty(&self) -> bool {
35        self.map.is_empty()
36    }
37
38    fn search(&self, l: Slot) -> Result<usize, usize> {
39        self.map.binary_search_by_key(&l, |(x, _)| *x)
40    }
41
42    pub fn contains_key(&self, k: Slot) -> bool {
43        self.get(k).is_some()
44    }
45
46    pub fn insert(&mut self, l: Slot, r: Slot) {
47        match self.search(l) {
48            Ok(i) => {
49                self.map[i] = (l, r);
50            }
51            Err(i) => {
52                self.map.insert(i, (l, r));
53            }
54        }
55    }
56
57    pub fn get(&self, l: Slot) -> Option<Slot> {
58        self.search(l).ok().map(|i| self.map[i].1)
59    }
60
61    pub fn iter(&self) -> impl Iterator<Item = (Slot, Slot)> + '_ {
62        self.map.iter().copied()
63    }
64
65    pub fn into_iter(self) -> impl Iterator<Item = (Slot, Slot)> {
66        self.map.into_iter()
67    }
68
69    // ordered by their keys!
70    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Slot> + '_ {
71        self.map.iter_mut().map(|(_, y)| y)
72    }
73
74    pub fn values_immut(&self) -> impl Iterator<Item = &Slot> + '_ {
75        self.map.iter().map(|(_, y)| y)
76    }
77
78    pub fn keys(&self) -> SmallHashSet<Slot> {
79        self.iter().map(|(x, _)| x).collect()
80    }
81    pub fn values(&self) -> SmallHashSet<Slot> {
82        self.iter().map(|(_, y)| y).collect()
83    }
84    pub fn keys_vec(&self) -> Vec<Slot> {
85        self.iter().map(|(x, _)| x).collect()
86    }
87    pub fn values_vec(&self) -> Vec<Slot> {
88        self.iter().map(|(_, y)| y).collect()
89    }
90
91    pub fn inverse(&self) -> SlotMap {
92        if CHECKS {
93            assert!(self.is_bijection());
94        }
95
96        let mut out = Self::new();
97        for (x, y) in self.iter() {
98            out.insert(y, x);
99        }
100
101        out
102    }
103
104    pub fn is_bijection(&self) -> bool {
105        let mut found = HashSet::default();
106
107        for (_, x) in self.iter() {
108            if found.contains(&x) {
109                return false;
110            }
111
112            found.insert(x);
113        }
114
115        true
116    }
117
118    pub fn is_perm(&self) -> bool {
119        self.is_bijection() && self.keys() == self.values()
120    }
121
122    #[track_caller]
123    pub fn compose(&self, other: &SlotMap) -> SlotMap {
124        if CHECKS {
125            assert_eq!(self.values(), other.keys(), "SlotMap::compose() failed!");
126        }
127
128        self.compose_partial(other)
129    }
130
131    // self :: X -> Y
132    // other :: Y -> Z
133    // -> out :: X -> Z
134    //
135    // In other words, compose_partial first runs "self" and then "other", for any given input Slot.
136    pub fn compose_partial(&self, other: &SlotMap) -> SlotMap {
137        let mut out = SlotMap::new();
138        for (x, y) in self.iter() {
139            if let Some(z) = other.get(y) {
140                out.insert(x, z);
141            }
142        }
143        out
144    }
145
146    // if some slot is missing in `other`, we just choose a fresh slot as output.
147    // self.keys() == self.compose_fresh(other).keys() is guaranteed.
148    pub fn compose_fresh(&self, other: &SlotMap) -> SlotMap {
149        let mut out = SlotMap::new();
150        for (x, y) in self.iter() {
151            if let Some(z) = other.get(y) {
152                out.insert(x, z);
153            } else {
154                out.insert(x, Slot::fresh());
155            }
156        }
157        out
158    }
159
160    pub fn identity(set: &SmallHashSet<Slot>) -> SlotMap {
161        let mut out = SlotMap::new();
162        for &x in set {
163            out.insert(x, x);
164        }
165        out
166    }
167
168    pub fn bijection_from_fresh_to(set: &SmallHashSet<Slot>) -> SlotMap {
169        let mut out = SlotMap::new();
170        for &x in set {
171            out.insert(Slot::fresh(), x);
172        }
173        out
174    }
175
176    pub fn remove(&mut self, x: Slot) {
177        if let Ok(i) = self.search(x) {
178            self.map.remove(i);
179        }
180    }
181
182    pub fn from_pairs(pairs: &[(Slot, Slot)]) -> SlotMap {
183        pairs.iter().copied().collect()
184    }
185
186    // will panic, if the maps are incompatible.
187    #[track_caller]
188    pub fn union(&self, other: &SlotMap) -> Self {
189        let mut out = self.clone();
190
191        for (x, y) in other.iter() {
192            if CHECKS {
193                if let Some(z) = out.get(x) {
194                    assert_eq!(
195                        y, z,
196                        "SlotMap::union: The SlotMaps disagree! {:?} -> {:?} / {:?}",
197                        x, z, y
198                    );
199                }
200            }
201            out.insert(x, y);
202        }
203
204        out
205    }
206
207    pub fn try_union(&self, other: &SlotMap) -> Option<Self> {
208        let mut out = self.clone();
209
210        for (x, y) in other.iter() {
211            if let Some(z) = out.get(x) {
212                if y != z {
213                    return None;
214                }
215            }
216            out.insert(x, y);
217        }
218
219        Some(out)
220    }
221
222    // checks invariants.
223    #[allow(unused)]
224    fn check(&self) {
225        // sortedness.
226        let mut sorted = self.map.clone();
227        sorted.sort_by_key(|(l, _)| *l);
228        assert_eq!(&self.map, &sorted);
229
230        // left-uniqueness.
231        let mut found = HashSet::default();
232        for &(x, _) in self.map.iter() {
233            assert!(!found.contains(&x));
234            found.insert(x);
235        }
236    }
237}
238
239impl Index<Slot> for SlotMap {
240    type Output = Slot;
241
242    #[track_caller]
243    #[inline]
244    fn index(&self, l: Slot) -> &Slot {
245        let Ok(i) = self.search(l) else {
246            panic!("SlotMap::index({:?}): index missing!", l);
247        };
248
249        &self.map[i].1
250    }
251}
252
253impl FromIterator<(Slot, Slot)> for SlotMap {
254    fn from_iter<T>(iter: T) -> Self
255    where
256        T: IntoIterator<Item = (Slot, Slot)>,
257    {
258        let mut m = SlotMap::new();
259        for (x, y) in iter.into_iter() {
260            if CHECKS {
261                assert!(!m.contains_key(x));
262            }
263            m.insert(x, y);
264        }
265        m
266    }
267}
268
269impl<const N: usize> From<[(Slot, Slot); N]> for SlotMap {
270    fn from(pairs: [(Slot, Slot); N]) -> Self {
271        let mut m = SlotMap::new();
272        for (x, y) in pairs {
273            if CHECKS {
274                assert!(!m.contains_key(x));
275            }
276
277            m.insert(x, y);
278        }
279        m
280    }
281}
282
283#[test]
284fn test_slotmap() {
285    let mut m: SlotMap = SlotMap::new();
286    m.insert(Slot::numeric(3), Slot::numeric(7));
287    m.insert(Slot::numeric(2), Slot::numeric(7));
288    m.insert(Slot::numeric(3), Slot::numeric(8));
289    m.insert(Slot::numeric(4), Slot::numeric(7));
290    assert_eq!(m[Slot::numeric(3)], Slot::numeric(8));
291
292    m.check();
293}