feat: introduce unified column view types for bit and int matrices

This commit introduces `BitColView` and `IntColView` to abstract over Columnar and Packed storage formats, implementing `BitSlice` and `IntSlice` for uniform column access. It adds `col_view()` accessors to `PersistentBitMatrix` and `PackedCompactIntMatrix`, explicitly panicking on implicit variants. The new types are publicly re-exported, and unit tests are added to validate per-element retrieval, aggregation methods, and parity with the original columnar representation.
This commit is contained in:
Eric Coissac
2026-06-17 14:48:31 +02:00
parent 1f0d77d5bf
commit 93559c3294
5 changed files with 186 additions and 5 deletions
+44
View File
@@ -224,6 +224,43 @@ impl Iterator for PackedIntColIter<'_> {
impl ExactSizeIterator for PackedIntColIter<'_> {}
// ── IntColView — uniform column access across Columnar and Packed ─────────────
enum IntColViewInner<'a> {
Columnar(&'a PersistentCompactIntVec),
Packed(PackedIntCol<'a>),
}
/// Opaque column view returned by [`PersistentCompactIntMatrix::col_view`].
/// Implements [`IntSlice`] uniformly for both Columnar and Packed matrix formats.
pub struct IntColView<'a>(IntColViewInner<'a>);
impl IntSlice for IntColView<'_> {
fn len(&self) -> usize {
match &self.0 { IntColViewInner::Columnar(c) => c.len(), IntColViewInner::Packed(c) => c.len() }
}
fn get(&self, slot: usize) -> u32 {
match &self.0 { IntColViewInner::Columnar(c) => c.get(slot), IntColViewInner::Packed(c) => c.get(slot) }
}
fn primary_bytes(&self) -> &[u8] {
match &self.0 { IntColViewInner::Columnar(c) => c.primary_bytes(), IntColViewInner::Packed(c) => c.primary_bytes() }
}
fn overflow_entries(&self) -> impl Iterator<Item = (usize, u32)> + '_ {
// Box<dyn Iterator> implements Iterator, satisfying RPITIT across two distinct types.
let it: Box<dyn Iterator<Item = (usize, u32)> + '_> = match &self.0 {
IntColViewInner::Columnar(c) => Box::new(c.overflow_entries()),
IntColViewInner::Packed(c) => Box::new(c.overflow_entries()),
};
it
}
fn sum(&self) -> u64 {
match &self.0 { IntColViewInner::Columnar(c) => c.sum(), IntColViewInner::Packed(c) => c.sum() }
}
fn count_nonzero(&self) -> u64 {
match &self.0 { IntColViewInner::Columnar(c) => c.count_nonzero(), IntColViewInner::Packed(c) => c.count_nonzero() }
}
}
// ─────────────────────────────────────────────────────────────────────────────
pub struct PackedCompactIntMatrix {
@@ -481,6 +518,13 @@ impl PersistentCompactIntMatrix {
}
}
pub fn col_view(&self, c: usize) -> IntColView<'_> {
match self {
Self::Columnar(m) => IntColView(IntColViewInner::Columnar(m.col(c))),
Self::Packed(m) => IntColView(IntColViewInner::Packed(m.col_slice(c))),
}
}
pub fn col_persist(&self, c: usize, path: &Path) -> io::Result<PersistentCompactIntVecBuilder> {
match self {
Self::Columnar(m) => PersistentCompactIntVecBuilder::build_from(m.col(c), path),