1use std::collections::HashSet;
8
9fn undirected_adjacency(from: &[usize], to: &[usize], n_nodes: usize) -> Vec<Vec<usize>> {
11 let mut sets: Vec<HashSet<usize>> = vec![HashSet::new(); n_nodes];
12 for i in 0..from.len() {
13 if from[i] != to[i] {
14 sets[from[i]].insert(to[i]);
15 sets[to[i]].insert(from[i]);
16 }
17 }
18 sets.into_iter()
19 .map(|s| {
20 let mut v: Vec<usize> = s.into_iter().collect();
21 v.sort_unstable();
22 v
23 })
24 .collect()
25}
26
27fn is_endpoint(adj: &[Vec<usize>], node: usize) -> bool {
31 adj[node].len() != 2
32}
33
34pub fn simplify_paths(from: &[usize], to: &[usize], n_nodes: usize) -> Vec<Vec<usize>> {
38 let adj = undirected_adjacency(from, to, n_nodes);
39 let mut paths: Vec<Vec<usize>> = Vec::new();
40 let mut seen: HashSet<(usize, usize)> = HashSet::new();
42
43 for u in 0..n_nodes {
44 if !is_endpoint(&adj, u) {
45 continue;
46 }
47 for &start in &adj[u] {
48 if seen.contains(&(u, start)) {
49 continue;
50 }
51 let mut path = vec![u];
52 let mut prev = u;
53 let mut cur = start;
54 loop {
55 path.push(cur);
56 if is_endpoint(&adj, cur) || cur == u {
57 break;
58 }
59 let nbrs = &adj[cur];
60 let next = if nbrs[0] == prev { nbrs[1] } else { nbrs[0] };
61 prev = cur;
62 cur = next;
63 }
64 let last = path[path.len() - 1];
65 let before_last = path[path.len() - 2];
66 seen.insert((u, start));
67 seen.insert((last, before_last));
68 paths.push(path);
69 }
70 }
71 paths
72}
73
74fn find(parent: &mut [usize], x: usize) -> usize {
76 let mut root = x;
77 while parent[root] != root {
78 root = parent[root];
79 }
80 let mut cur = x;
81 while parent[cur] != root {
82 let next = parent[cur];
83 parent[cur] = root;
84 cur = next;
85 }
86 root
87}
88
89pub fn connected_components(a: &[usize], b: &[usize], n: usize) -> Vec<i32> {
92 let mut parent: Vec<usize> = (0..n).collect();
93 for i in 0..a.len() {
94 let ra = find(&mut parent, a[i]);
95 let rb = find(&mut parent, b[i]);
96 if ra != rb {
97 parent[ra] = rb;
98 }
99 }
100 (0..n).map(|i| find(&mut parent, i) as i32).collect()
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn collapses_degree_two_chain() {
109 let from = vec![0, 1, 2];
111 let to = vec![1, 2, 3];
112 let paths = simplify_paths(&from, &to, 4);
113 assert_eq!(paths.len(), 1);
114 assert_eq!(paths[0], vec![0, 1, 2, 3]);
115 }
116
117 #[test]
118 fn keeps_intersection_nodes() {
119 let from = vec![0, 0, 0, 0];
121 let to = vec![1, 2, 3, 4];
122 let paths = simplify_paths(&from, &to, 5);
123 assert_eq!(paths.len(), 4);
125 assert!(paths.iter().all(|p| p.len() == 2));
126 }
127
128 #[test]
129 fn components_group_connected_nodes() {
130 let comp = connected_components(&[0, 2], &[1, 3], 5);
132 assert_eq!(comp[0], comp[1]);
133 assert_eq!(comp[2], comp[3]);
134 assert_ne!(comp[0], comp[2]);
135 assert_ne!(comp[4], comp[0]);
136 }
137}