Skip to main content

hurray_core/layout/addressing/
csf.rs

1//! CSF (Compressed Sparse Fiber) element lookup and storage-invariant validation.
2//!
3//! Implements the per-level binary-search descent from
4//! `docs/spec/layouts/csf.md § Element Lookup` and the invariant checks from
5//! `docs/spec/layouts/csf.md § Storage Invariants`.
6//!
7//! All arithmetic uses `checked_*` to avoid silent overflow — this code can run
8//! on arbitrarily large tensors.
9
10use crate::Error;
11
12/// Looks up the storage offset (`values` buffer index) for a logical index in a
13/// CSF tensor, or returns `None` if the element is structurally absent (implicit zero).
14///
15/// # Arguments
16///
17/// - `query_coords` — logical index `[idx[0], ..., idx[rank-1]]`; length must equal `rank`.
18/// - `mode_order` — the level-to-dimension permutation from the CSF descriptor.
19/// - `pos_levels` — slice of `rank` pos arrays; `pos_levels[L]` is the `pos_L` buffer
20///   as a slice of `uint64` values.
21/// - `crd_levels` — slice of `rank` crd arrays; `crd_levels[L]` is the `crd_L` buffer
22///   as a slice of `uint64` values.
23///
24/// # Return value
25///
26/// Returns `Ok(Some(p))` where `p` is the leaf position in `values`, or `Ok(None)` if
27/// the element is not stored (structural zero). Returns `Err` only on structural
28/// violations (malformed buffers that make the search impossible without panicking),
29/// not on valid structural-zero hits.
30///
31/// # Errors
32///
33/// - [`Error::IndexRankMismatch`] — `query_coords.len() != mode_order.len()`.
34/// - [`Error::IndexOutOfRange`] — any query coordinate is out of bounds for its mode.
35///   (Caller must validate coordinates against the shape before calling.)
36/// - [`Error::InvalidLayout`] — a pos/crd buffer is so malformed that a safe traversal
37///   is impossible (e.g. `pos[p] > crd.len()`).
38/// - [`Error::AddressOverflow`] — intermediate index arithmetic overflowed `u64`.
39///
40/// # Examples
41///
42/// ```
43/// use hurray_core::layout::addressing::csf::element_offset;
44///
45/// // Rank-3 sparse tensor with shape [2, 3, 4], mode_order = [0, 1, 2], nnz = 4.
46/// // Non-zeros: (0,0,1)→1.0, (0,2,3)→2.0, (1,1,0)→3.0, (1,1,2)→4.0
47/// //
48/// // Level 0:  pos_0 = [0, 2],     crd_0 = [0, 1]
49/// // Level 1:  pos_1 = [0, 2, 3],  crd_1 = [0, 2, 1]
50/// // Level 2:  pos_2 = [0, 1, 2, 4], crd_2 = [1, 3, 0, 2]
51///
52/// let mode_order: &[u32] = &[0, 1, 2];
53/// let pos_levels: &[&[u64]] = &[&[0, 2], &[0, 2, 3], &[0, 1, 2, 4]];
54/// let crd_levels: &[&[u64]] = &[&[0, 1], &[0, 2, 1], &[1, 3, 0, 2]];
55///
56/// // Lookup (1, 1, 2) → leaf position 3 (values[3] = 4.0).
57/// assert_eq!(
58///     element_offset(&[1, 1, 2], mode_order, pos_levels, crd_levels).unwrap(),
59///     Some(3)
60/// );
61///
62/// // Lookup (0, 1, 0) → not stored (structural zero).
63/// assert_eq!(
64///     element_offset(&[0, 1, 0], mode_order, pos_levels, crd_levels).unwrap(),
65///     None
66/// );
67/// ```
68pub fn element_offset(
69    query_coords: &[u64],
70    mode_order: &[u32],
71    pos_levels: &[&[u64]],
72    crd_levels: &[&[u64]],
73) -> crate::Result<Option<u64>> {
74    let rank = mode_order.len();
75
76    if query_coords.len() != rank {
77        return Err(Error::IndexRankMismatch {
78            index_rank: query_coords.len(),
79            shape_rank: rank,
80        });
81    }
82
83    // Spec §Element Lookup step 1: permute the query into storage order.
84    // q_L = query_coords[mode_order[L]] — the coordinate sought at level L.
85    // We do this inline below rather than pre-allocating a Vec, because this
86    // is potentially called in tight inner loops (structural zero hits are cheap).
87
88    // Spec §Element Lookup step 2: initialise parent position p = 0 (virtual root).
89    let mut p: u64 = 0;
90
91    // Spec §Element Lookup step 3: descend level by level.
92    for level in 0..rank {
93        let dim_idx = mode_order[level] as usize;
94        // Safety-guard: mode_order is validated by validate_against_shape before this
95        // function is typically called, but we return a crate error rather than panic.
96        if dim_idx >= query_coords.len() {
97            return Err(Error::InvalidLayout(format!(
98                "csf: mode_order[{level}]={dim_idx} out of range for rank {rank}"
99            )));
100        }
101        let q_l: u64 = query_coords[dim_idx];
102
103        let pos = pos_levels[level];
104        let crd = crd_levels[level];
105
106        // The children of the current parent occupy crd_L[pos_L[p]..pos_L[p+1]).
107        let p_usize = p as usize;
108
109        // Bounds-check: pos must have at least p+2 entries.
110        if p_usize + 1 >= pos.len() {
111            return Err(Error::InvalidLayout(format!(
112                "csf: pos_{level}[{p}..{}] out of bounds (pos_{level}.len()={})",
113                p_usize + 1,
114                pos.len()
115            )));
116        }
117
118        let slice_start = pos[p_usize] as usize;
119        let slice_end = pos[p_usize + 1] as usize;
120
121        // Guard against a malformed (non-monotone) pos array.
122        if slice_start > slice_end {
123            return Err(Error::InvalidLayout(format!(
124                "csf: pos_{level}[{p}]={slice_start} > pos_{level}[{}]={slice_end} (not non-decreasing)",
125                p_usize + 1
126            )));
127        }
128
129        // Guard against pos entries that exceed the crd array length.
130        if slice_end > crd.len() {
131            return Err(Error::InvalidLayout(format!(
132                "csf: pos_{level}[{}]={slice_end} exceeds crd_{level}.len()={}",
133                p_usize + 1,
134                crd.len()
135            )));
136        }
137
138        let slice = &crd[slice_start..slice_end];
139
140        // Spec §Element Lookup: binary search on the sorted sibling slice.
141        match slice.binary_search(&q_l) {
142            Ok(relative_offset) => {
143                // Found at relative offset k; new absolute parent position = pos_L[p] + k.
144                let abs = pos[p_usize]
145                    .checked_add(relative_offset as u64)
146                    .ok_or(Error::AddressOverflow)?;
147                p = abs;
148                // Continue to the next level.
149            }
150            Err(_) => {
151                // Not found — the element is an implicit zero.
152                return Ok(None);
153            }
154        }
155    }
156
157    // After rank levels, p is the leaf position in values[].
158    Ok(Some(p))
159}
160
161/// Validates the storage invariants for all CSF index buffers.
162///
163/// Per `docs/spec/layouts/csf.md § Storage Invariants`, for every level `L`:
164///
165/// 1. `pos_L[0] == 0`.
166/// 2. `pos_L` is non-decreasing.
167/// 3. The terminal `pos_L` entry equals the next level's node count:
168///    `pos_0[1] == n_0`, `pos_L[n_{L-1}] == n_L`, and `n_{rank-1} == nnz`.
169/// 4. Within each parent slice `crd_L[pos_L[k]..pos_L[k+1])`, coordinates are
170///    strictly increasing (no duplicate siblings).
171/// 5. All coordinates are within bounds: `crd_L[i] < shape[mode_order[L]]`.
172///
173/// For the empty tensor (`nnz == 0`):
174/// - `pos_0` MUST be `[0, 0]` (length 2).
175/// - Every `crd_L` MUST be empty (length 0).
176/// - For `L >= 1`, `pos_L` MUST be `[0]` (length 1).
177///
178/// Index buffers are `uint64` throughout to match CSR/COO/CSC.
179///
180/// # Arguments
181///
182/// - `nnz` — declared number of non-zeros (from the CSF descriptor).
183/// - `mode_order` — the level-to-dimension permutation.
184/// - `shape_dims` — the tensor's dimension sizes; `shape_dims[mode_order[L]]` is the
185///   coordinate bound for level `L`.
186/// - `pos_levels` — `pos_L` buffers as typed `u64` slices, one per level.
187/// - `crd_levels` — `crd_L` buffers as typed `u64` slices, one per level.
188///
189/// # Errors
190///
191/// Returns [`Error::InvalidLayout`] if any invariant is violated.
192///
193/// # Examples
194///
195/// ```
196/// use hurray_core::layout::addressing::csf::validate_index_buffers;
197///
198/// // The spec § Example: shape [2,3,4], mode_order=[0,1,2], nnz=4.
199/// let mode_order: &[u32] = &[0, 1, 2];
200/// let shape_dims: &[u64] = &[2, 3, 4];
201/// let pos_levels: &[&[u64]] = &[&[0, 2], &[0, 2, 3], &[0, 1, 2, 4]];
202/// let crd_levels: &[&[u64]] = &[&[0, 1], &[0, 2, 1], &[1, 3, 0, 2]];
203///
204/// assert!(validate_index_buffers(4, mode_order, shape_dims, pos_levels, crd_levels).is_ok());
205///
206/// // Empty tensor: nnz=0.
207/// let pos_levels_empty: &[&[u64]] = &[&[0, 0], &[0], &[0]];
208/// let crd_levels_empty: &[&[u64]] = &[&[], &[], &[]];
209/// assert!(validate_index_buffers(0, mode_order, shape_dims, pos_levels_empty, crd_levels_empty).is_ok());
210/// ```
211pub fn validate_index_buffers(
212    nnz: u64,
213    mode_order: &[u32],
214    shape_dims: &[u64],
215    pos_levels: &[&[u64]],
216    crd_levels: &[&[u64]],
217) -> crate::Result<()> {
218    let rank = mode_order.len();
219
220    if pos_levels.len() != rank {
221        return Err(Error::InvalidLayout(format!(
222            "csf: pos_levels.len()={} != rank={}",
223            pos_levels.len(),
224            rank
225        )));
226    }
227    if crd_levels.len() != rank {
228        return Err(Error::InvalidLayout(format!(
229            "csf: crd_levels.len()={} != rank={}",
230            crd_levels.len(),
231            rank
232        )));
233    }
234
235    // n_L: node count at each level. n_{-1} = 1 (virtual root).
236    let mut n_prev: u64 = 1; // n_{L-1}, starting as virtual-root count
237
238    for level in 0..rank {
239        let pos = pos_levels[level];
240        let crd = crd_levels[level];
241        let dim_idx = mode_order[level] as usize;
242
243        // Coordinate bound for this level.
244        let coord_bound = if dim_idx < shape_dims.len() {
245            shape_dims[dim_idx]
246        } else {
247            return Err(Error::InvalidLayout(format!(
248                "csf: mode_order[{level}]={dim_idx} out of range for shape.len()={}",
249                shape_dims.len()
250            )));
251        };
252
253        // Expected pos length: n_{L-1} + 1.
254        // For nnz=0: level 0 pos must be [0,0] (length 2, n_{-1}=1 → 1+1=2).
255        // For nnz=0, level ≥ 1: n_{L-1}=0, so pos must be length 1 → [0].
256        let expected_pos_len = (n_prev as usize).saturating_add(1);
257        if pos.len() != expected_pos_len {
258            return Err(Error::InvalidLayout(format!(
259                "csf: pos_{level}.len()={} != n_{{L-1}}+1={} (n_{{L-1}}={n_prev})",
260                pos.len(),
261                expected_pos_len
262            )));
263        }
264
265        // Invariant 1: pos_L[0] == 0.
266        if pos[0] != 0 {
267            return Err(Error::InvalidLayout(format!(
268                "csf: pos_{level}[0]={} must be 0",
269                pos[0]
270            )));
271        }
272
273        // Validate pos is non-decreasing (invariant 2) and collect n_L = pos[n_{L-1}].
274        for k in 0..(pos.len() - 1) {
275            if pos[k] > pos[k + 1] {
276                return Err(Error::InvalidLayout(format!(
277                    "csf: pos_{level}[{k}]={} > pos_{level}[{}]={} (not non-decreasing)",
278                    pos[k],
279                    k + 1,
280                    pos[k + 1]
281                )));
282            }
283        }
284
285        // Invariant 3: terminal pos entry equals this level's node count (n_L).
286        // For non-leaf levels n_L is enforced implicitly — it sizes `crd` (the
287        // crd.len() == n_L check below) and becomes n_{L-1} for the next level's
288        // pos-length check. Only the leaf level (n_{rank-1} == nnz) is checked here.
289        let n_l = pos[n_prev as usize]; // pos[n_{L-1}]
290        if level == rank - 1 && n_l != nnz {
291            return Err(Error::InvalidLayout(format!(
292                "csf: pos_{level}[n_{{rank-1}}]={n_l} must equal nnz={nnz}"
293            )));
294        }
295
296        // crd length must equal n_L.
297        if crd.len() as u64 != n_l {
298            return Err(Error::InvalidLayout(format!(
299                "csf: crd_{level}.len()={} != n_{level}={n_l}",
300                crd.len()
301            )));
302        }
303
304        // Invariant 4: strictly increasing within each parent slice.
305        // Invariant 5: coordinates < coord_bound.
306        for k in 0..(n_prev as usize) {
307            let start = pos[k] as usize;
308            let end = pos[k + 1] as usize;
309            let sibling_slice = &crd[start..end];
310
311            // Check bounds and strict monotonicity together in one pass.
312            let mut prev_coord: Option<u64> = None;
313            for (j, &coord) in sibling_slice.iter().enumerate() {
314                if coord >= coord_bound {
315                    return Err(Error::InvalidLayout(format!(
316                        "csf: crd_{level}[{}]={coord} >= shape[mode_order[{level}]]={coord_bound}",
317                        start + j
318                    )));
319                }
320                if let Some(prev) = prev_coord {
321                    if coord <= prev {
322                        return Err(Error::InvalidLayout(format!(
323                            "csf: crd_{level}[{start}..{end}] is not strictly increasing at index {} ({prev} >= {coord})",
324                            start + j
325                        )));
326                    }
327                }
328                prev_coord = Some(coord);
329            }
330        }
331
332        // Advance n_prev for the next level.
333        n_prev = n_l;
334    }
335
336    Ok(())
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    // ── Spec example from csf.md § Example ───────────────────────────────────
344    //
345    // Rank-3 shape [2,3,4], mode_order=[0,1,2], nnz=4:
346    // Non-zeros: (0,0,1)→1.0, (0,2,3)→2.0, (1,1,0)→3.0, (1,1,2)→4.0
347    //
348    // pos_0 = [0, 2]        crd_0 = [0, 1]
349    // pos_1 = [0, 2, 3]     crd_1 = [0, 2, 1]
350    // pos_2 = [0, 1, 2, 4]  crd_2 = [1, 3, 0, 2]
351
352    const MODE_ORDER: &[u32] = &[0, 1, 2];
353    const SHAPE_DIMS: &[u64] = &[2, 3, 4];
354    const POS_0: &[u64] = &[0, 2];
355    const CRD_0: &[u64] = &[0, 1];
356    const POS_1: &[u64] = &[0, 2, 3];
357    const CRD_1: &[u64] = &[0, 2, 1];
358    const POS_2: &[u64] = &[0, 1, 2, 4];
359    const CRD_2: &[u64] = &[1, 3, 0, 2];
360
361    fn spec_pos_levels() -> Vec<&'static [u64]> {
362        vec![POS_0, POS_1, POS_2]
363    }
364    fn spec_crd_levels() -> Vec<&'static [u64]> {
365        vec![CRD_0, CRD_1, CRD_2]
366    }
367
368    // ── element_offset: spec worked example ───────────────────────────────────
369
370    /// Spec §Element Lookup: (1, 1, 2) → leaf position 3.
371    #[test]
372    fn element_offset_spec_lookup_1_1_2() {
373        let result = element_offset(
374            &[1, 1, 2],
375            MODE_ORDER,
376            &spec_pos_levels(),
377            &spec_crd_levels(),
378        )
379        .unwrap();
380        assert_eq!(result, Some(3));
381    }
382
383    /// (0, 0, 1) → leaf position 0.
384    #[test]
385    fn element_offset_spec_lookup_0_0_1() {
386        let result = element_offset(
387            &[0, 0, 1],
388            MODE_ORDER,
389            &spec_pos_levels(),
390            &spec_crd_levels(),
391        )
392        .unwrap();
393        assert_eq!(result, Some(0));
394    }
395
396    /// (0, 2, 3) → leaf position 1.
397    #[test]
398    fn element_offset_spec_lookup_0_2_3() {
399        let result = element_offset(
400            &[0, 2, 3],
401            MODE_ORDER,
402            &spec_pos_levels(),
403            &spec_crd_levels(),
404        )
405        .unwrap();
406        assert_eq!(result, Some(1));
407    }
408
409    /// (1, 1, 0) → leaf position 2.
410    #[test]
411    fn element_offset_spec_lookup_1_1_0() {
412        let result = element_offset(
413            &[1, 1, 0],
414            MODE_ORDER,
415            &spec_pos_levels(),
416            &spec_crd_levels(),
417        )
418        .unwrap();
419        assert_eq!(result, Some(2));
420    }
421
422    /// (0, 1, 0) is not stored → structural zero.
423    #[test]
424    fn element_offset_structural_zero_returns_none() {
425        let result = element_offset(
426            &[0, 1, 0],
427            MODE_ORDER,
428            &spec_pos_levels(),
429            &spec_crd_levels(),
430        )
431        .unwrap();
432        assert_eq!(result, None);
433    }
434
435    /// (1, 0, 0) is not stored (i=1 child list has only dim-1 coordinate 1).
436    #[test]
437    fn element_offset_structural_zero_at_level_1() {
438        // Mode 0: i=1 found at level 0 (crd_0[1]=1).
439        // Mode 1: parent p=1, crd_1[pos_1[1]..pos_1[2])=crd_1[2..3)=[1]; search for 0 → not found.
440        let result = element_offset(
441            &[1, 0, 0],
442            MODE_ORDER,
443            &spec_pos_levels(),
444            &spec_crd_levels(),
445        )
446        .unwrap();
447        assert_eq!(result, None);
448    }
449
450    #[test]
451    fn element_offset_structural_zero_at_level_0() {
452        // i0=2 is not stored at the root: crd_0=[0,1] has no entry 2 → miss at the
453        // very first level → implicit zero, before descending any further.
454        let result = element_offset(
455            &[2, 0, 0],
456            MODE_ORDER,
457            &spec_pos_levels(),
458            &spec_crd_levels(),
459        )
460        .unwrap();
461        assert_eq!(result, None);
462    }
463
464    /// Rank mismatch returns IndexRankMismatch.
465    #[test]
466    fn element_offset_rank_mismatch_returns_error() {
467        let err = element_offset(
468            &[0, 1], // rank 2, but mode_order is rank 3
469            MODE_ORDER,
470            &spec_pos_levels(),
471            &spec_crd_levels(),
472        )
473        .unwrap_err();
474        assert!(matches!(err, crate::Error::IndexRankMismatch { .. }));
475    }
476
477    // ── element_offset: non-identity mode_order ───────────────────────────────
478
479    /// mode_order = [2, 1, 0]: column-first ordering.
480    /// Build a rank-3 tree [2,3,4] with a single non-zero (0,0,1).
481    /// With mode_order=[2,1,0], level 0 stores dim 2 (bound 4), etc.
482    #[test]
483    fn element_offset_non_identity_mode_order() {
484        // Single non-zero at logical (0, 0, 1).
485        // mode_order=[2,1,0]: level 0 → dim 2 (coord 1), level 1 → dim 1 (coord 0), level 2 → dim 0 (coord 0).
486        let mode_order: &[u32] = &[2, 1, 0];
487        // Level 0 (dim 2): pos=[0,1], crd=[1]
488        // Level 1 (dim 1): pos=[0,1], crd=[0]
489        // Level 2 (dim 0): pos=[0,1], crd=[0]
490        let pos_levels: Vec<&[u64]> = vec![&[0, 1], &[0, 1], &[0, 1]];
491        let crd_levels: Vec<&[u64]> = vec![&[1], &[0], &[0]];
492
493        // Lookup (0, 0, 1): permuted to q[2]=1, q[1]=0, q[0]=0.
494        let result = element_offset(&[0, 0, 1], mode_order, &pos_levels, &crd_levels).unwrap();
495        assert_eq!(result, Some(0));
496
497        // Lookup (0, 0, 0): q[2]=0 not in crd_0=[1] → structural zero.
498        let result = element_offset(&[0, 0, 0], mode_order, &pos_levels, &crd_levels).unwrap();
499        assert_eq!(result, None);
500    }
501
502    // ── validate_index_buffers: valid inputs ──────────────────────────────────
503
504    /// Spec §Storage Invariants: the spec worked example satisfies all invariants.
505    #[test]
506    fn validate_index_buffers_spec_example_is_valid() {
507        assert!(validate_index_buffers(
508            4,
509            MODE_ORDER,
510            SHAPE_DIMS,
511            &spec_pos_levels(),
512            &spec_crd_levels(),
513        )
514        .is_ok());
515    }
516
517    /// Empty tensor: nnz=0, correct buffer shapes.
518    #[test]
519    fn validate_index_buffers_empty_tensor_is_valid() {
520        // pos_0=[0,0], pos_1=[0], pos_2=[0]; crd_0=[], crd_1=[], crd_2=[].
521        let pos_levels: Vec<&[u64]> = vec![&[0, 0], &[0], &[0]];
522        let crd_levels: Vec<&[u64]> = vec![&[], &[], &[]];
523        assert!(
524            validate_index_buffers(0, MODE_ORDER, SHAPE_DIMS, &pos_levels, &crd_levels).is_ok()
525        );
526    }
527
528    // ── validate_index_buffers: invariant 1 — pos_L[0] != 0 ─────────────────
529
530    #[test]
531    fn validate_rejects_pos_not_starting_at_zero() {
532        let pos_levels: Vec<&[u64]> = vec![&[1, 2], &[0, 2, 3], &[0, 1, 2, 4]]; // pos_0[0]=1
533        let result =
534            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &pos_levels, &spec_crd_levels());
535        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
536    }
537
538    // ── validate_index_buffers: invariant 2 — non-decreasing ─────────────────
539
540    #[test]
541    fn validate_rejects_non_monotone_pos() {
542        // pos_1 = [0, 3, 2] — not non-decreasing at index 1.
543        let pos_levels: Vec<&[u64]> = vec![&[0, 2], &[0, 3, 2], &[0, 1, 2, 4]];
544        let result =
545            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &pos_levels, &spec_crd_levels());
546        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
547    }
548
549    // ── validate_index_buffers: invariant 3 — terminal count ─────────────────
550
551    #[test]
552    fn validate_rejects_terminal_pos_mismatch_nnz() {
553        // pos_2 = [0, 1, 2, 3] → terminal = 3, but nnz=4.
554        let pos_levels: Vec<&[u64]> = vec![&[0, 2], &[0, 2, 3], &[0, 1, 2, 3]];
555        let result =
556            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &pos_levels, &spec_crd_levels());
557        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
558    }
559
560    // ── validate_index_buffers: invariant 4 — strictly increasing ────────────
561
562    #[test]
563    fn validate_rejects_duplicate_siblings() {
564        // crd_0 = [0, 0] — duplicate in the only parent slice.
565        let crd_levels: Vec<&[u64]> = vec![&[0, 0], CRD_1, CRD_2];
566        let result =
567            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &spec_pos_levels(), &crd_levels);
568        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
569    }
570
571    #[test]
572    fn validate_rejects_decreasing_siblings() {
573        // crd_0 = [1, 0] — decreasing within the only parent slice.
574        let crd_levels: Vec<&[u64]> = vec![&[1, 0], CRD_1, CRD_2];
575        let result =
576            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &spec_pos_levels(), &crd_levels);
577        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
578    }
579
580    // ── validate_index_buffers: invariant 5 — coordinate bounds ──────────────
581
582    #[test]
583    fn validate_rejects_out_of_bounds_coordinate() {
584        // crd_2[3] = 4 >= shape[mode_order[2]]=shape[2]=4 → out of range.
585        let crd_levels: Vec<&[u64]> = vec![CRD_0, CRD_1, &[1, 3, 0, 4]];
586        let result =
587            validate_index_buffers(4, MODE_ORDER, SHAPE_DIMS, &spec_pos_levels(), &crd_levels);
588        assert!(matches!(result, Err(crate::Error::InvalidLayout(_))));
589    }
590}