Push zunrplorkwkt #70
Generated
+62
-6
@@ -300,7 +300,7 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af737c6c59cb018ecbe6472cbdf86d39c59d78252febfe311953a991b6e4ed85"
|
||||
dependencies = [
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde",
|
||||
"mem_dbg",
|
||||
]
|
||||
@@ -475,7 +475,18 @@ checksum = "fda9ae1f26adcae83adb2e92f69cf59421f2a277a942f49f8e59f2fcbd7cf062"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"half",
|
||||
"impl-tools",
|
||||
"impl-tools 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common_traits"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e0d7d0c5977b1e87f759d406740c38227125f374f54d81bc197e877f2c5f26"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"half",
|
||||
"impl-tools 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -783,7 +794,7 @@ checksum = "c40d342ff20a2ce62d9a85ce406e672dfa137f902ac9670034533184f1533976"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.11.1",
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde-derive",
|
||||
"maligned",
|
||||
"mem_dbg",
|
||||
@@ -1083,11 +1094,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ae95c9095c2f1126d7db785955c73cdc5fc33e7c3fa911bd4a42931672029a7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"impl-tools-lib",
|
||||
"impl-tools-lib 0.11.4",
|
||||
"proc-macro-error2",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c8567c75b167cdda5ecee024da9cc9581d41bd52460e2e0e768967972aeef1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"impl-tools-lib 0.12.0",
|
||||
"proc-macro-error3",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools-lib"
|
||||
version = "0.11.4"
|
||||
@@ -1100,6 +1123,18 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-tools-lib"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c81eb99ba7c6145aeccb8857ddc0c611cd52440091dcfddd605b6a930c9afbf4"
|
||||
dependencies = [
|
||||
"proc-macro-error3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -1519,7 +1554,7 @@ dependencies = [
|
||||
name = "obicompactvec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common_traits",
|
||||
"common_traits 0.13.0",
|
||||
"memmap2",
|
||||
"ndarray",
|
||||
"rayon",
|
||||
@@ -2009,6 +2044,16 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr3"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0084e6206a967a2dad822180626b2f6b07a3b379325e8f1ec0438e33a469ba7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
@@ -2020,6 +2065,17 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error3"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cf066225f2373bc711684792b69bdeac0356019b007e721090c24d92d5d5a50"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -2093,7 +2149,7 @@ dependencies = [
|
||||
"cacheline-ef",
|
||||
"clap",
|
||||
"colored",
|
||||
"common_traits",
|
||||
"common_traits 0.11.4",
|
||||
"epserde",
|
||||
"epserde-derive",
|
||||
"fastrand",
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
common_traits = "0.11"
|
||||
common_traits = "0.13"
|
||||
memmap2 = "0.9"
|
||||
ndarray = "0.17"
|
||||
rayon = "1"
|
||||
|
||||
@@ -200,7 +200,7 @@ impl PersistentBitMatrix {
|
||||
/// buffer, ever — the whole point, see
|
||||
/// `PersistentSparseBitMatrix::nonzero_iter`); `Columnar`/`Packed`
|
||||
/// reuse the same sorted-slot batching `fill_sub_matrix` always used
|
||||
/// (`BitSliceView::nonzero_among_sorted`), just eagerly collected here
|
||||
/// (`BitSliceView::enumerate_nonzero_slots`), just eagerly collected here
|
||||
/// rather than filled into a dense buffer — same `get` calls, same
|
||||
/// mmap locality, no per-format duplication of that logic elsewhere.
|
||||
/// `Implicit` is trivial (one column, always present).
|
||||
@@ -228,7 +228,7 @@ impl PersistentBitMatrix {
|
||||
for c in 0..n_cols {
|
||||
let view = self.col_view(c);
|
||||
hits.extend(
|
||||
view.nonzero_among_sorted(&sorted_slots)
|
||||
view.enumerate_nonzero_slots(&sorted_slots)
|
||||
.map(|pos| (perm[pos], c, 1u32)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,36 +75,15 @@ impl PersistentBitVec {
|
||||
/// The slots are sorted internally before reading so that accesses to
|
||||
/// the underlying mmap are as sequential as possible, then the results
|
||||
/// are reordered to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<bool> {
|
||||
let mut out = vec![false; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
out
|
||||
#[inline]
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<bool> {
|
||||
self.view().collect_slots_values(slots)
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![false; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
#[inline]
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [bool]) {
|
||||
self.view().fill_slots_values(slots, out)
|
||||
}
|
||||
|
||||
// SAFETY: mmap is page-aligned, HEADER_SIZE=16 divisible by 8 → u64-aligned.
|
||||
|
||||
@@ -372,7 +372,7 @@ impl PersistentCompactIntMatrix {
|
||||
let mut out: Vec<Vec<u32>> = Vec::with_capacity(n_cols);
|
||||
for c in 0..n_cols {
|
||||
let mut col_buf = vec![0u32; slots.len()];
|
||||
self.col_view(c).fill_batch(slots, &mut col_buf);
|
||||
self.col_view(c).fill_slots_values(slots, &mut col_buf);
|
||||
out.push(col_buf);
|
||||
}
|
||||
out
|
||||
@@ -397,7 +397,7 @@ impl PersistentCompactIntMatrix {
|
||||
for (c, col) in out.iter_mut().enumerate() {
|
||||
col.resize(n, 0);
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.col_view(c).fill_batch_sorted(&sorted_slots, &mut tmp);
|
||||
self.col_view(c).fill_slots_values_sorted(&sorted_slots, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
col[orig_idx] = tmp[i];
|
||||
}
|
||||
@@ -411,7 +411,7 @@ impl PersistentCompactIntMatrix {
|
||||
/// design even though no sparse count format exists yet). No native
|
||||
/// low-effort case here the way `PersistentSparseBitMatrix` has one —
|
||||
/// both variants reuse the same sorted-slot batching `fill_sub_matrix`
|
||||
/// already used, via `IntSliceView::nonzero_among_sorted`.
|
||||
/// already used, via `IntSliceView::enumerate_nonzero_slots`.
|
||||
pub fn nonzero_iter<'a>(&'a self, slots: &'a [usize]) -> impl Iterator<Item = (usize, usize, u32)> + 'a {
|
||||
let n = slots.len();
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
@@ -422,7 +422,7 @@ impl PersistentCompactIntMatrix {
|
||||
for c in 0..n_cols {
|
||||
hits.extend(
|
||||
self.col_view(c)
|
||||
.nonzero_among_sorted(&sorted_slots)
|
||||
.enumerate_nonzero_slots(&sorted_slots)
|
||||
.map(|(pos, v)| (perm[pos], c, v)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,34 +67,15 @@ impl PersistentCompactIntVec {
|
||||
/// The slots are sorted internally before reading so that accesses to
|
||||
/// the underlying mmap are as sequential as possible, then the results
|
||||
/// are reordered to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<u32> {
|
||||
let mut out = vec![0u32; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
out
|
||||
#[inline]
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<u32> {
|
||||
self.view().collect_slots_values(slots)
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
let mut perm: Vec<usize> = (0..n).collect();
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
#[inline]
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [u32]) {
|
||||
self.view().fill_slots_values(slots, out)
|
||||
}
|
||||
|
||||
fn overflow_get(&self, slot: usize) -> u32 {
|
||||
|
||||
@@ -215,65 +215,65 @@ fn hamming_dist_basic() {
|
||||
assert_eq!(ra.hamming_dist(&rb), 2);
|
||||
}
|
||||
|
||||
// ── get_batch tests ────────────────────────────────────────────────────────────
|
||||
// ── collect_slots_values tests ────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_in_order() {
|
||||
fn bitvec_collect_slots_values_in_order() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[0, 1, 2, 3, 4]);
|
||||
let got = r.collect_slots_values(&[0, 1, 2, 3, 4]);
|
||||
assert_eq!(got, bits);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_out_of_order() {
|
||||
fn bitvec_collect_slots_values_out_of_order() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[3, 0, 4, 1]);
|
||||
let got = r.collect_slots_values(&[3, 0, 4, 1]);
|
||||
assert_eq!(got, vec![false, true, true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_with_duplicates() {
|
||||
fn bitvec_collect_slots_values_with_duplicates() {
|
||||
let bits = vec![true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let got = r.get_batch(&[0, 2, 0, 1]);
|
||||
let got = r.collect_slots_values(&[0, 2, 0, 1]);
|
||||
assert_eq!(got, vec![true, true, true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_empty() {
|
||||
fn bitvec_collect_slots_values_empty() {
|
||||
let (_dir, r) = make_bv(&[true, false]);
|
||||
let got = r.get_batch(&[]);
|
||||
let got = r.collect_slots_values(&[]);
|
||||
assert!(got.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_get_batch_out_of_bounds_returns_false() {
|
||||
fn bitvec_collect_slots_values_out_of_bounds_returns_false() {
|
||||
// PersistentBitVec::get does NOT bounds-check — out-of-range slots read
|
||||
// whatever bit happens to be in the mmap (zero-initialised, so false).
|
||||
// This is a known gap; get_batch inherits it.
|
||||
// This is a known gap; collect_slots_values inherits it.
|
||||
let (_dir, r) = make_bv(&[true, false]);
|
||||
let got = r.get_batch(&[0, 2]);
|
||||
let got = r.collect_slots_values(&[0, 2]);
|
||||
assert_eq!(got, vec![true, false]);
|
||||
}
|
||||
|
||||
// BitSliceView get_batch (same logic, exercised through the view)
|
||||
// BitSliceView collect_slots_values (same logic, exercised through the view)
|
||||
#[test]
|
||||
fn bitslice_view_get_batch() {
|
||||
fn bitslice_view_collect_slots_values() {
|
||||
let bits = vec![true, false, true, false, true];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let view = r.view();
|
||||
assert_eq!(view.get_batch(&[0, 1, 2]), vec![true, false, true]);
|
||||
assert_eq!(view.get_batch(&[4, 3]), vec![true, false]);
|
||||
assert_eq!(view.collect_slots_values(&[0, 1, 2]), vec![true, false, true]);
|
||||
assert_eq!(view.collect_slots_values(&[4, 3]), vec![true, false]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitslice_view_get_batch_out_of_bounds_returns_false() {
|
||||
fn bitslice_view_collect_slots_values_out_of_bounds_returns_false() {
|
||||
// BitSliceView::get does NOT bounds-check either.
|
||||
let bits = vec![true, false];
|
||||
let (_dir, r) = make_bv(&bits);
|
||||
let view = r.view();
|
||||
let got = view.get_batch(&[0, 2]);
|
||||
let got = view.collect_slots_values(&[0, 2]);
|
||||
assert_eq!(got, vec![true, false]);
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ fn partial_relfreq_bray_additive_across_split() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── get_batch tests ────────────────────────────────────────────────────────────
|
||||
// ── collect_slots_values tests ────────────────────────────────────────────────────────────
|
||||
|
||||
fn make_pciv(counts: &[u32]) -> (tempfile::TempDir, PersistentCompactIntVec) {
|
||||
let dir = tempdir().unwrap();
|
||||
@@ -413,49 +413,49 @@ fn make_pciv(counts: &[u32]) -> (tempfile::TempDir, PersistentCompactIntVec) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_in_order() {
|
||||
fn pciv_collect_slots_values_in_order() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[0, 1, 2, 3]);
|
||||
let got = v.collect_slots_values(&[0, 1, 2, 3]);
|
||||
assert_eq!(got, counts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_out_of_order() {
|
||||
fn pciv_collect_slots_values_out_of_order() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[3, 0, 2, 1]);
|
||||
let got = v.collect_slots_values(&[3, 0, 2, 1]);
|
||||
assert_eq!(got, vec![1000, 10, 300, 255]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_with_duplicates() {
|
||||
fn pciv_collect_slots_values_with_duplicates() {
|
||||
let counts = vec![10u32, 255, 300];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let got = v.get_batch(&[0, 2, 0, 1]);
|
||||
let got = v.collect_slots_values(&[0, 2, 0, 1]);
|
||||
assert_eq!(got, vec![10, 300, 10, 255]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_empty() {
|
||||
fn pciv_collect_slots_values_empty() {
|
||||
let (_dir, v) = make_pciv(&[10u32, 20]);
|
||||
let got: Vec<u32> = v.get_batch(&[]);
|
||||
let got: Vec<u32> = v.collect_slots_values(&[]);
|
||||
assert!(got.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pciv_get_batch_out_of_bounds_panics() {
|
||||
fn pciv_collect_slots_values_out_of_bounds_panics() {
|
||||
let (_dir, v) = make_pciv(&[10u32, 20]);
|
||||
let result = std::panic::catch_unwind(|| v.get_batch(&[0, 2]));
|
||||
assert!(result.is_err(), "get_batch should panic on out-of-bounds slot");
|
||||
let result = std::panic::catch_unwind(|| v.collect_slots_values(&[0, 2]));
|
||||
assert!(result.is_err(), "collect_slots_values should panic on out-of-bounds slot");
|
||||
}
|
||||
|
||||
// IntSliceView get_batch (same logic, exercised through the view)
|
||||
// IntSliceView collect_slots_values (same logic, exercised through the view)
|
||||
#[test]
|
||||
fn intslice_view_get_batch() {
|
||||
fn intslice_view_collect_slots_values() {
|
||||
let counts = vec![10u32, 255, 300, 1000];
|
||||
let (_dir, v) = make_pciv(&counts);
|
||||
let view = v.view();
|
||||
assert_eq!(view.get_batch(&[0, 1, 2]), vec![10, 255, 300]);
|
||||
assert_eq!(view.get_batch(&[3, 1]), vec![1000, 255]);
|
||||
assert_eq!(view.collect_slots_values(&[0, 1, 2]), vec![10, 255, 300]);
|
||||
assert_eq!(view.collect_slots_values(&[3, 1]), vec![1000, 255]);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ impl<'a> BitSliceView<'a> {
|
||||
///
|
||||
/// Slots are sorted internally before reading, then results are reordered
|
||||
/// to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<bool> {
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<bool> {
|
||||
let mut out = vec![false; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
self.fill_slots_values(slots, &mut out);
|
||||
out
|
||||
}
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [bool]) {
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
@@ -44,32 +44,43 @@ impl<'a> BitSliceView<'a> {
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![false; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
self.fill_slots_values_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// Positions in `sorted_slots` (not the slots themselves) whose bit is
|
||||
/// set — a `filter` over `get`, not a new traversal. `sorted_slots`
|
||||
/// need not actually be sorted for correctness, but callers batching
|
||||
/// several columns over the same slot set sort once and reuse it for
|
||||
/// cache-friendlier `get` access (see `PersistentBitMatrix::nonzero_iter`).
|
||||
pub(crate) fn nonzero_among_sorted<'b>(self, sorted_slots: &'b [usize]) -> impl Iterator<Item = usize> + 'b
|
||||
/// `(position in slots, value)` for every slot. Plain `enumerate` +
|
||||
/// `get`: order never affects correctness here, only the caller's cache
|
||||
/// locality (see `enumerate_nonzero_slots`), so this takes `slots` as
|
||||
/// given.
|
||||
#[inline]
|
||||
pub fn enumerate_slots_values<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, bool)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
sorted_slots.iter().enumerate()
|
||||
.filter_map(move |(pos, &slot)| self.get(slot).then_some(pos))
|
||||
slots.iter().enumerate().map(move |(pos, &slot)| (pos, self.get(slot)))
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_slots_values_sorted(&self, sorted_slots: &[usize], out: &mut [bool]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (pos, v) in self.enumerate_slots_values(sorted_slots) {
|
||||
out[pos] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// Positions in `slots` (not the slots themselves) whose bit is set.
|
||||
/// Order of `slots` never matters for correctness; callers batching
|
||||
/// several columns over the same slot set sort it once beforehand
|
||||
/// purely for cache-friendlier `get` access (see
|
||||
/// `PersistentBitMatrix::nonzero_iter`).
|
||||
pub fn enumerate_nonzero_slots<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = usize> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
self.enumerate_slots_values(slots).filter_map(|(pos, v)| v.then_some(pos))
|
||||
}
|
||||
|
||||
pub fn count_ones(&self) -> u64 {
|
||||
@@ -184,14 +195,14 @@ impl<'a> IntSliceView<'a> {
|
||||
///
|
||||
/// Slots are sorted internally before reading, then results are reordered
|
||||
/// to match the input order.
|
||||
pub fn get_batch(&self, slots: &[usize]) -> Vec<u32> {
|
||||
pub fn collect_slots_values(&self, slots: &[usize]) -> Vec<u32> {
|
||||
let mut out = vec![0u32; slots.len()];
|
||||
self.fill_batch(slots, &mut out);
|
||||
self.fill_slots_values(slots, &mut out);
|
||||
out
|
||||
}
|
||||
|
||||
/// Like [`get_batch`](Self::get_batch), but fills a caller-provided buffer.
|
||||
pub fn fill_batch(&self, slots: &[usize], out: &mut [u32]) {
|
||||
/// Like [`collect_slots_values`](Self::collect_slots_values), but fills a caller-provided buffer.
|
||||
pub fn fill_slots_values(&self, slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(slots.len(), out.len());
|
||||
let n = slots.len();
|
||||
if n == 0 { return; }
|
||||
@@ -199,32 +210,40 @@ impl<'a> IntSliceView<'a> {
|
||||
perm.sort_by_key(|&i| slots[i]);
|
||||
let sorted: Vec<usize> = perm.iter().map(|&i| slots[i]).collect();
|
||||
let mut tmp = vec![0u32; n];
|
||||
self.fill_batch_sorted(&sorted, &mut tmp);
|
||||
self.fill_slots_values_sorted(&sorted, &mut tmp);
|
||||
for (i, &orig_idx) in perm.iter().enumerate() {
|
||||
out[orig_idx] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_batch_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (i, &slot) in sorted_slots.iter().enumerate() {
|
||||
out[i] = self.get(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`BitSliceView::nonzero_among_sorted`]: `(position in
|
||||
/// sorted_slots, value)` for every nonzero value — a `filter_map` over
|
||||
/// `get`, not a new traversal.
|
||||
pub(crate) fn nonzero_among_sorted<'b>(self, sorted_slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
/// `(position in slots, value)` for every slot. See
|
||||
/// [`BitSliceView::enumerate_slots_values`]: order never affects
|
||||
/// correctness here.
|
||||
#[inline]
|
||||
pub fn enumerate_slots_values<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
sorted_slots.iter().enumerate().filter_map(move |(pos, &slot)| {
|
||||
let v = self.get(slot);
|
||||
(v != 0).then_some((pos, v))
|
||||
})
|
||||
slots.iter().enumerate().map(move |(pos, &slot)| (pos, self.get(slot)))
|
||||
}
|
||||
|
||||
/// Fill `out` assuming `sorted_slots` is already in ascending order.
|
||||
/// Results are written in `sorted_slots` order (no reordering).
|
||||
pub(crate) fn fill_slots_values_sorted(&self, sorted_slots: &[usize], out: &mut [u32]) {
|
||||
assert_eq!(sorted_slots.len(), out.len());
|
||||
for (pos, v) in self.enumerate_slots_values(sorted_slots) {
|
||||
out[pos] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`BitSliceView::enumerate_nonzero_slots`]: `(position in slots,
|
||||
/// value)` for every nonzero value — a `filter_map` over `get`, not a
|
||||
/// new traversal.
|
||||
pub fn enumerate_nonzero_slots<'b>(self, slots: &'b [usize]) -> impl Iterator<Item = (usize, u32)> + 'b
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
self.enumerate_slots_values(slots).filter_map(|(pos, v)| (v != 0).then_some((pos, v)))
|
||||
}
|
||||
|
||||
/// Sequential merge scan: yields all n values in slot order.
|
||||
|
||||
Reference in New Issue
Block a user