use crate::{BitRead, BitSpan, BitSpanMut, BitVec, traits::BitWrite};
pub struct BitGrid {
v: BitVec<usize>,
width: usize,
}
impl BitGrid {
pub fn new(line: usize, column: usize) -> Self {
Self {
v: BitVec::<usize>::new(line * column),
width: column,
}
}
#[inline]
pub fn set(&mut self, line: usize, column: usize) {
self.v.set(self.width * line + column);
}
#[inline]
pub fn test(&self, line: usize, column: usize) -> bool {
self.v.test(self.width * line + column)
}
pub fn len(&self) -> (usize, usize) {
(self.v.len() / self.width, self.width)
}
pub fn line(&self, line: usize) -> BitSpan<'_, usize> {
self.v.slice(self.width * line, self.width)
}
pub fn line_mut(&mut self, line: usize) -> BitSpanMut<'_, usize> {
self.v.slice_mut(self.width * line, self.width)
}
pub fn line_and(&mut self, i: usize, j: usize) {
if i != j {
for k in 0..self.width {
if self.test(j, k) {
self.set(i, k);
}
}
}
}
pub fn line_or(&mut self, i: usize, j: usize) {
if i != j {
for k in 0..self.width {
if self.test(j, k) {
self.set(i, k);
}
}
}
}
pub fn line_xor(&mut self, i: usize, j: usize) {
if i != j {
for k in 0..self.width {
if self.test(i, k) != self.test(j, k) {
self.set(i, k);
}
}
}
}
pub fn is_symmetric(&self) -> bool {
let (rows, cols) = self.len();
if rows != cols {
return false;
}
for i in 0..rows {
for j in 0..i {
if self.test(i, j) != self.test(j, i) {
return false;
}
}
}
true
}
pub fn make_symmetric(&mut self) {
let (rows, cols) = self.len();
assert_eq!(rows, cols, "Cannot make non-square grid symmetric");
for i in 0..rows {
for j in 0..i {
if self.test(i, j) {
self.set(j, i);
}
if self.test(j, i) {
self.set(i, j);
}
}
}
}
pub fn transitive_closure(&mut self) {
let (line, column) = self.len();
assert_eq!(
line, column,
"square matrix expected, ({line}x{column}) found"
);
for i in 0..line {
for j in 0..column {
if self.test(j, i) {
for k in 0..self.width {
if self.test(i, k) {
self.set(j, k);
}
}
}
}
}
}
pub fn reflexive_transitive_closure(&mut self) {
self.transitive_closure();
let n = self.width;
for i in 0..n {
self.set(i, i);
}
}
pub fn print_matrix(&self) {
eprint!(" ");
let (line, column) = self.len();
for i in 0..column {
if i / 10 == 0 {
eprint!(" ");
} else {
eprint!("{}", (b'0' + i as u8 / 10) as char);
}
}
eprint!("\n");
eprint!(" ");
for i in 0..column {
eprint!("{}", i % 10);
}
eprint!("\n");
eprint!(" .{}.\n", "-".repeat(column));
for i in 0..line {
eprint!("{:2}|", i);
for j in 0..column {
eprint!("{}", if self.test(i, j) { "1" } else { " " });
}
eprint!("|\n");
}
eprint!(" `{}'\n", "-".repeat(column));
}
}
impl std::fmt::Debug for BitGrid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (nline, ncol) = self.len();
let buf = (0..nline)
.map(|i| {
(0..ncol)
.map(|j| if self.test(i, j) { "1" } else { "0" })
.collect::<String>()
})
.collect::<Vec<_>>();
if f.alternate() {
write!(f, "{:#?}", buf)
} else {
write!(f, "{:?}", buf)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grid() {
let line = 4;
let column = 7;
let mut grid = BitGrid::new(line, column);
assert_eq!(grid.len(), (line, column));
for (l, c) in [(0, 2), (1, 3), (2, 4), (3, 5)] {
assert!(!grid.test(l, c));
grid.set(l, c);
assert!(grid.test(l, c));
}
}
#[test]
fn test_transitive_closure() {
let n = 4;
let mut grid = BitGrid::new(n, n);
assert_eq!(grid.len(), (n, n));
for (l, c) in [(0, 1), (1, 2), (2, 3)] {
assert!(!grid.test(l, c));
grid.set(l, c);
assert!(grid.test(l, c));
}
grid.transitive_closure();
assert!(grid.test(0, 1));
assert!(grid.test(0, 2));
assert!(grid.test(0, 3));
}
#[test]
fn test_reflexive_transitive_closure() {
let n = 4;
let mut grid = BitGrid::new(n, n);
assert_eq!(grid.len(), (n, n));
grid.reflexive_transitive_closure();
for i in 0..n {
assert!(grid.test(i, i));
}
}
#[test]
fn test_print() {
let line = 12;
let column = 36;
let mut grid = BitGrid::new(line, column);
for i in 0..line {
for j in 0..column {
if i > j {
grid.set(i, j);
}
}
}
grid.print_matrix();
}
#[test]
fn test_line_or() {
let line = 12;
let column = 36;
let mut grid = BitGrid::new(line, column);
grid.set(1, 0);
grid.line_or(0, 1);
assert!(grid.test(0, 0));
}
}