use std::collections::HashSet; use std::hash::Hash; use petgraph::algo::{greedy_matching, maximum_matching}; use petgraph::prelude::*; macro_rules! assert_one_of { ($actual:expr, [$($expected:expr),+]) => { let expected = &[$($expected),+]; if !expected.iter().any(|expected| expected == &$actual) { let expected = expected.iter().map(|e| format!("\n{:?}", e)).collect::>(); let comma_separated = expected.join(", "); panic!("assertion failed: `actual does not equal to any of expected`\nactual:\n{:?}\nexpected:{}", $actual, comma_separated); } }; } macro_rules! set { () => { HashSet::new() }; ($(($source:expr, $target:expr)),+) => { { let mut set = HashSet::new(); $( set.insert(($source.into(), $target.into())); )* set } }; ($($elem:expr),+) => { { let mut set = HashSet::new(); $( set.insert($elem.into()); )* set } }; } // So we don't have to type `.collect::>`. fn collect<'a, T: Copy + Eq + Hash + 'a>(iter: impl Iterator) -> HashSet { iter.collect() } #[test] fn greedy_empty() { let g: UnGraph<(), ()> = UnGraph::default(); let m = greedy_matching(&g); assert_eq!(collect(m.edges()), set![]); assert_eq!(collect(m.nodes()), set![]); } #[test] fn greedy_disjoint() { let g: UnGraph<(), ()> = UnGraph::from_edges(&[(0, 1), (2, 3)]); let m = greedy_matching(&g); assert_eq!(collect(m.edges()), set![(0, 1), (2, 3)]); assert_eq!(collect(m.nodes()), set![0, 1, 2, 3]); } #[test] fn greedy_odd_path() { let g: UnGraph<(), ()> = UnGraph::from_edges(&[(0, 1), (1, 2), (2, 3)]); let m = greedy_matching(&g); assert_one_of!(collect(m.edges()), [set![(0, 1), (2, 3)], set![(1, 2)]]); assert_one_of!(collect(m.nodes()), [set![0, 1, 2, 3], set![1, 2]]); } #[test] fn greedy_star() { let g: UnGraph<(), ()> = UnGraph::from_edges(&[(0, 1), (0, 2), (0, 3)]); let m = greedy_matching(&g); assert_one_of!( collect(m.edges()), [set![(0, 1)], set![(0, 2)], set![(0, 3)]] ); assert_one_of!(collect(m.nodes()), [set![0, 1], set![0, 2], set![0, 3]]); } #[test] fn maximum_empty() { let g: UnGraph<(), ()> = UnGraph::default(); let m = maximum_matching(&g); assert_eq!(collect(m.edges()), set![]); assert_eq!(collect(m.nodes()), set![]); } #[test] fn maximum_disjoint() { let g: UnGraph<(), ()> = UnGraph::from_edges(&[(0, 1), (2, 3)]); let m = maximum_matching(&g); assert_eq!(collect(m.edges()), set![(0, 1), (2, 3)]); assert_eq!(collect(m.nodes()), set![0, 1, 2, 3]); } #[test] fn maximum_odd_path() { let g: UnGraph<(), ()> = UnGraph::from_edges(&[(0, 1), (1, 2), (2, 3)]); let m = maximum_matching(&g); assert_eq!(collect(m.edges()), set![(0, 1), (2, 3)]); assert_eq!(collect(m.nodes()), set![0, 1, 2, 3]); } #[cfg(feature = "stable_graph")] #[test] fn maximum_in_stable_graph() { let mut g: StableUnGraph<(), ()> = StableUnGraph::from_edges(&[(0, 1), (0, 2), (1, 2), (1, 3), (2, 4), (3, 4), (3, 5)]); // Create a hole by removing node that would otherwise belong to the maximum // matching. g.remove_node(NodeIndex::new(4)); let m = maximum_matching(&g); assert_one_of!( collect(m.edges()), [ set![(0, 1), (3, 5)], set![(0, 2), (1, 3)], set![(0, 2), (3, 5)] ] ); assert_one_of!( collect(m.nodes()), [set![0, 1, 3, 5], set![0, 2, 1, 3], set![0, 2, 3, 5]] ); } #[cfg(feature = "stable_graph")] #[test] fn is_perfect_in_stable_graph() { let mut g: StableUnGraph<(), ()> = StableUnGraph::from_edges(&[(0, 1), (1, 2), (2, 3)]); g.remove_node(NodeIndex::new(0)); g.remove_node(NodeIndex::new(1)); let m = maximum_matching(&g); assert_eq!(m.len(), 1); assert!(m.is_perfect()); }