@@ -88,64 +88,108 @@ pub const RAIL_PAIRS: usize = 6;
8888#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
8989pub 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 ) ]
103114pub 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
113128impl 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