1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
4use std::hash::Hash;
5
6use slotmap::{Key, SecondaryMap, SparseSecondaryMap};
7
8pub fn topo_sort<Id, PredsIter>(
18 node_ids: impl IntoIterator<Item = Id>,
19 mut preds_fn: impl FnMut(Id) -> PredsIter,
20) -> Result<Vec<Id>, Vec<Id>>
21where
22 Id: Copy + Eq + Hash,
23 PredsIter: IntoIterator<Item = Id>,
24{
25 let (mut marked, mut order) = Default::default();
26
27 fn pred_dfs_postorder<Id, PredsIter>(
28 node_id: Id,
29 preds_fn: &mut impl FnMut(Id) -> PredsIter,
30 marked: &mut HashMap<Id, bool>, order: &mut Vec<Id>,
32 ) -> Result<(), ()>
33 where
34 Id: Copy + Eq + Hash,
35 PredsIter: IntoIterator<Item = Id>,
36 {
37 match marked.get(&node_id) {
38 Some(_permanent @ true) => Ok(()),
39 Some(_temporary @ false) => {
40 order.clear();
42 order.push(node_id);
43 Err(())
44 }
45 None => {
46 marked.insert(node_id, false);
47 for next_pred in (preds_fn)(node_id) {
48 pred_dfs_postorder(next_pred, preds_fn, marked, order).map_err(|()| {
49 if order.len() == 1 || order.first().unwrap() != order.last().unwrap() {
50 order.push(node_id);
51 }
52 })?;
53 }
54 order.push(node_id);
55 marked.insert(node_id, true);
56 Ok(())
57 }
58 }
59 }
60
61 for node_id in node_ids {
62 if pred_dfs_postorder(node_id, &mut preds_fn, &mut marked, &mut order).is_err() {
63 let end = order.last().unwrap();
65 let beg = order.iter().position(|n| n == end).unwrap();
66 order.drain(0..=beg);
67 return Err(order);
68 }
69 }
70
71 Ok(order)
72}
73
74pub fn validate_topo_sort<Id, PredsIter>(
79 topo_sort: impl IntoIterator<Item = Id>,
80 mut preds_fn: impl FnMut(Id) -> PredsIter,
81) -> Result<(), (Id, Id)>
82where
83 Id: Copy + Eq + Ord,
84 PredsIter: IntoIterator<Item = Id>,
85{
86 let indexed = topo_sort
87 .into_iter()
88 .enumerate()
89 .map(|(i, n)| (n, i))
90 .collect::<BTreeMap<_, _>>();
91 for (&succ, &succ_idx) in indexed.iter() {
92 for pred in (preds_fn)(succ) {
93 let Some(&pred_idx) = indexed.get(&pred) else {
94 panic!("predecessor not in topo sort");
95 };
96 if succ_idx <= pred_idx {
97 return Err((pred, succ));
99 }
100 }
101 }
102 Ok(())
103}
104
105pub struct SubgraphMerge<K>
111where
112 K: Key,
113{
114 subgraph_preds: SecondaryMap<K, Vec<K>>,
116 toposort_node: Vec<K>,
119 sg_idx: SparseSecondaryMap<K, usize>,
122 sg_len: SparseSecondaryMap<K, usize>,
126
127 subgraph_unionfind: crate::union_find::UnionFind<K>,
129
130 enemies: SecondaryMap<K, HashSet<K>>,
133}
134
135impl<K> SubgraphMerge<K>
136where
137 K: Key,
138{
139 pub fn new<PredsIter>(
147 keys: impl IntoIterator<Item = K>,
148 mut preds_fn: impl FnMut(K) -> PredsIter,
149 enemies_iter: impl IntoIterator<Item = (K, K)>,
150 ) -> Result<Self, Vec<K>>
151 where
152 PredsIter: IntoIterator<Item = K>,
153 {
154 let subgraph_preds = keys
155 .into_iter()
156 .map(|k| (k, (preds_fn)(k).into_iter().collect()))
157 .collect::<SecondaryMap<K, Vec<K>>>();
158 let toposort_node =
159 topo_sort(subgraph_preds.keys(), |k| subgraph_preds[k].iter().copied())?;
160 let sg_idx = toposort_node
161 .iter()
162 .enumerate()
163 .map(|(i, &k)| (k, i))
164 .collect();
165 let sg_len = toposort_node.iter().map(|&k| (k, 1)).collect();
166 let subgraph_unionfind = crate::union_find::UnionFind::with_capacity(toposort_node.len());
167
168 let mut enemies = SecondaryMap::<K, HashSet<K>>::new();
169 for (a, b) in enemies_iter {
170 assert_ne!(a, b, "no-merge pair must not contain the same node twice");
171 enemies.entry(a).unwrap().or_default().insert(b);
172 enemies.entry(b).unwrap().or_default().insert(a);
173 }
174
175 Ok(Self {
176 subgraph_preds,
177 toposort_node,
178 sg_idx,
179 sg_len,
180 subgraph_unionfind,
181 enemies,
182 })
183 }
184
185 pub fn find(&mut self, k: K) -> K {
187 self.subgraph_unionfind.find(k)
188 }
189
190 pub fn same_set(&mut self, u: K, v: K) -> bool {
192 self.subgraph_unionfind.same_set(u, v)
193 }
194
195 pub fn subgraphs(&self) -> impl Iterator<Item = &[K]> {
198 let mut i = 0;
199 std::iter::from_fn(move || {
200 let Some(&sg_node) = self.toposort_node.get(i) else {
201 debug_assert_eq!(i, self.toposort_node.len());
202 return None;
203 };
204 debug_assert_eq!(i, self.sg_idx[sg_node]);
205 let sg_len = self.sg_len[sg_node];
206 let sg_slice = &self.toposort_node[i..i + sg_len];
207 i += sg_len;
208 Some(sg_slice)
209 })
210 }
211
212 pub fn try_merge(&mut self, u: K, v: K) -> bool {
216 let u = self.subgraph_unionfind.find(u);
220 let v = self.subgraph_unionfind.find(v);
221 if u == v {
222 return true;
224 }
225
226 if self
228 .enemies
229 .get(u)
230 .is_some_and(|enemy_set| enemy_set.contains(&v))
231 {
232 return false;
233 }
234
235 let (u, v) = if self.sg_idx[u] < self.sg_idx[v] {
237 (u, v)
238 } else {
239 (v, u)
240 };
241 let (u_nodes, v_nodes, window) = {
244 let (u_idx, u_len) = (self.sg_idx[u], self.sg_len[u]);
245 let (v_idx, v_len) = (self.sg_idx[v], self.sg_len[v]);
246 (
247 &self.toposort_node[u_idx..u_idx + u_len],
248 &self.toposort_node[v_idx..v_idx + v_len],
249 u_idx..v_idx + v_len,
250 )
251 };
252
253 let mut stack = vec![v];
258 let mut visited = HashSet::<_>::from_iter([v]);
259
260 while let Some(x) = stack.pop() {
261 for &p in self.subgraph_preds[x].iter() {
262 let root_p = self.subgraph_unionfind.find(p);
263
264 if root_p == u {
265 if x == v {
266 continue;
268 }
269 return false;
271 }
272
273 if window.contains(&self.sg_idx[root_p]) && visited.insert(root_p) {
275 stack.push(root_p);
276 }
277 }
278 }
279
280 {
283 let _new_root = self.subgraph_unionfind.union(u, v);
287 debug_assert_eq!(u, _new_root);
288 let v_preds = &mut self.subgraph_preds.remove(v).unwrap();
289 let u_preds = &mut self.subgraph_preds[u];
290 u_preds.append(v_preds);
291 u_preds.retain_mut(|x| {
293 *x = self.subgraph_unionfind.find(*x);
294 *x != u });
296 u_preds.sort_unstable();
298 u_preds.dedup();
299 }
300 {
302 self.sg_idx.remove(v).unwrap();
303 let v_len = self.sg_len.remove(v).unwrap();
304 self.sg_len[u] += v_len;
306 }
307 for w in self.enemies.remove(v).into_iter().flatten() {
309 debug_assert_ne!(
310 w, u,
311 "`w` in an enemy of `v`, so it can't be `w == u`, since we are merging `u` and `v`"
312 );
313 self.enemies.entry(u).unwrap().or_default().insert(w);
315 let w_enemies = self.enemies.get_mut(w).unwrap();
319 let _removed = w_enemies.remove(&v);
320 debug_assert!(_removed);
321 w_enemies.insert(u);
322 }
323
324 {
327 let sorted_groups = {
328 let reps_in_window = self.toposort_node[window.clone()]
329 .iter()
330 .map(|&k| self.subgraph_unionfind.find(k))
331 .collect::<BTreeSet<_>>();
332
333 let subgraph_preds = &self.subgraph_preds;
338 let subgraph_unionfind = &mut self.subgraph_unionfind;
339 let sg_idx = &self.sg_idx;
340 topo_sort(reps_in_window, |k| {
341 subgraph_preds[k]
342 .iter()
343 .map(|&p| subgraph_unionfind.find(p))
344 .filter(|&p| window.contains(&sg_idx[p])) .collect::<Vec<_>>()
346 .into_iter()
347 })
348 .expect("bug: cycle check passed but re-toposort found cycle")
349 };
350
351 let mut buf = Vec::with_capacity(window.len());
355 for &group in &sorted_groups {
356 if group == u {
357 buf.extend_from_slice(u_nodes);
358 buf.extend_from_slice(v_nodes);
359 } else {
360 let g_idx = self.sg_idx[group];
361 let g_len = self.sg_len[group];
362 buf.extend_from_slice(&self.toposort_node[g_idx..g_idx + g_len]);
363 }
364 }
365 self.toposort_node[window.clone()].copy_from_slice(&buf);
366
367 let mut pos = window.start;
369 for &group in &sorted_groups {
370 self.sg_idx[group] = pos;
371 pos += self.sg_len[group];
372 }
373 debug_assert_eq!(window.end, pos);
374 }
375
376 true
377 }
378}
379
380#[cfg(test)]
381mod test {
382 use std::collections::{BTreeMap, BTreeSet};
383
384 use itertools::Itertools;
385 use slotmap::SlotMap;
386
387 use super::*;
388
389 #[test]
390 pub fn test_toposort() {
391 let edges = [
392 (5, 11),
393 (11, 2),
394 (11, 9),
395 (11, 10),
396 (7, 11),
397 (7, 8),
398 (8, 9),
399 (3, 8),
400 (3, 10),
401 ];
402
403 let sort = topo_sort([2, 3, 5, 7, 8, 9, 10, 11], |v| {
405 edges
406 .iter()
407 .filter(move |&&(_, dst)| v == dst)
408 .map(|&(src, _)| src)
409 });
410 assert!(
411 sort.is_ok(),
412 "Did not expect cycle: {:?}",
413 sort.unwrap_err()
414 );
415
416 let sort = sort.unwrap();
417 println!("{:?}", sort);
418
419 let position: BTreeMap<_, _> = sort.iter().enumerate().map(|(i, &x)| (x, i)).collect();
420 for (src, dst) in edges.iter() {
421 assert!(position[src] < position[dst]);
422 }
423 }
424
425 #[test]
426 pub fn test_toposort_cycle() {
427 let edges = [
436 ('A', 'B'),
437 ('B', 'C'),
438 ('C', 'E'),
439 ('D', 'B'),
440 ('E', 'F'),
441 ('E', 'D'),
442 ];
443 let ids = edges
444 .iter()
445 .flat_map(|&(a, b)| [a, b])
446 .collect::<BTreeSet<_>>();
447 let cycle_rotations = BTreeSet::from_iter([
448 ['B', 'C', 'E', 'D'],
449 ['C', 'E', 'D', 'B'],
450 ['E', 'D', 'B', 'C'],
451 ['D', 'B', 'C', 'E'],
452 ]);
453
454 let permutations = ids.iter().copied().permutations(ids.len());
455 for permutation in permutations {
456 let result = topo_sort(permutation.iter().copied(), |v| {
457 edges
458 .iter()
459 .filter(move |&&(_, dst)| v == dst)
460 .map(|&(src, _)| src)
461 });
462 assert!(result.is_err());
463 let cycle = result.unwrap_err();
464 assert!(
465 cycle_rotations.contains(&*cycle),
466 "cycle: {:?}, vertex order: {:?}",
467 cycle,
468 permutation
469 );
470 }
471 }
472
473 #[test]
474 pub fn test_validate_topo_sort_valid() {
475 let edges: &[(i32, i32)] = &[(1, 2), (2, 3), (3, 4)];
478
479 let result = validate_topo_sort([1, 2, 3, 4], |v| {
480 edges
481 .iter()
482 .filter(move |&&(_src, dst)| v == dst)
483 .map(|&(src, _)| src)
484 });
485 assert_eq!(result, Ok(()));
486 }
487
488 #[test]
489 pub fn test_validate_topo_sort_invalid() {
490 let edges: &[(i32, i32)] = &[(1, 2), (2, 3), (3, 4)];
493
494 let result = validate_topo_sort([1, 3, 2, 4], |v| {
495 edges
496 .iter()
497 .filter(move |&&(_src, dst)| v == dst)
498 .map(|&(src, _)| src)
499 });
500 assert!(result.is_err());
501 }
502
503 #[test]
504 pub fn test_validate_topo_sort_diamond() {
505 let edges: &[(i32, i32)] = &[(1, 2), (1, 3), (2, 4), (3, 4)];
512
513 let result = validate_topo_sort([1, 2, 3, 4], |v| {
515 edges
516 .iter()
517 .filter(move |&&(_src, dst)| v == dst)
518 .map(|&(src, _)| src)
519 });
520 assert_eq!(result, Ok(()));
521
522 let result = validate_topo_sort([1, 3, 2, 4], |v| {
524 edges
525 .iter()
526 .filter(move |&&(_src, dst)| v == dst)
527 .map(|&(src, _)| src)
528 });
529 assert_eq!(result, Ok(()));
530
531 let result = validate_topo_sort([4, 1, 2, 3], |v| {
533 edges
534 .iter()
535 .filter(move |&&(_src, dst)| v == dst)
536 .map(|&(src, _)| src)
537 });
538 assert!(result.is_err());
539 }
540
541 #[test]
542 pub fn test_subgraph_merge_basic() {
543 let mut preds = SlotMap::new();
544
545 let a = preds.insert(vec![]);
546 let b = preds.insert(vec![]);
547 let c = preds.insert(vec![]);
548 let d = preds.insert(vec![]);
549 let e = preds.insert(vec![]);
550 let f = preds.insert(vec![]);
551
552 preds[b].push(a);
553 preds[c].push(b);
554 preds[d].push(b);
555 preds[e].push(c);
556 preds[e].push(d);
557 preds[f].push(e);
558
559 let mut merge = SubgraphMerge::new(
560 preds.keys(),
561 |v| preds[v].iter().copied(),
562 std::iter::empty(),
563 )
564 .unwrap();
565
566 assert!(merge.try_merge(a, a)); assert!(merge.try_merge(b, c));
573 assert!(merge.try_merge(b, c)); assert!(!merge.try_merge(c, e)); assert!(merge.try_merge(d, e));
580 assert!(merge.try_merge(c, e)); }
582
583 #[test]
584 pub fn test_subgraph_merge_enemies() {
585 let mut preds = SlotMap::new();
586
587 let a = preds.insert(vec![]);
589 let b = preds.insert(vec![]);
590 let c = preds.insert(vec![]);
591 let d = preds.insert(vec![]);
592
593 preds[b].push(a);
594 preds[c].push(b);
595 preds[d].push(c);
596
597 let mut merge =
599 SubgraphMerge::new(preds.keys(), |v| preds[v].iter().copied(), [(b, c)]).unwrap();
600
601 assert!(!merge.try_merge(b, c));
603
604 assert!(merge.try_merge(a, b));
606
607 assert!(!merge.try_merge(a, c));
609 assert!(!merge.try_merge(b, c));
610
611 assert!(merge.try_merge(c, d));
613
614 assert!(!merge.try_merge(a, d));
616 assert!(!merge.try_merge(b, d));
617 }
618}