use qec_code::sparse_gf2::{hconcat, identity, kron, transpose, SparseGf2Matrix};
use qec_code::QecError;
fn assert_shape_and_rows(
matrix: &SparseGf2Matrix,
num_rows: usize,
num_cols: usize,
rows: &[Vec<usize>],
) {
assert_eq!(matrix.num_rows(), num_rows);
assert_eq!(matrix.num_cols(), num_cols);
assert_eq!(matrix.rows(), rows);
}
#[test]
fn sparse_gf2_composition_matches_known_answers() {
let a = SparseGf2Matrix::new(2, 2, vec![vec![0], vec![0, 1]]).unwrap();
let b = SparseGf2Matrix::new(2, 2, vec![vec![1], vec![0]]).unwrap();
assert_shape_and_rows(&identity(2).unwrap(), 2, 2, &[vec![0], vec![1]]);
assert_shape_and_rows(&transpose(&a).unwrap(), 2, 2, &[vec![0, 1], vec![1]]);
assert_shape_and_rows(
&hconcat(&a, &b).unwrap(),
2,
4,
&[vec![0, 3], vec![0, 1, 2]],
);
assert_shape_and_rows(
&kron(&a, &b).unwrap(),
4,
4,
&[vec![1], vec![0], vec![1, 3], vec![0, 2]],
);
let canonicalized = SparseGf2Matrix::new(2, 4, vec![vec![3, 1, 3, 2, 1], vec![2, 2]]).unwrap();
assert_shape_and_rows(&canonicalized, 2, 4, &[vec![2], vec![]]);
assert_shape_and_rows(&identity(0).unwrap(), 0, 0, &[]);
let empty_wide = SparseGf2Matrix::new(0, 3, vec![]).unwrap();
assert_shape_and_rows(
&transpose(&empty_wide).unwrap(),
3,
0,
&[vec![], vec![], vec![]],
);
let empty_rows_left = SparseGf2Matrix::new(0, 2, vec![]).unwrap();
let empty_rows_right = SparseGf2Matrix::new(0, 5, vec![]).unwrap();
assert_shape_and_rows(
&hconcat(&empty_rows_left, &empty_rows_right).unwrap(),
0,
7,
&[],
);
assert_shape_and_rows(
&kron(&empty_rows_left, &empty_rows_right).unwrap(),
0,
10,
&[],
);
}
#[test]
fn sparse_gf2_methods_cover_rectangular_composition() {
let left = SparseGf2Matrix::new(2, 3, vec![vec![0, 2], vec![1]]).unwrap();
let right = SparseGf2Matrix::new(2, 1, vec![vec![0], vec![]]).unwrap();
let factor = SparseGf2Matrix::new(1, 2, vec![vec![1]]).unwrap();
assert_shape_and_rows(
&SparseGf2Matrix::identity(3).unwrap(),
3,
3,
&[vec![0], vec![1], vec![2]],
);
assert_shape_and_rows(
&left.transpose().unwrap(),
3,
2,
&[vec![0], vec![1], vec![0]],
);
assert_shape_and_rows(
&left.hconcat(&right).unwrap(),
2,
4,
&[vec![0, 2, 3], vec![1]],
);
assert_shape_and_rows(&left.kron(&factor).unwrap(), 2, 6, &[vec![1, 5], vec![3]]);
}
#[test]
fn sparse_gf2_composition_rejects_invalid_shapes() {
assert_eq!(
SparseGf2Matrix::new(1, 2, vec![vec![2]]),
Err(QecError::SparseGf2SupportOutOfRange {
row: 0,
support: 2,
num_cols: 2,
})
);
assert_eq!(
SparseGf2Matrix::new(2, 2, vec![vec![]]),
Err(QecError::SparseGf2RowCountMismatch {
expected: 2,
actual: 1,
})
);
let one_row = SparseGf2Matrix::new(1, 2, vec![vec![0]]).unwrap();
let two_rows = SparseGf2Matrix::new(2, 2, vec![vec![0], vec![1]]).unwrap();
assert_eq!(
hconcat(&one_row, &two_rows),
Err(QecError::SparseGf2HorizontalRowMismatch {
left_rows: 1,
right_rows: 2,
})
);
let max_width = SparseGf2Matrix::new(0, usize::MAX, vec![]).unwrap();
let one_col = SparseGf2Matrix::new(0, 1, vec![]).unwrap();
assert_eq!(
hconcat(&max_width, &one_col),
Err(QecError::SparseGf2DimensionOverflow {
operation: "hconcat",
})
);
let two_cols = SparseGf2Matrix::new(0, 2, vec![]).unwrap();
assert_eq!(
kron(&max_width, &two_cols),
Err(QecError::SparseGf2DimensionOverflow { operation: "kron" })
);
}
#[test]
fn sparse_gf2_composition_reports_reachable_allocation_overflows() {
assert_eq!(
SparseGf2Matrix::identity(usize::MAX),
Err(QecError::SparseGf2DimensionOverflow {
operation: "identity",
})
);
let max_width = SparseGf2Matrix::new(0, usize::MAX, vec![]).unwrap();
assert_eq!(
max_width.transpose(),
Err(QecError::SparseGf2DimensionOverflow {
operation: "transpose",
})
);
}