Skip to content
5 changes: 5 additions & 0 deletions .claude/blackboard.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2026-10-10 — the three `PackedDatabase`s (stroke split pinned)

- **`PackedDatabase` ×3 is not a duplicate.** `packed` (fixed 128/384/1536 + an index) and `cascade` (width-derived split) lay out the same strokes at 2048 bytes, now pinned by `stroke_split_equals_the_cascade_split_at_2048` (red if cascade's split point moves); they keep separate query paths. `cam_pq::PackedDatabase` holds 6-byte CAM codes and shares only the name. Doc cross-links added; not merged.
- **Withdrawn the same day:** `mask_row_window` (signed `i64` column) and `bit_sliced_weighted_count` (signed sum over positive/negative planes). Signed masks are not part of this facade. A 256×256 tile is the palette256 × palette256 address (`u8:u8`), answered by 256×256 distance/compose tables, not by geometric windows.

## 2026-10-10 — x86-64-v2 realization (SSE4.2 without AVX) via the scalar backend

- **What:** an eighth realization, selected at compile time by `all(target_arch = "x86_64", target_feature = "sse4.2", not(target_feature = "avx"))`. You get it with `target-cpu=native` on a v2-only CPU (Nehalem/Westmere, Silvermont-class Atom, VMs that hide AVX) or with `.cargo/config-v2.toml`. `simd.rs` routes this arm to `simd_scalar.rs`: the AVX2/AVX-512 types wrap 256/512-bit registers and cannot be the v2 realization, so plain arrays that LLVM compiles to SSE are used instead. Baseline x86-64 builds (no SSE4.2) are unchanged: they keep the AVX2 arm and the `cpu_guard` AVX2 floor, which now exempts `sse4.2 && !avx2` (the AVX and v2 arms together).
Expand Down
7 changes: 6 additions & 1 deletion src/hpc/cascade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,12 @@ pub fn adaptive_resolution(query_entropy: f32, corpus_cv: f32) -> Band {
}
}

/// Packed database for stroke-aligned cascade search.
/// Packed database for stroke-aligned cascade search, for rows of any width.
///
/// For 2048-byte rows the split is the fixed one in
/// [`crate::hpc::packed::PackedDatabase`] (128 / 384 / 1536); that type adds
/// an index and its own query path. `crate::hpc::cam_pq::PackedDatabase` is
/// unrelated (6-byte CAM codes).
pub struct PackedDatabase {
pub stroke1: Vec<u8>,
pub stroke2: Vec<u8>,
Expand Down
26 changes: 26 additions & 0 deletions src/hpc/packed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ pub const FINGERPRINT_BYTES: usize = 2048;
/// stroke2: [cand[0]_128..512 | cand[1]_128..512 | ...]
/// stroke3: [cand[0]_512..2048 | cand[1]_512..2048 | ...]
/// ```
///
/// Two other types carry this name and are not this one:
/// [`crate::hpc::cascade::PackedDatabase`] packs rows of any width, and for
/// 2048-byte rows its stroke split is exactly this one (128 / 384 / 1536,
/// pinned by `stroke_split_equals_the_cascade_split_at_2048`), with its own
/// query path and no index. `crate::hpc::cam_pq::PackedDatabase` holds 6-byte
/// CAM codes and shares only the name.
pub struct PackedDatabase {
/// Stroke 1 data: N × STROKE1_BYTES contiguous bytes.
stroke1: Vec<u8>,
Expand Down Expand Up @@ -277,6 +284,25 @@ impl PackedDatabase {

#[cfg(test)]
mod tests {
/// The fixed 2048-byte split here and cascade's width-derived split must
/// lay out the same bytes, or the two packers disagree on what a stroke
/// is.
#[test]
fn stroke_split_equals_the_cascade_split_at_2048() {
let n = 5;
let db: Vec<u8> = (0..n * FINGERPRINT_BYTES)
.map(|i| (i * 31 % 251) as u8)
.collect();
let fixed = PackedDatabase::pack(&db, FINGERPRINT_BYTES);
let cas = crate::hpc::cascade::PackedDatabase::pack(&db, FINGERPRINT_BYTES);
assert_eq!((cas.s1_bytes, cas.s2_bytes, cas.s3_bytes), (STROKE1_BYTES, STROKE2_BYTES, STROKE3_BYTES));
for i in 0..n {
assert_eq!(fixed.get_stroke1(i), &cas.stroke1[i * STROKE1_BYTES..(i + 1) * STROKE1_BYTES]);
assert_eq!(fixed.get_stroke2(i), &cas.stroke2[i * STROKE2_BYTES..(i + 1) * STROKE2_BYTES]);
assert_eq!(fixed.get_stroke3(i), &cas.stroke3[i * STROKE3_BYTES..(i + 1) * STROKE3_BYTES]);
}
}

use super::*;

#[test]
Expand Down
Loading