#![cfg(feature = "alloc")]
use proptest::prelude::*;
use rustebra::sparse::{CooMatrix, CsrMatrix, coo_to_csr, csc_to_csr, csr_to_coo, csr_to_csc};
use std::collections::HashMap;
proptest! {
#[test]
fn coo_csr_coo_roundtrip_preserves_values(
rows in 1usize..=10,
cols in 1usize..=10,
entries in prop::collection::vec(
(0usize..10, 0usize..10, -100.0..100.0f64),
0..50,
),
) {
let valid_entries: Vec<_> = entries
.into_iter()
.filter(|&(r, c, _)| r < rows && c < cols)
.collect();
prop_assume!(!valid_entries.is_empty());
let row_idx: Vec<usize> = valid_entries.iter().map(|e| e.0).collect();
let col_idx: Vec<usize> = valid_entries.iter().map(|e| e.1).collect();
let values: Vec<_> = valid_entries.iter().map(|e| e.2).collect();
let row_idx_u32: Vec<u32> = row_idx.iter().map(|&r| r as u32).collect();
let col_idx_u32: Vec<u32> = col_idx.iter().map(|&c| c as u32).collect();
let coo = CooMatrix::new(rows, cols, row_idx_u32, col_idx_u32, values.clone())
.expect("entries should be in bounds");
let sorted_csr = coo_to_csr(coo).expect("dimensions fit within test-generated bounds");
let csr: CsrMatrix<f64> = sorted_csr.into();
let coo_roundtrip = csr_to_coo(csr).expect("dimensions fit within test-generated bounds");
prop_assert_eq!(coo_roundtrip.rows(), rows);
prop_assert_eq!(coo_roundtrip.cols(), cols);
let mut expected: HashMap<(usize, usize), f64> = HashMap::new();
for i in 0..row_idx.len() {
*expected.entry((row_idx[i], col_idx[i])).or_insert(0.0) += values[i];
}
let mut actual: HashMap<(usize, usize), f64> = HashMap::new();
for i in 0..coo_roundtrip.nnz() {
let r = coo_roundtrip.row_indices()[i] as usize;
let c = coo_roundtrip.col_indices()[i] as usize;
let v = coo_roundtrip.values()[i];
*actual.entry((r, c)).or_insert(0.0) += v;
}
for (&(r, c), &expected_val) in &expected {
let actual_val = actual.get(&(r, c)).copied().unwrap_or(0.0);
prop_assert!(
(actual_val - expected_val).abs() < 1e-10,
"Value mismatch at ({}, {}): expected {}, got {}",
r,
c,
expected_val,
actual_val
);
}
prop_assert_eq!(
actual.len(),
expected.len(),
"Output has wrong number of entries"
);
}
#[test]
fn csr_csc_csr_roundtrip_preserves_values(
rows in 1usize..=10,
cols in 1usize..=10,
entries in prop::collection::vec(
(0usize..10, 0usize..10, -100.0..100.0f64),
0..50,
),
) {
let valid_entries: Vec<_> = entries
.into_iter()
.filter(|&(r, c, _)| r < rows && c < cols)
.collect();
prop_assume!(!valid_entries.is_empty());
let row_idx_u32: Vec<u32> = valid_entries.iter().map(|e| e.0 as u32).collect();
let col_idx_u32: Vec<u32> = valid_entries.iter().map(|e| e.1 as u32).collect();
let values: Vec<f64> = valid_entries.iter().map(|e| e.2).collect();
let coo = CooMatrix::new(rows, cols, row_idx_u32, col_idx_u32, values)
.expect("entries should be in bounds");
let sorted_csr = coo_to_csr(coo).expect("dimensions fit within test-generated bounds");
let csr: CsrMatrix<f64> = sorted_csr.into();
let expected_rows = csr.rows();
let expected_cols = csr.cols();
let expected_row_ptr = csr.row_ptr().to_vec();
let expected_col_indices = csr.col_indices().to_vec();
let expected_values = csr.values().to_vec();
let csc = csr_to_csc(csr).expect("dimensions fit within test-generated bounds");
let csr_roundtrip =
csc_to_csr(csc.into()).expect("dimensions fit within test-generated bounds");
prop_assert_eq!(csr_roundtrip.rows(), expected_rows);
prop_assert_eq!(csr_roundtrip.cols(), expected_cols);
prop_assert_eq!(csr_roundtrip.row_ptr(), expected_row_ptr.as_slice());
prop_assert_eq!(csr_roundtrip.col_indices(), expected_col_indices.as_slice());
prop_assert_eq!(csr_roundtrip.values(), expected_values.as_slice());
}
}