use std::fmt::Debug;
use std::ops::Index;
use std::ops::IndexMut;
use std::ops::Add;
use std::ops::Sub;
use std::cmp;
use std::collections::HashSet;
use std::iter::FromIterator;
extern crate num;
use num::Zero;
#[macro_use]
extern crate log;
pub type Edge = (usize, usize);
pub trait Weight: Zero + Add<Output=Self> + Sub<Output=Self> + Ord + Copy + Debug {}
impl<T: Zero + Add<Output=T> + Sub<Output=T> + Ord + Copy + Debug> Weight for T {}
pub struct MatrixSize {
pub rows: usize,
pub columns: usize,
}
pub fn solver<T>(matrix: &mut T, size: &MatrixSize) -> HashSet<Edge>
where T: IndexMut<Edge>,
T::Output: Weight {
debug_assert!(size.columns >= size.rows);
let k = cmp::min(size.columns, size.rows);
if size.columns == 0 || size.rows == 0 {
return HashSet::new();
}
reduce_edges(matrix, size);
let mut stars: HashSet<Edge> = HashSet::new();
star_isolated_set_of_zeros(&mut stars, matrix, size);
let mut primes: HashSet<Edge> = HashSet::new();
let mut columns_covered = HashSet::<usize>::new();
let mut rows_covered = HashSet::<usize>::new();
let mut covered_column_count = 0;
loop {
cover_starred_columns(&mut columns_covered, &stars);
debug_assert!(columns_covered.len() > covered_column_count);
covered_column_count = columns_covered.len();
debug_assert!(columns_covered.len() <= k);
if columns_covered.len() == k {
break;
}
while prime_zeros(&*matrix, &size, &mut columns_covered, &mut rows_covered, &mut stars, &mut primes) {
debug_assert!(
all_stars_covered(&stars, &size, &columns_covered, &rows_covered),
"stars = {:?}, columns covered = {:?}, rows covered = {:?}",
stars, columns_covered, rows_covered
);
debug_assert!(
stars.intersection(&primes).cloned().collect::<HashSet<_>>().len() == 0,
"The set of stars and primes intersect! stars = {:?}, primes = {:?}.",
stars,
primes
);
debug_assert!(
find_uncovered_zero(&*matrix, &size, &columns_covered, &rows_covered) == None
);
let smallest = find_smallest_uncovered(&*matrix, &size, &columns_covered, &rows_covered);
for row in 0..size.rows {
for column in 0..size.columns {
if rows_covered.contains(&row) {
matrix[(row, column)] = matrix[(row, column)] + smallest;
}
if !columns_covered.contains(&column) {
matrix[(row, column)] = matrix[(row, column)] - smallest;
}
debug_assert!(matrix[(row, column)] >= T::Output::zero());
}
}
}
}
debug_assert!(stars.len() == size.rows);
stars
}
fn prime_zeros<T>(matrix: &T, size: &MatrixSize,
columns_covered: &mut HashSet<usize>, rows_covered: &mut HashSet<usize>,
stars: &mut HashSet<Edge>, primes: &mut HashSet<Edge>) -> bool
where T: Index<Edge>,
T::Output: Weight {
loop {
debug_assert!(
all_stars_covered(stars, size, columns_covered, rows_covered),
"stars = {:?}, columns covered = {:?}, rows covered = {:?}",
stars, columns_covered, rows_covered
);
match find_uncovered_zero(matrix, size, columns_covered, rows_covered) {
Some(edge_to_prime) => {
debug_assert!(!primes.contains(&edge_to_prime));
debug_assert!(!stars.contains(&edge_to_prime));
primes.insert(edge_to_prime);
if stars.iter().any(|&(row, _)| row == edge_to_prime.0) {
rows_covered.insert(edge_to_prime.0);
debug_assert!(
rows_covered.len() < size.rows,
"prime_zeros covered the last row! row = {:?}, stars = {:?}, rows_covered = {:?}, columns_covered = {:?}",
edge_to_prime.0,
stars,
rows_covered,
columns_covered
);
columns_covered.remove(&edge_to_prime.1);
} else {
let path = find_alternating_path(edge_to_prime, &*stars, &*primes);
*stars = get_stars_from_path(path, stars);
columns_covered.clear();
rows_covered.clear();
primes.clear();
return false;
}
},
None => return true,
}
}
}
fn all_stars_covered(stars: &HashSet<Edge>, size: &MatrixSize,columns_covered: &HashSet<usize>, rows_covered: &HashSet<usize>) -> bool {
for row in 0..size.rows {
if !rows_covered.contains(&row) {
for column in 0..size.columns {
if !columns_covered.contains(&column) {
if stars.contains(&(row, column)) {
return false;
}
}
}
}
}
true
}
fn find_uncovered_zero<T>(matrix: &T, size: &MatrixSize,
columns_covered: &HashSet<usize>, rows_covered: &HashSet<usize>) -> Option<Edge>
where T: Index<Edge>,
T::Output: Weight {
for row in 0..size.rows {
if !rows_covered.contains(&row) {
for column in 0..size.columns {
if !columns_covered.contains(&column) {
if matrix[(row, column)] == T::Output::zero() {
return Some((row, column));
}
}
}
}
}
None::<Edge>
}
fn find_smallest_uncovered<T>(matrix: &T, size: &MatrixSize,
columns_covered: &HashSet<usize>, rows_covered: &HashSet<usize>) -> T::Output
where T: Index<Edge>,
T::Output: Weight {
debug_assert!(size.rows > 0 && size.columns > 0);
debug_assert!(columns_covered.len() < size.columns);
debug_assert!(rows_covered.len() < size.rows);
let mut smallest = None;
for row in 0..size.rows {
if !rows_covered.contains(&row) {
for column in 0..size.columns {
if !columns_covered.contains(&column) {
smallest = match smallest {
Some(smaller) => Some(cmp::min(smaller, matrix[(row, column)])),
None => Some(matrix[(row, column)]),
};
}
}
}
}
match smallest {
Some(value) => {
debug_assert!(value > T::Output::zero());
value
},
None => panic!(),
}
}
fn find_alternating_path(starting_edge: Edge,
stars: &HashSet<Edge>, primes: &HashSet<Edge>) -> Vec<Edge> {
let mut path = vec![starting_edge];
debug_assert!(
stars.intersection(&primes).cloned().collect::<HashSet<_>>().len() == 0,
"The set of stars and primes intersect! stars = {:?}, primes = {:?}.",
stars,
primes
);
loop {
let used: HashSet<Edge> = HashSet::from_iter(path.clone());
let primes: HashSet<Edge> = primes.difference(&used).map(|&x| x).collect();
let stars: HashSet<Edge> = stars.difference(&used).map(|&x| x).collect();
let z0: Edge = match path.last() {
Some(&z0) => z0,
None => panic!(),
};
match stars.iter().find(|&&(_, column)| column == z0.1) {
Some(&z1) => {
debug_assert!(!path.contains(&z1));
path.push(z1);
match primes.iter().find(|&&(row, _)| row == z1.0) {
Some(&z2) => {
debug_assert!(z0 != z2);
debug_assert!(!path.contains(&z2), "path = {:?}, z2 = {:?}", path, z2);
path.push(z2);
},
None => panic!(),
};
},
None => break,
}
}
path
}
fn get_stars_from_path(path: Vec<Edge>, stars: &mut HashSet<Edge>) -> HashSet<Edge> {
let path = path.into_iter().enumerate();
let mut new_stars: HashSet<Edge> = HashSet::new();
let mut old_stars: HashSet<Edge> = HashSet::new();
for (i, edge) in path {
if i % 2 == 0 {
new_stars.insert(edge);
} else {
old_stars.insert(edge);
}
}
let stars: HashSet<Edge> = stars.difference(&old_stars).map(|&edge| edge).collect();
stars.union(&new_stars).cloned().collect()
}
fn subtract_from_matrix<T, F>(matrix: &mut T, size: &MatrixSize, f: F)
where F: Fn(usize, usize) -> T::Output,
T: IndexMut<Edge>,
T::Output: Weight {
for row in 0..size.rows {
for column in 0..size.columns {
matrix[(row, column)] = matrix[(row, column)] - f(row, column);
}
}
}
fn find_smallest_vector<T, F>(matrix: &T, outer_size: usize, inner_size: usize, f: F) -> Vec<T::Output>
where F: Fn(usize, usize) -> Edge,
T: IndexMut<Edge>,
T::Output: Weight {
let mut smallest_in_outside = Vec::new();
for outer in 0..outer_size {
smallest_in_outside.push(matrix[f(outer, 0)]);
for inner in 1..inner_size {
let weight = matrix[f(outer, inner)];
if weight < smallest_in_outside[outer] {
smallest_in_outside[outer] = weight;
}
}
}
smallest_in_outside
}
fn reduce_edges<'a, T>(matrix: &'a mut T, size: &MatrixSize) -> &'a mut T
where T: IndexMut<Edge>,
T::Output: Weight {
let smallest_in_column = find_smallest_vector(matrix, size.columns, size.rows, |column, row| (row, column));
subtract_from_matrix(matrix, size, |_, column| smallest_in_column[column]);
let smallest_in_row = find_smallest_vector(matrix, size.rows, size.columns, |row, column| (row, column));
subtract_from_matrix(matrix, size, |row, _| smallest_in_row[row]);
matrix
}
fn star_isolated_set_of_zeros<'a, T>(stars: &'a mut HashSet<Edge>, matrix: &T, size: &MatrixSize) -> &'a mut HashSet<Edge>
where T: IndexMut<Edge>,
T::Output: Weight {
let mut columns = HashSet::new();
for row in 0..size.rows {
for column in 0..size.columns {
if columns.contains(&column) {
break;
}
if matrix[(row, column)] == T::Output::zero() {
columns.insert(column);
stars.insert((row, column));
break;
}
}
}
stars
}
fn cover_starred_columns(cover: &mut HashSet<usize>, stars: &HashSet<Edge>) {
cover.clear();
cover.extend(stars.iter().map(|x| x.1));
}