#[derive(Clone, Debug)]
pub struct MatrixGF2 {
    pub rows: usize,
    pub cols: usize,
    pub data: Vec<u8>,
}

impl MatrixGF2 {
    pub fn new(rows: usize, cols: usize, data: Vec<u8>) -> Self {
        assert_eq!(data.len(), rows * cols, "Taille incorrecte.");
        Self { rows, cols, data }
    }

    pub fn get(&self, i: usize, j: usize) -> u8 {
        self.data[i * self.cols + j]
    }

    pub fn set(&mut self, i: usize, j: usize, val: u8) {
        self.data[i * self.cols + j] = val;
    }

    pub fn print(&self) {
        for i in 0..self.rows {
            let start = i * self.cols;
            let end = start + self.cols;
            println!("{:?}", &self.data[start..end]);
        }
    }

    pub fn swap_rows(&mut self, r1: usize, r2: usize) {
        if r1 == r2 {
            return;
        }
        for c in 0..self.cols {
            let v1 = self.get(r1, c);
            let v2 = self.get(r2, c);
            self.set(r1, c, v2);
            self.set(r2, c, v1);
        }
    }

    pub fn swap_cols(&mut self, c1: usize, c2: usize) {
        if c1 == c2 {
            return;
        }
        for r in 0..self.rows {
            let v1 = self.get(r, c1);
            let v2 = self.get(r, c2);
            self.set(r, c1, v2);
            self.set(r, c2, v1);
        }
    }

    pub fn xor_rows(&mut self, src: usize, dest: usize) {
        for c in 0..self.cols {
            let v_src = self.get(src, c);
            let v_dest = self.get(dest, c);
            self.set(dest, c, v_src ^ v_dest);
        }
    }

    pub fn gauss_jordan(&mut self) {
        let mut l = 0;
        for r in 0..self.rows {
            if l >= self.cols {
                return;
            }
            let mut i = r;
            while self.get(i, l) == 0 {
                i += 1;
                if i == self.rows {
                    i = r;
                    l += 1;
                    if l == self.cols {
                        return;
                    }
                }
            }
            self.swap_rows(i, r);
            for i in 0..self.rows {
                if i != r && self.get(i, l) == 1 {
                    self.xor_rows(r, i);
                }
            }
            l += 1;
        }
    }

    pub fn gauss_jordan_swap_cols(&mut self) -> (usize, Vec<(usize, usize)>) {
        let mut rank = 0;
        let mut col_swaps = Vec::new();
        let rows = self.rows;
        let cols = self.cols;

        for c in 0..cols {
            if rank >= rows {
                break;
            }
            let mut pivot_r = rank;
            while pivot_r < rows && self.get(pivot_r, c) == 0 {
                pivot_r += 1;
            }
            if pivot_r == rows {
                let mut found_swap = false;
                for c2 in (c + 1)..cols {
                    let mut pr2 = rank;
                    while pr2 < rows && self.get(pr2, c2) == 0 {
                        pr2 += 1;
                    }
                    if pr2 < rows {
                        self.swap_cols(c, c2);
                        col_swaps.push((c, c2));
                        pivot_r = pr2;
                        found_swap = true;
                        break;
                    }
                }
                if !found_swap {
                    break;
                }
            }
            self.swap_rows(rank, pivot_r);

            for i in 0..rows {
                if i != rank && self.get(i, c) == 1 {
                    self.xor_rows(rank, i);
                }
            }
            rank += 1;
        }
        (rank, col_swaps)
    }
}
