use crate::{gf2::GF2, linalg, sparse::SparseMatrix};
use ndarray::Array2;
use num_traits::{One, Zero};
use thiserror::Error;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Error)]
pub enum Error {
#[error("the parity check matrix has more rows than columns")]
ParityOverdetermined,
#[error("the parity check matrix does not have full rank")]
NotFullRank,
}
pub fn parity_to_systematic(h: &SparseMatrix) -> Result<SparseMatrix, Error> {
let n = h.num_rows();
let m = h.num_cols();
if n > m {
return Err(Error::ParityOverdetermined);
}
let mut a = Array2::zeros((n, m));
for (j, k) in h.iter_all() {
a[[j, k]] = GF2::one();
}
linalg::row_echelon_form(&mut a);
if !(0..m).rev().any(|j| a[[n - 1, j]] != GF2::zero()) {
return Err(Error::NotFullRank);
}
let mut k = 0;
let mut h_new = SparseMatrix::new(n, m);
let mut j0 = 0;
for j in 0..n {
assert!(k < m - n);
let mut found = false;
for s in j0..m {
if a[[j, s]] == GF2::zero() {
for &u in h.iter_col(s) {
h_new.insert(u, k);
}
k += 1;
} else {
let col = m - n + j;
for &u in h.iter_col(s) {
h_new.insert(u, col);
}
found = true;
j0 = s + 1;
break;
}
}
assert!(found);
}
for j in j0..m {
assert!(k < m - n);
for &u in h.iter_col(j) {
h_new.insert(u, k);
}
k += 1;
}
Ok(h_new)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn to_systematic() {
let mut h = SparseMatrix::new(3, 9);
h.insert_col(0, [0, 1, 2].into_iter());
h.insert_col(1, [0, 2].into_iter());
h.insert_col(3, [1].into_iter());
h.insert_col(4, [0, 1].into_iter());
h.insert_col(5, [1, 2].into_iter());
h.insert_col(6, [0, 2].into_iter());
h.insert_col(7, [1].into_iter());
h.insert_col(8, [0, 2].into_iter());
let mut expected = SparseMatrix::new(3, 9);
expected.insert_col(6, [0, 1, 2].into_iter());
expected.insert_col(7, [0, 2].into_iter());
expected.insert_col(1, [1].into_iter());
expected.insert_col(8, [0, 1].into_iter());
expected.insert_col(2, [1, 2].into_iter());
expected.insert_col(3, [0, 2].into_iter());
expected.insert_col(4, [1].into_iter());
expected.insert_col(5, [0, 2].into_iter());
assert_eq!(parity_to_systematic(&h).unwrap(), expected);
}
}