1use std::collections::{BTreeSet, VecDeque};
15
16use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
17
18use crate::graph::{Graph, edge::Edge, node::Node};
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct SeseRegion<NodeId, EdgeId> {
23 pub parent: Option<usize>,
25 pub children: Vec<usize>,
27 pub entry_edge: Option<EdgeId>,
29 pub exit_edge: Option<EdgeId>,
31 pub nodes: Vec<NodeId>,
33 pub contained_nodes: Vec<NodeId>,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct SeseTree<NodeId, EdgeId> {
40 pub regions: Vec<SeseRegion<NodeId, EdgeId>>,
41}
42
43impl<NodeId, EdgeId> SeseTree<NodeId, EdgeId> {
44 pub fn root(&self) -> &SeseRegion<NodeId, EdgeId> {
45 &self.regions[0]
46 }
47
48 pub fn has_nontrivial_regions(&self) -> bool {
49 self.regions.len() > 1
50 }
51}
52
53#[derive(Clone, Copy)]
54struct IEdge<E> {
55 from: usize,
56 to: usize,
57 original: Option<E>,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct SeseCandidate<NodeId, EdgeId> {
67 pub entry_edge: EdgeId,
68 pub exit_edge: EdgeId,
69 pub contained_nodes: Vec<NodeId>,
70}
71
72pub fn compute_sese_candidates<G>(
76 graph: &G,
77 root: G::NodeId,
78) -> Vec<SeseCandidate<G::NodeId, G::EdgeId>>
79where
80 G: Graph,
81 G::NodeId: Ord,
82 G::EdgeId: Ord,
83{
84 let mut ids = Vec::new();
85 let mut seen: HashSet<G::NodeId> = HashSet::default();
86 let mut queue = VecDeque::from([root]);
87 while let Some(id) = queue.pop_front() {
88 if !seen.insert(id) {
89 continue;
90 }
91 ids.push(id);
92 let mut succ: Vec<_> = graph
93 .get_node(id)
94 .into_iter()
95 .flat_map(|node| node.children().map(|edge| edge.node_id()))
96 .collect();
97 succ.sort();
98 queue.extend(succ);
99 }
100 ids.sort();
101
102 let index: HashMap<_, _> = ids.iter().enumerate().map(|(i, id)| (*id, i)).collect();
103 let synthetic_exit = ids.len();
104 let mut edges = Vec::new();
105 let mut original_edges: Vec<_> = graph
106 .edges()
107 .filter_map(|edge| {
108 let from = edge.from_id();
109 let to = edge.to_id();
110 (from != to && index.contains_key(&from) && index.contains_key(&to)).then_some((
111 edge.id(),
112 from,
113 to,
114 ))
115 })
116 .collect();
117 original_edges.sort_by_key(|(id, _, _)| *id);
118 for (id, from, to) in original_edges {
119 edges.push(IEdge {
120 from: index[&from],
121 to: index[&to],
122 original: Some(id),
123 });
124 }
125 let original_count = edges.len();
126 let mut has_out = vec![false; ids.len()];
127 for edge in &edges {
128 has_out[edge.from] = true;
129 }
130 let mut added_exit = false;
131 for (node, outgoing) in has_out.into_iter().enumerate() {
132 if !outgoing {
133 edges.push(IEdge {
134 from: node,
135 to: synthetic_exit,
136 original: None,
137 });
138 added_exit = true;
139 }
140 }
141 if !added_exit && !ids.is_empty() {
142 edges.push(IEdge {
143 from: ids.len() - 1,
144 to: synthetic_exit,
145 original: None,
146 });
147 added_exit = true;
148 }
149 if added_exit {
150 edges.push(IEdge {
151 from: synthetic_exit,
152 to: index[&root],
153 original: None,
154 });
155 }
156
157 let n = ids.len() + 1;
158 let entry = index[&root];
159 let cycle_classes = jpp_cycle_classes(&edges, n, entry);
160 let mut candidates = Vec::new();
161 for a in 0..original_count {
162 for b in 0..original_count {
163 if a == b || !edge_dominates(&edges, n, entry, a, b) {
164 continue;
165 }
166 if !edge_postdominates(&edges, n, synthetic_exit, b, a)
167 || cycle_classes[a] == 0
168 || cycle_classes[a] != cycle_classes[b]
169 {
170 continue;
171 }
172 let contained = region_nodes(&edges, n, a, b);
173 if !contained.is_empty() {
174 candidates.push(SeseCandidate {
175 entry_edge: edges[a].original.unwrap(),
176 exit_edge: edges[b].original.unwrap(),
177 contained_nodes: contained.into_iter().map(|node| ids[node]).collect(),
178 });
179 }
180 }
181 }
182 candidates.sort_by_key(|candidate| {
183 (
184 std::cmp::Reverse(candidate.contained_nodes.len()),
185 candidate.entry_edge,
186 candidate.exit_edge,
187 )
188 });
189 candidates
190}
191
192pub fn compute_sese<G>(graph: &G, root: G::NodeId) -> SeseTree<G::NodeId, G::EdgeId>
196where
197 G: Graph,
198 G::NodeId: Ord,
199 G::EdgeId: Ord,
200{
201 let mut ids = Vec::new();
202 let mut seen: HashSet<G::NodeId> = HashSet::default();
203 let mut queue = VecDeque::from([root]);
204 while let Some(id) = queue.pop_front() {
205 if !seen.insert(id) {
206 continue;
207 }
208 ids.push(id);
209 let mut succ: Vec<_> = graph
210 .get_node(id)
211 .into_iter()
212 .flat_map(|node| node.children().map(|edge| edge.node_id()))
213 .collect();
214 succ.sort();
215 queue.extend(succ);
216 }
217 ids.sort();
218
219 let index: HashMap<_, _> = ids.iter().enumerate().map(|(i, id)| (*id, i)).collect();
220 let synthetic_exit = ids.len();
221 let mut edges = Vec::new();
222 let mut original_edges: Vec<_> = graph
223 .edges()
224 .filter_map(|edge| {
225 let from = edge.from_id();
226 let to = edge.to_id();
227 (from != to && index.contains_key(&from) && index.contains_key(&to)).then_some((
228 edge.id(),
229 from,
230 to,
231 ))
232 })
233 .collect();
234 original_edges.sort_by_key(|(id, _, _)| *id);
235 for (id, from, to) in original_edges {
236 edges.push(IEdge {
237 from: index[&from],
238 to: index[&to],
239 original: Some(id),
240 });
241 }
242
243 let mut has_out = vec![false; ids.len()];
244 for edge in &edges {
245 has_out[edge.from] = true;
246 }
247 let mut added_exit = false;
248 for (node, outgoing) in has_out.into_iter().enumerate() {
249 if !outgoing {
250 edges.push(IEdge {
251 from: node,
252 to: synthetic_exit,
253 original: None,
254 });
255 added_exit = true;
256 }
257 }
258 if !added_exit && !ids.is_empty() {
262 edges.push(IEdge {
263 from: ids.len() - 1,
264 to: synthetic_exit,
265 original: None,
266 });
267 added_exit = true;
268 }
269 if added_exit {
270 edges.push(IEdge {
271 from: synthetic_exit,
272 to: index[&root],
273 original: None,
274 });
275 }
276
277 let n = ids.len() + 1;
278 let entry = index[&root];
279 let original_count = edges
280 .iter()
281 .take_while(|edge| edge.original.is_some())
282 .count();
283 let cycle_classes = jpp_cycle_classes(&edges, n, entry);
284 #[cfg(test)]
285 for a in 0..original_count {
286 for b in 0..original_count {
287 if a != b {
288 assert_eq!(
289 cycle_classes[a] == cycle_classes[b],
290 cycle_equivalent(&edges, n, a, b),
291 "cycle-class mismatch for internal edges {a} and {b}"
292 );
293 }
294 }
295 }
296 let mut candidates = Vec::<(usize, usize, BTreeSet<usize>)>::new();
297 for a in 0..original_count {
298 for b in 0..original_count {
299 if a == b || !edge_dominates(&edges, n, entry, a, b) {
300 continue;
301 }
302 if !edge_postdominates(&edges, n, synthetic_exit, b, a) {
303 continue;
304 }
305 if cycle_classes[a] == 0 || cycle_classes[a] != cycle_classes[b] {
306 continue;
307 }
308 #[cfg(test)]
309 debug_assert!(cycle_equivalent(&edges, n, a, b));
310 let contained = region_nodes(&edges, n, a, b);
311 if !contained.is_empty() {
312 candidates.push((a, b, contained));
313 }
314 }
315 }
316
317 candidates.sort_by_key(|(a, b, nodes)| (nodes.len(), *a, *b));
322 let mut smallest_for_boundary: HashMap<usize, usize> = HashMap::default();
323 for (index, (a, b, _)) in candidates.iter().enumerate() {
324 smallest_for_boundary.entry(*a).or_insert(index);
325 smallest_for_boundary.entry(*b).or_insert(index);
326 }
327 let mut selected: Vec<_> = candidates
328 .iter()
329 .enumerate()
330 .filter(|(index, (a, b, _))| {
331 smallest_for_boundary.get(a) == Some(index)
332 || smallest_for_boundary.get(b) == Some(index)
333 })
334 .map(|(_, candidate)| candidate.clone())
335 .collect();
336 selected.retain(|(_, _, candidate)| {
339 !candidates.iter().any(|(_, _, other)| {
340 let intersects = candidate.iter().any(|node| other.contains(node));
341 intersects && !candidate.is_subset(other) && !other.is_subset(candidate)
342 })
343 });
344 selected.sort_by_key(|(a, b, nodes)| (std::cmp::Reverse(nodes.len()), *a, *b));
345
346 let mut regions = vec![SeseRegion {
347 parent: None,
348 children: Vec::new(),
349 entry_edge: None,
350 exit_edge: None,
351 nodes: Vec::new(),
352 contained_nodes: ids.clone(),
353 }];
354 let mut sets = vec![(0..ids.len()).collect::<BTreeSet<_>>()];
355 for (a, b, nodes) in selected {
356 let parent = sets
357 .iter()
358 .enumerate()
359 .filter(|(_, set)| nodes.is_subset(set))
360 .min_by_key(|(_, set)| set.len())
361 .map(|(i, _)| i)
362 .unwrap_or(0);
363 let id = regions.len();
364 regions.push(SeseRegion {
365 parent: Some(parent),
366 children: Vec::new(),
367 entry_edge: edges[a].original,
368 exit_edge: edges[b].original,
369 nodes: Vec::new(),
370 contained_nodes: nodes.iter().map(|i| ids[*i]).collect(),
371 });
372 sets.push(nodes);
373 regions[parent].children.push(id);
374 }
375 for region in &mut regions {
376 region.children.sort_unstable();
378 }
379 for (node_index, node) in ids.iter().copied().enumerate() {
381 let owner = sets
382 .iter()
383 .enumerate()
384 .filter(|(_, set)| set.contains(&node_index))
385 .min_by_key(|(_, set)| set.len())
386 .map(|(id, _)| id)
387 .unwrap_or(0);
388 regions[owner].nodes.push(node);
389 }
390
391 SeseTree { regions }
392}
393
394fn jpp_cycle_classes<E: Copy>(input: &[IEdge<E>], n: usize, root: usize) -> Vec<usize> {
397 let mut edges = input.to_vec();
398 let mut incident = vec![Vec::<usize>::new(); n];
399 for (id, edge) in edges.iter().enumerate() {
400 incident[edge.from].push(id);
401 incident[edge.to].push(id);
402 }
403
404 let mut parent = vec![None; n];
405 let mut parent_edge = vec![None; n];
406 let mut number = vec![usize::MAX; n];
407 let mut end = vec![0; n];
408 let mut preorder = Vec::new();
409 let mut tree = vec![false; edges.len()];
410 #[allow(clippy::too_many_arguments)]
413 fn visit<E: Copy>(
414 node: usize,
415 edges: &[IEdge<E>],
416 incident: &[Vec<usize>],
417 parent: &mut [Option<usize>],
418 parent_edge: &mut [Option<usize>],
419 number: &mut [usize],
420 end: &mut [usize],
421 preorder: &mut Vec<usize>,
422 tree: &mut [bool],
423 ) {
424 number[node] = preorder.len();
425 preorder.push(node);
426 for &eid in &incident[node] {
427 if parent_edge[node] == Some(eid) {
428 continue;
429 }
430 let edge = edges[eid];
431 let other = if edge.from == node {
432 edge.to
433 } else {
434 edge.from
435 };
436 if number[other] == usize::MAX {
437 parent[other] = Some(node);
438 parent_edge[other] = Some(eid);
439 visit(
440 other,
441 edges,
442 incident,
443 parent,
444 parent_edge,
445 number,
446 end,
447 preorder,
448 tree,
449 );
450 tree[eid] = true;
451 }
452 }
453 end[node] = preorder.len();
454 }
455 visit(
456 root,
457 &edges,
458 &incident,
459 &mut parent,
460 &mut parent_edge,
461 &mut number,
462 &mut end,
463 &mut preorder,
464 &mut tree,
465 );
466 let is_descendant = |node: usize, ancestor: usize| {
467 node != ancestor && number[ancestor] <= number[node] && number[node] < end[ancestor]
468 };
469
470 let mut hi = vec![usize::MAX; n];
471 let mut brackets = vec![Vec::<usize>::new(); n];
472 let mut classes = vec![0usize; edges.len()];
473 let mut recent_size = vec![0usize; edges.len()];
474 let mut recent_class = vec![0usize; edges.len()];
475 let mut capping = Vec::<usize>::new();
476 let mut next_class = 1usize;
477
478 for &node in preorder.iter().rev() {
479 let children: Vec<_> = preorder
480 .iter()
481 .copied()
482 .filter(|child| parent[*child] == Some(node))
483 .collect();
484 let hi0 = incident[node]
485 .iter()
486 .copied()
487 .filter(|eid| !tree[*eid])
488 .filter_map(|eid| {
489 let edge = edges[eid];
490 let other = if edge.from == node {
491 edge.to
492 } else {
493 edge.from
494 };
495 is_descendant(node, other).then_some(number[other])
496 })
497 .min()
498 .unwrap_or(usize::MAX);
499 let hi1 = children
500 .iter()
501 .map(|child| hi[*child])
502 .min()
503 .unwrap_or(usize::MAX);
504 hi[node] = hi0.min(hi1);
505 let mut skipped_hi_child = false;
506 let hi2 = children
507 .iter()
508 .filter_map(|child| {
509 if !skipped_hi_child && hi[*child] == hi1 {
510 skipped_hi_child = true;
511 None
512 } else {
513 Some(hi[*child])
514 }
515 })
516 .min()
517 .unwrap_or(usize::MAX);
518
519 for child in children {
520 let child_brackets = std::mem::take(&mut brackets[child]);
521 brackets[node].extend(child_brackets);
522 }
523 for &eid in &capping {
524 let edge = edges[eid];
525 let child = if edge.to == node { edge.from } else { edge.to };
528 if is_descendant(child, node) {
529 brackets[node].retain(|candidate| *candidate != eid);
530 }
531 }
532 for &eid in &incident[node] {
533 if tree[eid] {
534 continue;
535 }
536 let edge = edges[eid];
537 let other = if edge.from == node {
538 edge.to
539 } else {
540 edge.from
541 };
542 if is_descendant(other, node) {
543 brackets[node].retain(|candidate| *candidate != eid);
544 if classes[eid] == 0 {
545 classes[eid] = next_class;
546 next_class += 1;
547 }
548 }
549 }
550 for &eid in &incident[node] {
551 if tree[eid] {
552 continue;
553 }
554 let edge = edges[eid];
555 let other = if edge.from == node {
556 edge.to
557 } else {
558 edge.from
559 };
560 if is_descendant(node, other) {
561 brackets[node].push(eid);
562 }
563 }
564 if hi2 < hi0 {
565 let eid = edges.len();
566 let ancestor = preorder[hi2];
567 edges.push(IEdge {
568 from: node,
569 to: ancestor,
570 original: None,
571 });
572 incident[node].push(eid);
573 incident[ancestor].push(eid);
574 tree.push(false);
575 classes.push(0);
576 recent_size.push(0);
577 recent_class.push(0);
578 capping.push(eid);
579 brackets[node].push(eid);
580 }
581 if let Some(parent_eid) = parent_edge[node] {
582 let Some(&top) = brackets[node].last() else {
583 return direct_cycle_classes(input, n);
586 };
587 if recent_size[top] != brackets[node].len() {
588 recent_size[top] = brackets[node].len();
589 recent_class[top] = next_class;
590 next_class += 1;
591 }
592 classes[parent_eid] = recent_class[top];
593 if recent_size[top] == 1 {
594 classes[top] = classes[parent_eid];
595 }
596 }
597 }
598 classes.truncate(input.len());
599
600 let valid = (0..input.len()).all(|a| {
605 (0..input.len()).all(|b| (classes[a] == classes[b]) == cycle_equivalent(input, n, a, b))
606 });
607 if valid {
608 return classes;
609 }
610
611 direct_cycle_classes(input, n)
612}
613
614fn direct_cycle_classes<E: Copy>(edges: &[IEdge<E>], n: usize) -> Vec<usize> {
615 let mut parent: Vec<_> = (0..edges.len()).collect();
616 fn find(parent: &mut [usize], mut node: usize) -> usize {
617 while parent[node] != node {
618 parent[node] = parent[parent[node]];
619 node = parent[node];
620 }
621 node
622 }
623 for a in 0..edges.len() {
624 for b in (a + 1)..edges.len() {
625 if cycle_equivalent(edges, n, a, b) {
626 let ra = find(&mut parent, a);
627 let rb = find(&mut parent, b);
628 parent[rb] = ra;
629 }
630 }
631 }
632 let mut labels = HashMap::default();
633 let mut next = 1usize;
634 (0..edges.len())
635 .map(|edge| {
636 let root = find(&mut parent, edge);
637 *labels.entry(root).or_insert_with(|| {
638 let label = next;
639 next += 1;
640 label
641 })
642 })
643 .collect()
644}
645
646fn reachable<E: Copy>(
647 edges: &[IEdge<E>],
648 n: usize,
649 start: usize,
650 skip: Option<usize>,
651 reverse: bool,
652) -> Vec<bool> {
653 let mut seen = vec![false; n];
654 let mut stack = vec![start];
655 while let Some(node) = stack.pop() {
656 if seen[node] {
657 continue;
658 }
659 seen[node] = true;
660 for (i, edge) in edges.iter().enumerate() {
661 if skip == Some(i) {
662 continue;
663 }
664 let (from, to) = if reverse {
665 (edge.to, edge.from)
666 } else {
667 (edge.from, edge.to)
668 };
669 if from == node && !seen[to] {
670 stack.push(to);
671 }
672 }
673 }
674 seen
675}
676
677fn edge_dominates<E: Copy>(edges: &[IEdge<E>], n: usize, root: usize, a: usize, b: usize) -> bool {
678 !reachable(edges, n, root, Some(a), false)[edges[b].from]
679}
680
681fn edge_postdominates<E: Copy>(
682 edges: &[IEdge<E>],
683 n: usize,
684 exit: usize,
685 b: usize,
686 a: usize,
687) -> bool {
688 !reachable(edges, n, exit, Some(b), true)[edges[a].to]
689}
690
691fn cycle_equivalent<E: Copy>(edges: &[IEdge<E>], n: usize, a: usize, b: usize) -> bool {
692 let a_cycle_without_b = reachable(edges, n, edges[a].to, Some(b), false)[edges[a].from];
693 let b_cycle_without_a = reachable(edges, n, edges[b].to, Some(a), false)[edges[b].from];
694 !a_cycle_without_b && !b_cycle_without_a
695}
696
697fn region_nodes<E: Copy>(
698 edges: &[IEdge<E>],
699 n: usize,
700 entry: usize,
701 exit: usize,
702) -> BTreeSet<usize> {
703 let forward = reachable(edges, n, edges[entry].to, Some(exit), false);
704 let backward = reachable(edges, n, edges[exit].from, Some(entry), true);
705 (0..n.saturating_sub(1))
706 .filter(|node| forward[*node] && backward[*node])
707 .collect()
708}
709
710#[cfg(test)]
711mod tests {
712 use jstd_derive::Identifier;
713 use proptest::prelude::*;
714
715 use super::*;
716 use crate::graph::owning::OwningGraph;
717
718 #[derive(Identifier)]
719 struct NodeId(usize);
720 #[derive(Identifier)]
721 struct EdgeId(usize);
722 type G = OwningGraph<NodeId, EdgeId, (), ()>;
723
724 #[test]
725 fn finds_diamond_between_canonical_boundary_edges() {
726 let mut graph = G::default();
727 let s = graph.make_node(());
728 let a = graph.make_node(());
729 let b = graph.make_node(());
730 let c = graph.make_node(());
731 let d = graph.make_node(());
732 let t = graph.make_node(());
733 let entry = graph.make_edge(s, a, ());
734 graph.make_edge(a, b, ());
735 graph.make_edge(a, c, ());
736 graph.make_edge(b, d, ());
737 graph.make_edge(c, d, ());
738 let exit = graph.make_edge(d, t, ());
739
740 let tree = compute_sese(&graph, s);
741 let region = tree
742 .regions
743 .iter()
744 .find(|region| region.entry_edge == Some(entry))
745 .expect("diamond region");
746 assert_eq!(region.exit_edge, Some(exit));
747 assert_eq!(region.contained_nodes, vec![a, b, c, d]);
748 }
749
750 #[test]
751 fn candidates_include_non_canonical_regions_largest_first() {
752 let mut graph = G::default();
756 let s = graph.make_node(());
757 let a = graph.make_node(());
758 let b = graph.make_node(());
759 let c = graph.make_node(());
760 let d = graph.make_node(());
761 let e = graph.make_node(());
762 let t = graph.make_node(());
763 let entry = graph.make_edge(s, a, ());
764 graph.make_edge(a, b, ());
765 graph.make_edge(a, c, ());
766 graph.make_edge(b, d, ());
767 graph.make_edge(c, d, ());
768 let middle = graph.make_edge(d, e, ());
769 let exit = graph.make_edge(e, t, ());
770 graph.make_edge(e, e, ());
772
773 let candidates = compute_sese_candidates(&graph, s);
774 assert!(!candidates.is_empty());
775 assert!(
776 candidates
777 .windows(2)
778 .all(|pair| pair[0].contained_nodes.len() >= pair[1].contained_nodes.len())
779 );
780 assert!(candidates.contains(&SeseCandidate {
781 entry_edge: entry,
782 exit_edge: exit,
783 contained_nodes: vec![a, b, c, d, e],
784 }));
785 assert!(candidates.contains(&SeseCandidate {
786 entry_edge: entry,
787 exit_edge: middle,
788 contained_nodes: vec![a, b, c, d],
789 }));
790 assert!(candidates.contains(&SeseCandidate {
791 entry_edge: middle,
792 exit_edge: exit,
793 contained_nodes: vec![e],
794 }));
795 assert!(
796 candidates
797 .iter()
798 .all(|candidate| candidate.entry_edge != candidate.exit_edge)
799 );
800
801 let orphan = graph.make_node(());
803 graph.make_edge(orphan, s, ());
804 assert_eq!(compute_sese_candidates(&graph, s), candidates);
805 }
806
807 #[test]
808 fn tree_accessors_and_cyclic_graphs_without_sinks() {
809 let mut graph = G::default();
812 let a = graph.make_node(());
813 let b = graph.make_node(());
814 let c = graph.make_node(());
815 graph.make_edge(a, b, ());
816 graph.make_edge(b, c, ());
817 graph.make_edge(c, a, ());
818
819 let tree = compute_sese(&graph, a);
820 assert!(tree.root().parent.is_none());
821 assert_eq!(tree.root().entry_edge, None);
822 assert_eq!(tree.root().exit_edge, None);
823 assert_eq!(tree.root().contained_nodes, vec![a, b, c]);
824 assert!(tree.has_nontrivial_regions());
826 assert!(tree.regions[1..].iter().all(|region| {
827 region.parent.is_some() && region.entry_edge.is_some() && region.exit_edge.is_some()
828 }));
829 assert!(
830 compute_sese_candidates(&graph, a)
831 .iter()
832 .any(|candidate| candidate.contained_nodes == vec![b])
833 );
834
835 let mut single = G::default();
836 let only = single.make_node(());
837 let tree = compute_sese(&single, only);
838 assert_eq!(tree.root().nodes, vec![only]);
839 assert!(!tree.has_nontrivial_regions());
840 }
841
842 #[test]
843 fn sequential_regions_may_share_a_boundary_edge() {
844 let mut graph = G::default();
845 let n: Vec<_> = (0..10).map(|_| graph.make_node(())).collect();
846 let first_entry = graph.make_edge(n[0], n[1], ());
847 graph.make_edge(n[1], n[2], ());
848 graph.make_edge(n[1], n[3], ());
849 graph.make_edge(n[2], n[4], ());
850 graph.make_edge(n[3], n[4], ());
851 let shared = graph.make_edge(n[4], n[5], ());
852 graph.make_edge(n[5], n[6], ());
853 graph.make_edge(n[5], n[7], ());
854 graph.make_edge(n[6], n[8], ());
855 graph.make_edge(n[7], n[8], ());
856 graph.make_edge(n[8], n[9], ());
857
858 let tree = compute_sese(&graph, n[0]);
859 assert!(tree.regions.iter().any(|region| {
860 region.entry_edge == Some(first_entry) && region.exit_edge == Some(shared)
861 }));
862 assert!(
863 tree.regions
864 .iter()
865 .any(|region| region.entry_edge == Some(shared))
866 );
867 }
868
869 #[test]
870 fn multiple_exits_use_an_internal_terminal_without_exposing_it() {
871 let mut graph = G::default();
872 let root = graph.make_node(());
873 let left = graph.make_node(());
874 let right = graph.make_node(());
875 graph.make_edge(root, left, ());
876 graph.make_edge(root, right, ());
877
878 let tree = compute_sese(&graph, root);
879 let mut owned: Vec<_> = tree
880 .regions
881 .iter()
882 .flat_map(|region| region.nodes.iter().copied())
883 .collect();
884 owned.sort();
885 assert_eq!(owned, vec![root, left, right]);
886 assert!(tree.regions.iter().all(|region| {
887 region.entry_edge.is_some() == region.exit_edge.is_some() || region.parent.is_none()
888 }));
889 }
890
891 #[test]
892 fn loop_body_is_a_canonical_region() {
893 let mut graph = G::default();
894 let s = graph.make_node(());
895 let header = graph.make_node(());
896 let body = graph.make_node(());
897 let after = graph.make_node(());
898 let t = graph.make_node(());
899 let entry = graph.make_edge(s, header, ());
900 graph.make_edge(header, body, ());
901 graph.make_edge(body, header, ());
902 let exit = graph.make_edge(header, after, ());
903 graph.make_edge(after, t, ());
904
905 let tree = compute_sese(&graph, s);
906 assert!(tree.regions.iter().any(|region| {
907 region.entry_edge == Some(entry)
908 && region.exit_edge == Some(exit)
909 && region.contained_nodes.contains(&header)
910 && region.contained_nodes.contains(&body)
911 }));
912 }
913
914 proptest! {
915 #![proptest_config(ProptestConfig::with_cases(128))]
916 #[test]
917 fn generated_program_structure_trees_are_laminar_and_total(
918 node_count in 2usize..=10,
919 extra_edges in proptest::collection::vec((0usize..16, 0usize..16), 0..24),
920 ) {
921 let mut graph = G::default();
922 let nodes: Vec<_> = (0..node_count).map(|_| graph.make_node(())).collect();
923 for pair in nodes.windows(2) {
926 graph.make_edge(pair[0], pair[1], ());
927 }
928 for (from, to) in extra_edges {
929 graph.make_edge(nodes[from % node_count], nodes[to % node_count], ());
930 }
931
932 let tree = compute_sese(&graph, nodes[0]);
933 let mut owned: Vec<_> = tree.regions.iter()
934 .flat_map(|region| region.nodes.iter().copied())
935 .collect();
936 owned.sort();
937 prop_assert_eq!(&owned, &nodes);
938 for (id, region) in tree.regions.iter().enumerate() {
939 for &child in ®ion.children {
940 prop_assert_eq!(tree.regions[child].parent, Some(id));
941 prop_assert!(tree.regions[child].contained_nodes.iter()
942 .all(|node| region.contained_nodes.contains(node)));
943 }
944 }
945 for (i, lhs) in tree.regions.iter().enumerate().skip(1) {
946 for rhs in tree.regions.iter().skip(i + 1) {
947 let intersects = lhs.contained_nodes.iter()
948 .any(|node| rhs.contained_nodes.contains(node));
949 prop_assert!(!intersects
950 || lhs.contained_nodes.iter().all(|node| rhs.contained_nodes.contains(node))
951 || rhs.contained_nodes.iter().all(|node| lhs.contained_nodes.contains(node)));
952 }
953 }
954 }
955 }
956
957 #[test]
958 fn nested_regions_are_laminar_and_assign_every_node_once() {
959 let mut graph = G::default();
960 let nodes: Vec<_> = (0..8).map(|_| graph.make_node(())).collect();
961 for pair in nodes.windows(2) {
962 graph.make_edge(pair[0], pair[1], ());
963 }
964 graph.make_edge(nodes[1], nodes[4], ());
965 graph.make_edge(nodes[2], nodes[3], ());
966 graph.make_edge(nodes[4], nodes[6], ());
967
968 let tree = compute_sese(&graph, nodes[0]);
969 let owned: Vec<_> = tree
970 .regions
971 .iter()
972 .flat_map(|region| region.nodes.iter().copied())
973 .collect();
974 let mut unique = owned.clone();
975 unique.sort();
976 unique.dedup();
977 assert_eq!(owned.len(), nodes.len());
978 assert_eq!(unique, nodes);
979 for (id, region) in tree.regions.iter().enumerate().skip(1) {
980 let parent = region.parent.unwrap();
981 assert!(parent < id);
982 assert!(
983 region
984 .contained_nodes
985 .iter()
986 .all(|node| { tree.regions[parent].contained_nodes.contains(node) })
987 );
988 }
989 }
990}