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}