use nalgebra_block_triangularization::matching::hopcroft_karp;
#[test]
fn matching_empty_graph() {
let adj: Vec<Vec<usize>> = vec![];
let matching = hopcroft_karp(&adj, 0);
assert_eq!(matching.size, 0);
assert_eq!(matching.row_to_col.len(), 0);
assert_eq!(matching.col_to_row.len(), 0);
}
#[test]
fn matching_single_node_no_edges() {
let adj = vec![vec![]];
let matching = hopcroft_karp(&adj, 1);
assert_eq!(matching.size, 0);
assert_eq!(matching.row_to_col[0], None);
assert_eq!(matching.col_to_row[0], None);
}
#[test]
fn matching_single_edge() {
let adj = vec![vec![0]];
let matching = hopcroft_karp(&adj, 1);
assert_eq!(matching.size, 1);
assert_eq!(matching.row_to_col[0], Some(0));
assert_eq!(matching.col_to_row[0], Some(0));
}
#[test]
fn matching_perfect_matching_diagonal() {
let adj = vec![vec![0], vec![1], vec![2]];
let matching = hopcroft_karp(&adj, 3);
assert_eq!(matching.size, 3);
assert_eq!(matching.row_to_col[0], Some(0));
assert_eq!(matching.row_to_col[1], Some(1));
assert_eq!(matching.row_to_col[2], Some(2));
assert_eq!(matching.col_to_row[0], Some(0));
assert_eq!(matching.col_to_row[1], Some(1));
assert_eq!(matching.col_to_row[2], Some(2));
}
#[test]
fn matching_perfect_matching_shifted() {
let adj = vec![vec![1], vec![0, 2], vec![1]];
let matching = hopcroft_karp(&adj, 3);
assert_eq!(matching.size, 2);
}
#[test]
fn matching_complete_bipartite() {
let adj = vec![vec![0, 1, 2], vec![0, 1, 2], vec![0, 1, 2]];
let matching = hopcroft_karp(&adj, 3);
assert_eq!(matching.size, 3);
for i in 0..3 {
assert!(matching.row_to_col[i].is_some());
}
for j in 0..3 {
assert!(matching.col_to_row[j].is_some());
}
for i in 0..3 {
if let Some(j) = matching.row_to_col[i] {
assert_eq!(matching.col_to_row[j], Some(i));
}
}
}
#[test]
fn matching_more_rows_than_cols() {
let adj = vec![vec![0], vec![1], vec![0], vec![1]];
let matching = hopcroft_karp(&adj, 2);
assert_eq!(matching.size, 2);
let matched_rows = matching.row_to_col.iter().filter(|x| x.is_some()).count();
assert_eq!(matched_rows, 2);
}
#[test]
fn matching_more_cols_than_rows() {
let adj = vec![vec![0, 1, 2, 3], vec![0, 1, 2, 3]];
let matching = hopcroft_karp(&adj, 4);
assert_eq!(matching.size, 2);
let matched_cols = matching.col_to_row.iter().filter(|x| x.is_some()).count();
assert_eq!(matched_cols, 2);
}
#[test]
fn matching_no_possible_matching() {
let adj = vec![vec![], vec![], vec![]];
let matching = hopcroft_karp(&adj, 3);
assert_eq!(matching.size, 0);
for i in 0..3 {
assert_eq!(matching.row_to_col[i], None);
assert_eq!(matching.col_to_row[i], None);
}
}
#[test]
fn matching_disjoint_components() {
let adj = vec![vec![0], vec![1]];
let matching = hopcroft_karp(&adj, 2);
assert_eq!(matching.size, 2);
assert_eq!(matching.row_to_col[0], Some(0));
assert_eq!(matching.row_to_col[1], Some(1));
}
#[test]
fn matching_augmenting_path() {
let adj = vec![vec![0], vec![0, 1], vec![1]];
let matching = hopcroft_karp(&adj, 2);
assert_eq!(matching.size, 2);
}
#[test]
fn matching_consistency() {
let adj = vec![vec![0, 1, 2], vec![1, 2, 3], vec![0, 3], vec![2, 3]];
let matching = hopcroft_karp(&adj, 4);
for (i, &opt_j) in matching.row_to_col.iter().enumerate() {
if let Some(j) = opt_j {
assert_eq!(matching.col_to_row[j], Some(i));
}
}
for (j, &opt_i) in matching.col_to_row.iter().enumerate() {
if let Some(i) = opt_i {
assert_eq!(matching.row_to_col[i], Some(j));
}
}
}
#[test]
fn matching_respects_adjacency() {
let adj = vec![vec![0, 2], vec![1, 3], vec![0]];
let matching = hopcroft_karp(&adj, 4);
for (i, &opt_j) in matching.row_to_col.iter().enumerate() {
if let Some(j) = opt_j {
assert!(
adj[i].contains(&j),
"Row {} matched to col {} but no edge exists",
i,
j
);
}
}
}
#[test]
fn matching_large_graph() {
let n = 100;
let adj: Vec<Vec<usize>> = (0..n).map(|i| vec![i]).collect();
let matching = hopcroft_karp(&adj, n);
assert_eq!(matching.size, n);
}
#[test]
fn matching_hall_violation() {
let adj = vec![vec![0, 1], vec![0, 1], vec![0, 1]];
let matching = hopcroft_karp(&adj, 2);
assert_eq!(matching.size, 2);
}