// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. use super::bitvec::WORD_COUNT_DEFAULT; use super::{BitwiseNeutralElement, OverlapWeight}; use crate::bits::bitblock::{BitAccessor, BitBlock}; use crate::bits::{ BitVec, BitView, Bitwise, BitwiseBinaryOps, Dot, IndexAssignable, MutableBitView, }; use crate::NeutralElement; use sorted_iter::assume::AssumeSortedByItemExt; use sorted_iter::SortedIterator; use std::cmp::PartialEq; use std::hash::Hash; use std::iter::FromIterator; use std::mem::size_of; use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Mul}; use std::ops::{Index, Range}; use std::str::FromStr; #[must_use] #[derive(Debug, Eq)] pub struct BitMatrix { blocks: Vec>, rows: Vec<*mut BitBlock>, columncount: usize, } impl Hash for BitMatrix { fn hash(&self, state: &mut H) { self.blocks.hash(state); } } unsafe impl Sync for BitMatrix {} pub type Row<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> = BitView<'life, WORD_COUNT>; // should we use View in the name to indicate that it is a view and not a copy of a row ? pub type MutableRow<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> = MutableBitView<'life, WORD_COUNT>; // should we use View in the name to indicate that it is a view and not a copy of a row ? #[derive(Clone, Debug, Hash)] pub struct Column<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> { // TODO(VK) should we use View in the name to indicate that it is a view and not a copy of a column ? rows: &'life [*mut BitBlock], accessor: BitAccessor, block_index: usize, } impl BitMatrix { pub fn with_shape(rows: usize, columns: usize) -> Self { Self::zeros(rows, columns) } pub fn zeros(rows: usize, columns: usize) -> Self { Self::with_value(false, (rows, columns)) } pub fn ones(rows: usize, columns: usize) -> Self { Self::with_value(true, (rows, columns)) } pub fn identity(dimension: usize) -> Self { let mut res = Self::zeros(dimension, dimension); for index in 0..dimension { res.set((index, index), true); } res } pub fn from_row_iter<'life>( iter: impl ExactSizeIterator>, columns: usize, ) -> Self { let rows = iter.len(); let mut matrix = Self::zeros(rows, columns); for (row_from, mut row_to) in std::iter::zip(iter, matrix.row_iterator_mut(0..rows)) { row_to.assign(&row_from); } matrix } pub fn from_iter(iter: Rows, columncount: usize) -> Self where Row: IntoIterator, Rows: IntoIterator, { // TODO(AEP): Expanding first into Vec is // inefficient. Instead, append to Vec> as necessary. let mut rows = Vec::>::new(); let mut rowcount = 0; for row in iter { rows.push(row.into_iter().collect()); rowcount += 1; } let mut matrix = BitMatrix::with_shape(rowcount, columncount); for (row_index, row) in rows.iter().enumerate() { for (column_index, value) in row.iter().take(columncount).enumerate() { matrix.set((row_index, column_index), *value); } } matrix } fn with_value(value: bool, shape: (usize, usize)) -> Self { let (rowcount, columncount) = shape; let rowstride = Self::rowstride_of(columncount); let buffer = vec![BitBlock::::all(value); rowcount * rowstride]; Self::from_blocks(buffer, shape) } fn from_blocks(mut buffer: Vec>, shape: (usize, usize)) -> Self { let rows = Self::rows_of(buffer.as_mut_slice(), shape.0); Self::from_blocks_and_rows(buffer, shape, rows) } fn from_blocks_and_rows( buffer: Vec>, shape: (usize, usize), rows: Vec<*mut BitBlock>, ) -> Self { let matrix = Self { blocks: buffer, rows, columncount: shape.1, }; debug_assert!(matrix.is_aligned()); matrix } #[must_use] pub fn is_zero(&self) -> bool { let zero = BitBlock::::zeros(); for block in &self.blocks { if *block != zero { return false; } } true } fn is_aligned(&self) -> bool { let alignment = (self.blocks.as_ptr() as usize) % size_of::>(); if alignment != 0 { return false; } for row in &self.rows { let alignment = (*row as usize) % size_of::>(); if alignment != 0 { return false; } } true } fn rowstride(&self) -> usize { Self::rowstride_of(self.columncount) } fn rowstride_of(columncount: usize) -> usize { let rowstride = columncount / BitBlock::::BITS; let adjustment = !columncount.is_multiple_of(BitBlock::::BITS); rowstride + usize::from(adjustment) } fn rows_of( blocks: &mut [BitBlock], rowcount: usize, ) -> Vec<*mut BitBlock> { let mut rows = Vec::<*mut BitBlock>::new(); let rowstride = blocks.len().checked_div(rowcount).unwrap_or(0); if rowstride == 0 { rows = vec![blocks.as_mut_ptr(); rowcount]; } else { for row in blocks.chunks_exact_mut(rowstride) { rows.push(row.as_mut_ptr()); } } rows } #[must_use] pub fn rowcount(&self) -> usize { self.rows.len() } #[must_use] pub fn columncount(&self) -> usize { self.columncount } #[must_use] pub fn shape(&self) -> (usize, usize) { (self.rowcount(), self.columncount()) } #[must_use] pub fn row(&self, index: usize) -> Row<'_, WORD_COUNT> { Row:: { blocks: unsafe { std::slice::from_raw_parts((*self.rows[index]).array(), self.block_count()) }, } } #[must_use] pub fn rows(&self) -> impl ExactSizeIterator> { self.row_iterator(0..self.rowcount()) } pub fn row_iterator( &self, index_iterator: impl ExactSizeIterator, ) -> impl ExactSizeIterator> { index_iterator.map(|index| self.row(index)) } pub fn row_iterator_mut( &mut self, index_iterator: impl ExactSizeIterator, ) -> impl ExactSizeIterator> { index_iterator.map(|index| self.build_mutable_row(index)) } pub fn row_mut(&mut self, index: usize) -> MutableRow<'_, WORD_COUNT> { self.build_mutable_row(index) } #[inline] fn block_count(&self) -> usize { let mut block_count = self.columncount() / BitBlock::::BITS; if !self .columncount() .is_multiple_of(BitBlock::::BITS) { block_count += 1; } block_count } fn build_mutable_row(&self, index: usize) -> MutableRow<'_, WORD_COUNT> { let ptr = self.rows[index]; MutableRow:: { blocks: unsafe { std::slice::from_raw_parts_mut((*ptr).array_mut(), self.block_count()) }, } } pub fn rows_mut( &mut self, index: usize, index2: usize, ) -> (MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>) { ( self.build_mutable_row(index), self.build_mutable_row(index2), ) } pub fn rows2_mut( &mut self, index: (usize, usize), ) -> (MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>) { ( self.build_mutable_row(index.0), self.build_mutable_row(index.1), ) } #[must_use] pub fn rows2(&self, index: (usize, usize)) -> (Row<'_, WORD_COUNT>, Row<'_, WORD_COUNT>) { (self.row(index.0), self.row(index.1)) } /// # Safety /// Does not check if all indexes are distinct pub unsafe fn rows4_mut( &mut self, index: (usize, usize, usize, usize), ) -> ( MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>, ) { ( self.build_mutable_row(index.0), self.build_mutable_row(index.1), self.build_mutable_row(index.2), self.build_mutable_row(index.3), ) } /// TODO(VK): Maybe use when it becomes stable /// # Safety /// Does not check if all indexes are distinct pub unsafe fn rows8_mut( &mut self, index: crate::Tuple8, ) -> crate::Tuple8> { ( self.build_mutable_row(index.0), self.build_mutable_row(index.1), self.build_mutable_row(index.2), self.build_mutable_row(index.3), self.build_mutable_row(index.4), self.build_mutable_row(index.5), self.build_mutable_row(index.6), self.build_mutable_row(index.7), ) } #[must_use] pub fn column(&self, index: usize) -> Column<'_, WORD_COUNT> { let block_index = index / BitBlock::::BITS; let bit_index = index % BitBlock::::BITS; Column:: { rows: &self.rows, accessor: BitAccessor::for_index(bit_index), block_index, } } #[must_use] pub fn columns(&self) -> impl ExactSizeIterator> { let indexes = 0..self.columncount(); indexes.map(|index| self.column(index)) } /// # Panics /// /// Will panic if index out of range pub fn set(&mut self, index: (usize, usize), to: bool) { assert!(index.0 < self.rowcount() && index.1 < self.columncount()); unsafe { self.set_unchecked(index, to) }; } /// # Safety /// Dose not check if index is out of bounds pub unsafe fn set_unchecked(&mut self, index: (usize, usize), to: bool) { let (block, bit_index) = self.block_index_of_mut(index); block.set(bit_index, to); } /// # Panics /// /// Will panic if index out of range #[must_use] pub fn get(&self, index: (usize, usize)) -> bool { assert!(index.0 < self.rowcount() && index.1 < self.columncount()); unsafe { self.get_unchecked(index) } } /// # Safety /// Does not check if index is out of bounds #[must_use] pub unsafe fn get_unchecked(&self, index: (usize, usize)) -> bool { let (block, bit_index) = self.block_index_of(index); block.get_unchecked(bit_index) } pub fn echelonize(&mut self) -> Vec { let mut pivot = pivot_of(self, (0, 0)); let mut rank_profile = Vec::::with_capacity(self.columncount()); for row_index in 0..self.rowcount() { if pivot.1 >= self.columncount() { break; } self.swap_rows(pivot.0, row_index); pivot.0 = row_index; rank_profile.push(pivot.1); reduce(self, pivot); pivot = pivot_of(self, (pivot.0 + 1, pivot.1 + 1)); } rank_profile } #[must_use] pub fn rank(&self) -> usize { self.clone().echelonize().len() } pub fn transposed(&self) -> Self { let mut res = Self::with_shape(self.columncount(), self.rowcount()); for i in 0..self.rowcount() { for j in 0..self.columncount() { res.set((j, i), self[(i, j)]); } } res } pub fn submatrix(&self, rows: &[usize], columns: &[usize]) -> Self { let mut res = Self::with_shape(rows.len(), columns.len()); for (row_index, &row) in rows.iter().enumerate() { for (column_index, &column) in columns.iter().enumerate() { res.set((row_index, column_index), self[(row, column)]); } } res } /// # Panics /// /// Will panic if matrix is not invertible pub fn inverted(&self) -> BitMatrix { assert!(self.columncount() == self.rowcount()); let (_, t, _, profile) = rref_with_transforms(self.clone()); assert!(profile.len() == self.rowcount()); debug_assert_eq!( self * &t, BitMatrix::::identity(self.rowcount()) ); t } pub fn swap_rows(&mut self, left_row_index: usize, right_row_index: usize) { self.rows.swap(left_row_index, right_row_index); } pub fn swap_columns(&mut self, left_column_index: usize, right_column_index: usize) { for row_index in 0..self.rowcount() { let left_bit = self.get((row_index, left_column_index)); let right_bit = self.get((row_index, right_column_index)); self.set((row_index, left_column_index), right_bit); self.set((row_index, right_column_index), left_bit); } } pub fn permute_rows(&mut self, permutation: &[usize]) { let old_rows = self.rows.clone(); for index in 0..permutation.len() { self.rows[index] = old_rows[permutation[index]]; } } pub fn add_into_row(&mut self, to_index: usize, from_index: usize) { let mut to_block = self.rows[to_index]; let mut from_block = self.rows[from_index]; for _ in 0..self.rowstride() { unsafe { BitwiseBinaryOps::bitxor_assign(&mut *to_block, &*from_block); to_block = to_block.add(1); from_block = from_block.add(1); } } } // TODO(VK): check if we also need dot_transposed /// # Panics /// /// Will panic if matrix dimensions are incompatible pub fn dot(&self, rhs: &BitMatrix) -> BitMatrix { assert_eq!(self.columncount(), rhs.rowcount()); let mut rows = Vec::with_capacity(self.rowcount()); for output_row in 0..self.rowcount() { let mut row = BitVec::::zeros(rhs.columncount()); for column_index in 0..self.columncount() { if self[(output_row, column_index)] { // TODO(AEP): This is needlessly slow. Make it fast. for into_column_index in 0..rhs.columncount() { row.assign_index( into_column_index, row.index(into_column_index) ^ rhs.get((column_index, into_column_index)), ); } } } rows.push(row); } Self::from_iter(rows.iter(), rhs.columncount()) } fn block_index_of_mut(&mut self, index: (usize, usize)) -> (&mut BitBlock, usize) { let column_block = index.1 / BitBlock::::BITS; let column_remainder = index.1 % BitBlock::::BITS; let bit_index = column_remainder % BitBlock::::BITS; unsafe { let block = self.rows[index.0].add(column_block); (&mut *block, bit_index) } } fn block_index_of(&self, index: (usize, usize)) -> (&BitBlock, usize) { let column_block = index.1 / BitBlock::::BITS; let column_remainder = index.1 % BitBlock::::BITS; let bit_index = column_remainder % BitBlock::::BITS; unsafe { let block = self.rows[index.0].add(column_block); (&*block, bit_index) } } } unsafe impl Send for BitMatrix {} impl Clone for BitMatrix { fn clone(&self) -> Self { let mut blocks = self.blocks.clone(); let mut rows = Vec::<*mut BitBlock>::new(); let offset = unsafe { blocks.as_mut_ptr().offset_from(self.blocks.as_ptr()) }; for row in &self.rows { rows.push(unsafe { row.offset(offset) }); } BitMatrix::from_blocks_and_rows(blocks, self.shape(), rows) } } impl Index<[usize; 2]> for BitMatrix { type Output = bool; fn index(&self, index: [usize; 2]) -> &Self::Output { &self[(index[0], index[1])] } } impl Index<(usize, usize)> for BitMatrix { type Output = bool; fn index(&self, index: (usize, usize)) -> &Self::Output { if self.get(index) { return &true; } &false } } impl PartialEq for BitMatrix { fn eq(&self, other: &Self) -> bool { if self.shape() != other.shape() { return false; } for irow in 0..self.rowcount() { for icol in 0..self.columncount() { if self[(irow, icol)] != other[(irow, icol)] { return false; } } } true } } impl AddAssign<&BitMatrix> for BitMatrix { fn add_assign(&mut self, other: &BitMatrix) { assert_eq!(self.shape(), other.shape()); for index in 0..self.rowcount() { self.row_mut(index).bitxor_assign(&other.row(index)); } } } impl Add for &BitMatrix { type Output = BitMatrix; fn add(self, other: Self) -> Self::Output { let mut clone = (*self).clone(); clone += other; clone } } impl BitXor for &BitMatrix { type Output = BitMatrix; fn bitxor(self, other: Self) -> Self::Output { self.add(other) } } impl BitXorAssign<&BitMatrix> for BitMatrix { fn bitxor_assign(&mut self, other: &BitMatrix) { self.add_assign(other); } } impl BitAndAssign<&BitMatrix> for BitMatrix { fn bitand_assign(&mut self, other: &Self) { assert_eq!(self.shape(), other.shape()); for index in 0..self.rowcount() { self.row_mut(index).bitand_assign(&other.row(index)); } } } impl BitAnd for &BitMatrix { type Output = BitMatrix; fn bitand(self, other: Self) -> Self::Output { let mut clone = (*self).clone(); clone &= other; clone } } impl Mul for &BitMatrix { type Output = BitMatrix; fn mul(self, other: Self) -> Self::Output { assert_eq!(self.columncount(), other.rowcount()); let mut result = BitMatrix::::with_shape(self.rowcount(), other.columncount()); for i in 0..self.rowcount() { for j in 0..other.columncount() { let mut val = false; for k in 0..self.columncount() { val ^= self[[i, k]] && other[[k, j]]; } result.set((i, j), val); } } result } } impl std::fmt::Display for BitMatrix { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for row_index in 0..self.rowcount() { for column_index in 0..self.columncount() { let value = i32::from(self.get((row_index, column_index))); write!(f, "{value:?}")?; } writeln!(f)?; } Ok(()) } } impl FromStr for BitMatrix { type Err = usize; fn from_str(s: &str) -> Result { let mut rows = Vec::::new(); let mut column_count = 0; for row_string in s.split(&['|', '[', ']', '(', ')', ';', '\n']) { if !row_string.is_empty() { let mut res = Vec::::new(); for char in row_string.chars() { match char { '0' | '.' | '▫' | '□' => res.push(false), '1' | '▪' | '■' => res.push(true), ' ' | '-' | ',' => {} _ => return Err(0), } } if !res.is_empty() { column_count = column_count.max(res.len()); rows.push(res.into_iter().collect()); } } } Ok(BitMatrix::from_iter(rows.iter(), column_count)) } } /// # Panics /// /// Should not panic. TODO: refactor so clippy does not complain pub fn row_stacked<'t, Matrices, const WORD_COUNT: usize>( matrices: Matrices, ) -> BitMatrix where Matrices: IntoIterator>, { let mut buffer = Vec::>::new(); let mut columncount: Option = None; let mut rowcount = 0; for matrix in matrices { debug_assert!(columncount.is_none() || columncount.unwrap() == matrix.columncount()); buffer.append(&mut matrix.blocks.clone()); columncount = Some(matrix.columncount()); rowcount += matrix.rowcount(); } BitMatrix::::from_blocks(buffer, (rowcount, *columncount.get_or_insert(0))) } pub fn directly_summed<'t, Matrices>(matrices: Matrices) -> BitMatrix where Matrices: IntoIterator, { let mut rowcount = 0; let mut columncount = 0; let vec_matrices = Vec::from_iter(matrices); for matrix in &vec_matrices { rowcount += matrix.rowcount(); columncount += matrix.columncount(); } let mut sum = BitMatrix::zeros(rowcount, columncount); let mut sum_row_offset = 0; let mut sum_column_offset = 0; for matrix in &vec_matrices { for row_index in 0..matrix.rowcount() { for column_index in 0..matrix.columncount() { sum.set( (row_index + sum_row_offset, column_index + sum_column_offset), matrix[(row_index, column_index)], ); } } sum_row_offset += matrix.rowcount(); sum_column_offset += matrix.columncount(); } sum } fn pivot_of( matrix: &BitMatrix, starting_at: (usize, usize), ) -> (usize, usize) { let (mut row_index, mut column_index) = starting_at; if row_index >= matrix.rowcount() || column_index >= matrix.columncount() { return (row_index, column_index); } while !matrix.get((row_index, column_index)) { row_index += 1; if row_index == matrix.rowcount() { column_index += 1; row_index = starting_at.0; if column_index == matrix.columncount() { break; } } } (row_index, column_index) } struct ReductionData { column_accessor: BitAccessor, blocks_per_row: usize, rowstride: usize, base_block: *const BitBlock, from_block: *mut BitBlock, } impl ReductionData { pub fn for_pivot(pivot: (usize, usize), within: &BitMatrix) -> Self { let start_block_offset = pivot.1 / BitBlock::::BITS; let bit_index = pivot.1 % BitBlock::::BITS; let from_block = unsafe { within.rows.get_unchecked(pivot.0).add(start_block_offset) }; let base_block = unsafe { within.blocks.as_ptr().add(start_block_offset) }; let rowstride = within.rowstride(); ReductionData { column_accessor: BitAccessor::for_index(bit_index), blocks_per_row: rowstride - start_block_offset, rowstride, from_block, base_block, } } } fn reduce(matrix: &mut BitMatrix, from: (usize, usize)) { let data = ReductionData::for_pivot(from, matrix); let mut to_block = data.from_block; to_block = reduce_backward_until(data.base_block, to_block, &data); to_block = unsafe { to_block.add(data.rowstride * matrix.rowcount()) }; let until_block = unsafe { data.from_block.add(data.rowstride) }; reduce_backward_until(until_block, to_block, &data); } fn reduce_backward_until( until_block: *const BitBlock, mut to_block: *mut BitBlock, data: &ReductionData, ) -> *mut BitBlock { while until_block != to_block { to_block = unsafe { to_block.sub(data.rowstride) }; let column_value = unsafe { data.column_accessor.array_value_of((*to_block).array()) }; if column_value { add_into_block(to_block, data.from_block, data.blocks_per_row); } } to_block } fn add_into_block( mut to_block: *mut BitBlock, mut from_block: *const BitBlock, block_count: usize, ) { for _ in 0..block_count { unsafe { *to_block ^= &*from_block; to_block = to_block.add(1); from_block = from_block.add(1); } } } /// # Returns /// Row reduced echelon form R of `matrix` , transformation matrix T and , inverse transpose of T, /// and row rank profile. /// T * `matrix` equals R pub fn rref_with_transforms( mut matrix: BitMatrix, ) -> ( BitMatrix, BitMatrix, BitMatrix, Vec, ) { let num_rows = matrix.rowcount(); let mut transform = BitMatrix::identity(num_rows); let mut transform_inv_t = BitMatrix::identity(num_rows); let mut pivot = pivot_of(&matrix, (0, 0)); let mut rank_profile = Vec::::with_capacity(matrix.columncount()); for row_index in 0..matrix.rowcount() { if pivot.1 >= matrix.columncount() { break; } matrix.swap_rows(pivot.0, row_index); transform_inv_t.swap_rows(pivot.0, row_index); transform.swap_rows(pivot.0, row_index); pivot.0 = row_index; rank_profile.push(pivot.1); reduce_with_transforms(&mut matrix, &mut transform, &mut transform_inv_t, pivot); pivot = pivot_of(&matrix, (pivot.0 + 1, pivot.1 + 1)); } (matrix, transform, transform_inv_t, rank_profile) } fn reduce_with_transforms( matrix: &mut BitMatrix, transform: &mut BitMatrix, transform_inv_t: &mut BitMatrix, from: (usize, usize), ) { let rowcount = matrix.rowcount(); for row_index in 0..from.0 { xor_if_column_with_transforms( from.1, matrix, transform, transform_inv_t, row_index, from.0, ); } for row_index in from.0 + 1..rowcount { xor_if_column_with_transforms( from.1, matrix, transform, transform_inv_t, row_index, from.0, ); } } fn xor_if_column_with_transforms( column_index: usize, matrix: &mut BitMatrix, transform: &mut BitMatrix, transform_inv_t: &mut BitMatrix, row_index: usize, from_row_index: usize, ) { if matrix[(row_index, column_index)] { matrix.add_into_row(row_index, from_row_index); transform.add_into_row(row_index, from_row_index); transform_inv_t.add_into_row(from_row_index, row_index); } } pub fn kernel_basis_matrix( matrix: &BitMatrix, ) -> BitMatrix { let num_cols = matrix.columncount(); let mut rr = matrix.clone(); let rank_profile = rr.echelonize(); let rank_profile_complement = crate::setwise::complement(&rank_profile, num_cols); let res_row_count = num_cols - rank_profile.len(); let mut res = BitMatrix::zeros(res_row_count, num_cols); for (index, elt) in rank_profile.into_iter().enumerate() { for (row_pos, col_src) in rank_profile_complement .iter() .enumerate() .take(res_row_count) { res.set((row_pos, elt), rr[(index, *col_src)]); } } for (index, position) in rank_profile_complement.into_iter().enumerate() { res.set((index, position), true); } res } pub fn full_rank_row_completion_with_inv( _matrix: &BitMatrix, ) -> (BitMatrix, BitMatrix) { // let _num_cols = matrix.columncount(); // let rr = matrix.clone(); // let rr2 = matrix.clone(); // (rr,rr2); todo!() } impl Bitwise for Column<'_, WORD_COUNT> { fn weight(&self) -> usize { self.into_iter().filter(|bit| *bit).count() } fn support(&self) -> impl SortedIterator { Box::new( self.into_iter() .enumerate() .filter(|pair| pair.1) .map(|pair| pair.0), ) .assume_sorted_by_item() } fn index(&self, index: usize) -> bool { let block = unsafe { &*self.rows[index].add(self.block_index) }; self.accessor.array_value_of(block.array()) } } impl Dot for Column<'_, WORD_COUNT> { fn dot(&self, other: &Self) -> bool { assert_eq!(self.rows.len(), other.rows.len()); let mut result = false; for index in 0..self.rows.len() { result ^= self.index(index) & other.index(index); } result } } impl OverlapWeight for Column<'_, WORD_COUNT> { fn and_weight(&self, other: &Self) -> usize { assert_eq!(self.rows.len(), other.rows.len()); let mut result = 0usize; for index in 0..self.rows.len() { if self.index(index) & other.index(index) { result += 1; } } result } fn or_weight(&self, other: &Self) -> usize { assert_eq!(self.rows.len(), other.rows.len()); let mut result = 0usize; for index in 0..self.rows.len() { if self.index(index) | other.index(index) { result += 1; } } result } } impl PartialEq for Column<'_, WORD_COUNT> { fn eq(&self, other: &Self) -> bool { if self.rows.len() != other.rows.len() { return false; } for index in 0..self.rows.len() { if self.index(index) != other.index(index) { return false; } } true } } impl Eq for Column<'_, WORD_COUNT> {} impl Column<'_, WORD_COUNT> { #[must_use] pub fn slice(&self, range: Range) -> Self { Column { rows: &self.rows[range], accessor: self.accessor.clone(), block_index: self.block_index, } } #[must_use] pub fn len(&self) -> usize { self.rows.len() } #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } } // impl<'life,const WORD_COUNT: usize> IntoIterator for Column<'life,WORD_COUNT> { // type Item = bool; // type IntoIter = ColumnIterator<'life,WORD_COUNT>; // fn into_iter(self) -> Self::IntoIter { // ColumnIterator { // column: self, // row_index: 0, // } // } // } impl<'life, const WORD_COUNT: usize> IntoIterator for &'life Column<'_, WORD_COUNT> { type Item = bool; type IntoIter = ColumnIterator<'life, WORD_COUNT>; fn into_iter(self) -> Self::IntoIter { ColumnIterator { column: self, row_index: 0, } } } impl Column<'_, WORD_COUNT> { #[must_use] pub fn iter(&self) -> ColumnIterator<'_, WORD_COUNT> { <&Self as IntoIterator>::into_iter(self) } } pub struct ColumnIterator<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> { column: &'life Column<'life, WORD_COUNT>, row_index: usize, } impl Iterator for ColumnIterator<'_, WORD_COUNT> { type Item = bool; fn next(&mut self) -> Option { if self.row_index >= self.column.rows.len() { return None; } let output = self.column.index(self.row_index); self.row_index += 1; Some(output) } } impl NeutralElement for Column<'_, WORD_COUNT> { type NeutralElementType = BitVec; fn neutral_element(&self) -> Self::NeutralElementType { BitVec::::zeros(self.rows.len()) } fn default_size_neutral_element() -> Self::NeutralElementType { Self::NeutralElementType::default_size_neutral_element() } fn neutral_element_of_size(size: usize) -> Self::NeutralElementType { Self::NeutralElementType::neutral_element_of_size(size) } } impl BitwiseNeutralElement for Column<'_, WORD_COUNT> {} impl<'life, const WORD_COUNT: usize> BitwiseBinaryOps> for BitVec { fn assign(&mut self, other: &Column<'life, WORD_COUNT>) { for (index, val) in other.into_iter().enumerate() { self.assign_index(index, val); } } fn bitxor_assign(&mut self, other: &Column<'life, WORD_COUNT>) { for (index, val) in other.into_iter().enumerate() { if val { self.negate_index(index); } } } fn bitand_assign(&mut self, other: &Column<'life, WORD_COUNT>) { for (index, val) in other.into_iter().enumerate() { if !val { self.assign_index(index, false); } } } } impl<'life1, const WORD_COUNT_1: usize, const WORD_COUNT_2: usize> BitwiseBinaryOps> for MutableBitView<'_, WORD_COUNT_2> { fn assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) { for (index, val) in other.into_iter().enumerate() { self.assign_index(index, val); } } fn bitxor_assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) { for (index, val) in other.into_iter().enumerate() { if val { self.negate_index(index); } } } fn bitand_assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) { for (index, val) in other.into_iter().enumerate() { if !val { self.assign_index(index, false); } } } } pub fn is_zero_padded_identity(row_iterator: impl ExactSizeIterator) -> bool { row_iterator .into_iter() .enumerate() .all(|(row_index, row)| row.is_one_bit(row_index)) } pub fn is_zero_padded_symmetric<'life, const WORD_COUNT: usize>( row_iterator: impl ExactSizeIterator>, column_count: usize, ) -> bool { let matrix = BitMatrix::from_row_iter(row_iterator, column_count); matrix == matrix.transposed() } pub fn are_zero_rows(mut row_iterator: impl Iterator) -> bool { row_iterator.all(|row| row.is_zero()) }