use crate::conversions::FromGraph;
use crate::graph::{Graph, GraphError, GraphWithMutableEdges};
pub struct BitMatrix<const C: usize, const R: usize> {
bits: [[u8; C]; R], }
impl<const C: usize, const R: usize> FromGraph<usize, GraphError<usize>> for BitMatrix<C, R> {
fn from_graph<G>(source_graph: &G) -> Result<Self, GraphError<usize>>
where
G: Graph<usize>,
GraphError<usize>: From<G::Error>,
{
let mut bits = [[0u8; C]; R];
for node in source_graph.iter_nodes()? {
if node >= 8 * C {
return Err(GraphError::EdgeHasInvalidNode(node));
}
}
for (source, destination) in source_graph.iter_edges()? {
if source >= R {
return Err(GraphError::EdgeHasInvalidNode(source));
}
if destination >= 8 * C {
return Err(GraphError::EdgeHasInvalidNode(destination));
}
let byte_col = destination / 8;
let bit_col = destination % 8;
bits[source][byte_col] |= 1 << bit_col;
}
Ok(Self::new_unchecked(bits))
}
}
impl<const C: usize, const R: usize> BitMatrix<C, R> {
pub fn new(bits: [[u8; C]; R]) -> Result<Self, GraphError<usize>> {
if R != 8 * C {
return Err(GraphError::InvalidMatrixSize);
}
Ok(Self { bits })
}
pub fn new_unchecked(bits: [[u8; C]; R]) -> Self {
Self { bits }
}
pub(super) fn get(&self, row: usize, col: usize) -> bool {
if row >= R || col >= 8 * C {
return false;
}
let byte_col = col / 8;
let bit_col = col % 8;
(self.bits[row][byte_col] & (1 << bit_col)) != 0
}
}
impl<const C: usize, const R: usize> Graph<usize> for BitMatrix<C, R> {
type Error = GraphError<usize>;
fn iter_nodes(&self) -> Result<impl Iterator<Item = usize>, Self::Error> {
Ok(0..8 * C)
}
fn iter_edges(&self) -> Result<impl Iterator<Item = (usize, usize)>, Self::Error> {
let iter = (0..8 * C)
.flat_map(move |row| (0..8 * C).map(move |col| (row, col)))
.filter(move |(row, col)| self.get(*row, *col));
Ok(iter)
}
fn outgoing_edges(&self, node: usize) -> Result<impl Iterator<Item = usize>, Self::Error> {
Ok((0..8 * C).filter(move |&col| self.get(node, col)))
}
fn incoming_edges(&self, node: usize) -> Result<impl Iterator<Item = usize>, Self::Error> {
Ok((0..8 * C).filter(move |&row| self.get(row, node)))
}
}
impl<const C: usize, const R: usize> GraphWithMutableEdges<usize> for BitMatrix<C, R> {
fn add_edge(&mut self, source: usize, destination: usize) -> Result<(), Self::Error> {
if source >= R {
return Err(GraphError::EdgeHasInvalidNode(source));
}
if destination >= 8 * C {
return Err(GraphError::EdgeHasInvalidNode(destination));
}
let byte_col = destination / 8;
let bit_col = destination % 8;
self.bits[source][byte_col] |= 1 << bit_col;
Ok(())
}
fn remove_edge(&mut self, source: usize, destination: usize) -> Result<(), Self::Error> {
if source >= R || destination >= 8 * C || !self.get(source, destination) {
return Err(GraphError::EdgeNotFound(source, destination));
}
let byte_col = destination / 8;
let bit_col = destination % 8;
self.bits[source][byte_col] &= !(1 << bit_col);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::adjacency_list::map_adjacency_list::MapAdjacencyList;
use crate::containers::maps::staticdict::Dictionary;
use crate::containers::maps::MapTrait;
use crate::graph::GraphWithMutableEdges;
use crate::tests::{collect, collect_sorted};
#[test]
fn test_bit_matrix_basic() {
let bits = [
[0b00001011u8], [0b00000000u8], [0b00000010u8], [0b00000100u8], [0b00000000u8], [0b00000001u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
let mut nodes = [0usize; 16];
let nodes_slice = collect(matrix.iter_nodes().unwrap(), &mut nodes);
assert_eq!(nodes_slice, &[0, 1, 2, 3, 4, 5, 6, 7]);
let mut edges = [(0usize, 0usize); 16];
let edges_slice = collect(matrix.iter_edges().unwrap(), &mut edges);
let expected_edges = [(0, 0), (0, 1), (0, 3), (2, 1), (3, 2), (5, 0)];
assert_eq!(edges_slice, &expected_edges);
let mut outgoing_0 = [0usize; 8];
let outgoing_slice = collect(matrix.outgoing_edges(0).unwrap(), &mut outgoing_0);
assert_eq!(outgoing_slice, &[0, 1, 3]);
assert_eq!(matrix.outgoing_edges(1).unwrap().count(), 0);
let mut outgoing_2 = [0usize; 8];
let outgoing_slice = collect(matrix.outgoing_edges(2).unwrap(), &mut outgoing_2);
assert_eq!(outgoing_slice, &[1]);
let mut outgoing_5 = [0usize; 8];
let outgoing_slice = collect(matrix.outgoing_edges(5).unwrap(), &mut outgoing_5);
assert_eq!(outgoing_slice, &[0]);
}
#[test]
fn test_bit_matrix_get() {
let bits = [
[0b00000101u8], [0b00000010u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
assert!(matrix.get(0, 0)); assert!(!matrix.get(0, 1)); assert!(matrix.get(0, 2)); assert!(!matrix.get(0, 3));
assert!(!matrix.get(1, 0)); assert!(matrix.get(1, 1)); assert!(!matrix.get(1, 2));
assert!(!matrix.get(2, 0)); assert!(!matrix.get(2, 1)); }
#[test]
fn test_bit_matrix_bounds_checking() {
let bits = [
[0b00000001u8], [0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
];
let matrix = BitMatrix::new_unchecked(bits);
assert!(matrix.get(0, 0)); assert!(!matrix.get(0, 1)); assert!(!matrix.get(7, 7));
assert!(!matrix.get(8, 0)); assert!(!matrix.get(100, 0)); assert!(!matrix.get(usize::MAX, 0));
assert!(!matrix.get(0, 8)); assert!(!matrix.get(0, 100)); assert!(!matrix.get(0, usize::MAX));
assert!(!matrix.get(8, 8));
assert!(!matrix.get(usize::MAX, usize::MAX));
}
#[test]
fn test_bit_matrix_outgoing_edges_bounds() {
let bits = [
[0b00000011u8], [0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
];
let matrix = BitMatrix::new_unchecked(bits);
let mut outgoing_0 = [0usize; 8];
let outgoing_slice = collect(matrix.outgoing_edges(0).unwrap(), &mut outgoing_0);
assert_eq!(outgoing_slice, &[0, 1]);
assert_eq!(matrix.outgoing_edges(8).unwrap().count(), 0); assert_eq!(matrix.outgoing_edges(100).unwrap().count(), 0); assert_eq!(matrix.outgoing_edges(usize::MAX).unwrap().count(), 0); }
#[test]
fn test_bit_matrix_all_edges_set() {
let bits = [
[0b11111111u8], [0b11111111u8], [0b11111111u8], [0b11111111u8], [0b11111111u8], [0b11111111u8], [0b11111111u8], [0b11111111u8], ];
let matrix = BitMatrix::new_unchecked(bits);
assert_eq!(matrix.iter_nodes().unwrap().count(), 8);
assert_eq!(matrix.iter_edges().unwrap().count(), 64);
for node in 0..8 {
assert_eq!(matrix.outgoing_edges(node).unwrap().count(), 8);
}
for row in 0..8 {
for col in 0..8 {
assert!(matrix.get(row, col), "Edge {}->{} should exist", row, col);
}
}
}
#[test]
fn test_bit_matrix_empty() {
let bits = [[0u8], [0u8], [0u8], [0u8], [0u8], [0u8], [0u8], [0u8]];
let matrix = BitMatrix::new_unchecked(bits);
let mut nodes = [0usize; 16];
let nodes_slice = collect(matrix.iter_nodes().unwrap(), &mut nodes);
assert_eq!(nodes_slice, &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(matrix.iter_edges().unwrap().count(), 0);
for node in 0..8 {
assert_eq!(matrix.outgoing_edges(node).unwrap().count(), 0);
}
}
#[test]
fn test_bit_matrix_multi_byte() {
let bits = [
[0b00000001u8, 0b10000000u8], [0b11110000u8, 0b00001111u8], [0b00000000u8, 0b00000000u8], [0b01010101u8, 0b10101010u8], [0b00000000u8, 0b00000000u8], [0b00000000u8, 0b00000000u8], [0b00000000u8, 0b00000000u8], [0b00000000u8, 0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
assert_eq!(matrix.iter_nodes().unwrap().count(), 16);
assert!(matrix.get(0, 0)); assert!(matrix.get(0, 15)); assert!(!matrix.get(0, 1)); assert!(!matrix.get(0, 14));
assert!(!matrix.get(1, 0)); assert!(!matrix.get(1, 1));
assert!(!matrix.get(1, 2));
assert!(!matrix.get(1, 3));
assert!(matrix.get(1, 4)); assert!(matrix.get(1, 5));
assert!(matrix.get(1, 6));
assert!(matrix.get(1, 7));
assert!(matrix.get(1, 8)); assert!(matrix.get(1, 9));
assert!(matrix.get(1, 10));
assert!(matrix.get(1, 11));
assert!(!matrix.get(1, 12)); assert!(!matrix.get(1, 13));
assert!(!matrix.get(1, 14));
assert!(!matrix.get(1, 15));
assert!(matrix.get(3, 0)); assert!(!matrix.get(3, 1)); assert!(matrix.get(3, 2)); assert!(!matrix.get(3, 3)); assert!(matrix.get(3, 4)); assert!(!matrix.get(3, 5)); assert!(matrix.get(3, 6)); assert!(!matrix.get(3, 7));
assert!(!matrix.get(3, 8)); assert!(matrix.get(3, 9)); assert!(!matrix.get(3, 10)); assert!(matrix.get(3, 11)); assert!(!matrix.get(3, 12)); assert!(matrix.get(3, 13)); assert!(!matrix.get(3, 14)); assert!(matrix.get(3, 15));
assert_eq!(matrix.outgoing_edges(0).unwrap().count(), 2); assert_eq!(matrix.outgoing_edges(1).unwrap().count(), 8); assert_eq!(matrix.outgoing_edges(2).unwrap().count(), 0); assert_eq!(matrix.outgoing_edges(3).unwrap().count(), 8);
assert!(!matrix.get(0, 16)); assert!(!matrix.get(0, 100)); assert_eq!(matrix.outgoing_edges(8).unwrap().count(), 0); }
#[test]
fn test_incoming_edges() {
let bits = [
[0b00000010u8], [0b00000100u8], [0b00000001u8], [0b00000010u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(0).unwrap(), &mut edges);
assert_eq!(edges_slice, &[2]);
let mut edges = [0usize; 4];
let edges_slice = collect_sorted(matrix.incoming_edges(1).unwrap(), &mut edges);
assert_eq!(edges_slice, &[0, 3]);
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(2).unwrap(), &mut edges);
assert_eq!(edges_slice, &[1]);
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(3).unwrap(), &mut edges);
assert_eq!(edges_slice, &[]);
}
#[test]
fn test_incoming_edges_empty() {
let bits = [
[0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
for node in 0..4 {
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(node).unwrap(), &mut edges);
assert_eq!(edges_slice, &[]);
}
}
#[test]
fn test_incoming_edges_self_loops() {
let bits = [
[0b00000001u8], [0b00000000u8], [0b00000100u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(0).unwrap(), &mut edges);
assert_eq!(edges_slice, &[0]);
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(2).unwrap(), &mut edges);
assert_eq!(edges_slice, &[2]);
for node in [1, 3] {
let mut edges = [0usize; 4];
let edges_slice = collect(matrix.incoming_edges(node).unwrap(), &mut edges);
assert_eq!(edges_slice, &[]);
}
}
#[test]
fn test_incoming_edges_multiple_incoming() {
let bits = [
[0b00000010u8], [0b00000000u8], [0b00000010u8], [0b00000010u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], [0b00000000u8], ];
let matrix = BitMatrix::new_unchecked(bits);
let mut edges = [0usize; 4];
let edges_slice = collect_sorted(matrix.incoming_edges(1).unwrap(), &mut edges);
assert_eq!(edges_slice, &[0, 2, 3]);
}
#[test]
fn test_bit_matrix_new_constructor() {
let valid_bits = [
[0b00000001u8],
[0b00000010u8],
[0b00000100u8],
[0b00001000u8],
[0b00010000u8],
[0b00100000u8],
[0b01000000u8],
[0b10000000u8],
];
assert!(BitMatrix::<1, 8>::new(valid_bits).is_ok());
let invalid_bits = [
[0b00000001u8],
[0b00000010u8],
[0b00000100u8],
[0b00001000u8],
];
assert!(matches!(
BitMatrix::<1, 4>::new(invalid_bits),
Err(GraphError::InvalidMatrixSize)
));
let valid_bits_2 = [
[0b00000001u8, 0b00000000u8],
[0b00000010u8, 0b00000000u8],
[0b00000100u8, 0b00000000u8],
[0b00001000u8, 0b00000000u8],
[0b00010000u8, 0b00000000u8],
[0b00100000u8, 0b00000000u8],
[0b01000000u8, 0b00000000u8],
[0b10000000u8, 0b00000000u8],
[0b00000000u8, 0b00000001u8],
[0b00000000u8, 0b00000010u8],
[0b00000000u8, 0b00000100u8],
[0b00000000u8, 0b00001000u8],
[0b00000000u8, 0b00010000u8],
[0b00000000u8, 0b00100000u8],
[0b00000000u8, 0b01000000u8],
[0b00000000u8, 0b10000000u8],
];
assert!(BitMatrix::<2, 16>::new(valid_bits_2).is_ok());
}
#[test]
fn test_bit_matrix_add_edge_success() {
let bits = [
[0b00000000u8], [0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
];
let mut matrix = BitMatrix::new_unchecked(bits);
assert!(matrix.add_edge(0, 1).is_ok());
assert!(matrix.add_edge(1, 2).is_ok());
assert!(matrix.add_edge(0, 7).is_ok());
assert!(matrix.get(0, 1));
assert!(matrix.get(1, 2));
assert!(matrix.get(0, 7));
assert!(!matrix.get(0, 2));
assert_eq!(matrix.iter_edges().unwrap().count(), 3);
let mut edges = [(0usize, 0usize); 8];
let edges_slice = collect_sorted(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(edges_slice, &[(0, 1), (0, 7), (1, 2)]);
}
#[test]
fn test_bit_matrix_add_edge_invalid_nodes() {
let bits = [[0b00000000u8]; 8];
let mut matrix = BitMatrix::new_unchecked(bits);
let result = matrix.add_edge(8, 1);
assert!(matches!(result, Err(GraphError::EdgeHasInvalidNode(8))));
let result = matrix.add_edge(0, 8);
assert!(matches!(result, Err(GraphError::EdgeHasInvalidNode(8))));
let result = matrix.add_edge(100, 200);
assert!(matches!(result, Err(GraphError::EdgeHasInvalidNode(100))));
}
#[test]
fn test_bit_matrix_remove_edge_success() {
let bits = [
[0b00001010u8], [0b00000100u8], [0b00000001u8], [0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
];
let mut matrix = BitMatrix::new_unchecked(bits);
assert_eq!(matrix.iter_edges().unwrap().count(), 4);
assert!(matrix.get(0, 1));
assert!(matrix.get(0, 3));
assert!(matrix.remove_edge(0, 1).is_ok());
assert!(!matrix.get(0, 1));
assert!(matrix.get(0, 3)); assert_eq!(matrix.iter_edges().unwrap().count(), 3);
let mut edges = [(0usize, 0usize); 8];
let edges_slice = collect_sorted(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(edges_slice, &[(0, 3), (1, 2), (2, 0)]);
}
#[test]
fn test_bit_matrix_remove_edge_not_found() {
let bits = [
[0b00000010u8], [0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
[0b00000000u8],
];
let mut matrix = BitMatrix::new_unchecked(bits);
let result = matrix.remove_edge(0, 2);
assert!(matches!(result, Err(GraphError::EdgeNotFound(0, 2))));
let result = matrix.remove_edge(8, 1);
assert!(matches!(result, Err(GraphError::EdgeNotFound(8, 1))));
assert_eq!(matrix.iter_edges().unwrap().count(), 1);
assert!(matrix.get(0, 1));
}
#[test]
fn test_bit_matrix_add_remove_edge_comprehensive() {
let bits = [[0b00000000u8]; 8];
let mut matrix = BitMatrix::new_unchecked(bits);
assert_eq!(matrix.iter_edges().unwrap().count(), 0);
assert!(matrix.add_edge(0, 1).is_ok());
assert!(matrix.add_edge(1, 2).is_ok());
assert!(matrix.add_edge(2, 3).is_ok());
assert!(matrix.add_edge(0, 3).is_ok());
assert_eq!(matrix.iter_edges().unwrap().count(), 4);
assert!(matrix.remove_edge(1, 2).is_ok());
assert_eq!(matrix.iter_edges().unwrap().count(), 3);
let result = matrix.remove_edge(1, 2);
assert!(matches!(result, Err(GraphError::EdgeNotFound(1, 2))));
assert!(matrix.add_edge(1, 2).is_ok());
assert_eq!(matrix.iter_edges().unwrap().count(), 4);
let mut edges = [(0usize, 0usize); 8];
let edges_slice = collect_sorted(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(edges_slice, &[(0, 1), (0, 3), (1, 2), (2, 3)]);
}
#[test]
fn test_bit_matrix_self_loops() {
let bits = [[0b00000000u8]; 8];
let mut matrix = BitMatrix::new_unchecked(bits);
assert!(matrix.add_edge(0, 0).is_ok()); assert!(matrix.add_edge(0, 1).is_ok());
assert!(matrix.add_edge(2, 2).is_ok());
assert_eq!(matrix.iter_edges().unwrap().count(), 3);
assert!(matrix.get(0, 0));
assert!(matrix.get(0, 1));
assert!(matrix.get(2, 2));
assert!(matrix.remove_edge(0, 0).is_ok());
assert_eq!(matrix.iter_edges().unwrap().count(), 2);
assert!(!matrix.get(0, 0));
assert!(matrix.get(0, 1)); }
#[test]
fn test_bit_matrix_edge_overwrite() {
let bits = [[0b00000000u8]; 8];
let mut matrix = BitMatrix::new_unchecked(bits);
assert!(matrix.add_edge(0, 1).is_ok());
assert!(matrix.get(0, 1));
assert_eq!(matrix.iter_edges().unwrap().count(), 1);
assert!(matrix.add_edge(0, 1).is_ok());
assert!(matrix.get(0, 1));
assert_eq!(matrix.iter_edges().unwrap().count(), 1); }
#[test]
fn test_bit_matrix_from_graph() {
let mut dict = Dictionary::<usize, [usize; 2], 8>::new();
dict.insert(0, [1, 2]).unwrap(); dict.insert(1, [2, 0]).unwrap(); dict.insert(2, [0, 1]).unwrap(); let source = MapAdjacencyList::new_unchecked(dict);
let matrix: BitMatrix<1, 8> = BitMatrix::from_graph(&source).unwrap();
let node_count = matrix.iter_nodes().unwrap().count();
assert_eq!(node_count, 8);
let mut edges = [(0usize, 0usize); 16];
let edges_slice = collect_sorted(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(
edges_slice,
&[(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
);
}
#[test]
fn test_bit_matrix_from_graph_empty() {
let dict = Dictionary::<usize, [usize; 2], 8>::default();
let source = MapAdjacencyList::new_unchecked(dict);
let matrix: BitMatrix<1, 8> = BitMatrix::from_graph(&source).unwrap();
assert_eq!(matrix.iter_edges().unwrap().count(), 0);
assert_eq!(matrix.iter_nodes().unwrap().count(), 8);
}
#[test]
fn test_bit_matrix_from_graph_node_out_of_range() {
let mut dict = Dictionary::<usize, [usize; 1], 8>::new();
dict.insert(0, [1]).unwrap();
dict.insert(1, [8]).unwrap(); let source = MapAdjacencyList::new_unchecked(dict);
let result: Result<BitMatrix<1, 8>, _> = BitMatrix::from_graph(&source);
assert!(matches!(result, Err(GraphError::EdgeHasInvalidNode(8))));
}
#[test]
fn test_bit_matrix_from_graph_single_node() {
let mut dict = Dictionary::<usize, [usize; 1], 8>::new();
dict.insert(5, [5]).unwrap(); let source = MapAdjacencyList::new_unchecked(dict);
let matrix: BitMatrix<1, 8> = BitMatrix::from_graph(&source).unwrap();
let mut edges = [(0usize, 0usize); 16];
let edges_slice = collect(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(edges_slice, &[(5, 5)]);
assert_eq!(matrix.iter_nodes().unwrap().count(), 8);
}
#[test]
fn test_bit_matrix_from_graph_larger_matrix() {
let mut dict = Dictionary::<usize, [usize; 2], 8>::new();
dict.insert(8, [9, 10]).unwrap(); dict.insert(9, [10, 11]).unwrap(); dict.insert(15, [8, 9]).unwrap(); let source = MapAdjacencyList::new_unchecked(dict);
let matrix: BitMatrix<2, 16> = BitMatrix::from_graph(&source).unwrap();
let node_count = matrix.iter_nodes().unwrap().count();
assert_eq!(node_count, 16);
let mut edges = [(0usize, 0usize); 16];
let edges_slice = collect_sorted(matrix.iter_edges().unwrap(), &mut edges);
assert_eq!(
edges_slice,
&[(8, 9), (8, 10), (9, 10), (9, 11), (15, 8), (15, 9)]
);
}
}