Skip to content

Commit fa25370

Browse files
committed
feat(hpc): RailSpec generalisiert — beide realen Carvings, gemessen begruendet
Der erste Wurf konnte nur interleaved 6x(u8:u8)-Paare (stride 2) lesen. Die medcare-Bake hat genau diese Paar-Lesart GEMESSEN UND VERWORFEN (traegt nur 44,25 % der Pfade; der per-Achse-Slab fasst 99,62 % in zwoelf Ebenen, rails.rs). Ihre aufgeloeste Lesart ist: zusammenhaengender 12-Byte-Slab je Achse, stride 1, diskontinuierliche Fortsetzung. RailSpec traegt jetzt { reg, levels, stride, cont } und drueckt beide Carvings aus: v3_facet(axis) 6 Ebenen, stride 2 (Facet-Payload, Fallback) slab(reg, levels, cont) N Ebenen, stride 1 (per-Achse-Register) Welches Carving eine Zeile nutzt, ist Eigenschaft ihrer Bake und wird dort aufgeloest, wo die ClassView lebt — hier bleibt es ein Parameter. RailAxis bekommt explizite Diskriminanten (Lo=0, Hi=1), damit der Byte-Versatz die Achse IST statt sie zu kodieren. Neuer Test spiegelt die medcare-Carving byte-genau (Wert 44..56 + Fortsetzung 68..80, zeilen-absolut 76..88/100..112) und pinnt, dass das fremde Register dazwischen (part_of, 56..68) unsichtbar bleibt. 8/8.
1 parent 698c1c3 commit fa25370

1 file changed

Lines changed: 103 additions & 33 deletions

File tree

src/hpc/clam_v3.rs

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -88,64 +88,108 @@ pub const RAIL_PAIRS: usize = 6;
8888
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8989
pub enum RailAxis {
9090
/// Byte 0 of each pair (the `X` of `X:Y`).
91-
Lo,
91+
Lo = 0,
9292
/// Byte 1 of each pair (the `Y` of `X:Y`).
93-
Hi,
93+
Hi = 1,
9494
}
9595

96-
/// The byte-range + axis handle for reading a row's rail register(s).
96+
/// The byte-range handle for reading a row's rail register(s) — the
97+
/// ClassView's reading made portable.
9798
///
98-
/// This is the ClassView's reading made portable: the caller resolves WHICH
99-
/// bytes carry the register and which axis is walked from its
100-
/// ClassView / WideFieldMask, and hands the result here. ndarray never
101-
/// resolves a classid — it only refuses to guess.
99+
/// The caller resolves WHICH bytes carry the register, how many levels it
100+
/// holds, and how they are laid out from its ClassView / WideFieldMask, and
101+
/// hands the result here. ndarray never resolves a classid — it only refuses
102+
/// to guess. Two carvings are real today, and the spec expresses both:
103+
///
104+
/// | carving | levels | stride | constructor |
105+
/// |---|---|---|---|
106+
/// | interleaved axis pairs `X:Y` in the facet payload | 6 | 2 | [`RailSpec::v3_facet`] |
107+
/// | contiguous per-axis slab (one axis per register) | 12 | 1 | [`RailSpec::slab`] |
108+
///
109+
/// The second is not a variant for variety's sake: on the medcare bake the
110+
/// pair reading was **measured and rejected** (it fits only 44.25 % of
111+
/// paths; the per-axis slab fits 99.62 % in twelve levels). Which carving a
112+
/// row uses is a property of its bake, resolved where the ClassView lives.
102113
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103114
pub struct RailSpec {
104-
/// Byte offset of the primary register (6 pairs = 12 bytes).
115+
/// Byte offset of the walked axis' first level in the primary register.
105116
pub reg: usize,
106-
/// Optional stacked continuation register (6 more pairs), e.g. carved
107-
/// into the edge lane when the ClassView says so. `None` = 6 levels max.
117+
/// Levels per register.
118+
pub levels: usize,
119+
/// Bytes between consecutive levels: 2 for interleaved `X:Y` pairs,
120+
/// 1 for a contiguous per-axis slab.
121+
pub stride: usize,
122+
/// Optional continuation register (another `levels` levels), possibly
123+
/// discontiguous — e.g. carved into the edge lane, or further down the
124+
/// value slab. `None` = the primary register is the whole story.
108125
pub cont: Option<usize>,
109-
/// Which byte of each level pair the walk reads.
110-
pub axis: RailAxis,
111126
}
112127

113128
impl RailSpec {
114-
/// The zero-fallback default: primary register at `4..16` (the facet
115-
/// payload), axis `Lo`, no continuation. Correct for a class whose
116-
/// ClassView has not said otherwise — and ONLY for such a class.
129+
/// The facet-payload reading: 6 interleaved `(u8:u8)` pairs at `4..16`,
130+
/// walking one axis of each pair. The zero-fallback default for a class
131+
/// whose ClassView has not said otherwise — and ONLY for such a class.
117132
#[must_use]
118-
pub const fn v3_facet() -> Self {
119-
Self { reg: 4, cont: None, axis: RailAxis::Lo }
133+
pub const fn v3_facet(axis: RailAxis) -> Self {
134+
Self {
135+
reg: 4 + axis as usize,
136+
levels: RAIL_PAIRS,
137+
stride: 2,
138+
cont: None,
139+
}
120140
}
121141

122-
/// The same spec with a continuation register stacked at `at` —
123-
/// e.g. `V3_KEY_LEN` when the ClassView carves it into the edge lane.
142+
/// A contiguous per-axis slab: `levels` at `reg..reg+levels`, one byte
143+
/// per level, with an optional (possibly discontiguous) continuation.
144+
/// This is the carving the medcare bake measured its way to.
145+
#[must_use]
146+
pub const fn slab(reg: usize, levels: usize, cont: Option<usize>) -> Self {
147+
Self {
148+
reg,
149+
levels,
150+
stride: 1,
151+
cont,
152+
}
153+
}
154+
155+
/// The same spec with a continuation register stacked at `at`.
124156
#[must_use]
125157
pub const fn stacked(self, at: usize) -> Self {
126-
Self { cont: Some(at), ..self }
158+
Self {
159+
cont: Some(at),
160+
..self
161+
}
127162
}
128163

129164
/// Maximum representable depth under this spec.
130165
#[must_use]
131166
pub const fn max_depth(&self) -> u32 {
132-
if self.cont.is_some() { 2 * RAIL_PAIRS as u32 } else { RAIL_PAIRS as u32 }
167+
if self.cont.is_some() {
168+
2 * self.levels as u32
169+
} else {
170+
self.levels as u32
171+
}
133172
}
134173

135174
/// The walked-axis byte of level `i` (0-based), or 0 if out of range.
136-
/// Position is the information: byte index IS the level.
175+
/// Position is the information: level index maps to a byte position and
176+
/// nothing else.
137177
#[inline]
138178
fn level(&self, row: &[u8], i: usize) -> u8 {
139-
let (base, k) = if i < RAIL_PAIRS {
179+
let (base, k) = if i < self.levels {
140180
(self.reg, i)
141181
} else {
142182
match self.cont {
143-
Some(c) => (c, i - RAIL_PAIRS),
183+
Some(c) => (c, i - self.levels),
144184
None => return 0,
145185
}
146186
};
147-
let at = base + 2 * k + match self.axis { RailAxis::Lo => 0, RailAxis::Hi => 1 };
148-
if at < row.len() { row[at] } else { 0 }
187+
let at = base + self.stride * k;
188+
if at < row.len() {
189+
row[at]
190+
} else {
191+
0
192+
}
149193
}
150194

151195
/// Depth = occupied leading levels. Stops at the first zero: a hole ends
@@ -260,7 +304,7 @@ mod tests {
260304

261305
#[test]
262306
fn depth_stops_at_the_first_hole() {
263-
let spec = RailSpec::v3_facet();
307+
let spec = RailSpec::v3_facet(RailAxis::Lo);
264308
assert_eq!(spec.depth(&row(&[1, 2, 3], RailAxis::Lo, 0)), 3);
265309
// a value AFTER a hole is not ancestry
266310
assert_eq!(spec.depth(&row(&[1, 0, 7], RailAxis::Lo, 0)), 1);
@@ -269,7 +313,7 @@ mod tests {
269313

270314
#[test]
271315
fn geodesic_is_the_tree_distance_on_the_linearisation() {
272-
let spec = RailSpec::v3_facet();
316+
let spec = RailSpec::v3_facet(RailAxis::Lo);
273317
let parent = row(&[3, 5], RailAxis::Lo, 0);
274318
let child = row(&[3, 5, 2], RailAxis::Lo, 0);
275319
let sibling = row(&[3, 5, 4], RailAxis::Lo, 0);
@@ -287,8 +331,8 @@ mod tests {
287331
/// This is the test a widened u16 could not pass.
288332
#[test]
289333
fn the_axes_of_a_pair_are_independent() {
290-
let spec_lo = RailSpec::v3_facet();
291-
let spec_hi = RailSpec { axis: RailAxis::Hi, ..spec_lo };
334+
let spec_lo = RailSpec::v3_facet(RailAxis::Lo);
335+
let spec_hi = RailSpec::v3_facet(RailAxis::Hi);
292336
let mut r = vec![0u8; 512];
293337
r[4] = 2; // level 0, Lo
294338
r[5] = 7; // level 0, Hi — different chain entirely
@@ -305,7 +349,7 @@ mod tests {
305349
/// walk past six levels with the same hole rule.
306350
#[test]
307351
fn a_stacked_register_extends_depth_past_six() {
308-
let plain = RailSpec::v3_facet();
352+
let plain = RailSpec::v3_facet(RailAxis::Lo);
309353
let stacked = plain.stacked(V3_KEY_LEN);
310354
let mut r = vec![0u8; 512];
311355
for i in 0..RAIL_PAIRS {
@@ -337,7 +381,7 @@ mod tests {
337381
/// CLAM's pruning actually relies on.
338382
#[test]
339383
fn the_geodesic_satisfies_the_triangle_inequality() {
340-
let spec = RailSpec::v3_facet();
384+
let spec = RailSpec::v3_facet(RailAxis::Lo);
341385
let rows = [
342386
row(&[1], RailAxis::Lo, 0),
343387
row(&[1, 2], RailAxis::Lo, 0),
@@ -363,7 +407,7 @@ mod tests {
363407
/// of indiscernibles must refine over stored ancestry, not trust this.
364408
#[test]
365409
fn distinct_nodes_past_the_stored_depth_measure_zero() {
366-
let spec = RailSpec::v3_facet();
410+
let spec = RailSpec::v3_facet(RailAxis::Lo);
367411
let mut a = vec![0u8; 512];
368412
let mut b = vec![0u8; 512];
369413
for i in 0..RAIL_PAIRS {
@@ -375,4 +419,30 @@ mod tests {
375419
assert_eq!(spec.geodesic(&a, &b), 0, "das ist die dokumentierte Grenze");
376420
assert!(v3_value_hamming(&a, &b) > 0, "der Inhalt unterscheidet sie");
377421
}
422+
423+
/// The medcare carving, mirrored byte-exact: per-axis slab at
424+
/// value 44..56 (row-absolute 76..88), continuation at value 68..80
425+
/// (row-absolute 100..112), stride 1, twelve levels per register.
426+
/// The pair reading was measured and REJECTED for that bake (44.25 %),
427+
/// so this test exists to keep the slab expressible forever.
428+
#[test]
429+
fn a_per_axis_slab_reads_stride_one_with_discontiguous_continuation() {
430+
let spec = RailSpec::slab(V3_VALUE_OFF + 44, 12, Some(V3_VALUE_OFF + 68));
431+
assert_eq!(spec.max_depth(), 24);
432+
let mut r = vec![0u8; 512];
433+
for i in 0..12 {
434+
r[V3_VALUE_OFF + 44 + i] = 1; // primary full
435+
}
436+
r[V3_VALUE_OFF + 68] = 3; // continuation level 12
437+
r[V3_VALUE_OFF + 69] = 5; // continuation level 13
438+
assert_eq!(spec.depth(&r), 14);
439+
// the byte BETWEEN the registers (value 56..68 = part_of) is never read
440+
let mut s2 = r.clone();
441+
s2[V3_VALUE_OFF + 60] = 9;
442+
assert_eq!(spec.geodesic(&r, &s2), 0, "fremde Register sind unsichtbar");
443+
// sibling at continuation depth
444+
let mut sib = r.clone();
445+
sib[V3_VALUE_OFF + 69] = 6;
446+
assert_eq!(spec.geodesic(&r, &sib), 2);
447+
}
378448
}

0 commit comments

Comments
 (0)