rustc_mir_transform/coverage/counters/union_find.rs
1use std::cmp::Ordering;
2use std::mem;
3
4use rustc_index::{Idx, IndexVec};
5
6#[cfg(test)]
7mod tests;
8
9/// Simple implementation of a union-find data structure, i.e. a disjoint-set
10/// forest.
11#[derive(Debug)]
12pub(crate) struct UnionFind<Key: Idx> {
13 table: IndexVec<Key, UnionFindEntry<Key>>,
14}
15
16#[derive(Debug)]
17struct UnionFindEntry<Key> {
18 /// Transitively points towards the "root" of the set containing this key.
19 ///
20 /// Invariant: A root key is its own parent.
21 parent: Key,
22 /// When merging two "root" keys, their ranks determine which key becomes
23 /// the new root, to prevent the parent tree from becoming unnecessarily
24 /// tall. See [`UnionFind::unify`] for details.
25 rank: u32,
26}
27
28impl<Key: Idx> UnionFind<Key> {
29 /// Creates a new disjoint-set forest containing the keys `0..num_keys`.
30 /// Initially, every key is part of its own one-element set.
31 pub(crate) fn new(num_keys: usize) -> Self {
32 // Initially, every key is the root of its own set, so its parent is itself.
33 Self { table: IndexVec::from_fn_n(|key| UnionFindEntry { parent: key, rank: 0 }, num_keys) }
34 }
35
36 /// Returns the "root" key of the disjoint-set containing the given key.
37 /// If two keys have the same root, they belong to the same set.
38 ///
39 /// Also updates internal data structures to make subsequent `find`
40 /// operations faster.
41 pub(crate) fn find(&mut self, key: Key) -> Key {
42 // Loop until we find a key that is its own parent.
43 let mut curr = key;
44 while let parent = self.table[curr].parent
45 && curr != parent
46 {
47 // Perform "path compression" by peeking one layer ahead, and
48 // setting the current key's parent to that value.
49 // (This works even when `parent` is the root of its set, because
50 // of the invariant that a root is its own parent.)
51 let parent_parent = self.table[parent].parent;
52 self.table[curr].parent = parent_parent;
53
54 // Advance by one step and continue.
55 curr = parent;
56 }
57 curr
58 }
59
60 /// Merges the set containing `a` and the set containing `b` into one set.
61 ///
62 /// Returns the common root of both keys, after the merge.
63 pub(crate) fn unify(&mut self, a: Key, b: Key) -> Key {
64 let mut a = self.find(a);
65 let mut b = self.find(b);
66
67 // If both keys have the same root, they're already in the same set,
68 // so there's nothing more to do.
69 if a == b {
70 return a;
71 };
72
73 // Ensure that `a` has strictly greater rank, swapping if necessary.
74 // If both keys have the same rank, increment the rank of `a` so that
75 // future unifications will also prefer `a`, leading to flatter trees.
76 match Ord::cmp(&self.table[a].rank, &self.table[b].rank) {
77 Ordering::Less => mem::swap(&mut a, &mut b),
78 Ordering::Equal => self.table[a].rank += 1,
79 Ordering::Greater => {}
80 }
81
82 debug_assert!(self.table[a].rank > self.table[b].rank);
83 debug_assert_eq!(self.table[b].parent, b);
84
85 // Make `a` the parent of `b`.
86 self.table[b].parent = a;
87
88 a
89 }
90
91 /// Takes a "snapshot" of the current state of this disjoint-set forest, in
92 /// the form of a vector that directly maps each key to its current root.
93 pub(crate) fn snapshot(&mut self) -> IndexVec<Key, Key> {
94 self.table.indices().map(|key| self.find(key)).collect()
95 }
96}