believer 0.2.1

A toolbox to simulate error correcting code performance.
Documentation
use super::get_bitwise_sum;
use super::ParityCheckMatrix;

type Row = Vec<usize>;

pub(super) struct Ranker {
    n_columns: usize,
    n_rows: usize,
    rank: usize,
    active_column: usize,
    rows: Vec<Row>,
}

impl Ranker {
    pub(super) fn from_parity_check_matrix(matrix: &ParityCheckMatrix) -> Self {
        Self {
            n_columns: matrix.get_n_bits(),
            n_rows: matrix.get_n_checks(),
            rank: 0,
            active_column: 0,
            rows: matrix.checks_iter().map(|check| check.to_vec()).collect(),
        }
    }

    pub(super) fn get_rank(mut self) -> usize {
        while self.is_not_in_echelon_form() {
            self.pivot_active_column();
            self.go_to_next_column();
        }
        self.rank
    }

    fn is_not_in_echelon_form(&self) -> bool {
        self.active_column < self.n_columns
    }

    fn pivot_active_column(&mut self) {
        if let Some(pivot) = self.find_and_remove_pivot() {
            self.pivot_rows_that_start_in_active_column_with(pivot);
            self.rank += 1;
        }
    }

    fn find_and_remove_pivot(&mut self) -> Option<Row> {
        for row_index in 0..self.n_rows {
            if self.row_at_index_start_at_active_column(row_index) {
                let row = self.get_and_remove_row_at_index(row_index);
                return Some(row);
            }
        }
        None
    }

    fn row_at_index_start_at_active_column(&self, index: usize) -> bool {
        self.rows[index]
            .first()
            .map(|column| *column == self.active_column)
            .unwrap_or(false)
    }

    fn get_and_remove_row_at_index(&mut self, index: usize) -> Row {
        std::mem::replace(&mut self.rows[index], Vec::new())
    }

    fn pivot_rows_that_start_in_active_column_with(&mut self, pivot: Row) {
        for row_index in 0..self.n_rows {
            if self.row_at_index_start_at_active_column(row_index) {
                self.rows[row_index] = get_bitwise_sum(&pivot, &self.rows[row_index])
            }
        }
    }

    fn go_to_next_column(&mut self) {
        self.active_column += 1;
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn rank_for_empty_matrix_is_zero() {
        let matrix = ParityCheckMatrix::new();
        let rank = Ranker::from_parity_check_matrix(&matrix).get_rank();
        assert_eq!(rank, 0);
    }

    #[test]
    fn rank_for_the_repetition_code_is_one_less_than_the_number_of_bits() {
        let checks = vec![vec![0, 1], vec![1, 2], vec![2, 3], vec![3, 4], vec![4, 0]];
        let matrix = ParityCheckMatrix::with_n_bits(5).with_checks(checks);
        let rank = Ranker::from_parity_check_matrix(&matrix).get_rank();
        assert_eq!(rank, 4);
    }

    #[test]
    fn rank_for_a_full_rank_matrix_is_the_number_of_checks() {
        let checks = vec![vec![0, 1], vec![1, 2], vec![0, 1, 3]];
        let matrix = ParityCheckMatrix::with_n_bits(4).with_checks(checks);
        let rank = Ranker::from_parity_check_matrix(&matrix).get_rank();
        assert_eq!(rank, 3);
    }

    #[test]
    fn rank_for_a_given_matrix_is_accurate() {
        let checks = vec![
            vec![0, 1, 2],
            vec![1, 2, 3],
            vec![0, 3],
            vec![3, 4, 5],
            vec![0, 4, 6],
            vec![5, 6],
        ];
        let matrix = ParityCheckMatrix::with_n_bits(7).with_checks(checks);
        let rank = Ranker::from_parity_check_matrix(&matrix).get_rank();
        assert_eq!(rank, 4);
    }
}