use super::{build_csr_from_csc_banded, slab_end};
use crate::sparse_io::{create_sparse_streaming_empty, SparseIoBackend};
fn by_col(entries: &[(u64, u64)]) -> Vec<(u64, u64, f32)> {
let mut t: Vec<(u64, u64, f32)> = entries.iter().map(|&(r, c)| (r, c, 1.0)).collect();
t.sort_unstable_by_key(|&(r, c, _)| (c, r));
t
}
#[test]
fn a_slab_never_splits_a_column() {
let t = by_col(&[(0, 0), (1, 0), (2, 0), (0, 1), (5, 1), (3, 2)]);
assert_eq!(
slab_end(&t, 0, 1, 3, |t| t.1),
(3, 1),
"column 0 has three entries"
);
assert_eq!(slab_end(&t, 3, 1, 3, |t| t.1), (5, 2));
}
#[test]
fn the_last_slab_extends_to_the_axis_length() {
let t = by_col(&[(0, 0), (1, 2)]);
assert_eq!(
slab_end(&t, 0, 1, 7, |t| t.1),
(1, 2),
"column 1 is empty: next band starts there"
);
assert_eq!(
slab_end(&t, 1, 1, 7, |t| t.1),
(2, 7),
"columns 3..7 are empty and belong to the last band"
);
}
#[test]
fn a_slab_larger_than_the_matrix_is_one_band() {
let t = by_col(&[(0, 0), (1, 1), (2, 2)]);
assert_eq!(slab_end(&t, 0, 1 << 20, 3, |t| t.1), (3, 3));
}
#[test]
fn the_walker_follows_the_row_axis_for_csr() {
let mut t = by_col(&[(2, 0), (2, 1), (0, 3), (4, 2)]);
t.sort_unstable_by_key(|&(r, c, _)| (r, c));
assert_eq!(slab_end(&t, 0, 1, 6, |t| t.0), (1, 2), "row 1 is empty");
assert_eq!(
slab_end(&t, 1, 1, 6, |t| t.0),
(3, 4),
"row 2 has two entries, row 3 is empty"
);
assert_eq!(
slab_end(&t, 3, 1, 6, |t| t.0),
(4, 6),
"row 5 is empty and belongs to the last band"
);
}
#[test]
fn the_transpose_does_not_depend_on_the_band_size() -> anyhow::Result<()> {
let (nrow, ncol) = (37u64, 23u64);
let mut triplets = Vec::new();
let mut colptr = Vec::new();
for j in 0..ncol {
colptr.push(triplets.len() as u64);
for i in 0..nrow {
let dense_row = i == 5 && j % 2 == 0;
let scattered = (i * 7 + j * 13) % 11 == 0 && i % 9 != 4 && j != 17;
if dense_row || scattered {
triplets.push((i, j, (i * 100 + j + 1) as f32));
}
}
}
let rows: Vec<u64> = triplets.iter().map(|t| t.0).collect();
let vals: Vec<f32> = triplets.iter().map(|t| t.2).collect();
let shape = (nrow as usize, ncol as usize, triplets.len());
let mut by_rows = triplets.clone();
by_rows.sort_by_key(|t| (t.0, t.1));
let dir = tempfile::tempdir()?;
for (k, band_bytes) in [1usize, 12, 60, 300, usize::MAX].into_iter().enumerate() {
let path = dir.path().join(format!("band{k}.zarr"));
let mut out = create_sparse_streaming_empty(
Some(path.to_str().expect("utf8")),
Some(&SparseIoBackend::Zarr),
)?;
out.begin_streaming_csc(shape)?;
out.append_csc_slab(0, 0, &colptr, &rows, &vals)?;
out.finalize_streaming_csc()?;
build_csr_from_csc_banded(&mut *out, band_bytes)?;
let (_, _, got) = out.read_triplets_by_rows((0..nrow as usize).collect())?;
assert_eq!(got, by_rows, "band of {band_bytes} bytes");
let (_, _, one) = out.read_triplets_by_rows(vec![5])?;
let dense_row = by_rows.iter().filter(|t| t.0 == 5).count();
assert!(dense_row >= 12);
assert_eq!(
one.len(),
dense_row,
"the dense row, band of {band_bytes} bytes"
);
}
Ok(())
}