use crate::{matrix::Number, GF2Matrix};
#[derive(Clone, Copy, Debug)]
pub enum BitOrder {
LSB,
MSB,
}
#[derive(Clone)]
pub struct PackedGF2Matrix<T: Number> {
elements: Vec<T>,
n: usize,
}
impl<T: Number> PackedGF2Matrix<T> {
pub fn new(elements: Vec<T>, n: usize) -> Self {
Self {
elements: elements,
n: n,
}
}
pub fn nrows(&self) -> usize {
self.elements.len()
}
pub fn ncols(&self) -> usize {
self.n
}
pub fn row(&self, row_index: usize) -> T {
self.elements[row_index]
}
pub fn from_int_matrix_to_gf2_matrix(&self, bit_order: BitOrder) -> GF2Matrix {
let mut matrix_elements = Vec::with_capacity(self.nrows());
match bit_order {
BitOrder::LSB => {
for &row in &self.elements {
let mut r = Vec::with_capacity(self.n);
for i in 0..self.n {
r.push((((row >> i) & T::one()) != T::zero()) as u8);
}
matrix_elements.push(r);
}
}
BitOrder::MSB => {
for &row in &self.elements {
let mut r = Vec::with_capacity(self.n);
for i in (0..self.n).rev() {
r.push((((row >> i) & T::one()) != T::zero()) as u8);
}
matrix_elements.push(r);
}
}
};
GF2Matrix::new(matrix_elements)
}
pub fn from_vec(vect: Vec<T>) -> Self {
let n = vect.len();
Self {
elements: vect,
n: n,
}
}
pub fn from_vec_referenced(vect: &Vec<T>) -> Self {
let n = vect.len();
Self {
elements: vect.to_vec(),
n: n,
}
}
fn get_element(&self, row: usize, col: usize) -> u8 {
((self.elements[row].into_usize() >> (self.ncols() - 1 - col)) & 1) as u8
}
fn swap_rows(&mut self, idx1: usize, idx2: usize) {
self.elements.swap(idx1, idx2);
}
pub fn echelon_form(&self) -> (Self, Vec<(usize, usize)>) {
let mut m_copy = self.clone();
let mut lead = 0;
let mut operations: Vec<(usize, usize)> = Vec::new();
for r in 0..self.nrows() {
if lead >= self.ncols() {
break;
}
let mut i = r;
while m_copy.get_element(i, lead) == 0 {
i += 1;
if i == self.nrows() {
i = r;
lead += 1;
if lead == self.ncols() {
return (m_copy, operations);
}
}
}
m_copy.swap_rows(r, i);
if r != i {
operations.push((r, i));
operations.push((i, r));
operations.push((r, i));
}
for i in 0..self.nrows() {
if i != r && m_copy.get_element(i, lead) == 1 {
m_copy.elements[i] = m_copy.elements[i] ^ m_copy.elements[r];
operations.push((i, r));
}
}
lead += 1
}
(m_copy, operations)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_int_matrix_to_gf2_matrix_u8_lsb() {
let elements = vec![0, 1, 2, 4, 8];
let int_matrix = PackedGF2Matrix::<u8>::new(elements.clone(), 4);
let gf2_matrix = int_matrix.from_int_matrix_to_gf2_matrix(BitOrder::LSB);
let expected = vec![
vec![0, 0, 0, 0],
vec![1, 0, 0, 0],
vec![0, 1, 0, 0],
vec![0, 0, 1, 0],
vec![0, 0, 0, 1],
];
assert_eq!(gf2_matrix.elements, expected);
let elements = vec![0, 1, 2, 3, 5];
let int_matrix = PackedGF2Matrix::<u8>::new(elements.clone(), 4);
let gf2_matrix = int_matrix.from_int_matrix_to_gf2_matrix(BitOrder::LSB);
let expected = vec![
vec![0, 0, 0, 0],
vec![1, 0, 0, 0],
vec![0, 1, 0, 0],
vec![1, 1, 0, 0],
vec![1, 0, 1, 0],
];
assert_eq!(gf2_matrix.elements, expected);
}
#[test]
fn test_from_int_matrix_to_gf2_matrix_u8_msb() {
let elements = vec![0, 1, 2, 4, 8];
let int_matrix = PackedGF2Matrix::<u8>::new(elements.clone(), 4);
let gf2_matrix = int_matrix.from_int_matrix_to_gf2_matrix(BitOrder::MSB);
let expected = vec![
vec![0, 0, 0, 0],
vec![0, 0, 0, 1],
vec![0, 0, 1, 0],
vec![0, 1, 0, 0],
vec![1, 0, 0, 0],
];
assert_eq!(gf2_matrix.elements, expected);
let elements = vec![0, 1, 2, 3, 5];
let int_matrix = PackedGF2Matrix::<u8>::new(elements.clone(), 4);
let gf2_matrix = int_matrix.from_int_matrix_to_gf2_matrix(BitOrder::MSB);
let expected = vec![
vec![0, 0, 0, 0],
vec![0, 0, 0, 1],
vec![0, 0, 1, 0],
vec![0, 0, 1, 1],
vec![0, 1, 0, 1],
];
assert_eq!(gf2_matrix.elements, expected);
}
}