fuel-memory 0.20.71

Unified storage abstraction for fuel: byte-shaped Storage owning bytes + dtype + backend memory region. Closed enum over CPU/CUDA/Vulkan/Metal variants. Backends provide kernels against these types; backends do not own their own storage type.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Born-red tests for the DLPack + FDX borrowed view (plan §2, gates T7–T9).
//!
//! All CPU — the only backend wired in this slice. Each test asserts the
//! field-by-field mapping (§2.2) and the sidecar-presence rules (§2.3), and runs
//! the FDX validator surface so the honesty invariant is mechanically checked.

use super::*;
use fuel_cpu_backend::CpuStorageBytes;
use fuel_ir::dlpack::abi::{device_type, dtype_code};
use fuel_ir::shape::Shape;
use fuel_ir::symbol::SymId;
use fuel_ir::{DType, Layout, SymEnv};

/// Canonical FNV-1a 64-bit known-answer vectors — **the same table** pinned by
/// `fuel_dispatch::fkc::revhash`'s `FNV1A64_KAT`.
///
/// §8 describes a "shared hash function with FDX"; in reality there are two
/// independent implementations (this one and revhash's) that agree only because
/// both happen to use the standard constants. Cross-checking them against each
/// OTHER would prove only mutual agreement — a refactor changing both
/// consistently (FNV-1a to FNV-1, a length prefix, a reseed) would keep them
/// equal while silently invalidating every bundle slot-name hash and persisted
/// plan key already on disk. Anchoring BOTH to the spec is what makes the
/// agreement load-bearing.
///
/// Kept as a literal duplicate rather than imported: fuel-memory does not (and
/// should not) depend on fuel-dispatch, and a shared constant would reintroduce
/// exactly the single-point-of-drift this guards against.
const FNV1A64_KAT: &[(&str, u64)] = &[
    ("", 0xcbf2_9ce4_8422_2325),
    ("a", 0xaf63_dc4c_8601_ec8c),
    ("foobar", 0x8594_4171_f739_67e8),
    ("fuel", 0x8644_7a78_daee_11cd),
    ("FNV-1a", 0xd498_8d18_eaff_5dc2),
    ("flash_attn_f32", 0x737f_6f3f_3150_8709),
];

/// The FDX slot-name hash must match the FNV-1a spec, not merely match its
/// sibling in fuel-dispatch.
///
/// `""` hashing to the offset basis is the built-in positive control — the one
/// input whose answer is structurally forced, so a table that got the basis
/// wrong cannot pass.
#[test]
fn fnv1a_matches_the_canonical_vectors() {
    for (input, want) in FNV1A64_KAT {
        let got = fnv1a(input);
        assert_eq!(
            got, *want,
            "FNV-1a(\"{input}\") = 0x{got:016x}, want 0x{want:016x} — the FDX slot-name hash drifted from the spec, which silently breaks every bundle name side-table already written"
        );
    }
}

/// GAP-075: `sub_byte_bits_and_packing` must DECLINE for a dtype it has no
/// authored sub-byte encoding for, rather than fabricate `(8, byte-aligned)`.
/// `bit_width` is an FDX wire value read across the process boundary, so a
/// fabricated width makes a foreign runtime stride over memory wrongly — and
/// nothing in-repo distinguishes a correct width from an undecided one, so no
/// downstream test could catch it. F32 stands in for "any dtype outside the
/// authored sub-byte set" (the future-sub-byte-dtype hazard: a new
/// `size_in_bytes() == 0` dtype would route here via `is_sub_byte`).
#[test]
fn sub_byte_bits_and_packing_declines_unauthored_dtype() {
    assert!(
        sub_byte_bits_and_packing(DType::F32).is_err(),
        "unauthored dtype must DECLINE, not emit an invented FDX bit-width",
    );
}

/// The authored sub-byte dtypes keep their spec §6.1 `(bit_width, packing)` —
/// a regression lock so the fallible conversion did not disturb the wire values
/// a foreign consumer already relies on.
#[test]
fn sub_byte_bits_and_packing_pins_authored_dtypes() {
    assert_eq!(
        sub_byte_bits_and_packing(DType::F4).unwrap(),
        (4, FDX_PACKING_DENSE_SUBBYTE),
    );
    assert_eq!(
        sub_byte_bits_and_packing(DType::F6E2M3).unwrap(),
        (6, FDX_PACKING_DENSE_SUBBYTE),
    );
    assert_eq!(
        sub_byte_bits_and_packing(DType::F6E3M2).unwrap(),
        (6, FDX_PACKING_DENSE_SUBBYTE),
    );
}

use crate::{BackendStorage, Storage};

/// Build a CPU F32 `Storage` holding `n` zeroed elements.
fn cpu_f32(n: usize) -> Storage {
    Storage::new(
        BackendStorage::Cpu(CpuStorageBytes::from_zero_bytes(n * 4)),
        DType::F32,
    )
}

/// Build a CPU `Storage` of `len_bytes` zeroed bytes tagged with `dtype`.
fn cpu_bytes(dtype: DType, len_bytes: usize) -> Storage {
    Storage::new(
        BackendStorage::Cpu(CpuStorageBytes::from_zero_bytes(len_bytes)),
        dtype,
    )
}

/// The CPU backend's base byte pointer for a `Storage` (the same pointer
/// `view()` puts in `dl.data`). Robust across feature sets.
fn cpu_base_ptr(storage: &Storage) -> usize {
    #[allow(irrefutable_let_patterns)]
    if let BackendStorage::Cpu(c) = &storage.inner {
        c.bytes().as_ptr() as usize
    } else {
        panic!("test storage must be CPU")
    }
}

// ── T7a: plain contiguous F32 → faithful dtype, sidecar None ────────────────

#[test]
fn plain_f32_contiguous_is_faithful_no_sidecar() {
    let storage = cpu_f32(3 * 4); // [3,4]
    let layout = Layout::contiguous(Shape::from_dims(&[3, 4]));
    let v = view(&storage, &layout, None).expect("view");

    // sidecar absent ⇒ plain DLPack (P2).
    assert!(v.sidecar.is_none(), "plain F32 must have no sidecar");

    // faithful dtype {kDLFloat, 32, 1}.
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_FLOAT);
    assert_eq!(v.dl.dtype.bits, 32);
    assert_eq!(v.dl.dtype.lanes, 1);

    // ndim/shape == layout.dims() (capacity).
    assert_eq!(v.ndim(), 2);
    assert_eq!(v.shape(), &[3, 4]);

    // row-major strides, explicit (never NULL).
    assert_eq!(v.strides(), &[4, 1]);

    // byte_offset == start_offset * size_in_bytes (0 here).
    assert_eq!(v.dl.byte_offset, 0);

    // dl_tensor() materializes valid shape/strides pointers.
    let dl = v.dl_tensor();
    assert!(!dl.shape.is_null());
    assert!(!dl.strides.is_null());
    assert_eq!(unsafe { core::slice::from_raw_parts(dl.shape, 2) }, &[3, 4]);
    assert_eq!(
        unsafe { core::slice::from_raw_parts(dl.strides, 2) },
        &[4, 1]
    );

    // validator surface is a no-op (and Ok) for sidecar-None.
    v.validate().expect("plain view validates");
}

#[test]
fn plain_f32_with_offset_byte_offset_scales() {
    let storage = cpu_f32(100);
    // start_offset 5 elements → 20 bytes.
    let layout = Layout::contiguous_with_offset(Shape::from_dims(&[4]), 5);
    let v = view(&storage, &layout, None).expect("view");
    assert_eq!(v.dl.byte_offset, 20);
    assert_eq!(v.strides(), &[1]);
}

// ── T7b: device pointer is the borrowed base, no offset folded ──────────────

#[test]
fn data_pointer_is_storage_base_and_cpu_device() {
    let storage = cpu_f32(8);
    let layout = Layout::contiguous_with_offset(Shape::from_dims(&[2]), 3);
    let v = view(&storage, &layout, None).expect("view");

    // dl.data is the base pointer (NOT offset-folded — the offset is in
    // byte_offset).
    let expected = cpu_base_ptr(&storage);
    assert_eq!(v.dl.data as usize, expected);
    assert_eq!(v.dl.byte_offset, 12); // 3 * 4
    assert_eq!(v.dl.device.device_type, device_type::K_DL_CPU); // kDLCPU == 1
    assert_eq!(v.dl.device.device_id, 0);
}

// ── Negative-stride (Op::Flip) view: strides pass through unchanged ─────────

#[test]
fn flipped_layout_carries_negative_stride_and_iteration_first_offset() {
    // [3,4] contiguous, flip dim 0 → stride [-4, 1], start_offset 8.
    let storage = cpu_f32(3 * 4);
    let base = Layout::contiguous(Shape::from_dims(&[3, 4]));
    let flipped = base.flip(0).expect("flip");
    assert_eq!(flipped.stride(), &[-4_isize, 1]);
    assert_eq!(flipped.start_offset(), 8); // (3-1)*4 — the Layout invariant.

    let v = view(&storage, &flipped, None).expect("view");
    // Negative stride passes through unchanged.
    assert_eq!(v.strides(), &[-4, 1]);
    // byte_offset is the (non-negative) iteration-first offset, in bytes.
    assert_eq!(v.dl.byte_offset, 8 * 4); // 8 elements * 4 bytes = 32

    // Still faithful F32, no sidecar (a flip is orthogonal to FDX meaning).
    assert!(v.sidecar.is_none());
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_FLOAT);
}

// ── T7c: sub-byte F4 → uint8 stand-in + HAS_DTYPE_EXT + bit_width ───────────

#[test]
fn sub_byte_f4_uses_uint8_standin_with_dtype_ext() {
    // F4: 2 elements per byte. Logical [8, 4096] → 8*4096/2 = 16384 bytes.
    let len_bytes = 8 * 4096 / 2;
    let storage = cpu_bytes(DType::F4, len_bytes);
    let layout = Layout::contiguous(Shape::from_dims(&[8, 4096]));
    let v = view(&storage, &layout, None).expect("view");

    // Base dtype is the {kDLUInt, 8, 1} honesty stand-in.
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_UINT);
    assert_eq!(v.dl.dtype.bits, 8);
    assert_eq!(v.dl.dtype.lanes, 1);

    // Base is the honest 1-D physical byte buffer.
    assert_eq!(v.ndim(), 1);
    assert_eq!(v.shape(), &[len_bytes as i64]);
    assert_eq!(v.strides(), &[1]);

    // Sidecar present with HAS_DTYPE_EXT + a non-zero bit_width.
    let sc = v.sidecar.as_ref().expect("sub-byte sidecar");
    assert_ne!(sc.flags & FDX_FLAG_HAS_DTYPE_EXT, 0);
    assert_ne!(sc.flags & FDX_FLAG_MEANING_REQUIRES_EXT, 0);
    assert_eq!(sc.dtype_ext.logical_dtype, FDX_DTYPE_F4);
    assert_eq!(sc.dtype_ext.bit_width, 4);
    assert_ne!(sc.dtype_ext.bit_width, 0);
    assert_eq!(sc.dtype_ext.packing, 1); // DENSE_SUBBYTE

    // Validator confirms the honesty invariant mechanically (V3 dtype, V4
    // sub-byte sizing, V11 strides, V13 range).
    v.validate().expect("sub-byte view validates");
}

#[test]
fn sub_byte_f6_has_six_bit_width() {
    let storage = cpu_bytes(DType::F6E2M3, 96);
    let layout = Layout::contiguous(Shape::from_dims(&[128]));
    let v = view(&storage, &layout, None).expect("view");
    let sc = v.sidecar.as_ref().expect("sidecar");
    assert_eq!(sc.dtype_ext.bit_width, 6);
    assert_eq!(sc.dtype_ext.logical_dtype, FDX_DTYPE_F6E2M3);
}

// ── Bool rides the base (§6.1.1): no sidecar when plain; reserved 0x0201 in a
//    namespace field when a sidecar exists for ANOTHER reason ─────────────────

/// A plain `Bool` tensor is faithfully named by the base descriptor (`kDLBool`)
/// and emits NO `FDXDTypeExt` at all — `Bool` is not sub-byte, so it can never
/// reach the dtype-ext branch. This is the view-level half of the `RidesBase`
/// disposition: a sidecar-blind consumer reads the base and is correct.
#[test]
fn plain_bool_rides_the_base_with_no_sidecar() {
    let storage = cpu_bytes(DType::Bool, 3 * 4); // [3,4] Bool = 12 bytes
    let layout = Layout::contiguous(Shape::from_dims(&[3, 4]));
    let v = view(&storage, &layout, None).expect("view");

    assert!(
        v.sidecar.is_none(),
        "a plain Bool tensor must emit no sidecar — it rides the base (§6.1.1)"
    );
    // The base descriptor is the honest kDLBool, not any FDX code.
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_BOOL);
    assert_eq!(v.dl.dtype.bits, 8);
    assert_eq!(v.dl.dtype.lanes, 1);
}

/// When a `Bool` tensor gets a sidecar for a DIFFERENT reason (here a symbolic
/// extent), the base still carries `kDLBool` and NO `FDXDTypeExt` is emitted for
/// the dtype — but the `FDXBufferRef.dtype` namespace field must still be filled,
/// and §6.1.1 reserves `0x0201` for exactly that. This is where `RidesBase`'s
/// payload is consumed; it is the ONLY place a base-riding code is written.
#[test]
fn bool_with_a_sidecar_writes_reserved_0x0201_and_no_dtype_ext() {
    let sym = SymId(7);
    // Full capacity 2*8*4 = 64 Bool elements = 64 bytes (matches symbolic_kv_layout).
    let storage = cpu_bytes(DType::Bool, 64);
    let layout = symbolic_kv_layout(sym);
    let v = view(&storage, &layout, None).expect("view");
    let sc = v
        .sidecar
        .as_ref()
        .expect("a symbolic Bool tensor emits a sidecar (for the symbolic reason)");

    // The sidecar exists for the SYMBOLIC reason, not a dtype reason: Bool carries
    // no FDXDTypeExt even here.
    assert_ne!(sc.flags & FDX_FLAG_HAS_SYMBOLIC, 0, "symbolic flag set");
    assert_eq!(
        sc.flags & FDX_FLAG_HAS_DTYPE_EXT,
        0,
        "Bool must not carry an FDXDTypeExt even when a sidecar exists for another reason"
    );

    // buffers[0] is the base data buffer; its namespace dtype is the reserved
    // 0x0201 (FDX_DTYPE_BOOL) — the sole place a base-riding code is ever written.
    let buffers = v.buffers();
    assert_eq!(buffers[0].role, FDX_BUFFER_ROLE_DATA);
    assert_eq!(
        buffers[0].dtype, 0x0201,
        "a base-riding Bool fills FDXBufferRef.dtype with the reserved namespace code 0x0201"
    );
}

// ── T8: symbolic layout → HAS_SYMBOLIC + extents; V14 in/out of range ───────

/// A KV-cache-style layout: `[n_heads=2, K_capacity=8, head_dim=4]` with the
/// middle axis bounded-symbolic `[min=1, max=8]` under `SymId(7)`.
fn symbolic_kv_layout(sym: SymId) -> Layout {
    let shape = Shape::from_dims(&[2, 8, 4]).with_dynamic_axis(1, 1, sym);
    Layout::contiguous(shape)
}

#[test]
fn symbolic_layout_transports_symbol_not_value() {
    let sym = SymId(7);
    // Allocate at full capacity (2*8*4 = 64 F32 elements) so V8 backing holds.
    let storage = cpu_f32(64);
    let layout = symbolic_kv_layout(sym);
    assert!(layout.has_dynamic());

    // No env: build-time only. Symbol transported, value not resolved.
    let v = view(&storage, &layout, None).expect("view");
    let sc = v.sidecar.as_ref().expect("symbolic sidecar");
    assert_ne!(sc.flags & FDX_FLAG_HAS_SYMBOLIC, 0);
    assert_eq!(sc.extents_count, 3);

    // Faithful F32 base (symbolic is orthogonal to dtype honesty).
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_FLOAT);
    // Capacity shape (max), strides keyed to capacity.
    assert_eq!(v.shape(), &[2, 8, 4]);
    assert_eq!(v.strides(), &[32, 4, 1]);

    // The middle extent carries the SymId and the [min, capacity] window — the
    // SYMBOL, never a resolved value (P4).
    let extents = v.sidecar.as_ref().unwrap();
    let ext = unsafe { &*extents.extents.add(1) };
    assert_eq!(ext.kind as u16, FDX_EXTENT_RANGE);
    assert_eq!(ext.sym_id, 7);
    assert_eq!(ext.min, 1);
    assert_eq!(ext.capacity, 8);

    // Build-time validate passes.
    v.validate().expect("symbolic build-time validates");
}

#[test]
fn symbolic_v14_passes_in_range_fails_out_of_range() {
    let sym = SymId(7);
    let storage = cpu_f32(64);
    let layout = symbolic_kv_layout(sym);

    // In-range: live = 5 ∈ [1, 8]. With a binding env, view() runs V14 and
    // succeeds.
    let mut env_ok = SymEnv::new();
    env_ok.bind(sym, 5).unwrap();
    let v_ok = view(&storage, &layout, Some(&env_ok));
    assert!(
        v_ok.is_ok(),
        "live=5 in [1,8] must pass V14: {:?}",
        v_ok.err()
    );

    // Out-of-range: live = 99 > capacity 8 ⇒ view() fails (V14 caught).
    let mut env_bad = SymEnv::new();
    env_bad.bind(sym, 99).unwrap();
    let v_bad = view(&storage, &layout, Some(&env_bad));
    assert!(v_bad.is_err(), "live=99 > capacity 8 must fail V14");
    let msg = format!("{}", v_bad.err().unwrap());
    assert!(
        msg.contains("validation failed") || msg.contains("out of range"),
        "error should mention V14 failure, got: {msg}"
    );
}

#[test]
fn symbolic_v14_does_not_bake_resolved_value() {
    // Even with a binding env, the sidecar extent keeps the SYMBOL and the
    // capacity window — the resolved value (5) is NOT written back (P4).
    let sym = SymId(7);
    let storage = cpu_f32(64);
    let layout = symbolic_kv_layout(sym);
    let mut env = SymEnv::new();
    env.bind(sym, 5).unwrap();
    let v = view(&storage, &layout, Some(&env)).expect("view");
    let ext = unsafe { &*v.sidecar.as_ref().unwrap().extents.add(1) };
    assert_eq!(ext.sym_id, 7, "symbol must survive");
    assert_eq!(ext.min, 1);
    assert_eq!(ext.capacity, 8, "capacity stays the max, not the live 5");
    assert_ne!(ext.capacity, 5);
}

// ── T9: bundled storage → IS_BUNDLE + views ────────────────────────────────

#[test]
fn bundled_storage_sets_is_bundle_and_views() {
    use fuel_ir::storage::{OutputViewSpec, compose_bundle};
    use std::sync::Arc;

    // 2-slot bundle: F32[2,3] (y) + F32[1] (extra) — primary dtype F32.
    let specs = vec![
        OutputViewSpec {
            dtype: DType::F32,
            shape: Shape::from_dims(&[2, 3]),
            layout: Layout::contiguous(Shape::from_dims(&[2, 3])),
            name: Some("y"),
        },
        OutputViewSpec {
            dtype: DType::F32,
            shape: Shape::from_dims(&[1]),
            layout: Layout::contiguous(Shape::from_dims(&[1])),
            name: Some("extra"),
        },
    ];
    let (total_bytes, views) = compose_bundle(&specs).expect("compose");
    let storage = Storage::new_bundled(
        BackendStorage::Cpu(CpuStorageBytes::from_zero_bytes(total_bytes)),
        DType::F32,
        Arc::from(views.into_boxed_slice()),
    )
    .expect("bundled storage");
    assert!(storage.is_bundled());

    // The view describes the whole bundle as a 1-D uint8 byte buffer.
    let layout = Layout::contiguous(Shape::from_dims(&[total_bytes]));
    let v = view(&storage, &layout, None).expect("view");

    let sc = v.sidecar.as_ref().expect("bundle sidecar");
    assert_ne!(sc.flags & FDX_FLAG_IS_BUNDLE, 0);
    assert_eq!(sc.views_count, 2);

    // Base is the honest uint8 buffer.
    assert_eq!(v.dl.dtype.code, dtype_code::K_DL_UINT);
    assert_eq!(v.dl.dtype.bits, 8);

    // The two FDXOutputViews mirror the slots.
    let fdx_views = unsafe { core::slice::from_raw_parts(sc.views, sc.views_count as usize) };
    assert_eq!(fdx_views[0].ndim, 2);
    assert_eq!(&fdx_views[0].shape[..2], &[2u64, 3]);
    assert_ne!(fdx_views[0].name_hash, 0); // "y" hashed
    assert_eq!(fdx_views[1].ndim, 1);
    assert_eq!(fdx_views[1].shape[0], 1);

    v.validate().expect("bundle view validates");
}

// ── RankExceeds6 ────────────────────────────────────────────────────────────

#[test]
fn rank_exceeds_6_is_typed_error() {
    let storage = cpu_f32(2 * 2 * 2 * 2 * 2 * 2 * 2);
    let layout = Layout::contiguous(Shape::from_dims(&[2, 2, 2, 2, 2, 2, 2])); // rank 7
    let err = view(&storage, &layout, None)
        .err()
        .expect("rank 7 must error");
    assert!(format!("{err}").contains("exceeds 6"));
}

// ── Quant sidecar from SType (step 3) ───────────────────────────────────────

/// A GGML-block SType projects to an inline-scale FDX GGML_BLOCK sidecar
/// (self-contained, no scale sibling) and validates end-to-end. The base
/// DLTensor is the honest uint8 byte buffer (V3: meaning-bearing ⇒ base uint8).
#[test]
fn ggml_block_stype_emits_inline_quant() {
    use fuel_ir::{Encoding, GgmlDType, SType};
    let storage = cpu_bytes(DType::F32, 18) // one Q4_0 block (18 bytes)
        .with_stype(SType::from_layer(Encoding::GgmlBlock {
            ggml_dtype: GgmlDType::Q4_0,
        }));
    let layout = Layout::contiguous(Shape::from_dims(&[18]));
    let v = view(&storage, &layout, None).expect("view");

    let sc = v.sidecar.as_ref().expect("GGML must emit a sidecar");
    assert_ne!(sc.flags & FDX_FLAG_HAS_QUANT, 0, "HAS_QUANT must be set");
    assert_eq!(sc.quant.family, FDX_QUANT_GGML_BLOCK);
    assert_eq!(sc.quant.scale_present, 0);
    assert_eq!(sc.quant.scale_placement, FDX_SCALE_PLACEMENT_INLINE);
    assert_eq!(sc.quant.scale_buffer, FDX_BUFFER_INLINE);

    // Honesty: the base is the {kDLUInt,8,1} byte stand-in (V3).
    assert_eq!(v.dl.dtype.code, fuel_ir::dlpack::abi::dtype_code::K_DL_UINT);
    assert_eq!(v.dl.dtype.bits, 8);

    v.validate()
        .expect("GGML_BLOCK sidecar must pass FDX validators");
}

/// An AFFINE_BLOCK (NF4) SType projects to FDX AFFINE_BLOCK with
/// scale_placement=SEPARATE_BUFFER and HAS_QUANT set. A bare `view()` (no op
/// context) emits the SCHEME with `scale_buffer = FDX_BUFFER_NONE` — the scale
/// operand is bound by the consuming op (next step), so the validator flags the
/// unbound scale buffer until then.
#[test]
fn affine_block_stype_emits_scheme_scale_unbound() {
    use fuel_ir::ScaleGranularity;
    use fuel_ir::{Encoding, SType, ScaleSpec};
    let storage = cpu_bytes(DType::F4, 64).with_stype(SType::from_layer(Encoding::AffineBlock {
        packed: DType::F4,
        block_shape: smallvec::smallvec![64],
        scale: ScaleSpec {
            dtype: DType::F32,
            granularity: ScaleGranularity::PerChannel,
        },
        zero_point: None,
    }));
    let layout = Layout::contiguous(Shape::from_dims(&[64]));
    let v = view(&storage, &layout, None).expect("view");

    let sc = v
        .sidecar
        .as_ref()
        .expect("AFFINE_BLOCK must emit a sidecar");
    assert_ne!(sc.flags & FDX_FLAG_HAS_QUANT, 0, "HAS_QUANT must be set");
    assert_eq!(sc.quant.family, FDX_QUANT_AFFINE_BLOCK);
    assert_eq!(sc.quant.scale_present, 1);
    assert_eq!(
        sc.quant.scale_placement,
        FDX_SCALE_PLACEMENT_SEPARATE_BUFFER
    );
    assert_eq!(
        sc.quant.scale_buffer, FDX_BUFFER_NONE,
        "scale unbound until op binds it"
    );
    assert_eq!(sc.quant.block_ndim, 1);
    assert_eq!(sc.quant.block_shape[0], 64);

    // The SCHEME is described, but the scale operand is not yet bound, so the
    // validator correctly reports the out-of-range scale buffer (the documented
    // step-4 boundary: the consuming op binds the sibling via view_with_quant).
    assert!(
        v.validate().is_err(),
        "unbound AFFINE scale must fail validation"
    );
}

/// view_with_quant binds the sibling absmax scale operand into the FDX buffer
/// table (role SCALE) and fills quant.scale_buffer, so an AFFINE_BLOCK weight
/// validates END-TO-END (the consuming-op binding the bare view() left unbound).
#[test]
fn affine_block_view_with_quant_validates() {
    use fuel_ir::ScaleGranularity;
    use fuel_ir::{Encoding, SType, ScaleSpec};
    // 64 packed bytes = 128 logical F4 elements; block 64 ⇒ 2 blocks ⇒ scale [2].
    let weight = cpu_bytes(DType::F4, 64).with_stype(SType::from_layer(Encoding::AffineBlock {
        packed: DType::F4,
        block_shape: smallvec::smallvec![64],
        scale: ScaleSpec {
            dtype: DType::F32,
            granularity: ScaleGranularity::PerChannel,
        },
        zero_point: None,
    }));
    let wlayout = Layout::contiguous(Shape::from_dims(&[128]));
    let scale = cpu_f32(2);
    let slayout = Layout::contiguous(Shape::from_dims(&[2]));

    let v = view_with_quant(&weight, &wlayout, None, (&scale, &slayout)).expect("view_with_quant");
    let sc = v.sidecar.as_ref().expect("AFFINE_BLOCK sidecar");
    assert_eq!(sc.quant.family, FDX_QUANT_AFFINE_BLOCK);
    assert_eq!(
        sc.quant.scale_buffer, 1,
        "scale operand bound at buffer index 1"
    );
    assert_eq!(sc.buffers_count, 2, "data buffer + scale buffer");

    // The scale operand is buffers[1], role SCALE, shape [2].
    let buffers = v.buffers();
    assert_eq!(buffers.len(), 2);
    assert_eq!(buffers[1].role, FDX_BUFFER_ROLE_SCALE);
    assert_eq!(
        buffers[1].dtype,
        fuel_ir::dlpack::convert::dtype_to_fdx(DType::F32)
            .unwrap()
            .namespace_code()
    );
    assert_eq!(buffers[1].shape[0], 2);

    v.validate()
        .expect("bound AFFINE_BLOCK must validate end-to-end");
}

/// view_with_quant on a non-separate-scale weight (plain or inline GGML) is a
/// no-op pass-through — there is no sibling scale to bind.
#[test]
fn view_with_quant_passthrough_when_no_scale_sibling() {
    use fuel_ir::{Encoding, GgmlDType, SType};
    let weight = cpu_bytes(DType::F32, 18).with_stype(SType::from_layer(Encoding::GgmlBlock {
        ggml_dtype: GgmlDType::Q4_0,
    }));
    let wlayout = Layout::contiguous(Shape::from_dims(&[18]));
    let scale = cpu_f32(1);
    let slayout = Layout::contiguous(Shape::from_dims(&[1]));
    let v = view_with_quant(&weight, &wlayout, None, (&scale, &slayout)).expect("view_with_quant");
    let sc = v.sidecar.as_ref().expect("GGML sidecar");
    // GGML is inline/self-contained: no scale buffer bound (still 1 buffer).
    assert_eq!(sc.buffers_count, 1);
    assert_eq!(sc.quant.scale_buffer, FDX_BUFFER_INLINE);
    v.validate().expect("GGML still validates");
}

/// A plain Storage still emits NO sidecar (byte-identical to pre-SType).
#[test]
fn plain_storage_still_no_quant_sidecar() {
    let storage = cpu_f32(12);
    let layout = Layout::contiguous(Shape::from_dims(&[3, 4]));
    let v = view(&storage, &layout, None).expect("view");
    assert!(v.sidecar.is_none(), "plain storage must stay sidecar-free");
}

// ── Finding 5.4 (FDX side): name-table side-car for bundle slot names ──────

#[test]
fn fdx_output_view_slot_name_is_recoverable_from_the_name_table() {
    use fuel_ir::{DType, Layout, Shape};
    let ov = OutputView {
        byte_offset: 0,
        len_elements: 6,
        dtype: DType::F32,
        shape: Shape::from_dims(&[2, 3]),
        layout: Layout::contiguous(Shape::from_dims(&[2, 3])),
        name: Some("last_state"),
    };
    let fdx = output_view_to_fdx(&ov).expect("rank 2 lowers");
    assert_eq!(fdx.name_hash, fnv1a("last_state"));
    assert_eq!(fdx_slot_name(fdx.name_hash), Some("last_state".to_string()));
    assert_eq!(fdx_slot_name(0), None);
}