use std::borrow::Borrow;
use std::slice::Iter;
mod bfs;
mod girth;
pub use bfs::BFSResults;
pub type Error = String;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct SparseMatrix {
rows: Vec<Vec<usize>>,
cols: Vec<Vec<usize>>,
}
impl SparseMatrix {
pub fn new(nrows: usize, ncols: usize) -> SparseMatrix {
use std::iter::repeat_with;
let rows = repeat_with(Vec::new).take(nrows).collect();
let cols = repeat_with(Vec::new).take(ncols).collect();
SparseMatrix { rows, cols }
}
pub fn num_rows(&self) -> usize {
self.rows.len()
}
pub fn num_cols(&self) -> usize {
self.cols.len()
}
pub fn row_weight(&self, row: usize) -> usize {
self.rows[row].len()
}
pub fn col_weight(&self, col: usize) -> usize {
self.cols[col].len()
}
pub fn contains(&self, row: usize, col: usize) -> bool {
self.cols[col].contains(&row)
}
pub fn insert(&mut self, row: usize, col: usize) {
if !self.contains(row, col) {
self.rows[row].push(col);
self.cols[col].push(row);
}
}
pub fn remove(&mut self, row: usize, col: usize) {
self.rows[row].retain(|&c| c != col);
self.cols[col].retain(|&r| r != row);
}
pub fn toggle(&mut self, row: usize, col: usize) {
match self.contains(row, col) {
true => self.remove(row, col),
false => self.insert(row, col),
}
}
pub fn insert_row<T, S>(&mut self, row: usize, cols: T)
where
T: Iterator<Item = S>,
S: Borrow<usize>,
{
for col in cols {
self.insert(row, *col.borrow());
}
}
pub fn insert_col<T, S>(&mut self, col: usize, rows: T)
where
T: Iterator<Item = S>,
S: Borrow<usize>,
{
for row in rows {
self.insert(*row.borrow(), col);
}
}
pub fn clear_row(&mut self, row: usize) {
for &col in &self.rows[row] {
self.cols[col].retain(|r| *r != row);
}
self.rows[row].clear();
}
pub fn clear_col(&mut self, col: usize) {
for &row in &self.cols[col] {
self.rows[row].retain(|c| *c != col);
}
self.cols[col].clear();
}
pub fn set_row<T, S>(&mut self, row: usize, cols: T)
where
T: Iterator<Item = S>,
S: Borrow<usize>,
{
self.clear_row(row);
self.insert_row(row, cols);
}
pub fn set_col<T, S>(&mut self, col: usize, rows: T)
where
T: Iterator<Item = S>,
S: Borrow<usize>,
{
self.clear_col(col);
self.insert_col(col, rows);
}
pub fn iter_all(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
self.rows
.iter()
.enumerate()
.flat_map(|(j, r)| r.iter().map(move |&k| (j, k)))
}
pub fn iter_row(&self, row: usize) -> Iter<'_, usize> {
self.rows[row].iter()
}
pub fn iter_col(&self, col: usize) -> Iter<'_, usize> {
self.cols[col].iter()
}
pub fn write_alist<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
writeln!(w, "{} {}", self.num_cols(), self.num_rows())?;
let directions = [&self.cols, &self.rows];
for dir in directions.iter() {
write!(w, "{} ", dir.iter().map(|el| el.len()).max().unwrap_or(0))?;
}
writeln!(w)?;
for dir in directions.iter() {
for el in *dir {
write!(w, "{} ", el.len())?;
}
writeln!(w)?;
}
for dir in directions.iter() {
for el in *dir {
let mut v = el.clone();
v.sort_unstable();
for x in &v {
write!(w, "{} ", x + 1)?;
}
writeln!(w)?;
}
}
Ok(())
}
pub fn alist(&self) -> String {
let mut s = String::new();
self.write_alist(&mut s).unwrap();
s
}
pub fn from_alist(alist: &str) -> Result<SparseMatrix> {
let mut alist = alist.split('\n');
let sizes = alist
.next()
.ok_or_else(|| String::from("alist first line not found"))?;
let mut sizes = sizes.split_whitespace();
let ncols = sizes
.next()
.ok_or_else(|| String::from("alist first line does not contain enough elements"))?
.parse()
.map_err(|_| String::from("ncols is not a number"))?;
let nrows = sizes
.next()
.ok_or_else(|| String::from("alist first line does not contain enough elements"))?
.parse()
.map_err(|_| String::from("nrows is not a number"))?;
let mut h = SparseMatrix::new(nrows, ncols);
alist.next(); alist.next();
alist.next(); for col in 0..ncols {
let col_data = alist
.next()
.ok_or_else(|| String::from("alist does not contain expected number of lines"))?;
let col_data = col_data.split_whitespace();
for row in col_data {
let row: usize = row
.parse()
.map_err(|_| String::from("row value is not a number"))?;
h.insert(row - 1, col);
}
}
Ok(h)
}
pub fn girth(&self) -> Option<usize> {
self.girth_with_max(usize::MAX)
}
pub fn girth_with_max(&self, max: usize) -> Option<usize> {
(0..self.num_cols())
.filter_map(|c| self.girth_at_node_with_max(Node::Col(c), max))
.min()
}
pub fn girth_at_node(&self, node: Node) -> Option<usize> {
self.girth_at_node_with_max(node, usize::MAX)
}
pub fn girth_at_node_with_max(&self, node: Node, max: usize) -> Option<usize> {
bfs::BFSContext::new(self, node).local_girth(max)
}
pub fn bfs(&self, node: Node) -> BFSResults {
bfs::BFSContext::new(self, node).bfs()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Node {
Row(usize),
Col(usize),
}
impl Node {
fn iter(self, h: &SparseMatrix) -> impl Iterator<Item = Node> + '_ {
match self {
Node::Row(n) => h.iter_row(n),
Node::Col(n) => h.iter_col(n),
}
.map(move |&x| match self {
Node::Row(_) => Node::Col(x),
Node::Col(_) => Node::Row(x),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert() {
let mut h = SparseMatrix::new(100, 300);
assert!(!h.contains(27, 154));
h.insert(27, 154);
assert!(h.contains(27, 154));
assert!(!h.contains(28, 154));
}
#[test]
fn test_insert_twice() {
let mut h = SparseMatrix::new(100, 300);
h.insert(27, 154);
h.insert(43, 28);
h.insert(53, 135);
let h2 = h.clone();
h.insert(43, 28);
assert_eq!(h, h2);
}
#[test]
fn iter_all() {
use std::collections::HashSet;
let mut h = SparseMatrix::new(10, 20);
let entries = [
(7, 8),
(5, 14),
(6, 6),
(6, 7),
(8, 10),
(0, 4),
(0, 0),
(0, 15),
];
for entry in &entries {
h.insert(entry.0, entry.1);
}
let result = h.iter_all().collect::<HashSet<_>>();
assert_eq!(result, HashSet::from(entries));
}
#[test]
fn test_alist() {
let mut h = SparseMatrix::new(4, 12);
for j in 0..4 {
h.insert(j, j);
h.insert(j, j + 4);
h.insert(j, j + 8);
}
let expected = "12 4
1 3
1 1 1 1 1 1 1 1 1 1 1 1
3 3 3 3
1
2
3
4
1
2
3
4
1
2
3
4
1 5 9
2 6 10
3 7 11
4 8 12
";
assert_eq!(h.alist(), expected);
let h2 = SparseMatrix::from_alist(expected).unwrap();
assert_eq!(h2.alist(), expected);
}
}