| extern crate test; | |
| use super::*; | |
| use crate::graph::tests::TestGraph; | |
| #[derive(Copy, Clone, Debug)] | |
| struct MaxReached(usize); | |
| struct Maxes(IndexVec<usize, MaxReached>, fn(usize) -> usize); | |
| type UsizeSccs = Sccs<usize, usize>; | |
| impl Annotations<usize> for Maxes { | |
| fn new(&self, element: usize) -> MaxReached { | |
| MaxReached(self.1(element)) | |
| } | |
| fn annotate_scc(&mut self, scc: usize, annotation: MaxReached) { | |
| let i = self.0.push(annotation); | |
| assert!(i == scc); | |
| } | |
| type Ann = MaxReached; | |
| type SccIdx = usize; | |
| } | |
| impl Maxes { | |
| fn annotation(&self, scc: usize) -> MaxReached { | |
| self.0[scc] | |
| } | |
| fn new(mapping: fn(usize) -> usize) -> Self { | |
| Self(IndexVec::new(), mapping) | |
| } | |
| } | |
| impl Annotation for MaxReached { | |
| fn update_scc(&mut self, other: &Self) { | |
| self.0 = self.0.max(other.0); | |
| } | |
| fn update_reachable(&mut self, other: &Self) { | |
| self.0 = self.0.max(other.0); | |
| } | |
| } | |
| impl PartialEq<usize> for MaxReached { | |
| fn eq(&self, other: &usize) -> bool { | |
| &self.0 == other | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug)] | |
| struct MinMaxIn { | |
| min: usize, | |
| max: usize, | |
| } | |
| struct MinMaxes(IndexVec<usize, MinMaxIn>, fn(usize) -> MinMaxIn); | |
| impl MinMaxes { | |
| fn annotation(&self, scc: usize) -> MinMaxIn { | |
| self.0[scc] | |
| } | |
| } | |
| impl Annotations<usize> for MinMaxes { | |
| fn new(&self, element: usize) -> MinMaxIn { | |
| self.1(element) | |
| } | |
| fn annotate_scc(&mut self, scc: usize, annotation: MinMaxIn) { | |
| let i = self.0.push(annotation); | |
| assert!(i == scc); | |
| } | |
| type Ann = MinMaxIn; | |
| type SccIdx = usize; | |
| } | |
| impl Annotation for MinMaxIn { | |
| fn update_scc(&mut self, other: &Self) { | |
| self.min = self.min.min(other.min); | |
| self.max = self.max.max(other.max); | |
| } | |
| fn update_reachable(&mut self, _other: &Self) {} | |
| } | |
| #[test] | |
| fn diamond() { | |
| let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 4); | |
| assert_eq!(sccs.num_sccs(), 4); | |
| } | |
| #[test] | |
| fn test_big_scc() { | |
| // The order in which things will be visited is important to this | |
| // test. | |
| // | |
| // We will visit: | |
| // | |
| // 0 -> 1 -> 2 -> 0 | |
| // | |
| // and at this point detect a cycle. 2 will return back to 1 which | |
| // will visit 3. 3 will visit 2 before the cycle is complete, and | |
| // hence it too will return a cycle. | |
| /* | |
| +-> 0 | |
| | | | |
| | v | |
| | 1 -> 3 | |
| | | | | |
| | v | | |
| +-- 2 <--+ | |
| */ | |
| let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]); | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 1); | |
| } | |
| #[test] | |
| fn test_three_sccs() { | |
| /* | |
| 0 | |
| | | |
| v | |
| +-> 1 3 | |
| | | | | |
| | v | | |
| +-- 2 <--+ | |
| */ | |
| let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]); | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 3); | |
| assert_eq!(sccs.scc(0), 1); | |
| assert_eq!(sccs.scc(1), 0); | |
| assert_eq!(sccs.scc(2), 0); | |
| assert_eq!(sccs.scc(3), 2); | |
| assert_eq!(sccs.successors(0), &[] as &[usize]); | |
| assert_eq!(sccs.successors(1), &[0]); | |
| assert_eq!(sccs.successors(2), &[0]); | |
| } | |
| #[test] | |
| fn test_find_state_2() { | |
| // The order in which things will be visited is important to this | |
| // test. It tests part of the `find_state` behavior. Here is the | |
| // graph: | |
| // | |
| // | |
| // /----+ | |
| // 0 <--+ | | |
| // | | | | |
| // v | | | |
| // +-> 1 -> 3 4 | |
| // | | | | |
| // | v | | |
| // +-- 2 <----+ | |
| let graph = TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2)]); | |
| // For this graph, we will start in our DFS by visiting: | |
| // | |
| // 0 -> 1 -> 2 -> 1 | |
| // | |
| // and at this point detect a cycle. The state of 2 will thus be | |
| // `InCycleWith { 1 }`. We will then visit the 1 -> 3 edge, which | |
| // will attempt to visit 0 as well, thus going to the state | |
| // `InCycleWith { 0 }`. Finally, node 1 will complete; the lowest | |
| // depth of any successor was 3 which had depth 0, and thus it | |
| // will be in the state `InCycleWith { 3 }`. | |
| // | |
| // When we finally traverse the `0 -> 4` edge and then visit node 2, | |
| // the states of the nodes are: | |
| // | |
| // 0 BeingVisited { 0 } | |
| // 1 InCycleWith { 3 } | |
| // 2 InCycleWith { 1 } | |
| // 3 InCycleWith { 0 } | |
| // | |
| // and hence 4 will traverse the links, finding an ultimate depth of 0. | |
| // If will also collapse the states to the following: | |
| // | |
| // 0 BeingVisited { 0 } | |
| // 1 InCycleWith { 3 } | |
| // 2 InCycleWith { 1 } | |
| // 3 InCycleWith { 0 } | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 1); | |
| assert_eq!(sccs.scc(0), 0); | |
| assert_eq!(sccs.scc(1), 0); | |
| assert_eq!(sccs.scc(2), 0); | |
| assert_eq!(sccs.scc(3), 0); | |
| assert_eq!(sccs.scc(4), 0); | |
| assert_eq!(sccs.successors(0), &[] as &[usize]); | |
| } | |
| #[test] | |
| fn test_find_state_3() { | |
| /* | |
| /----+ | |
| 0 <--+ | | |
| | | | | |
| v | | | |
| +-> 1 -> 3 4 5 | |
| | | | | | |
| | v | | | |
| +-- 2 <----+-+ | |
| */ | |
| let graph = | |
| TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]); | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 2); | |
| assert_eq!(sccs.scc(0), 0); | |
| assert_eq!(sccs.scc(1), 0); | |
| assert_eq!(sccs.scc(2), 0); | |
| assert_eq!(sccs.scc(3), 0); | |
| assert_eq!(sccs.scc(4), 0); | |
| assert_eq!(sccs.scc(5), 1); | |
| assert_eq!(sccs.successors(0), &[] as &[usize]); | |
| assert_eq!(sccs.successors(1), &[0]); | |
| } | |
| #[test] | |
| fn test_deep_linear() { | |
| /* | |
| 0 | |
| | | |
| v | |
| 1 | |
| | | |
| v | |
| 2 | |
| | | |
| v | |
| … | |
| */ | |
| #[cfg(not(miri))] | |
| const NR_NODES: usize = 1 << 14; | |
| #[cfg(miri)] | |
| const NR_NODES: usize = 1 << 3; | |
| let mut nodes = vec![]; | |
| for i in 1..NR_NODES { | |
| nodes.push((i - 1, i)); | |
| } | |
| let graph = TestGraph::new(0, nodes.as_slice()); | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), NR_NODES); | |
| assert_eq!(sccs.scc(0), NR_NODES - 1); | |
| assert_eq!(sccs.scc(NR_NODES - 1), 0); | |
| } | |
| #[bench] | |
| fn bench_sccc(b: &mut test::Bencher) { | |
| // Like `test_three_sccs` but each state is replaced by a group of | |
| // three or four to have some amount of test data. | |
| /* | |
| 0-3 | |
| | | |
| v | |
| +->4-6 11-14 | |
| | | | | |
| | v | | |
| +--7-10<-+ | |
| */ | |
| fn make_3_clique(slice: &mut [(usize, usize)], base: usize) { | |
| slice[0] = (base + 0, base + 1); | |
| slice[1] = (base + 1, base + 2); | |
| slice[2] = (base + 2, base + 0); | |
| } | |
| // Not actually a clique but strongly connected. | |
| fn make_4_clique(slice: &mut [(usize, usize)], base: usize) { | |
| slice[0] = (base + 0, base + 1); | |
| slice[1] = (base + 1, base + 2); | |
| slice[2] = (base + 2, base + 3); | |
| slice[3] = (base + 3, base + 0); | |
| slice[4] = (base + 1, base + 3); | |
| slice[5] = (base + 2, base + 1); | |
| } | |
| let mut graph = [(0, 0); 6 + 3 + 6 + 3 + 4]; | |
| make_4_clique(&mut graph[0..6], 0); | |
| make_3_clique(&mut graph[6..9], 4); | |
| make_4_clique(&mut graph[9..15], 7); | |
| make_3_clique(&mut graph[15..18], 11); | |
| graph[18] = (0, 4); | |
| graph[19] = (5, 7); | |
| graph[20] = (11, 10); | |
| graph[21] = (7, 4); | |
| let graph = TestGraph::new(0, &graph[..]); | |
| b.iter(|| { | |
| let sccs: UsizeSccs = Sccs::new(&graph); | |
| assert_eq!(sccs.num_sccs(), 3); | |
| }); | |
| } | |
| #[test] | |
| fn test_max_self_loop() { | |
| let graph = TestGraph::new(0, &[(0, 0)]); | |
| let mut annotations = Maxes(IndexVec::new(), |n| if n == 0 { 17 } else { 0 }); | |
| Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.0[0], 17); | |
| } | |
| #[test] | |
| fn test_max_branch() { | |
| let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]); | |
| let mut annotations = Maxes(IndexVec::new(), |n| n); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.0[sccs.scc(0)], 4); | |
| assert_eq!(annotations.0[sccs.scc(1)], 3); | |
| assert_eq!(annotations.0[sccs.scc(2)], 4); | |
| } | |
| #[test] | |
| fn test_single_cycle_max() { | |
| let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]); | |
| let mut annotations = Maxes(IndexVec::new(), |n| n); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.0[sccs.scc(2)], 4); | |
| assert_eq!(annotations.0[sccs.scc(0)], 4); | |
| } | |
| #[test] | |
| fn test_double_cycle_max() { | |
| let graph = | |
| TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]); | |
| let mut annotations = Maxes(IndexVec::new(), |n| if n == 5 { 2 } else { 1 }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.0[sccs.scc(0)].0, 2); | |
| } | |
| #[test] | |
| fn test_bug_minimised() { | |
| let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]); | |
| let mut annotations = Maxes(IndexVec::new(), |n| match n { | |
| 3 => 1, | |
| _ => 0, | |
| }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.annotation(sccs.scc(2)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(1)), 0); | |
| assert_eq!(annotations.annotation(sccs.scc(4)), 0); | |
| } | |
| #[test] | |
| fn test_bug_max_leak_minimised() { | |
| let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]); | |
| let mut annotations = Maxes(IndexVec::new(), |w| match w { | |
| 4 => 1, | |
| _ => 0, | |
| }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.annotation(sccs.scc(2)), 0); | |
| assert_eq!(annotations.annotation(sccs.scc(3)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(0)), 1); | |
| } | |
| #[test] | |
| fn test_bug_max_leak() { | |
| let graph = TestGraph::new( | |
| 8, | |
| &[ | |
| (0, 0), | |
| (0, 18), | |
| (0, 19), | |
| (0, 1), | |
| (0, 2), | |
| (0, 7), | |
| (0, 8), | |
| (0, 23), | |
| (18, 0), | |
| (18, 12), | |
| (19, 0), | |
| (19, 25), | |
| (12, 18), | |
| (12, 3), | |
| (12, 5), | |
| (3, 12), | |
| (3, 21), | |
| (3, 22), | |
| (5, 13), | |
| (21, 3), | |
| (22, 3), | |
| (13, 5), | |
| (13, 4), | |
| (4, 13), | |
| (4, 0), | |
| (2, 11), | |
| (7, 6), | |
| (6, 20), | |
| (20, 6), | |
| (8, 17), | |
| (17, 9), | |
| (9, 16), | |
| (16, 26), | |
| (26, 15), | |
| (15, 10), | |
| (10, 14), | |
| (14, 27), | |
| (23, 24), | |
| ], | |
| ); | |
| let mut annotations = Maxes::new(|w| match w { | |
| 22 => 1, | |
| 24 => 2, | |
| 27 => 2, | |
| _ => 0, | |
| }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.annotation(sccs.scc(2)), 0); | |
| assert_eq!(annotations.annotation(sccs.scc(7)), 0); | |
| assert_eq!(annotations.annotation(sccs.scc(8)), 2); | |
| assert_eq!(annotations.annotation(sccs.scc(23)), 2); | |
| assert_eq!(annotations.annotation(sccs.scc(3)), 2); | |
| assert_eq!(annotations.annotation(sccs.scc(0)), 2); | |
| } | |
| #[test] | |
| fn test_bug_max_zero_stick_shape() { | |
| let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]); | |
| let mut annotations = Maxes::new(|w| match w { | |
| 4 => 1, | |
| _ => 0, | |
| }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.annotation(sccs.scc(0)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(1)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(2)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(3)), 1); | |
| assert_eq!(annotations.annotation(sccs.scc(4)), 1); | |
| } | |
| #[test] | |
| fn test_min_max_in() { | |
| let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]); | |
| let mut annotations = MinMaxes(IndexVec::new(), |w| MinMaxIn { min: w, max: w }); | |
| let sccs = Sccs::new_with_annotation(&graph, &mut annotations); | |
| assert_eq!(annotations.annotation(sccs.scc(2)).min, 2); | |
| assert_eq!(annotations.annotation(sccs.scc(2)).max, 2); | |
| assert_eq!(annotations.annotation(sccs.scc(0)).min, 0); | |
| assert_eq!(annotations.annotation(sccs.scc(0)).max, 4); | |
| assert_eq!(annotations.annotation(sccs.scc(3)).min, 0); | |
| assert_eq!(annotations.annotation(sccs.scc(3)).max, 4); | |
| assert_eq!(annotations.annotation(sccs.scc(5)).min, 5); | |
| } |