use crate::{
BitGauss,
BitLU,
BitPolynomial,
BitSlice,
BitStore,
BitVector,
Unsigned,
rng,
};
#[cfg(feature = "unstable")]
use crate::array::BitArray;
use core::f64;
use std::{
fmt,
fmt::Write,
ops::{
Add,
AddAssign,
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
Bound,
Index,
IndexMut,
Mul,
MulAssign,
Not,
RangeBounds,
Sub,
SubAssign,
},
};
#[doc = include_str!("../docs/matrix.md")]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct BitMatrix<Word: Unsigned = usize> {
m_rows: Vec<BitVector<Word>>,
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn new() -> Self { Self { m_rows: Vec::new() } }
#[must_use]
#[inline]
pub fn zeros(r: usize, c: usize) -> Self { Self { m_rows: vec![BitVector::zeros(c); r] } }
#[must_use]
#[inline]
pub fn square(n: usize) -> Self { Self::zeros(n, n) }
#[must_use]
#[inline]
pub fn ones(r: usize, c: usize) -> Self { Self { m_rows: vec![BitVector::ones(c); r] } }
#[must_use]
pub fn alternating(r: usize, c: usize) -> Self {
let mut result = Self { m_rows: vec![BitVector::alternating(c); r] };
for i in (1..r).step_by(2) {
result.m_rows[i].flip_all();
}
result
}
#[must_use]
pub fn from_outer_product(a: &BitVector<Word>, b: &BitVector<Word>) -> Self {
let r = a.len();
let c = b.len();
let mut result = Self::zeros(r, c);
for i in 0..r {
if a[i] {
result.m_rows[i].copy_store(b);
}
}
result
}
#[must_use]
pub fn from_outer_sum(a: &BitVector<Word>, b: &BitVector<Word>) -> Self {
let r = a.len();
let c = b.len();
let mut result = Self::zeros(r, c);
for i in 0..r {
result.m_rows[i].copy_store(b);
if a[i] {
result.m_rows[i].flip_all();
}
}
result
}
#[must_use]
pub fn from_fn(r: usize, c: usize, f: impl Fn(usize, usize) -> bool) -> Self {
let mut result = Self::zeros(r, c);
for i in 0..r {
for j in 0..c {
if f(i, j) {
result.set(i, j, true);
}
}
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn random_biased_seeded(r: usize, c: usize, p: f64, seed: u64) -> Self {
static TWO_POWER_64: std::sync::LazyLock<f64> = std::sync::LazyLock::new(|| 2.0_f64.powi(64));
if r == 0 || c == 0 {
return Self::new();
}
if p <= 0.0 {
return Self::zeros(r, c);
}
if p >= 1.0 {
return Self::ones(r, c);
}
let old_seed = rng::seed();
if seed != 0 {
rng::set_seed(seed);
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let scaled_p = (*TWO_POWER_64 * p) as u64;
let mut result = Self::zeros(r, c);
for i in 0..r {
for j in 0..c {
if rng::u64() < scaled_p {
result.set(i, j, true);
}
}
}
if seed != 0 {
rng::set_seed(old_seed);
}
result
}
#[must_use]
pub fn random(r: usize, c: usize) -> Self { Self::random_biased_seeded(r, c, 0.5, 0) }
#[must_use]
pub fn random_seeded(r: usize, c: usize, seed: u64) -> Self { Self::random_biased_seeded(r, c, 0.5, seed) }
#[must_use]
pub fn random_biased(r: usize, c: usize, p: f64) -> Self { Self::random_biased_seeded(r, c, p, 0) }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn zero(n: usize) -> Self { Self::zeros(n, n) }
#[must_use]
#[inline]
pub fn identity(n: usize) -> Self {
let mut result = Self::zeros(n, n);
for i in 0..n {
result.set(i, i, true);
}
result
}
#[must_use]
pub fn left_shift(n: usize, p: usize) -> Self {
let mut result = Self::zeros(n, n);
result.set_super_diagonal(p, true);
result
}
#[must_use]
pub fn right_shift(n: usize, p: usize) -> Self {
let mut result = Self::zeros(n, n);
result.set_sub_diagonal(p, true);
result
}
#[must_use]
pub fn left_rotation(n: usize, p: usize) -> Self {
let mut result = Self::zeros(n, n);
for i in 0..n {
let j = (i + n - p) % n;
result.set(i, j, true);
}
result
}
#[must_use]
pub fn right_rotation(n: usize, p: usize) -> Self {
let mut result = Self::zeros(n, n);
for i in 0..n {
let j = (i + p) % n;
result.set(i, j, true);
}
result
}
#[must_use]
pub fn companion<Src: BitStore<Word>>(top_row: &Src) -> Self {
if top_row.len() == 0 {
return Self::new();
}
let mut result = Self::zero(top_row.len());
result.m_rows[0].copy_store(top_row);
result.set_sub_diagonal(1, true);
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn from_vector_of_rows(src: &BitVector<Word>, r: usize) -> Option<Self> {
if src.len() == 0 {
return Some(Self::new());
}
if r == 0 || src.len() % r != 0 {
return None;
}
let c = src.len() / r;
let mut result = Self::zeros(r, c);
for i in 0..r {
let start = i * c;
let end = start + c;
result.m_rows[i].copy_store(&src.slice(start..end));
}
Some(result)
}
#[must_use]
pub fn from_vector_of_cols(src: &BitVector<Word>, c: usize) -> Option<Self> {
if src.len() == 0 {
return Some(Self::new());
}
if c == 0 || src.len() % c != 0 {
return None;
}
let r = src.len() / c;
let mut result = Self::zeros(r, c);
let mut src_index = 0;
for j in 0..c {
for i in 0..r {
if src[src_index] {
result.set(i, j, true);
}
src_index += 1;
}
}
Some(result)
}
#[must_use]
pub fn from_string(s: &str) -> Option<Self> {
if s.is_empty() {
return Some(Self::new());
}
let row_strings: Vec<&str> =
s.split(|c: char| c.is_whitespace() || c == ';').filter(|s| !s.is_empty()).collect();
let n_rows = row_strings.len();
let mut n_cols = 0;
let mut result = BitMatrix::new();
for (i, row_string) in row_strings.iter().enumerate() {
if let Some(row) = BitVector::<Word>::from_string(row_string) {
if i == 0 {
n_cols = row.len();
result.resize(n_rows, n_cols);
}
else {
if row.len() != n_cols {
return None;
}
}
result.m_rows[i].copy_store(&row);
}
else {
return None;
}
}
Some(result)
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn rows(&self) -> usize { self.m_rows.len() }
#[must_use]
#[inline]
pub fn cols(&self) -> usize { if self.m_rows.is_empty() { 0 } else { self.m_rows[0].len() } }
#[must_use]
#[inline]
pub fn len(&self) -> usize { self.m_rows.len() * self.cols() }
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool { self.m_rows.is_empty() }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn any(&self) -> bool { self.m_rows.iter().any(super::store::BitStore::any) }
#[must_use]
#[inline]
pub fn all(&self) -> bool { self.m_rows.iter().all(super::store::BitStore::all) }
#[must_use]
#[inline]
pub fn none(&self) -> bool { self.m_rows.iter().all(super::store::BitStore::none) }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn is_square(&self) -> bool { !self.is_empty() && self.rows() == self.cols() }
#[must_use]
#[inline]
pub fn is_zero(&self) -> bool { self.is_square() && self.none() }
#[must_use]
pub fn is_identity(&self) -> bool {
if !self.is_square() {
return false;
}
for i in 0..self.rows() {
let mut row = self.m_rows[i].clone();
row.flip(i);
if row.any() {
return false;
}
}
true
}
#[must_use]
pub fn is_symmetric(&self) -> bool {
if !self.is_square() {
return false;
}
for i in 0..self.rows() {
for j in 0..self.cols() {
if self.get(i, j) != self.get(j, i) {
return false;
}
}
}
true
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn count_ones(&self) -> usize { self.m_rows.iter().map(super::store::BitStore::count_ones).sum() }
#[must_use]
#[inline]
pub fn count_zeros(&self) -> usize { self.len() - self.count_ones() }
#[must_use]
pub fn count_ones_on_diagonal(&self) -> usize {
debug_assert!(self.is_square(), "Bit-matrix is not square");
let mut count = 0;
for i in 0..self.rows() {
if self.get(i, i) {
count += 1;
}
}
count
}
#[must_use]
#[inline]
pub fn trace(&self) -> bool { self.count_ones_on_diagonal() % 2 == 1 }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn get(&self, r: usize, c: usize) -> bool {
debug_assert!(r < self.rows(), "Row index {r} out of bounds [0,{})", self.rows());
debug_assert!(c < self.cols(), "Column index {c} out of bounds [0,{})", self.cols());
self.m_rows[r].get(c)
}
#[inline]
pub fn set(&mut self, r: usize, c: usize, val: bool) -> &mut Self {
debug_assert!(r < self.rows(), "Row index {r} out of bounds [0,{})", self.rows());
debug_assert!(c < self.cols(), "Column index {c} out of bounds [0,{})", self.cols());
self.m_rows[r].set(c, val);
self
}
#[inline]
pub fn flip(&mut self, r: usize, c: usize) -> &mut Self {
debug_assert!(r < self.rows(), "Row index {r} out of bounds [0,{})", self.rows());
debug_assert!(c < self.cols(), "Column index {c} out of bounds [0,{})", self.cols());
self.m_rows[r].flip(c);
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
#[inline]
pub fn row(&self, i: usize) -> &BitVector<Word> {
debug_assert!(i < self.rows(), "Row index {i} out of bounds [0, {})", self.rows());
&self.m_rows[i]
}
#[must_use]
#[inline]
pub fn row_mut(&mut self, i: usize) -> &mut BitVector<Word> {
debug_assert!(i < self.rows(), "Row index {i} out of bounds [0, {})", self.rows());
&mut self.m_rows[i]
}
#[inline]
pub fn set_row<Src: BitStore<Word>>(&mut self, i: usize, src: &Src) -> &mut Self {
debug_assert!(i < self.rows(), "Row index {i} out of bounds [0, {})", self.rows());
debug_assert_eq!(src.len(), self.cols(), "Source length mismatch {} != {}", src.len(), self.cols());
self.m_rows[i].copy_store(src);
self
}
#[inline]
pub fn flip_row(&mut self, i: usize) -> &mut Self {
debug_assert!(i < self.rows(), "Row index {i} out of bounds [0, {})", self.rows());
self.row_mut(i).flip_all();
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn col(&self, c: usize) -> BitVector<Word> {
debug_assert!(c < self.cols(), "Column {c} is not in bounds [0, {})", self.cols());
let mut result = BitVector::zeros(self.rows());
for r in 0..self.rows() {
if self.get(r, c) {
result.set(r, true);
}
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn set_all(&mut self, v: bool) -> &mut Self {
for row in &mut self.m_rows {
row.set_all(v);
}
self
}
pub fn flip_all(&mut self) -> &mut Self {
for row in &mut self.m_rows {
row.flip_all();
}
self
}
#[must_use]
pub fn flipped(&self) -> BitMatrix<Word> {
let mut result: BitMatrix<Word> = self.clone();
result.flip_all();
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn set_diagonal(&mut self, val: bool) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..self.rows() {
self.set(i, i, val);
}
self
}
pub fn flip_diagonal(&mut self) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..self.rows() {
self.flip(i, i);
}
self
}
pub fn set_super_diagonal(&mut self, d: usize, val: bool) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..(self.rows() - d) {
self.set(i, i + d, val);
}
self
}
pub fn flip_super_diagonal(&mut self, d: usize) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..(self.rows() - d) {
self.flip(i, i + d);
}
self
}
pub fn set_sub_diagonal(&mut self, d: usize, val: bool) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..(self.rows() - d) {
self.set(i + d, i, val);
}
self
}
pub fn flip_sub_diagonal(&mut self, d: usize) -> &mut Self {
debug_assert!(self.is_square(), "Bit-matrix is not square");
for i in 0..(self.rows() - d) {
self.flip(i + d, i);
}
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn resize(&mut self, r: usize, c: usize) -> &mut Self {
if r == self.rows() && c == self.cols() {
return self;
}
if r == 0 || c == 0 {
for row in &mut self.m_rows {
row.resize(0);
}
self.m_rows.resize(0, BitVector::default());
return self;
}
let old_cols = self.cols();
self.m_rows.resize(r, BitVector::zeros(c));
if c != old_cols {
for row in &mut self.m_rows {
row.resize(c);
}
}
self
}
#[inline]
pub fn clear(&mut self) -> &mut Self { self.resize(0, 0) }
pub fn shrink_to_fit(&mut self) -> &mut Self {
for row in &mut self.m_rows {
row.shrink_to_fit();
}
self.m_rows.shrink_to_fit();
self
}
pub fn make_square(&mut self, n: usize) -> &mut Self {
self.resize(n, n);
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn append_row(&mut self, row: BitVector<Word>) -> &mut Self {
assert_eq!(row.len(), self.cols(), "Row must have same number of elements as the matrix has columns");
self.m_rows.push(row);
self
}
pub fn remove_row(&mut self) -> Option<BitVector<Word>> { self.m_rows.pop() }
pub fn append_col(&mut self, col: &BitVector<Word>) -> &mut Self {
assert_eq!(col.len(), self.rows(), "Column must have same number of elements as the matrix has rows");
for (i, row) in self.m_rows.iter_mut().enumerate() {
row.push(col[i]);
}
self
}
pub fn remove_col(&mut self) -> Option<BitVector<Word>> {
if self.is_empty() {
return None;
}
let result = self.col(self.cols() - 1);
for row in &mut self.m_rows {
row.pop();
}
Some(result)
}
pub fn append_cols(&mut self, src: &BitMatrix<Word>) -> &mut Self {
assert_eq!(src.rows(), self.rows(), "Input matrix must have same number of rows as the matrix");
for (i, row) in self.m_rows.iter_mut().enumerate() {
row.append_store(&src.m_rows[i]);
}
self
}
pub fn remove_cols(&mut self, k: usize) -> Option<BitMatrix<Word>> {
if k > self.cols() {
return None;
}
let result = self.sub_matrix(0..self.rows(), self.cols() - k..self.cols());
self.resize(self.rows(), self.cols() - k);
Some(result)
}
pub fn append_rows(&mut self, src: BitMatrix<Word>) -> &mut Self {
assert_eq!(src.cols(), self.cols(), "Input matrix must have same number of columns as the matrix");
self.m_rows.extend(src.m_rows);
self
}
pub fn remove_rows(&mut self, k: usize) -> Option<BitMatrix<Word>> {
if k > self.rows() {
return None;
}
let result = self.sub_matrix(self.rows() - k..self.rows(), 0..self.cols());
self.resize(self.rows() - k, self.cols());
Some(result)
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[inline]
pub fn swap_rows(&mut self, i0: usize, i1: usize) -> &mut Self {
self.m_rows.swap(i0, i1);
self
}
#[inline]
pub fn swap_cols(&mut self, j0: usize, j1: usize) -> &mut Self {
for i in 0..self.rows() {
self.m_rows[i].swap(j0, j1);
}
self
}
pub fn add_identity(&mut self) -> &mut Self {
assert!(self.is_square(), "`add_identity` requires a square matrix");
for i in 0..self.rows() {
self.flip(i, i);
}
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn transpose(&mut self) -> &mut Self {
assert!(self.is_square(), "`transpose_in_place` requires a square matrix");
for i in 0..self.rows() {
for j in 0..i {
if self.get(i, j) != self.get(j, i) {
self.flip(i, j);
self.flip(j, i);
}
}
}
self
}
#[must_use]
pub fn transposed(&self) -> Self {
let r = self.rows();
let c = self.cols();
let mut result = BitMatrix::zeros(c, r);
for i in 0..r {
for j in 0..c {
if self.get(i, j) {
result.set(j, i, true);
}
}
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn sub_matrix<R: RangeBounds<usize>>(&self, rows: R, cols: R) -> Self {
let r_start = match rows.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start + 1,
Bound::Unbounded => 0,
};
let r_end = match rows.end_bound() {
Bound::Included(end) => *end + 1,
Bound::Excluded(end) => *end,
Bound::Unbounded => self.rows(),
};
assert!(r_start <= r_end, "Invalid row range");
assert!(r_end <= self.rows(), "Row range extends beyond the end of the bit-matrix");
let c_start = match cols.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start + 1,
Bound::Unbounded => 0,
};
let c_end = match cols.end_bound() {
Bound::Included(end) => *end + 1,
Bound::Excluded(end) => *end,
Bound::Unbounded => self.cols(),
};
assert!(c_start <= c_end, "Invalid column range");
assert!(c_end <= self.cols(), "Column range extends beyond the right edge of the bit-matrix");
let r = r_end - r_start;
let c = c_end - c_start;
let mut result = BitMatrix::zeros(r, c);
for i in 0..r {
result.m_rows[i].copy_store(&self.m_rows[i + r_start].slice(c_start..c_end));
}
result
}
pub fn replace_sub_matrix(&mut self, top: usize, left: usize, src: &BitMatrix<Word>) -> &mut Self {
let r = src.rows();
let c = src.cols();
assert!(top + r <= self.rows(), "Too many rows for the replacement sub-matrix to fit");
assert!(left + c <= self.cols(), "Too many columns for the replacement sub-matrix to fit");
for i in 0..r {
self.m_rows[top + i].slice_mut(left..left + c).copy_store(&src.m_rows[i]);
}
self
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn lower(&self) -> Self {
if self.is_empty() {
return BitMatrix::new();
}
let mut result = self.clone();
let c = self.cols();
for i in 0..self.rows() {
let first = i + 1;
if first < c {
result.m_rows[i].slice_mut(first..c).set_all(false);
}
}
result
}
#[must_use]
pub fn upper(&self) -> Self {
if self.is_empty() {
return BitMatrix::new();
}
let mut result = self.clone();
let c = self.cols();
for i in 0..self.rows() {
let len = std::cmp::min(i, c);
if len > 0 {
result.m_rows[i].slice_mut(0..len).set_all(false);
}
}
result
}
#[must_use]
pub fn strictly_lower(&self) -> Self {
let mut result = self.lower();
result.set_diagonal(false);
result
}
#[must_use]
pub fn strictly_upper(&self) -> Self {
let mut result = self.upper();
result.set_diagonal(false);
result
}
#[must_use]
pub fn unit_lower(&self) -> Self {
let mut result = self.lower();
result.set_diagonal(true);
result
}
#[must_use]
pub fn unit_upper(&self) -> Self {
let mut result = self.upper();
result.set_diagonal(true);
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn dot<Rhs: BitStore<Word>>(&self, rhs: &Rhs) -> BitVector<Word> {
assert_eq!(self.cols(), rhs.len(), "Incompatible dimensions: {} != {}", self.cols(), rhs.len());
let mut result = BitVector::zeros(self.rows());
for i in 0..self.rows() {
if self.row(i).dot(rhs) {
result.set(i, true);
}
}
result
}
pub fn left_dot<Lhs: BitStore<Word>>(&self, lhs: &Lhs) -> BitVector<Word> {
assert_eq!(self.rows(), lhs.len(), "Incompatible dimensions: {} != {}", self.rows(), lhs.len());
let mut result = BitVector::zeros(self.cols());
for i in 0..self.cols() {
if lhs.dot(&self.col(i)) {
result.set(i, true);
}
}
result
}
#[must_use]
pub fn dot_matrix(&self, rhs: &BitMatrix<Word>) -> Self {
assert_eq!(self.cols(), rhs.rows(), "Incompatible dimensions: {} != {}", self.cols(), rhs.rows());
let r = self.rows();
let c = rhs.cols();
let mut result = BitMatrix::zeros(r, c);
for j in 0..c {
let rhs_col = rhs.col(j);
for i in 0..r {
if self.row(i).dot(&rhs_col) {
result.set(i, j, true);
}
}
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn to_the(&self, n: usize) -> Self {
assert!(self.is_square(), "Bit-matrix must be square");
if n == 0 {
return Self::identity(self.rows());
}
let mut n_bit = n.prev_power_of_two();
let mut result = self.clone();
n_bit >>= 1;
while n_bit > 0 {
result = &result * &result;
if (n & n_bit) != 0 {
result = &result * self;
}
n_bit >>= 1;
}
result
}
#[must_use]
pub fn to_the_2_to_the(&self, n: usize) -> Self {
assert!(self.is_square(), "Bit-matrix must be square");
let mut result = self.clone();
for _ in 0..n {
result = &result * &result;
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn to_vector(&self) -> BitVector<Word> {
let mut result = BitVector::with_capacity(self.len());
for row in &self.m_rows {
result.append_store(row);
}
result
}
#[must_use]
pub fn to_vector_of_cols(&self) -> BitVector<Word> {
let mut result = BitVector::with_capacity(self.len());
for col in 0..self.cols() {
result.append_store(&self.col(col));
}
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn to_echelon_form(&mut self) -> BitVector<Word> {
assert!(!self.is_empty(), "Bit-matrix must not be empty");
let mut has_pivot: BitVector<Word> = BitVector::zeros(self.cols());
let mut r = 0;
let num_rows = self.rows();
for j in 0..self.cols() {
let mut p = r;
while p < num_rows && !self[p][j] {
p += 1;
}
if p < num_rows {
has_pivot.set(j, true);
if p != r {
self.swap_rows(p, r);
}
let row_r = self[r].clone();
for i in r + 1..num_rows {
if self[i][j] {
self[i] ^= &row_r;
}
}
r += 1;
if r == num_rows {
break;
}
}
}
has_pivot
}
#[must_use]
pub fn to_reduced_echelon_form(&mut self) -> BitVector<Word> {
let has_pivot = self.to_echelon_form();
for r in (0..self.rows()).rev() {
if let Some(p) = self[r].first_set() {
let row_r = self[r].clone();
for i in 0..r {
if self[i][p] {
self[i] ^= &row_r;
}
}
}
}
has_pivot
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn inverse(&self) -> Option<BitMatrix<Word>> {
if !self.is_square() {
return None;
}
let mut matrix = self.clone();
matrix.append_cols(&BitMatrix::identity(self.rows()));
let _ = matrix.to_reduced_echelon_form();
if matrix.sub_matrix(0..self.rows(), 0..self.cols()).is_identity() {
let inverse = matrix.sub_matrix(0..self.rows(), self.cols()..self.cols() * 2);
Some(inverse)
}
else {
None
}
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn probability_invertible(n: usize) -> f64 {
assert!(n > 0, "Querying the probability of a 0 x 0 bit-matrix being invertible. Upstream error???");
let mut n_prod = f64::MANTISSA_DIGITS;
let mut result = 1.0;
let mut pow2 = 1.0;
while n_prod > 0 {
pow2 *= 0.5;
result *= 1.0 - pow2;
n_prod -= 1;
}
result
}
#[must_use]
pub fn probability_singular(n: usize) -> f64 { 1.0 - Self::probability_invertible(n) }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn solver_for(&self, b: &BitVector<Word>) -> BitGauss<Word> { BitGauss::new(self, b) }
#[must_use]
pub fn x_for(&self, b: &BitVector<Word>) -> Option<BitVector<Word>> { self.solver_for(b).x() }
#[must_use]
pub fn lu_decomposition(&self) -> BitLU<Word> { BitLU::new(self) }
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn characteristic_polynomial(&self) -> BitPolynomial<Word> {
assert!(self.is_square(), "Bit-matrix must be square not {}x{}", self.rows(), self.cols());
Self::characteristic_polynomial_frobenius_matrix(&self.frobenius_form())
}
#[must_use]
pub fn characteristic_polynomial_frobenius_matrix(top_rows: &[BitVector<Word>]) -> BitPolynomial<Word> {
let n_companions = top_rows.len();
if n_companions == 0 {
return BitPolynomial::zero();
}
let mut result = Self::characteristic_polynomial_companion_matrix(&top_rows[0]);
#[allow(clippy::needless_range_loop)]
for i in 1..n_companions {
result *= Self::characteristic_polynomial_companion_matrix(&top_rows[i]);
}
result
}
#[must_use]
pub fn characteristic_polynomial_companion_matrix(top_row: &BitVector<Word>) -> BitPolynomial<Word> {
let n = top_row.len();
let mut coeffs = BitVector::ones(n + 1);
for j in 0..n {
coeffs.set(n - j - 1, top_row[j]);
}
BitPolynomial::from_coefficients(coeffs)
}
#[must_use]
pub fn frobenius_form(&self) -> Vec<BitVector<Word>> {
assert!(self.is_square(), "Bit-matrix must be square not {}x{}", self.rows(), self.cols());
let mut top_rows = Vec::new();
let mut copy = self.clone();
let mut n = copy.rows();
while n > 0 {
let companion = copy.danilevsky_step(n);
n -= companion.len();
top_rows.push(companion);
}
top_rows
}
#[must_use]
fn danilevsky_step(&mut self, n: usize) -> BitVector<Word> {
assert!(
n <= self.rows(),
"Asked to look at the top-left {n} x {n} sub-matrix but the matrix has only {} rows",
self.rows()
);
if n == 1 {
return BitVector::constant(self[0][0], 1);
}
let mut k = n - 1;
while k > 0 {
if !self[k][k - 1] {
for j in 0..k - 1 {
if self[k][j] {
self.swap_rows(j, k - 1);
self.swap_cols(j, k - 1);
break;
}
}
}
if !self[k][k - 1] {
break;
}
let m = self[k].clone();
for j in 0..n {
self.set(k - 1, j, &m * &self.col(j));
}
for i in 0..k {
for j in 0..n {
let tmp = self[i][k - 1] & m[j];
if j == k - 1 {
self.set(i, j, tmp);
}
else {
self.set(i, j, self[i][j] ^ tmp);
}
}
}
self.m_rows[k].set_all(false);
self.set(k, k - 1, true);
k -= 1;
}
let mut top_row = BitVector::zeros(n - k);
for j in 0..n - k {
top_row.set(j, self[k][k + j]);
}
top_row
}
}
impl<Word: Unsigned> BitMatrix<Word> {
#[must_use]
pub fn to_binary_string(&self) -> String { self.to_custom_binary_string("\n", " ", "", "") }
#[must_use]
pub fn to_pretty_binary_string(&self) -> String {
const BAR: &str = "\u{2502}";
self.to_custom_binary_string("\n", " ", BAR, BAR)
}
#[must_use]
pub fn to_compact_binary_string(&self) -> String { self.to_custom_binary_string(" ", "", "", "") }
#[must_use]
pub fn to_custom_binary_string(&self, row_separator: &str, separator: &str, left: &str, right: &str) -> String {
self.m_rows
.iter()
.map(|row| row.to_custom_binary_string(separator, left, right))
.collect::<Vec<_>>()
.join(row_separator)
}
#[must_use]
pub fn to_hex_string(&self) -> String {
self.m_rows.iter().map(super::store::BitStore::to_hex_string).collect::<Vec<_>>().join("\n")
}
#[must_use]
pub fn to_compact_hex_string(&self) -> String {
self.m_rows.iter().map(super::store::BitStore::to_hex_string).collect::<Vec<_>>().join(" ")
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn xor_eq(&mut self, rhs: &BitMatrix<Word>) {
assert_eq!(self.rows(), rhs.rows(), "Length mismatch {} != {}", self.rows(), rhs.rows());
assert_eq!(self.cols(), rhs.cols(), "Length mismatch {} != {}", self.cols(), rhs.cols());
for i in 0..self.rows() {
self.m_rows[i].xor_eq(&rhs.m_rows[i]);
}
}
#[must_use]
pub fn xor(&self, rhs: &BitMatrix<Word>) -> BitMatrix<Word> {
let mut result = self.clone();
result.xor_eq(rhs);
result
}
pub fn and_eq(&mut self, rhs: &BitMatrix<Word>) {
assert_eq!(self.rows(), rhs.rows(), "Length mismatch {} != {}", self.rows(), rhs.rows());
assert_eq!(self.cols(), rhs.cols(), "Length mismatch {} != {}", self.cols(), rhs.cols());
for i in 0..self.rows() {
self.m_rows[i].and_eq(&rhs.m_rows[i]);
}
}
#[must_use]
pub fn and(&self, rhs: &BitMatrix<Word>) -> BitMatrix<Word> {
let mut result = self.clone();
result.and_eq(rhs);
result
}
pub fn or_eq(&mut self, rhs: &BitMatrix<Word>) {
assert_eq!(self.rows(), rhs.rows(), "Length mismatch {} != {}", self.rows(), rhs.rows());
assert_eq!(self.cols(), rhs.cols(), "Length mismatch {} != {}", self.cols(), rhs.cols());
for i in 0..self.rows() {
self.m_rows[i].or_eq(&rhs.m_rows[i]);
}
}
#[must_use]
pub fn or(&self, rhs: &BitMatrix<Word>) -> BitMatrix<Word> {
let mut result = self.clone();
result.or_eq(rhs);
result
}
}
impl<Word: Unsigned> BitMatrix<Word> {
pub fn plus_eq(&mut self, rhs: &BitMatrix<Word>) { self.xor_eq(rhs); }
#[must_use]
pub fn plus(&self, rhs: &BitMatrix<Word>) -> BitMatrix<Word> {
let mut result = self.clone();
result.xor_eq(rhs);
result
}
pub fn minus_eq(&mut self, rhs: &BitMatrix<Word>) { self.xor_eq(rhs); }
#[must_use]
pub fn minus(&self, rhs: &BitMatrix<Word>) -> BitMatrix<Word> {
let mut result = self.clone();
result.xor_eq(rhs);
result
}
}
impl<Word: Unsigned> Default for BitMatrix<Word> {
fn default() -> Self { Self::new() }
}
impl<Word: Unsigned> Index<usize> for BitMatrix<Word> {
type Output = BitVector<Word>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
debug_assert!(index < self.rows(), "Row {} is not in bounds [0, {})", index, self.rows());
&self.m_rows[index]
}
}
impl<Word: Unsigned> IndexMut<usize> for BitMatrix<Word> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
debug_assert!(index < self.rows(), "Row {} is not in bounds [0, {})", index, self.rows());
&mut self.m_rows[index]
}
}
impl<Word: Unsigned> fmt::Debug for BitMatrix<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_compact_binary_string()) }
}
impl<Word: Unsigned> fmt::Display for BitMatrix<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}", self.to_compact_binary_string())
}
else {
write!(f, "{}", self.to_pretty_binary_string())
}
}
}
impl<Word: Unsigned> fmt::Binary for BitMatrix<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let row_strings: Vec<String> = self.m_rows.iter().map(|row| format!("{row:#b}")).collect();
if f.alternate() { write!(f, "{}", row_strings.join(" ")) } else { write!(f, "{}", row_strings.join("\n")) }
}
}
impl<Word: Unsigned> fmt::UpperHex for BitMatrix<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let row_strings: Vec<String> = self.m_rows.iter().map(|row| format!("{row:#X}")).collect();
if f.alternate() { write!(f, "{}", row_strings.join(" ")) } else { write!(f, "{}", row_strings.join("\n")) }
}
}
impl<Word: Unsigned> fmt::LowerHex for BitMatrix<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let row_strings: Vec<String> = self.m_rows.iter().map(|row| format!("{row:#x}")).collect();
if f.alternate() { write!(f, "{}", row_strings.join(" ")) } else { write!(f, "{}", row_strings.join("\n")) }
}
}
impl<Word: Unsigned> Not for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn not(self) -> Self::Output { self.flipped() }
}
impl<Word: Unsigned> Not for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn not(self) -> Self::Output { self.flipped() }
}
impl<Word: Unsigned> BitXorAssign<&BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitxor_assign(&mut self, rhs: &BitMatrix<Word>) { self.xor_eq(rhs); }
}
impl<Word: Unsigned> BitXorAssign<BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitxor_assign(&mut self, rhs: BitMatrix<Word>) { self.xor_eq(&rhs); }
}
impl<Word: Unsigned> BitAndAssign<&BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitand_assign(&mut self, rhs: &BitMatrix<Word>) { self.and_eq(rhs); }
}
impl<Word: Unsigned> BitAndAssign<BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitand_assign(&mut self, rhs: BitMatrix<Word>) { self.and_eq(&rhs); }
}
impl<Word: Unsigned> BitOrAssign<&BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitor_assign(&mut self, rhs: &BitMatrix<Word>) { self.or_eq(rhs); }
}
impl<Word: Unsigned> BitOrAssign<BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn bitor_assign(&mut self, rhs: BitMatrix<Word>) { self.or_eq(&rhs); }
}
impl<Word: Unsigned> AddAssign<&BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn add_assign(&mut self, rhs: &BitMatrix<Word>) { self.xor_eq(rhs); }
}
impl<Word: Unsigned> AddAssign<BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn add_assign(&mut self, rhs: BitMatrix<Word>) { self.xor_eq(&rhs); }
}
impl<Word: Unsigned> SubAssign<&BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn sub_assign(&mut self, rhs: &BitMatrix<Word>) { self.xor_eq(rhs); }
}
impl<Word: Unsigned> SubAssign<BitMatrix<Word>> for BitMatrix<Word> {
#[inline]
fn sub_assign(&mut self, rhs: BitMatrix<Word>) { self.xor_eq(&rhs); }
}
impl<Word: Unsigned> BitXor<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitxor(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> BitXor<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitxor(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> BitXor<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitxor(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> BitXor<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitxor(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> BitAnd<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitand(self, rhs: &BitMatrix<Word>) -> Self::Output { self.and(rhs) }
}
impl<Word: Unsigned> BitAnd<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitand(self, rhs: &BitMatrix<Word>) -> Self::Output { self.and(rhs) }
}
impl<Word: Unsigned> BitAnd<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitand(self, rhs: BitMatrix<Word>) -> Self::Output { self.and(&rhs) }
}
impl<Word: Unsigned> BitAnd<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitand(self, rhs: BitMatrix<Word>) -> Self::Output { self.and(&rhs) }
}
impl<Word: Unsigned> BitOr<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitor(self, rhs: &BitMatrix<Word>) -> Self::Output { self.or(rhs) }
}
impl<Word: Unsigned> BitOr<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitor(self, rhs: &BitMatrix<Word>) -> Self::Output { self.or(rhs) }
}
impl<Word: Unsigned> BitOr<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitor(self, rhs: BitMatrix<Word>) -> Self::Output { self.or(&rhs) }
}
impl<Word: Unsigned> BitOr<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn bitor(self, rhs: BitMatrix<Word>) -> Self::Output { self.or(&rhs) }
}
impl<Word: Unsigned> Add<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn add(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> Add<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn add(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> Add<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn add(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> Add<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn add(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> Sub<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn sub(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> Sub<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn sub(self, rhs: &BitMatrix<Word>) -> Self::Output { self.xor(rhs) }
}
impl<Word: Unsigned> Sub<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn sub(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> Sub<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn sub(self, rhs: BitMatrix<Word>) -> Self::Output { self.xor(&rhs) }
}
impl<Word: Unsigned> Mul<&BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn mul(self, rhs: &BitMatrix<Word>) -> Self::Output { self.dot_matrix(rhs) }
}
impl<Word: Unsigned> Mul<BitMatrix<Word>> for &BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn mul(self, rhs: BitMatrix<Word>) -> Self::Output { self.dot_matrix(&rhs) }
}
impl<Word: Unsigned> Mul<&BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn mul(self, rhs: &BitMatrix<Word>) -> Self::Output { self.dot_matrix(rhs) }
}
impl<Word: Unsigned> Mul<BitMatrix<Word>> for BitMatrix<Word> {
type Output = BitMatrix<Word>;
#[inline]
fn mul(self, rhs: BitMatrix<Word>) -> Self::Output { self.dot_matrix(&rhs) }
}
impl<Word: Unsigned> MulAssign<&BitMatrix<Word>> for BitMatrix<Word> {
fn mul_assign(&mut self, rhs: &BitMatrix<Word>) { *self = &*self * rhs; }
}
impl<Word: Unsigned> MulAssign<BitMatrix<Word>> for BitMatrix<Word> {
fn mul_assign(&mut self, rhs: BitMatrix<Word>) { *self = &*self * rhs; }
}
macro_rules! M_dot_v {
(BitVector) => {
M_dot_v!(@impl BitVector[Word]; [Word: Unsigned]);
};
(BitSlice) => {
M_dot_v!(@impl BitSlice['a, Word]; ['a, Word: Unsigned]);
};
(BitArray) => {
M_dot_v!(@impl BitArray[N, Word, WORDS]; [const N: usize, Word: Unsigned, const WORDS: usize]);
};
(@impl $Rhs:ident[$($RhsParams:tt)*]; [$($ImplParams:tt)*]) => {
#[doc = concat!("`BitMatrix`, `", stringify!($Rhs), "` multiplication where neither operand is consumed by the operation.")]
impl<$($ImplParams)*> Mul<&$Rhs<$($RhsParams)*>> for &BitMatrix<Word> {
type Output = BitVector<Word>;
#[inline] fn mul(self, rhs: &$Rhs<$($RhsParams)*>) -> Self::Output { self.dot(rhs) }
}
#[doc = concat!("`BitMatrix`, `", stringify!($Rhs), "` multiplication where the vector is consumed by the operation.")]
impl<$($ImplParams)*> Mul<$Rhs<$($RhsParams)*>> for &BitMatrix<Word> {
type Output = BitVector<Word>;
#[inline] fn mul(self, rhs: $Rhs<$($RhsParams)*>) -> Self::Output { self.dot(&rhs) }
}
#[doc = concat!("`BitMatrix`, `", stringify!($Rhs), "` multiplication where the matrix is consumed by the operation.")]
impl<$($ImplParams)*> Mul<&$Rhs<$($RhsParams)*>> for BitMatrix<Word> {
type Output = BitVector<Word>;
#[inline] fn mul(self, rhs: &$Rhs<$($RhsParams)*>) -> Self::Output { self.dot(rhs) }
}
#[doc = concat!("`BitMatrix`, `", stringify!($Rhs), "` multiplication where both operands are consumed by the operation.")]
impl<$($ImplParams)*> Mul<$Rhs<$($RhsParams)*>> for BitMatrix<Word> {
type Output = BitVector<Word>;
#[inline] fn mul(self, rhs: $Rhs<$($RhsParams)*>) -> Self::Output { self.dot(&rhs) }
}
};}
M_dot_v!(BitVector);
M_dot_v!(BitSlice);
#[cfg(feature = "unstable")]
M_dot_v!(BitArray);
macro_rules! u_dot_M {
(BitVector) => {
u_dot_M!(@impl BitVector[Word]; [Word: Unsigned]);
};
(BitSlice) => {
u_dot_M!(@impl BitSlice['a, Word]; ['a, Word: Unsigned]);
};
(BitArray) => {
u_dot_M!(@impl BitArray[N, Word, WORDS]; [const N: usize, Word: Unsigned, const WORDS: usize]);
};
(@impl $Lhs:ident[$($LhsParams:tt)*]; [$($ImplParams:tt)*]) => {
#[doc = concat!("Vector-matrix multiplication for a `", stringify!($Lhs), "`")]
impl<$($ImplParams)*> $Lhs<$($LhsParams)*> {
#[doc = concat!(stringify!($Lhs), " matrix multiplication` as a new [`BitVector`].")]
#[inline] #[must_use]
pub fn dot_matrix(&self, rhs: &BitMatrix<Word>) -> BitVector<Word> { rhs.left_dot(self) }
}
#[doc = concat!("`", stringify!($Rhs), "`, `BitMatrix` multiplication where neither operand is consumed by the operation.")]
impl<$($ImplParams)*> Mul<&BitMatrix<Word>> for &$Lhs<$($LhsParams)*> {
type Output = BitVector<Word>;
fn mul(self, rhs: &BitMatrix<Word>) -> Self::Output {
self.dot_matrix(rhs)
}
}
#[doc = concat!("`", stringify!($Rhs), "`, `BitMatrix` multiplication where the vector is consumed by the operation.")]
impl<$($ImplParams)*> Mul<&BitMatrix<Word>> for $Lhs<$($LhsParams)*> {
type Output = BitVector<Word>;
fn mul(self, rhs: &BitMatrix<Word>) -> Self::Output {
self.dot_matrix(rhs)
}
}
#[doc = concat!("`", stringify!($Rhs), "`, `BitMatrix` multiplication where the matrix is consumed by the operation.")]
impl<$($ImplParams)*> Mul<BitMatrix<Word>> for &$Lhs<$($LhsParams)*> {
type Output = BitVector<Word>;
fn mul(self, rhs: BitMatrix<Word>) -> Self::Output {
self.dot_matrix(&rhs)
}
}
#[doc = concat!("`", stringify!($Rhs), "`, `BitMatrix` multiplication where both operands are consumed by the operation.")]
impl<$($ImplParams)*> Mul<BitMatrix<Word>> for $Lhs<$($LhsParams)*> {
type Output = BitVector<Word>;
fn mul(self, rhs: BitMatrix<Word>) -> Self::Output {
self.dot_matrix(&rhs)
}
}
};}
u_dot_M!(BitVector);
u_dot_M!(BitSlice);
#[cfg(feature = "unstable")]
u_dot_M!(BitArray);
#[allow(non_snake_case)]
#[must_use]
pub fn string_for_AB<Word: Unsigned>(A: &BitMatrix<Word>, B: &BitMatrix<Word>) -> String {
let num_rows = A.rows().max(B.rows());
let A_fill = " ".repeat(A.cols() + 1);
let B_fill = " ".repeat(B.cols() + 1);
let mut result = String::new();
for i in 0..num_rows {
let A_str = if i < A.rows() { A[i].to_string() } else { A_fill.clone() };
let B_str = if i < B.rows() { B[i].to_string() } else { B_fill.clone() };
let _ = write!(result, "{}", &format!("| {A_str} | {B_str} |\n"));
}
result
}
#[allow(non_snake_case)]
#[must_use]
pub fn string_for_ABC<Word: Unsigned>(A: &BitMatrix<Word>, B: &BitMatrix<Word>, C: &BitMatrix<Word>) -> String {
let num_rows = A.rows().max(B.rows()).max(C.rows());
let A_fill = " ".repeat(A.cols() + 1);
let B_fill = " ".repeat(B.cols() + 1);
let C_fill = " ".repeat(C.cols() + 1);
let mut result = String::new();
for i in 0..num_rows {
let A_str = if i < A.rows() { A[i].to_string() } else { A_fill.clone() };
let B_str = if i < B.rows() { B[i].to_string() } else { B_fill.clone() };
let C_str = if i < C.rows() { C[i].to_string() } else { C_fill.clone() };
let _ = write!(result, "{}", &format!("| {A_str} | {B_str} | {C_str} |\n"));
}
result
}
#[allow(non_snake_case)]
#[must_use]
pub fn string_for_Au<Word: Unsigned>(A: &BitMatrix<Word>, u: &BitVector<Word>) -> String {
let num_rows = A.rows().max(u.len());
let A_fill = " ".repeat(A.cols() + 1);
let u_fill = " ";
let mut result = String::new();
for i in 0..num_rows {
let A_str = if i < A.rows() { A[i].to_string() } else { A_fill.clone() };
let u_str = if i < u.len() { i32::from(u[i]).to_string() } else { u_fill.to_string() };
let _ = write!(result, "{}", &format!("| {A_str} | {u_str} |\n"));
}
result
}
#[allow(non_snake_case)]
#[must_use]
pub fn string_for_Auv<Word: Unsigned>(A: &BitMatrix<Word>, u: &BitVector<Word>, v: &BitVector<Word>) -> String {
let num_rows = A.rows().max(u.len()).max(v.len());
let A_fill = " ".repeat(A.cols() + 1);
let u_fill = " ";
let v_fill = " ";
let mut result = String::new();
for i in 0..num_rows {
let A_str = if i < A.rows() { A[i].to_string() } else { A_fill.clone() };
let u_str = if i < u.len() { i32::from(u[i]).to_string() } else { u_fill.to_string() };
let v_str = if i < v.len() { i32::from(v[i]).to_string() } else { v_fill.to_string() };
let _ = write!(result, "{}", &format!("| {A_str} | {u_str} | {v_str} |\n"));
}
result
}
#[allow(non_snake_case)]
#[must_use]
pub fn string_for_Auvw<Word: Unsigned>(
A: &BitMatrix<Word>, u: &BitVector<Word>, v: &BitVector<Word>, w: &BitVector<Word>,
) -> String {
let num_rows = A.rows().max(u.len()).max(v.len()).max(w.len());
let A_fill = " ".repeat(A.cols() + 1);
let u_fill = " ";
let v_fill = " ";
let w_fill = " ";
let mut result = String::new();
for i in 0..num_rows {
let A_str = if i < A.rows() { A[i].to_string() } else { A_fill.clone() };
let u_str = if i < u.len() { i32::from(u[i]).to_string() } else { u_fill.to_string() };
let v_str = if i < v.len() { i32::from(v[i]).to_string() } else { v_fill.to_string() };
let w_str = if i < w.len() { i32::from(w[i]).to_string() } else { w_fill.to_string() };
let _ = write!(result, "{}", &format!("| {A_str} | {u_str} | {v_str} | {w_str} |\n"));
}
result
}