Skip to main content

shape_vm/executor/v2_handlers/
v2_array_detect.rs

1//! Runtime detection and uniform access for v2 typed arrays.
2//!
3//! v2 typed arrays are heap-allocated `TypedArray<T>` instances, where the
4//! element type `T` is monomorphized at compile time. The bytecode compiler
5//! emits typed allocation/push opcodes (e.g. `NewTypedArrayF64`,
6//! `TypedArrayPushF64`) that create the right `TypedArray<T>` instantiation.
7//!
8//! However, generic consumer-side opcodes (`Length`, `GetProp`, `SetProp`,
9//! `IterNext`) and generic method dispatch (`.len()`, `.first()`, `.last()`,
10//! `.clone()`, `.sum()`, `.push()`, `.map()`, `.filter()`) only have a runtime
11//! `(bits, NativeKind)` pair to inspect — they need to recognize the v2 typed
12//! array pointer and dispatch to a typed implementation based on the element
13//! type.
14//!
15//! ## Element type encoding
16//!
17//! The compile-time element type is preserved at runtime by stamping the
18//! `_pad` byte (offset 7) of the `HeapHeader` with an `ElemType` discriminant.
19//! This piggybacks on existing layout — no struct change required.
20//!
21//! Allocation handlers in `array.rs` stamp the byte after allocating;
22//! consumer paths in this module read the byte to dispatch.
23//!
24//! ## ADR-006 §2.7.7 / Wave 6.5 cluster D-v2-array-detect
25//!
26//! API surface uses the kinded `(u64, NativeKind)` carrier shape. v2 typed
27//! array pointers flow through the VM stack as raw `*mut TypedArray<T>` bits
28//! tagged with `NativeKind::UInt64` (no Arc, no refcount — see
29//! `v2_handlers/array.rs`). Detection rejects any other kind. Element reads
30//! return the element's native bit pattern paired with the element's
31//! `NativeKind` (Float64 / Int64 / Int32 / Bool). Writes accept the same
32//! pair, decode bits per kind, and reject incompatible kinds.
33
34use shape_value::NativeKind;
35use shape_value::heap_value::TypedObjectStorage;
36use shape_value::v2::decimal_obj::DecimalObj;
37use shape_value::v2::heap_element::HeapElement;
38use shape_value::v2::heap_header::{HEAP_KIND_V2_TYPED_ARRAY, HeapHeader};
39use shape_value::v2::refcount::v2_retain;
40use shape_value::v2::string_obj::StringObj;
41use shape_value::v2::typed_array::TypedArray;
42use shape_value::HeapKind;
43
44// ── Element type discriminants ──────────────────────────────────────────────
45//
46// r5c-2-β-δ-(α): the canonical discriminant definitions moved to
47// `shape_value::v2::typed_array` so the kind-blind `release_v2_typed_array`
48// helper there (called by the four `Ptr(HeapKind::TypedArray)` lockstep
49// clone/drop tables, two of which live in the `shape-value` crate) can
50// dispatch on the stamped `_pad` byte without a cross-crate constant
51// duplication. The `pub use` below preserves every existing import path
52// (`v2_array_detect::ELEM_TYPE_*`) for `shape-vm` / `shape-jit` consumers.
53//
54// W12 S1 sized-integer discriminants; Wave 2 Agent A1 F32 + Char; Wave 2
55// Agent A2 String + Decimal; Phase 4b Round 4 W16.2-A TypedObject. ELEM_TYPE
56// discriminant 10 stays reserved for `Array<u64>` (deferred per the S1
57// reopen — Array<u64> excluded pending the §2.7.7/Q9 native-kind
58// discriminator).
59pub use shape_value::v2::typed_array::{
60    ELEM_TYPE_BOOL, ELEM_TYPE_CHAR, ELEM_TYPE_DECIMAL, ELEM_TYPE_F32, ELEM_TYPE_F64,
61    ELEM_TYPE_I16, ELEM_TYPE_I32, ELEM_TYPE_I64, ELEM_TYPE_I8, ELEM_TYPE_STRING,
62    ELEM_TYPE_TYPED_OBJECT, ELEM_TYPE_U16, ELEM_TYPE_U32, ELEM_TYPE_U8, ELEM_TYPE_UNKNOWN,
63};
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum V2ElemType {
67    F64,
68    I64,
69    I32,
70    Bool,
71    // W12 S1 — sized-integer monomorphizations.
72    I8,
73    U8,
74    I16,
75    U16,
76    U32,
77    // U64 omitted — deferred to S1.5 per S1 reopen.
78    // Wave 2 Agent A1 (2026-05-14) — F32 + Char scalar monomorphizations.
79    F32,
80    Char,
81    // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element monomorphizations
82    // per ADR-006 §2.7.24 Q25.A SUPERSEDED + audit §3.2 S2-prime. Each is a v2-raw
83    // heap-pointer carrier (`*const StringObj` / `*const DecimalObj`); element-read
84    // pushes the carrier pointer with `NativeKind::StringV2` / `NativeKind::DecimalV2`
85    // after per-element `v2_retain` of the header.
86    String,
87    Decimal,
88    // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18) —
89    // v2-raw heap-pointer carrier (`*const TypedObjectStorage`); element-read
90    // pushes the carrier pointer with `NativeKind::Ptr(HeapKind::TypedObject)`
91    // after per-element `v2_retain` of the header.
92    TypedObject,
93}
94
95impl V2ElemType {
96    #[inline]
97    pub fn from_byte(b: u8) -> Option<Self> {
98        match b {
99            ELEM_TYPE_F64 => Some(V2ElemType::F64),
100            ELEM_TYPE_I64 => Some(V2ElemType::I64),
101            ELEM_TYPE_I32 => Some(V2ElemType::I32),
102            ELEM_TYPE_BOOL => Some(V2ElemType::Bool),
103            ELEM_TYPE_I8 => Some(V2ElemType::I8),
104            ELEM_TYPE_U8 => Some(V2ElemType::U8),
105            ELEM_TYPE_I16 => Some(V2ElemType::I16),
106            ELEM_TYPE_U16 => Some(V2ElemType::U16),
107            ELEM_TYPE_U32 => Some(V2ElemType::U32),
108            // Tag byte 10 (ELEM_TYPE_U64) reserved for S1.5; not produced
109            // by any current allocation path.
110            ELEM_TYPE_F32 => Some(V2ElemType::F32),
111            ELEM_TYPE_CHAR => Some(V2ElemType::Char),
112            ELEM_TYPE_STRING => Some(V2ElemType::String),
113            ELEM_TYPE_DECIMAL => Some(V2ElemType::Decimal),
114            // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18).
115            ELEM_TYPE_TYPED_OBJECT => Some(V2ElemType::TypedObject),
116            _ => None,
117        }
118    }
119
120    /// Native kind of the array's elements (read result kind / write input
121    /// kind family).
122    #[inline]
123    pub fn elem_kind(self) -> NativeKind {
124        match self {
125            V2ElemType::F64 => NativeKind::Float64,
126            V2ElemType::I64 => NativeKind::Int64,
127            V2ElemType::I32 => NativeKind::Int32,
128            V2ElemType::Bool => NativeKind::Bool,
129            V2ElemType::I8 => NativeKind::Int8,
130            V2ElemType::U8 => NativeKind::UInt8,
131            V2ElemType::I16 => NativeKind::Int16,
132            V2ElemType::U16 => NativeKind::UInt16,
133            V2ElemType::U32 => NativeKind::UInt32,
134            V2ElemType::F32 => NativeKind::Float32,
135            V2ElemType::Char => NativeKind::Char,
136            V2ElemType::String => NativeKind::StringV2,
137            V2ElemType::Decimal => NativeKind::DecimalV2,
138            // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18) —
139            // element-read result carries the existing TypedObject pointer kind label.
140            V2ElemType::TypedObject => {
141                NativeKind::Ptr(shape_value::HeapKind::TypedObject)
142            }
143        }
144    }
145}
146
147// ── Detection ───────────────────────────────────────────────────────────────
148
149#[derive(Debug, Clone, Copy)]
150pub struct V2TypedArrayView {
151    pub ptr: *mut u8,
152    pub elem_type: V2ElemType,
153    pub len: u32,
154}
155
156/// Stamp the element type byte (`_pad` at offset 7 of the HeapHeader) on a
157/// freshly-allocated v2 typed array.
158#[inline]
159pub unsafe fn stamp_elem_type(ptr: *mut u8, elem_type: u8) {
160    if ptr.is_null() {
161        return;
162    }
163    unsafe {
164        let pad = ptr.add(7);
165        *pad = elem_type;
166    }
167}
168
169/// Read the element type byte from a v2 typed array's header.
170#[inline]
171unsafe fn read_elem_type_byte(ptr: *const u8) -> u8 {
172    if ptr.is_null() {
173        return ELEM_TYPE_UNKNOWN;
174    }
175    unsafe { *ptr.add(7) }
176}
177
178/// Try to interpret a `(bits, kind)` pair as a v2 typed array pointer.
179///
180/// v2 typed array pointers flow through the kinded API under a *single*
181/// carrier kind — `NativeKind::Ptr(HeapKind::TypedArray)` — holding the raw
182/// `*mut TypedArray<T>` pointer. This is the canonical carrier for every
183/// producer: the `NewTypedArray*` allocation opcodes
184/// (`v2_handlers/array.rs`), the `op_slice_access` / `op_array_push`
185/// re-stash sites (`objects/array_operations.rs`), the struct-field array
186/// read (`field_tag_to_native_kind` maps `FIELD_TAG_ARRAY` here) and the
187/// closure capture (`closure_layout.rs::native_kind_from_concrete_type`
188/// maps `ConcreteType::Array(_)` here). The slot's `clone_with_kind` /
189/// `drop_with_kind` arms route through `retain_v2_typed_array` /
190/// `release_v2_typed_array`. The on-header `HeapHeader.kind` check below
191/// confirms the pointee is a genuine `TypedArray<T>`.
192///
193/// Any other `kind` is rejected — crucially `NativeKind::UInt64` is NOT
194/// accepted. r5c-2-β-CKPT-C u64-carrier-disambiguation (2026-05-20): the
195/// pre-fix arm `NativeKind::UInt64 | Ptr(HeapKind::TypedArray)` overloaded
196/// the scalar-`u64` kind with the array-pointer carrier, so a genuine
197/// scalar `u64` value (e.g. `u64::MAX`) reaching this function was
198/// dereferenced as a `*const HeapHeader` → SIGSEGV (`let x: u64 = ...;
199/// print(x)`). The producers now stamp the array carrier with
200/// `Ptr(HeapKind::TypedArray)` exclusively, so the kind track itself is
201/// the discriminator: a `UInt64` slot is unambiguously a scalar and never
202/// reaches a pointer dereference here. NO value/low-address heuristic, NO
203/// `is_heap()` probe — the kind track separates the two carriers
204/// structurally (CLAUDE.md §"Parallel-implementation across
205/// producer/consumer carrier-shape boundaries").
206#[inline]
207pub fn as_v2_typed_array(bits: u64, kind: NativeKind) -> Option<V2TypedArrayView> {
208    if !matches!(kind, NativeKind::Ptr(HeapKind::TypedArray)) {
209        return None;
210    }
211    if bits == 0 {
212        return None;
213    }
214    let ptr = bits as usize as *mut u8;
215    let header = unsafe { &*(ptr as *const HeapHeader) };
216    if header.kind != HEAP_KIND_V2_TYPED_ARRAY {
217        return None;
218    }
219    let elem_byte = unsafe { read_elem_type_byte(ptr) };
220    let elem_type = V2ElemType::from_byte(elem_byte)?;
221    let arr_u8 = ptr as *const TypedArray<u8>;
222    let len = unsafe { (*arr_u8).len };
223    Some(V2TypedArrayView {
224        ptr,
225        elem_type,
226        len,
227    })
228}
229
230// ── Bit/kind decode helpers (call-site, ADR-006 §2.7.6) ─────────────────────
231
232/// Decode `(bits, kind)` to an `f64`. Accepts `Float64` directly and any
233/// integer-family kind (cast to f64). Returns `None` on incompatible kinds.
234#[inline]
235fn decode_f64(bits: u64, kind: NativeKind) -> Option<f64> {
236    if matches!(kind, NativeKind::Float64 | NativeKind::NullableFloat64) {
237        return Some(f64::from_bits(bits));
238    }
239    if kind.is_integer_family() {
240        return Some(decode_i64(bits, kind)? as f64);
241    }
242    None
243}
244
245/// Decode `(bits, kind)` to an `i64`. Accepts integer-family kinds with the
246/// proper sign-extension; also accepts `Float64` (truncate). Returns `None`
247/// on incompatible kinds.
248#[inline]
249fn decode_i64(bits: u64, kind: NativeKind) -> Option<i64> {
250    match kind {
251        NativeKind::Int64 | NativeKind::NullableInt64 => Some(bits as i64),
252        NativeKind::Int32 | NativeKind::NullableInt32 => Some(bits as u32 as i32 as i64),
253        NativeKind::Int16 | NativeKind::NullableInt16 => Some(bits as u16 as i16 as i64),
254        NativeKind::Int8 | NativeKind::NullableInt8 => Some(bits as u8 as i8 as i64),
255        NativeKind::IntSize | NativeKind::NullableIntSize => Some(bits as isize as i64),
256        NativeKind::UInt64 | NativeKind::NullableUInt64 => Some(bits as i64),
257        NativeKind::UInt32 | NativeKind::NullableUInt32 => Some(bits as u32 as i64),
258        NativeKind::UInt16 | NativeKind::NullableUInt16 => Some(bits as u16 as i64),
259        NativeKind::UInt8 | NativeKind::NullableUInt8 => Some(bits as u8 as i64),
260        NativeKind::UIntSize | NativeKind::NullableUIntSize => Some(bits as usize as i64),
261        NativeKind::Float64 | NativeKind::NullableFloat64 => Some(f64::from_bits(bits) as i64),
262        _ => None,
263    }
264}
265
266/// Decode `(bits, kind)` to a `bool`. Accepts only `NativeKind::Bool`.
267#[inline]
268fn decode_bool(bits: u64, kind: NativeKind) -> Option<bool> {
269    if matches!(kind, NativeKind::Bool) {
270        Some(bits != 0)
271    } else {
272        None
273    }
274}
275
276/// Decode `(bits, kind)` to an `f32`. Accepts `Float32` directly (low 32
277/// bits hold the f32 bit pattern), `Float64` (narrowed via cast), and any
278/// integer-family kind (cast to f32). Returns `None` on incompatible kinds.
279#[inline]
280fn decode_f32(bits: u64, kind: NativeKind) -> Option<f32> {
281    if matches!(kind, NativeKind::Float32) {
282        return Some(f32::from_bits(bits as u32));
283    }
284    if let Some(v) = decode_f64(bits, kind) {
285        return Some(v as f32);
286    }
287    None
288}
289
290/// Decode `(bits, kind)` to a `char`. Accepts `NativeKind::Char` directly
291/// (bits are the codepoint per `KindedSlot::from_char`); for integer kinds
292/// in the valid range (0..=0x10FFFF, excluding surrogates), produces the
293/// corresponding `char`. Returns `None` on out-of-range codepoints or
294/// incompatible kinds.
295#[inline]
296fn decode_char(bits: u64, kind: NativeKind) -> Option<char> {
297    if matches!(kind, NativeKind::Char) {
298        return char::from_u32(bits as u32);
299    }
300    if kind.is_integer_family() {
301        let cp = decode_i64(bits, kind)?;
302        if cp < 0 {
303            return None;
304        }
305        return char::from_u32(cp as u32);
306    }
307    None
308}
309
310/// Read element `index` from a v2 typed array, returning `(bits, NativeKind)`.
311///
312/// The `NativeKind` is the element kind (`Float64` / `Int64` / `Int32` /
313/// `Bool` / sized-integer kinds) — callers consume it directly without
314/// further inspection.
315#[inline]
316pub fn read_element(view: &V2TypedArrayView, index: u32) -> Option<(u64, NativeKind)> {
317    if index >= view.len {
318        return None;
319    }
320    let pair = match view.elem_type {
321        V2ElemType::F64 => unsafe {
322            let arr = view.ptr as *const TypedArray<f64>;
323            let v = TypedArray::<f64>::get_unchecked(arr, index);
324            (v.to_bits(), NativeKind::Float64)
325        },
326        V2ElemType::I64 => unsafe {
327            let arr = view.ptr as *const TypedArray<i64>;
328            let v = TypedArray::<i64>::get_unchecked(arr, index);
329            (v as u64, NativeKind::Int64)
330        },
331        V2ElemType::I32 => unsafe {
332            let arr = view.ptr as *const TypedArray<i32>;
333            let v = TypedArray::<i32>::get_unchecked(arr, index) as i64;
334            (v as u64, NativeKind::Int32)
335        },
336        V2ElemType::Bool => unsafe {
337            let arr = view.ptr as *const TypedArray<u8>;
338            let v = TypedArray::<u8>::get_unchecked(arr, index) != 0;
339            (v as u64, NativeKind::Bool)
340        },
341        // W12 S1 (2026-05-13) — sized-integer element reads.
342        V2ElemType::I8 => unsafe {
343            let arr = view.ptr as *const TypedArray<i8>;
344            let v = TypedArray::<i8>::get_unchecked(arr, index) as i64;
345            (v as u64, NativeKind::Int8)
346        },
347        V2ElemType::U8 => unsafe {
348            let arr = view.ptr as *const TypedArray<u8>;
349            let v = TypedArray::<u8>::get_unchecked(arr, index) as u64;
350            (v, NativeKind::UInt8)
351        },
352        V2ElemType::I16 => unsafe {
353            let arr = view.ptr as *const TypedArray<i16>;
354            let v = TypedArray::<i16>::get_unchecked(arr, index) as i64;
355            (v as u64, NativeKind::Int16)
356        },
357        V2ElemType::U16 => unsafe {
358            let arr = view.ptr as *const TypedArray<u16>;
359            let v = TypedArray::<u16>::get_unchecked(arr, index) as u64;
360            (v, NativeKind::UInt16)
361        },
362        V2ElemType::U32 => unsafe {
363            let arr = view.ptr as *const TypedArray<u32>;
364            let v = TypedArray::<u32>::get_unchecked(arr, index) as u64;
365            (v, NativeKind::UInt32)
366        },
367        // Wave 2 Agent A1 (2026-05-14) — F32 + Char element reads.
368        V2ElemType::F32 => unsafe {
369            let arr = view.ptr as *const TypedArray<f32>;
370            let v = TypedArray::<f32>::get_unchecked(arr, index);
371            (v.to_bits() as u64, NativeKind::Float32)
372        },
373        V2ElemType::Char => unsafe {
374            let arr = view.ptr as *const TypedArray<char>;
375            let v = TypedArray::<char>::get_unchecked(arr, index);
376            (v as u32 as u64, NativeKind::Char)
377        },
378        // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element reads.
379        // Per audit §4.1.B.4 migration recipe: retain the element header before
380        // pushing the slot bits — the array owns one share per stored pointer;
381        // the caller of read_element gets a fresh share that must be released
382        // when the slot is dropped (via NativeKind::StringV2 / DecimalV2 arm in
383        // `clone_with_kind` / `drop_with_kind` lockstep per Agent B Round 1).
384        V2ElemType::String => unsafe {
385            let arr = view.ptr as *const TypedArray<*const StringObj>;
386            let elem_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, index);
387            v2_retain(&(*elem_ptr).header);
388            (elem_ptr as u64, NativeKind::StringV2)
389        },
390        V2ElemType::Decimal => unsafe {
391            let arr = view.ptr as *const TypedArray<*const DecimalObj>;
392            let elem_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, index);
393            v2_retain(&(*elem_ptr).header);
394            (elem_ptr as u64, NativeKind::DecimalV2)
395        },
396        // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18) —
397        // mirror of the String/Decimal arms. The array owns one share per stored
398        // pointer (refcount discipline on the on-header `v2_retain`/`v2_release`
399        // counter; matches the existing single-TypedObject carrier in
400        // `vm_impl/stack.rs:115`). The caller of read_element gets a fresh share
401        // released by the matching `clone_with_kind` / `drop_with_kind`
402        // `NativeKind::Ptr(HeapKind::TypedObject)` arm.
403        V2ElemType::TypedObject => unsafe {
404            let arr = view.ptr as *const TypedArray<*const TypedObjectStorage>;
405            let elem_ptr =
406                TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, index);
407            v2_retain(&(*elem_ptr).header);
408            (elem_ptr as u64, NativeKind::Ptr(HeapKind::TypedObject))
409        },
410    };
411    Some(pair)
412}
413
414/// Write `(bits, kind)` to element `index` of a v2 typed array.
415#[inline]
416pub fn write_element(
417    view: &V2TypedArrayView,
418    index: u32,
419    bits: u64,
420    kind: NativeKind,
421) -> Result<(), &'static str> {
422    if index >= view.len {
423        return Err("index out of bounds");
424    }
425    match view.elem_type {
426        V2ElemType::F64 => {
427            let v = decode_f64(bits, kind).ok_or("expected f64-compatible value")?;
428            unsafe {
429                let arr = view.ptr as *mut TypedArray<f64>;
430                TypedArray::<f64>::set(arr, index, v);
431            }
432        }
433        V2ElemType::I64 => {
434            let v = decode_i64(bits, kind).ok_or("expected i64-compatible value")?;
435            unsafe {
436                let arr = view.ptr as *mut TypedArray<i64>;
437                TypedArray::<i64>::set(arr, index, v);
438            }
439        }
440        V2ElemType::I32 => {
441            let v = decode_i64(bits, kind).ok_or("expected i32-compatible value")?;
442            unsafe {
443                let arr = view.ptr as *mut TypedArray<i32>;
444                TypedArray::<i32>::set(arr, index, v as i32);
445            }
446        }
447        V2ElemType::Bool => {
448            let v = decode_bool(bits, kind).ok_or("expected bool value")?;
449            unsafe {
450                let arr = view.ptr as *mut TypedArray<u8>;
451                TypedArray::<u8>::set(arr, index, if v { 1 } else { 0 });
452            }
453        }
454        // W12 S1 (2026-05-13) — sized-integer element writes.
455        V2ElemType::I8 => {
456            let v = decode_i64(bits, kind).ok_or("expected i8-compatible value")?;
457            unsafe {
458                let arr = view.ptr as *mut TypedArray<i8>;
459                TypedArray::<i8>::set(arr, index, v as i8);
460            }
461        }
462        V2ElemType::U8 => {
463            let v = decode_i64(bits, kind).ok_or("expected u8-compatible value")?;
464            unsafe {
465                let arr = view.ptr as *mut TypedArray<u8>;
466                TypedArray::<u8>::set(arr, index, v as u8);
467            }
468        }
469        V2ElemType::I16 => {
470            let v = decode_i64(bits, kind).ok_or("expected i16-compatible value")?;
471            unsafe {
472                let arr = view.ptr as *mut TypedArray<i16>;
473                TypedArray::<i16>::set(arr, index, v as i16);
474            }
475        }
476        V2ElemType::U16 => {
477            let v = decode_i64(bits, kind).ok_or("expected u16-compatible value")?;
478            unsafe {
479                let arr = view.ptr as *mut TypedArray<u16>;
480                TypedArray::<u16>::set(arr, index, v as u16);
481            }
482        }
483        V2ElemType::U32 => {
484            let v = decode_i64(bits, kind).ok_or("expected u32-compatible value")?;
485            unsafe {
486                let arr = view.ptr as *mut TypedArray<u32>;
487                TypedArray::<u32>::set(arr, index, v as u32);
488            }
489        }
490        // Wave 2 Agent A1 (2026-05-14) — F32 + Char element writes.
491        V2ElemType::F32 => {
492            let v = decode_f32(bits, kind).ok_or("expected f32-compatible value")?;
493            unsafe {
494                let arr = view.ptr as *mut TypedArray<f32>;
495                TypedArray::<f32>::set(arr, index, v);
496            }
497        }
498        V2ElemType::Char => {
499            let v = decode_char(bits, kind).ok_or("expected char-compatible value")?;
500            unsafe {
501                let arr = view.ptr as *mut TypedArray<char>;
502                TypedArray::<char>::set(arr, index, v);
503            }
504        }
505        // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element writes.
506        // Per audit §4.1.B.4 migration recipe: release the prior element (the
507        // array's owned share), transfer the caller's share to the array. Kind
508        // mismatch refuses on sight — `NativeKind::String` (Phase-2c Arc<String>)
509        // is structurally NOT the same carrier as `NativeKind::StringV2`
510        // (v2-raw *const StringObj). No materialize-on-read fallback per
511        // §4.1.B.3 forbidden patterns. Per Q25.A SUPERSEDED #3 mixed-migration
512        // forbidden pattern, only StringV2 / DecimalV2 are accepted.
513        V2ElemType::String => {
514            if kind != NativeKind::StringV2 {
515                return Err("expected NativeKind::StringV2 for Array<string> write");
516            }
517            let new_ptr = bits as usize as *const StringObj;
518            unsafe {
519                let arr = view.ptr as *mut TypedArray<*const StringObj>;
520                let old_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, index);
521                <StringObj as HeapElement>::release_elem(old_ptr);
522                TypedArray::<*const StringObj>::set(arr, index, new_ptr);
523            }
524        }
525        V2ElemType::Decimal => {
526            if kind != NativeKind::DecimalV2 {
527                return Err("expected NativeKind::DecimalV2 for Array<decimal> write");
528            }
529            let new_ptr = bits as usize as *const DecimalObj;
530            unsafe {
531                let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
532                let old_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, index);
533                <DecimalObj as HeapElement>::release_elem(old_ptr);
534                TypedArray::<*const DecimalObj>::set(arr, index, new_ptr);
535            }
536        }
537        // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18) —
538        // mirror of the String/Decimal write arms. Kind discriminator strict:
539        // only `NativeKind::Ptr(HeapKind::TypedObject)` accepted (matches the
540        // single-TypedObject carrier label used elsewhere).
541        V2ElemType::TypedObject => {
542            if kind != NativeKind::Ptr(HeapKind::TypedObject) {
543                return Err(
544                    "expected NativeKind::Ptr(HeapKind::TypedObject) for Array<TypedObject> write",
545                );
546            }
547            let new_ptr = bits as usize as *const TypedObjectStorage;
548            unsafe {
549                let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
550                let old_ptr =
551                    TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, index);
552                <TypedObjectStorage as HeapElement>::release_elem(old_ptr);
553                TypedArray::<*const TypedObjectStorage>::set(arr, index, new_ptr);
554            }
555        }
556    }
557    Ok(())
558}
559
560/// Append `(bits, kind)` to a v2 typed array.
561#[inline]
562pub fn push_element(
563    view: &V2TypedArrayView,
564    bits: u64,
565    kind: NativeKind,
566) -> Result<(), &'static str> {
567    match view.elem_type {
568        V2ElemType::F64 => {
569            let v = decode_f64(bits, kind).ok_or("expected f64-compatible value")?;
570            unsafe {
571                let arr = view.ptr as *mut TypedArray<f64>;
572                TypedArray::<f64>::push(arr, v);
573            }
574        }
575        V2ElemType::I64 => {
576            let v = decode_i64(bits, kind).ok_or("expected i64-compatible value")?;
577            unsafe {
578                let arr = view.ptr as *mut TypedArray<i64>;
579                TypedArray::<i64>::push(arr, v);
580            }
581        }
582        V2ElemType::I32 => {
583            let v = decode_i64(bits, kind).ok_or("expected i32-compatible value")?;
584            unsafe {
585                let arr = view.ptr as *mut TypedArray<i32>;
586                TypedArray::<i32>::push(arr, v as i32);
587            }
588        }
589        V2ElemType::Bool => {
590            let v = decode_bool(bits, kind).ok_or("expected bool value")?;
591            unsafe {
592                let arr = view.ptr as *mut TypedArray<u8>;
593                TypedArray::<u8>::push(arr, if v { 1 } else { 0 });
594            }
595        }
596        // W12 S1 (2026-05-13) — sized-integer element pushes.
597        V2ElemType::I8 => {
598            let v = decode_i64(bits, kind).ok_or("expected i8-compatible value")?;
599            unsafe {
600                let arr = view.ptr as *mut TypedArray<i8>;
601                TypedArray::<i8>::push(arr, v as i8);
602            }
603        }
604        V2ElemType::U8 => {
605            let v = decode_i64(bits, kind).ok_or("expected u8-compatible value")?;
606            unsafe {
607                let arr = view.ptr as *mut TypedArray<u8>;
608                TypedArray::<u8>::push(arr, v as u8);
609            }
610        }
611        V2ElemType::I16 => {
612            let v = decode_i64(bits, kind).ok_or("expected i16-compatible value")?;
613            unsafe {
614                let arr = view.ptr as *mut TypedArray<i16>;
615                TypedArray::<i16>::push(arr, v as i16);
616            }
617        }
618        V2ElemType::U16 => {
619            let v = decode_i64(bits, kind).ok_or("expected u16-compatible value")?;
620            unsafe {
621                let arr = view.ptr as *mut TypedArray<u16>;
622                TypedArray::<u16>::push(arr, v as u16);
623            }
624        }
625        V2ElemType::U32 => {
626            let v = decode_i64(bits, kind).ok_or("expected u32-compatible value")?;
627            unsafe {
628                let arr = view.ptr as *mut TypedArray<u32>;
629                TypedArray::<u32>::push(arr, v as u32);
630            }
631        }
632        // Wave 2 Agent A1 (2026-05-14) — F32 + Char element pushes.
633        V2ElemType::F32 => {
634            let v = decode_f32(bits, kind).ok_or("expected f32-compatible value")?;
635            unsafe {
636                let arr = view.ptr as *mut TypedArray<f32>;
637                TypedArray::<f32>::push(arr, v);
638            }
639        }
640        V2ElemType::Char => {
641            let v = decode_char(bits, kind).ok_or("expected char-compatible value")?;
642            unsafe {
643                let arr = view.ptr as *mut TypedArray<char>;
644                TypedArray::<char>::push(arr, v);
645            }
646        }
647        // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element pushes.
648        // Caller's refcount share transfers to the array (the array stores one
649        // share per element; pop / drop_array_heap releases). Kind discriminator
650        // refuses any non-StringV2 / DecimalV2 input per §2.7.5 stamp-at-compile-
651        // time + Q25.A SUPERSEDED #3 mixed-migration forbidden pattern.
652        V2ElemType::String => {
653            if kind != NativeKind::StringV2 {
654                return Err("expected NativeKind::StringV2 for Array<string> push");
655            }
656            let new_ptr = bits as usize as *const StringObj;
657            unsafe {
658                let arr = view.ptr as *mut TypedArray<*const StringObj>;
659                TypedArray::<*const StringObj>::push(arr, new_ptr);
660            }
661        }
662        V2ElemType::Decimal => {
663            if kind != NativeKind::DecimalV2 {
664                return Err("expected NativeKind::DecimalV2 for Array<decimal> push");
665            }
666            let new_ptr = bits as usize as *const DecimalObj;
667            unsafe {
668                let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
669                TypedArray::<*const DecimalObj>::push(arr, new_ptr);
670            }
671        }
672        // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18).
673        V2ElemType::TypedObject => {
674            if kind != NativeKind::Ptr(HeapKind::TypedObject) {
675                return Err(
676                    "expected NativeKind::Ptr(HeapKind::TypedObject) for Array<TypedObject> push",
677                );
678            }
679            let new_ptr = bits as usize as *const TypedObjectStorage;
680            unsafe {
681                let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
682                TypedArray::<*const TypedObjectStorage>::push(arr, new_ptr);
683            }
684        }
685    }
686    Ok(())
687}
688
689/// Pop the last element from a v2 typed array, returning `(bits, NativeKind)`.
690#[inline]
691pub fn pop_element(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
692    match view.elem_type {
693        V2ElemType::F64 => unsafe {
694            let arr = view.ptr as *mut TypedArray<f64>;
695            TypedArray::<f64>::pop(arr).map(|v| (v.to_bits(), NativeKind::Float64))
696        },
697        V2ElemType::I64 => unsafe {
698            let arr = view.ptr as *mut TypedArray<i64>;
699            TypedArray::<i64>::pop(arr).map(|v| (v as u64, NativeKind::Int64))
700        },
701        V2ElemType::I32 => unsafe {
702            let arr = view.ptr as *mut TypedArray<i32>;
703            TypedArray::<i32>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int32))
704        },
705        V2ElemType::Bool => unsafe {
706            let arr = view.ptr as *mut TypedArray<u8>;
707            TypedArray::<u8>::pop(arr).map(|v| ((v != 0) as u64, NativeKind::Bool))
708        },
709        // W12 S1 (2026-05-13) — sized-integer element pops.
710        V2ElemType::I8 => unsafe {
711            let arr = view.ptr as *mut TypedArray<i8>;
712            TypedArray::<i8>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int8))
713        },
714        V2ElemType::U8 => unsafe {
715            let arr = view.ptr as *mut TypedArray<u8>;
716            TypedArray::<u8>::pop(arr).map(|v| (v as u64, NativeKind::UInt8))
717        },
718        V2ElemType::I16 => unsafe {
719            let arr = view.ptr as *mut TypedArray<i16>;
720            TypedArray::<i16>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int16))
721        },
722        V2ElemType::U16 => unsafe {
723            let arr = view.ptr as *mut TypedArray<u16>;
724            TypedArray::<u16>::pop(arr).map(|v| (v as u64, NativeKind::UInt16))
725        },
726        V2ElemType::U32 => unsafe {
727            let arr = view.ptr as *mut TypedArray<u32>;
728            TypedArray::<u32>::pop(arr).map(|v| (v as u64, NativeKind::UInt32))
729        },
730        // Wave 2 Agent A1 (2026-05-14) — F32 + Char element pops.
731        V2ElemType::F32 => unsafe {
732            let arr = view.ptr as *mut TypedArray<f32>;
733            TypedArray::<f32>::pop(arr).map(|v| (v.to_bits() as u64, NativeKind::Float32))
734        },
735        V2ElemType::Char => unsafe {
736            let arr = view.ptr as *mut TypedArray<char>;
737            TypedArray::<char>::pop(arr).map(|v| (v as u32 as u64, NativeKind::Char))
738        },
739        // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element pops.
740        // Transfer the array's owned share to the caller (the slot bits carry
741        // an owning share; caller is responsible for releasing via the
742        // StringV2 / DecimalV2 arm in drop_with_kind). No additional retain.
743        V2ElemType::String => unsafe {
744            let arr = view.ptr as *mut TypedArray<*const StringObj>;
745            TypedArray::<*const StringObj>::pop(arr).map(|v| (v as u64, NativeKind::StringV2))
746        },
747        V2ElemType::Decimal => unsafe {
748            let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
749            TypedArray::<*const DecimalObj>::pop(arr).map(|v| (v as u64, NativeKind::DecimalV2))
750        },
751        // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18).
752        // Transfer the array's owned share to the caller; release runs via the
753        // `clone_with_kind` / `drop_with_kind` `Ptr(HeapKind::TypedObject)` arm.
754        V2ElemType::TypedObject => unsafe {
755            let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
756            TypedArray::<*const TypedObjectStorage>::pop(arr)
757                .map(|v| (v as u64, NativeKind::Ptr(HeapKind::TypedObject)))
758        },
759    }
760}
761
762/// Sum all elements of a numeric (F64/I64/I32) v2 typed array.
763///
764/// F64 and I64 variants use `wide::f64x4`/`wide::i64x4` SIMD reduction on
765/// arrays with >= `SIMD_SUM_THRESHOLD` elements, delivering ~4x throughput
766/// on AVX2-capable CPUs. Smaller arrays fall back to scalar accumulation
767/// where the SIMD setup overhead would exceed the savings.
768///
769/// Returns `(bits, NativeKind::Float64)` for F64 inputs and
770/// `(bits, NativeKind::Int64)` for integer inputs. `None` for Bool inputs.
771pub fn sum_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
772    /// Minimum element count at which SIMD reduction beats scalar accumulation.
773    /// Determined empirically — below this, vector load/splat overhead dominates.
774    const SIMD_SUM_THRESHOLD: u32 = 16;
775
776    match view.elem_type {
777        V2ElemType::F64 => {
778            let len = view.len;
779            if len == 0 {
780                return Some((0.0_f64.to_bits(), NativeKind::Float64));
781            }
782            let data = unsafe {
783                let arr = view.ptr as *const TypedArray<f64>;
784                (*arr).data as *const f64
785            };
786            let s = unsafe { simd_sum_f64(data, len as usize, SIMD_SUM_THRESHOLD as usize) };
787            Some((s.to_bits(), NativeKind::Float64))
788        }
789        V2ElemType::I64 => {
790            let len = view.len;
791            if len == 0 {
792                return Some((0u64, NativeKind::Int64));
793            }
794            let data = unsafe {
795                let arr = view.ptr as *const TypedArray<i64>;
796                (*arr).data as *const i64
797            };
798            let s = unsafe { simd_sum_i64(data, len as usize, SIMD_SUM_THRESHOLD as usize) };
799            Some((s as u64, NativeKind::Int64))
800        }
801        V2ElemType::I32 => {
802            let mut s: i64 = 0;
803            for i in 0..view.len {
804                let val = unsafe {
805                    let arr = view.ptr as *const TypedArray<i32>;
806                    TypedArray::<i32>::get_unchecked(arr, i) as i64
807                };
808                s = s.wrapping_add(val);
809            }
810            Some((s as u64, NativeKind::Int64))
811        }
812        // W12 S1 — sum/avg/min/max/variance/std/dot/norm not defined for
813        // Bool or sized-integer-narrower-than-i64 element kinds. The
814        // caller falls back to a non-SIMD path or returns an error.
815        // Wave 2 Agent A1 — F32 / Char also fall through; F32 reductions
816        // are domain-deferred to a follow-up SIMD lane sub-cluster.
817        V2ElemType::Bool
818        | V2ElemType::I8
819        | V2ElemType::U8
820        | V2ElemType::I16
821        | V2ElemType::U16
822        | V2ElemType::U32
823        | V2ElemType::F32
824        | V2ElemType::Char
825        // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element variants
826        // have no numeric sum semantics; concat for String is a method-level
827        // operation, not a sum reduction.
828        | V2ElemType::String
829        | V2ElemType::Decimal
830        | V2ElemType::TypedObject => None,
831    }
832}
833
834/// SIMD-accelerated f64 sum using `wide::f64x4` lanes.
835///
836/// # Safety
837/// `data` must point to at least `len` valid, contiguous `f64` values.
838#[inline]
839unsafe fn simd_sum_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
840    use wide::f64x4;
841
842    if len < threshold {
843        let mut s = 0.0_f64;
844        for i in 0..len {
845            s += unsafe { *data.add(i) };
846        }
847        return s;
848    }
849
850    let chunks = len / 4;
851    let mut acc = f64x4::splat(0.0);
852    for i in 0..chunks {
853        let base = i * 4;
854        let v = unsafe {
855            f64x4::from([
856                *data.add(base),
857                *data.add(base + 1),
858                *data.add(base + 2),
859                *data.add(base + 3),
860            ])
861        };
862        acc += v;
863    }
864    let parts = acc.to_array();
865    let mut s = parts[0] + parts[1] + parts[2] + parts[3];
866    for i in (chunks * 4)..len {
867        s += unsafe { *data.add(i) };
868    }
869    s
870}
871
872/// Scan a f64 buffer for any NaN. Used to short-circuit min/max where
873/// hardware `min_pd`/`max_pd` don't reliably propagate NaN.
874///
875/// # Safety
876/// `data` must point to at least `len` valid `f64` values.
877#[inline]
878unsafe fn contains_nan_f64(data: *const f64, len: usize) -> bool {
879    for i in 0..len {
880        if unsafe { *data.add(i) }.is_nan() {
881            return true;
882        }
883    }
884    false
885}
886
887/// SIMD-accelerated f64 minimum using `wide::f64x4::fast_min`. Falls back to
888/// a scalar loop below the threshold. Requires `len > 0`.
889///
890/// Hardware `min_pd` returns the non-NaN operand rather than propagating
891/// NaN, so we scan for NaN up front to match scalar `f64::min` semantics.
892///
893/// # Safety
894/// `data` must point to at least `len` valid, contiguous `f64` values and
895/// `len` must be at least 1.
896#[inline]
897unsafe fn simd_min_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
898    use wide::f64x4;
899    debug_assert!(len > 0);
900    if unsafe { contains_nan_f64(data, len) } {
901        return f64::NAN;
902    }
903    if len < threshold {
904        let mut m = unsafe { *data };
905        for i in 1..len {
906            let v = unsafe { *data.add(i) };
907            if v < m {
908                m = v;
909            }
910        }
911        return m;
912    }
913    let chunks = len / 4;
914    let mut acc = unsafe {
915        f64x4::from([
916            *data,
917            *data.add(1),
918            *data.add(2),
919            *data.add(3),
920        ])
921    };
922    for i in 1..chunks {
923        let base = i * 4;
924        let v = unsafe {
925            f64x4::from([
926                *data.add(base),
927                *data.add(base + 1),
928                *data.add(base + 2),
929                *data.add(base + 3),
930            ])
931        };
932        acc = acc.fast_min(v);
933    }
934    let parts = acc.to_array();
935    let mut m = parts[0];
936    for &p in &parts[1..] {
937        if p < m {
938            m = p;
939        }
940    }
941    for i in (chunks * 4)..len {
942        let v = unsafe { *data.add(i) };
943        if v < m {
944            m = v;
945        }
946    }
947    m
948}
949
950/// SIMD-accelerated f64 maximum. Mirrors [`simd_min_f64`].
951///
952/// # Safety
953/// See [`simd_min_f64`].
954#[inline]
955unsafe fn simd_max_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
956    use wide::f64x4;
957    debug_assert!(len > 0);
958    if unsafe { contains_nan_f64(data, len) } {
959        return f64::NAN;
960    }
961    if len < threshold {
962        let mut m = unsafe { *data };
963        for i in 1..len {
964            let v = unsafe { *data.add(i) };
965            if v > m {
966                m = v;
967            }
968        }
969        return m;
970    }
971    let chunks = len / 4;
972    let mut acc = unsafe {
973        f64x4::from([
974            *data,
975            *data.add(1),
976            *data.add(2),
977            *data.add(3),
978        ])
979    };
980    for i in 1..chunks {
981        let base = i * 4;
982        let v = unsafe {
983            f64x4::from([
984                *data.add(base),
985                *data.add(base + 1),
986                *data.add(base + 2),
987                *data.add(base + 3),
988            ])
989        };
990        acc = acc.fast_max(v);
991    }
992    let parts = acc.to_array();
993    let mut m = parts[0];
994    for &p in &parts[1..] {
995        if p > m {
996            m = p;
997        }
998    }
999    for i in (chunks * 4)..len {
1000        let v = unsafe { *data.add(i) };
1001        if v > m {
1002            m = v;
1003        }
1004    }
1005    m
1006}
1007
1008/// SIMD-accelerated i64 sum using `wide::i64x4` lanes.
1009///
1010/// Uses `wrapping_add` semantics at the lane level (Shape's int sum on Vec<int>
1011/// never panics on overflow for the v2 path — matches scalar `wrapping_add`).
1012///
1013/// # Safety
1014/// `data` must point to at least `len` valid, contiguous `i64` values.
1015#[inline]
1016unsafe fn simd_sum_i64(data: *const i64, len: usize, threshold: usize) -> i64 {
1017    use wide::i64x4;
1018
1019    if len < threshold {
1020        let mut s: i64 = 0;
1021        for i in 0..len {
1022            s = s.wrapping_add(unsafe { *data.add(i) });
1023        }
1024        return s;
1025    }
1026
1027    let chunks = len / 4;
1028    let mut acc = i64x4::splat(0);
1029    for i in 0..chunks {
1030        let base = i * 4;
1031        let v = unsafe {
1032            i64x4::from([
1033                *data.add(base),
1034                *data.add(base + 1),
1035                *data.add(base + 2),
1036                *data.add(base + 3),
1037            ])
1038        };
1039        // wide::i64x4 uses wrapping add on overflow. It does not implement
1040        // AddAssign, so reassign via the binary + operator.
1041        acc = acc + v;
1042    }
1043    let parts = acc.to_array();
1044    let mut s = parts[0]
1045        .wrapping_add(parts[1])
1046        .wrapping_add(parts[2])
1047        .wrapping_add(parts[3]);
1048    for i in (chunks * 4)..len {
1049        s = s.wrapping_add(unsafe { *data.add(i) });
1050    }
1051    s
1052}
1053
1054/// Compute the average (mean) of all elements of a numeric v2 typed array.
1055/// Returns NaN for empty arrays. Returns `(bits, NativeKind::Float64)` always
1056/// (mean of integer arrays is a float).
1057pub fn avg_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1058    if view.len == 0 {
1059        return match view.elem_type {
1060            V2ElemType::F64 | V2ElemType::I64 | V2ElemType::I32 => {
1061                Some((f64::NAN.to_bits(), NativeKind::Float64))
1062            }
1063            // W12 S1 — sized-integer narrow kinds and Bool don't have an
1064            // empty-array mean sentinel at this layer; caller surfaces None.
1065            V2ElemType::Bool
1066            | V2ElemType::I8
1067            | V2ElemType::U8
1068            | V2ElemType::I16
1069            | V2ElemType::U16
1070            | V2ElemType::U32
1071            | V2ElemType::F32
1072            | V2ElemType::Char
1073            | V2ElemType::String
1074            | V2ElemType::Decimal
1075            | V2ElemType::TypedObject => None,
1076        };
1077    }
1078    match view.elem_type {
1079        V2ElemType::F64 => {
1080            // Reuse the SIMD sum path; below threshold it runs the scalar
1081            // fallback internally so small arrays still see the simple loop.
1082            let data = unsafe {
1083                let arr = view.ptr as *const TypedArray<f64>;
1084                (*arr).data as *const f64
1085            };
1086            let s = unsafe { simd_sum_f64(data, view.len as usize, 16) };
1087            Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
1088        }
1089        V2ElemType::I64 => {
1090            let mut s = 0.0_f64;
1091            for i in 0..view.len {
1092                s += unsafe {
1093                    let arr = view.ptr as *const TypedArray<i64>;
1094                    TypedArray::<i64>::get_unchecked(arr, i) as f64
1095                };
1096            }
1097            Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
1098        }
1099        V2ElemType::I32 => {
1100            let mut s = 0.0_f64;
1101            for i in 0..view.len {
1102                s += unsafe {
1103                    let arr = view.ptr as *const TypedArray<i32>;
1104                    TypedArray::<i32>::get_unchecked(arr, i) as f64
1105                };
1106            }
1107            Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
1108        }
1109        // W12 S1 — sum/avg/min/max/variance/std/dot/norm not defined for
1110        // Bool or sized-integer-narrower-than-i64 element kinds. The
1111        // caller falls back to a non-SIMD path or returns an error.
1112        // Wave 2 Agent A1 — F32 / Char also fall through; F32 reductions
1113        // are domain-deferred to a follow-up SIMD lane sub-cluster.
1114        V2ElemType::Bool
1115        | V2ElemType::I8
1116        | V2ElemType::U8
1117        | V2ElemType::I16
1118        | V2ElemType::U16
1119        | V2ElemType::U32
1120        | V2ElemType::F32
1121        | V2ElemType::Char
1122        | V2ElemType::String
1123        | V2ElemType::Decimal
1124        | V2ElemType::TypedObject => None,
1125    }
1126}
1127
1128/// Compute the minimum element of a numeric v2 typed array.
1129///
1130/// Empty arrays return:
1131///   - F64 input → `(NaN.to_bits(), Float64)`
1132///   - I64/I32 input → `(0, Bool)` (the §2.7 null/unit sentinel)
1133///   - Bool input → `None`
1134pub fn min_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1135    if view.len == 0 {
1136        return match view.elem_type {
1137            V2ElemType::F64 => Some((f64::NAN.to_bits(), NativeKind::Float64)),
1138            V2ElemType::I64 | V2ElemType::I32 => Some((0u64, NativeKind::Bool)),
1139            // W12 S1 — narrow-int and Bool element kinds have no canonical
1140            // empty-array sentinel for min/max; caller treats None as a
1141            // runtime error per §2.7 sentinel discipline.
1142            V2ElemType::Bool
1143            | V2ElemType::I8
1144            | V2ElemType::U8
1145            | V2ElemType::I16
1146            | V2ElemType::U16
1147            | V2ElemType::U32
1148            | V2ElemType::F32
1149            | V2ElemType::Char
1150            | V2ElemType::String
1151            | V2ElemType::Decimal
1152            | V2ElemType::TypedObject => None,
1153        };
1154    }
1155    match view.elem_type {
1156        V2ElemType::F64 => {
1157            let data = unsafe {
1158                let arr = view.ptr as *const TypedArray<f64>;
1159                (*arr).data as *const f64
1160            };
1161            let min = unsafe { simd_min_f64(data, view.len as usize, 16) };
1162            Some((min.to_bits(), NativeKind::Float64))
1163        }
1164        V2ElemType::I64 => {
1165            let mut min = i64::MAX;
1166            for i in 0..view.len {
1167                let v = unsafe {
1168                    let arr = view.ptr as *const TypedArray<i64>;
1169                    TypedArray::<i64>::get_unchecked(arr, i)
1170                };
1171                if v < min {
1172                    min = v;
1173                }
1174            }
1175            Some((min as u64, NativeKind::Int64))
1176        }
1177        V2ElemType::I32 => {
1178            let mut min = i32::MAX as i64;
1179            for i in 0..view.len {
1180                let v = unsafe {
1181                    let arr = view.ptr as *const TypedArray<i32>;
1182                    TypedArray::<i32>::get_unchecked(arr, i) as i64
1183                };
1184                if v < min {
1185                    min = v;
1186                }
1187            }
1188            Some((min as u64, NativeKind::Int64))
1189        }
1190        // W12 S1 — sum/avg/min/max/variance/std/dot/norm not defined for
1191        // Bool or sized-integer-narrower-than-i64 element kinds. The
1192        // caller falls back to a non-SIMD path or returns an error.
1193        // Wave 2 Agent A1 — F32 / Char also fall through; F32 reductions
1194        // are domain-deferred to a follow-up SIMD lane sub-cluster.
1195        V2ElemType::Bool
1196        | V2ElemType::I8
1197        | V2ElemType::U8
1198        | V2ElemType::I16
1199        | V2ElemType::U16
1200        | V2ElemType::U32
1201        | V2ElemType::F32
1202        | V2ElemType::Char
1203        | V2ElemType::String
1204        | V2ElemType::Decimal
1205        | V2ElemType::TypedObject => None,
1206    }
1207}
1208
1209/// Compute the maximum element of a numeric v2 typed array.
1210pub fn max_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1211    if view.len == 0 {
1212        return match view.elem_type {
1213            V2ElemType::F64 => Some((f64::NAN.to_bits(), NativeKind::Float64)),
1214            V2ElemType::I64 | V2ElemType::I32 => Some((0u64, NativeKind::Bool)),
1215            // W12 S1 — narrow-int and Bool element kinds have no canonical
1216            // empty-array sentinel for min/max; caller treats None as a
1217            // runtime error per §2.7 sentinel discipline.
1218            V2ElemType::Bool
1219            | V2ElemType::I8
1220            | V2ElemType::U8
1221            | V2ElemType::I16
1222            | V2ElemType::U16
1223            | V2ElemType::U32
1224            | V2ElemType::F32
1225            | V2ElemType::Char
1226            | V2ElemType::String
1227            | V2ElemType::Decimal
1228            | V2ElemType::TypedObject => None,
1229        };
1230    }
1231    match view.elem_type {
1232        V2ElemType::F64 => {
1233            let data = unsafe {
1234                let arr = view.ptr as *const TypedArray<f64>;
1235                (*arr).data as *const f64
1236            };
1237            let max = unsafe { simd_max_f64(data, view.len as usize, 16) };
1238            Some((max.to_bits(), NativeKind::Float64))
1239        }
1240        V2ElemType::I64 => {
1241            let mut max = i64::MIN;
1242            for i in 0..view.len {
1243                let v = unsafe {
1244                    let arr = view.ptr as *const TypedArray<i64>;
1245                    TypedArray::<i64>::get_unchecked(arr, i)
1246                };
1247                if v > max {
1248                    max = v;
1249                }
1250            }
1251            Some((max as u64, NativeKind::Int64))
1252        }
1253        V2ElemType::I32 => {
1254            let mut max = i32::MIN as i64;
1255            for i in 0..view.len {
1256                let v = unsafe {
1257                    let arr = view.ptr as *const TypedArray<i32>;
1258                    TypedArray::<i32>::get_unchecked(arr, i) as i64
1259                };
1260                if v > max {
1261                    max = v;
1262                }
1263            }
1264            Some((max as u64, NativeKind::Int64))
1265        }
1266        // W12 S1 — sum/avg/min/max/variance/std/dot/norm not defined for
1267        // Bool or sized-integer-narrower-than-i64 element kinds. The
1268        // caller falls back to a non-SIMD path or returns an error.
1269        // Wave 2 Agent A1 — F32 / Char also fall through; F32 reductions
1270        // are domain-deferred to a follow-up SIMD lane sub-cluster.
1271        V2ElemType::Bool
1272        | V2ElemType::I8
1273        | V2ElemType::U8
1274        | V2ElemType::I16
1275        | V2ElemType::U16
1276        | V2ElemType::U32
1277        | V2ElemType::F32
1278        | V2ElemType::Char
1279        | V2ElemType::String
1280        | V2ElemType::Decimal
1281        | V2ElemType::TypedObject => None,
1282    }
1283}
1284
1285/// Compute the sample variance of a float v2 typed array.
1286/// Returns NaN for arrays with fewer than 2 elements. Always returns Float64.
1287pub fn variance_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1288    match view.elem_type {
1289        V2ElemType::F64 => {
1290            if view.len < 2 {
1291                return Some((f64::NAN.to_bits(), NativeKind::Float64));
1292            }
1293            let n = view.len as f64;
1294            let mut sum = 0.0_f64;
1295            for i in 0..view.len {
1296                sum += unsafe {
1297                    let arr = view.ptr as *const TypedArray<f64>;
1298                    TypedArray::<f64>::get_unchecked(arr, i)
1299                };
1300            }
1301            let mean = sum / n;
1302            let mut var_sum = 0.0_f64;
1303            for i in 0..view.len {
1304                let v = unsafe {
1305                    let arr = view.ptr as *const TypedArray<f64>;
1306                    TypedArray::<f64>::get_unchecked(arr, i)
1307                };
1308                let d = v - mean;
1309                var_sum += d * d;
1310            }
1311            Some(((var_sum / (n - 1.0)).to_bits(), NativeKind::Float64))
1312        }
1313        _ => None,
1314    }
1315}
1316
1317/// Compute the sample standard deviation of a float v2 typed array.
1318pub fn std_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1319    variance_elements(view).map(|(bits, _kind)| {
1320        let v = f64::from_bits(bits);
1321        (v.sqrt().to_bits(), NativeKind::Float64)
1322    })
1323}
1324
1325/// Compute the dot product of two float v2 typed arrays.
1326pub fn dot_elements(
1327    view_a: &V2TypedArrayView,
1328    view_b: &V2TypedArrayView,
1329) -> Option<(u64, NativeKind)> {
1330    if view_a.elem_type != V2ElemType::F64 || view_b.elem_type != V2ElemType::F64 {
1331        return None;
1332    }
1333    if view_a.len != view_b.len {
1334        return None; // caller should produce an error
1335    }
1336    let mut sum = 0.0_f64;
1337    for i in 0..view_a.len {
1338        let a = unsafe {
1339            let arr = view_a.ptr as *const TypedArray<f64>;
1340            TypedArray::<f64>::get_unchecked(arr, i)
1341        };
1342        let b = unsafe {
1343            let arr = view_b.ptr as *const TypedArray<f64>;
1344            TypedArray::<f64>::get_unchecked(arr, i)
1345        };
1346        sum += a * b;
1347    }
1348    Some((sum.to_bits(), NativeKind::Float64))
1349}
1350
1351/// Compute the Euclidean norm of a float v2 typed array.
1352pub fn norm_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1353    match view.elem_type {
1354        V2ElemType::F64 => {
1355            let mut sum_sq = 0.0_f64;
1356            for i in 0..view.len {
1357                let v = unsafe {
1358                    let arr = view.ptr as *const TypedArray<f64>;
1359                    TypedArray::<f64>::get_unchecked(arr, i)
1360                };
1361                sum_sq += v * v;
1362            }
1363            Some((sum_sq.sqrt().to_bits(), NativeKind::Float64))
1364        }
1365        _ => None,
1366    }
1367}
1368
1369/// Count `true` values in a bool v2 typed array. Returns `(count, Int64)`.
1370pub fn count_true_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1371    match view.elem_type {
1372        V2ElemType::Bool => {
1373            let mut count = 0_i64;
1374            for i in 0..view.len {
1375                let v = unsafe {
1376                    let arr = view.ptr as *const TypedArray<u8>;
1377                    TypedArray::<u8>::get_unchecked(arr, i)
1378                };
1379                if v != 0 {
1380                    count += 1;
1381                }
1382            }
1383            Some((count as u64, NativeKind::Int64))
1384        }
1385        _ => None,
1386    }
1387}
1388
1389/// Check if any element in a bool v2 typed array is true.
1390pub fn any_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1391    match view.elem_type {
1392        V2ElemType::Bool => {
1393            for i in 0..view.len {
1394                let v = unsafe {
1395                    let arr = view.ptr as *const TypedArray<u8>;
1396                    TypedArray::<u8>::get_unchecked(arr, i)
1397                };
1398                if v != 0 {
1399                    return Some((1u64, NativeKind::Bool));
1400                }
1401            }
1402            Some((0u64, NativeKind::Bool))
1403        }
1404        _ => None,
1405    }
1406}
1407
1408/// Check if all elements in a bool v2 typed array are true.
1409pub fn all_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
1410    match view.elem_type {
1411        V2ElemType::Bool => {
1412            for i in 0..view.len {
1413                let v = unsafe {
1414                    let arr = view.ptr as *const TypedArray<u8>;
1415                    TypedArray::<u8>::get_unchecked(arr, i)
1416                };
1417                if v == 0 {
1418                    return Some((0u64, NativeKind::Bool));
1419                }
1420            }
1421            Some((1u64, NativeKind::Bool))
1422        }
1423        _ => None,
1424    }
1425}
1426
1427/// Allocate a fresh v2 typed array, copy all elements from `view`, stamp
1428/// elem_type, and return its raw pointer.
1429pub fn clone_array(view: &V2TypedArrayView) -> *mut u8 {
1430    match view.elem_type {
1431        V2ElemType::F64 => {
1432            let new_arr = TypedArray::<f64>::with_capacity(view.len);
1433            unsafe {
1434                let src = view.ptr as *const TypedArray<f64>;
1435                let src_data = (*src).data;
1436                let dst_data = (*new_arr).data;
1437                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1438                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1439                }
1440                (*new_arr).len = view.len;
1441                let p = new_arr as *mut u8;
1442                stamp_elem_type(p, ELEM_TYPE_F64);
1443                p
1444            }
1445        }
1446        V2ElemType::I64 => {
1447            let new_arr = TypedArray::<i64>::with_capacity(view.len);
1448            unsafe {
1449                let src = view.ptr as *const TypedArray<i64>;
1450                let src_data = (*src).data;
1451                let dst_data = (*new_arr).data;
1452                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1453                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1454                }
1455                (*new_arr).len = view.len;
1456                let p = new_arr as *mut u8;
1457                stamp_elem_type(p, ELEM_TYPE_I64);
1458                p
1459            }
1460        }
1461        V2ElemType::I32 => {
1462            let new_arr = TypedArray::<i32>::with_capacity(view.len);
1463            unsafe {
1464                let src = view.ptr as *const TypedArray<i32>;
1465                let src_data = (*src).data;
1466                let dst_data = (*new_arr).data;
1467                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1468                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1469                }
1470                (*new_arr).len = view.len;
1471                let p = new_arr as *mut u8;
1472                stamp_elem_type(p, ELEM_TYPE_I32);
1473                p
1474            }
1475        }
1476        V2ElemType::Bool => {
1477            let new_arr = TypedArray::<u8>::with_capacity(view.len);
1478            unsafe {
1479                let src = view.ptr as *const TypedArray<u8>;
1480                let src_data = (*src).data;
1481                let dst_data = (*new_arr).data;
1482                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1483                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1484                }
1485                (*new_arr).len = view.len;
1486                let p = new_arr as *mut u8;
1487                stamp_elem_type(p, ELEM_TYPE_BOOL);
1488                p
1489            }
1490        }
1491        // W12 S1 (2026-05-13) — sized-integer element clone implementations.
1492        // Each variant allocates a fresh `TypedArray<T>` with matching `T`,
1493        // memcpy's the element buffer, and stamps the proper `ELEM_TYPE_X`
1494        // byte so subsequent `as_v2_typed_array` calls dispatch correctly.
1495        V2ElemType::I8 => {
1496            let new_arr = TypedArray::<i8>::with_capacity(view.len);
1497            unsafe {
1498                let src = view.ptr as *const TypedArray<i8>;
1499                let src_data = (*src).data;
1500                let dst_data = (*new_arr).data;
1501                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1502                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1503                }
1504                (*new_arr).len = view.len;
1505                let p = new_arr as *mut u8;
1506                stamp_elem_type(p, ELEM_TYPE_I8);
1507                p
1508            }
1509        }
1510        V2ElemType::U8 => {
1511            let new_arr = TypedArray::<u8>::with_capacity(view.len);
1512            unsafe {
1513                let src = view.ptr as *const TypedArray<u8>;
1514                let src_data = (*src).data;
1515                let dst_data = (*new_arr).data;
1516                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1517                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1518                }
1519                (*new_arr).len = view.len;
1520                let p = new_arr as *mut u8;
1521                stamp_elem_type(p, ELEM_TYPE_U8);
1522                p
1523            }
1524        }
1525        V2ElemType::I16 => {
1526            let new_arr = TypedArray::<i16>::with_capacity(view.len);
1527            unsafe {
1528                let src = view.ptr as *const TypedArray<i16>;
1529                let src_data = (*src).data;
1530                let dst_data = (*new_arr).data;
1531                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1532                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1533                }
1534                (*new_arr).len = view.len;
1535                let p = new_arr as *mut u8;
1536                stamp_elem_type(p, ELEM_TYPE_I16);
1537                p
1538            }
1539        }
1540        V2ElemType::U16 => {
1541            let new_arr = TypedArray::<u16>::with_capacity(view.len);
1542            unsafe {
1543                let src = view.ptr as *const TypedArray<u16>;
1544                let src_data = (*src).data;
1545                let dst_data = (*new_arr).data;
1546                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1547                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1548                }
1549                (*new_arr).len = view.len;
1550                let p = new_arr as *mut u8;
1551                stamp_elem_type(p, ELEM_TYPE_U16);
1552                p
1553            }
1554        }
1555        V2ElemType::U32 => {
1556            let new_arr = TypedArray::<u32>::with_capacity(view.len);
1557            unsafe {
1558                let src = view.ptr as *const TypedArray<u32>;
1559                let src_data = (*src).data;
1560                let dst_data = (*new_arr).data;
1561                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1562                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1563                }
1564                (*new_arr).len = view.len;
1565                let p = new_arr as *mut u8;
1566                stamp_elem_type(p, ELEM_TYPE_U32);
1567                p
1568            }
1569        }
1570        // V2ElemType::U64 omitted — deferred to S1.5 per S1 reopen.
1571        // Wave 2 Agent A1 (2026-05-14) — F32 + Char element clone.
1572        V2ElemType::F32 => {
1573            let new_arr = TypedArray::<f32>::with_capacity(view.len);
1574            unsafe {
1575                let src = view.ptr as *const TypedArray<f32>;
1576                let src_data = (*src).data;
1577                let dst_data = (*new_arr).data;
1578                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1579                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1580                }
1581                (*new_arr).len = view.len;
1582                let p = new_arr as *mut u8;
1583                stamp_elem_type(p, ELEM_TYPE_F32);
1584                p
1585            }
1586        }
1587        V2ElemType::Char => {
1588            let new_arr = TypedArray::<char>::with_capacity(view.len);
1589            unsafe {
1590                let src = view.ptr as *const TypedArray<char>;
1591                let src_data = (*src).data;
1592                let dst_data = (*new_arr).data;
1593                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1594                    std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
1595                }
1596                (*new_arr).len = view.len;
1597                let p = new_arr as *mut u8;
1598                stamp_elem_type(p, ELEM_TYPE_CHAR);
1599                p
1600            }
1601        }
1602        // Wave 2 Agent A2 (2026-05-14) — String + Decimal element clone.
1603        // Each clone shares the same heap-element pointers as the source array
1604        // (no deep copy of the StringObj / DecimalObj allocations themselves);
1605        // we retain per-element so both arrays own valid shares.
1606        V2ElemType::String => {
1607            let new_arr = TypedArray::<*const StringObj>::with_capacity(view.len);
1608            unsafe {
1609                let src = view.ptr as *const TypedArray<*const StringObj>;
1610                let src_data = (*src).data;
1611                let dst_data = (*new_arr).data;
1612                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1613                    for i in 0..(view.len as usize) {
1614                        let elem = *src_data.add(i);
1615                        v2_retain(&(*elem).header);
1616                        *dst_data.add(i) = elem;
1617                    }
1618                }
1619                (*new_arr).len = view.len;
1620                let p = new_arr as *mut u8;
1621                stamp_elem_type(p, ELEM_TYPE_STRING);
1622                p
1623            }
1624        }
1625        V2ElemType::Decimal => {
1626            let new_arr = TypedArray::<*const DecimalObj>::with_capacity(view.len);
1627            unsafe {
1628                let src = view.ptr as *const TypedArray<*const DecimalObj>;
1629                let src_data = (*src).data;
1630                let dst_data = (*new_arr).data;
1631                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1632                    for i in 0..(view.len as usize) {
1633                        let elem = *src_data.add(i);
1634                        v2_retain(&(*elem).header);
1635                        *dst_data.add(i) = elem;
1636                    }
1637                }
1638                (*new_arr).len = view.len;
1639                let p = new_arr as *mut u8;
1640                stamp_elem_type(p, ELEM_TYPE_DECIMAL);
1641                p
1642            }
1643        }
1644        // Phase 4b Round 4 W16.2-A op_new_array-typed-object-element (2026-05-18).
1645        // Each clone shares the same heap-element pointers as the source array
1646        // (no deep copy of the TypedObjectStorage allocations themselves);
1647        // retain per-element so both arrays own valid shares — mirror of the
1648        // String/Decimal clone arms above.
1649        V2ElemType::TypedObject => {
1650            let new_arr =
1651                TypedArray::<*const TypedObjectStorage>::with_capacity(view.len);
1652            unsafe {
1653                let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
1654                let src_data = (*src).data;
1655                let dst_data = (*new_arr).data;
1656                if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
1657                    for i in 0..(view.len as usize) {
1658                        let elem = *src_data.add(i);
1659                        v2_retain(&(*elem).header);
1660                        *dst_data.add(i) = elem;
1661                    }
1662                }
1663                (*new_arr).len = view.len;
1664                let p = new_arr as *mut u8;
1665                stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
1666                p
1667            }
1668        }
1669    }
1670}
1671
1672// ── PC.2: SIMD-vectorized unary element-wise transforms on F64 views ────────
1673//
1674// These helpers produce a fresh v2 `TypedArray<f64>` by applying a pure
1675// element-wise function to each f64 element of `view`. The allocation stamps
1676// `ELEM_TYPE_F64` so the result is a first-class v2 typed array recognizable
1677// by downstream `.sum()` / `.map()` / etc.
1678//
1679// `simd_op`/`scalar_op` mirror the pattern used in the shape-runtime
1680// `intrinsic_vec_*` helpers. Arrays at or above `SIMD_UNARY_THRESHOLD` take
1681// the `wide::f64x4` fast path; smaller arrays fall back to scalar to avoid
1682// SIMD setup overhead.
1683//
1684// Callers use these via `dispatch_v2_typed_array_method` to implement
1685// `.abs()`, `.sqrt()`, `.ln()`, `.exp()` on v2 typed arrays. For non-F64
1686// element types the helper returns `None`, triggering the caller's legacy
1687// fallback.
1688
1689/// Minimum F64 element count at which unary SIMD transforms beat scalar.
1690/// Matches [`SIMD_SUM_THRESHOLD`]; determined empirically.
1691const SIMD_UNARY_THRESHOLD: u32 = 16;
1692
1693/// Apply a unary element-wise f64 transform to `view`, returning a newly
1694/// allocated v2 `TypedArray<f64>` pointer with `ELEM_TYPE_F64` stamped.
1695///
1696/// `simd_op` must be the `wide::f64x4` form of `scalar_op`; this is checked
1697/// by the parity tests in `typed_array_methods::tests`.
1698///
1699/// Returns `None` for non-F64 element types — the caller should fall back to
1700/// the legacy FLOAT_ARRAY_METHODS handler after materializing.
1701pub fn unary_f64_transform(
1702    view: &V2TypedArrayView,
1703    simd_op: fn(wide::f64x4) -> wide::f64x4,
1704    scalar_op: fn(f64) -> f64,
1705) -> Option<*mut u8> {
1706    use wide::f64x4;
1707
1708    if view.elem_type != V2ElemType::F64 {
1709        return None;
1710    }
1711    let len = view.len;
1712    let out = TypedArray::<f64>::with_capacity(len);
1713    if len == 0 {
1714        unsafe {
1715            (*out).len = 0;
1716            let p = out as *mut u8;
1717            stamp_elem_type(p, ELEM_TYPE_F64);
1718            return Some(p);
1719        }
1720    }
1721
1722    unsafe {
1723        let src_arr = view.ptr as *const TypedArray<f64>;
1724        let src = (*src_arr).data as *const f64;
1725        let dst = (*out).data as *mut f64;
1726
1727        if len >= SIMD_UNARY_THRESHOLD {
1728            let chunks = (len / 4) as usize;
1729            for i in 0..chunks {
1730                let base = i * 4;
1731                let v = f64x4::from([
1732                    *src.add(base),
1733                    *src.add(base + 1),
1734                    *src.add(base + 2),
1735                    *src.add(base + 3),
1736                ]);
1737                let r = simd_op(v);
1738                let arr = r.to_array();
1739                *dst.add(base) = arr[0];
1740                *dst.add(base + 1) = arr[1];
1741                *dst.add(base + 2) = arr[2];
1742                *dst.add(base + 3) = arr[3];
1743            }
1744            for i in (chunks * 4)..(len as usize) {
1745                *dst.add(i) = scalar_op(*src.add(i));
1746            }
1747        } else {
1748            for i in 0..(len as usize) {
1749                *dst.add(i) = scalar_op(*src.add(i));
1750            }
1751        }
1752
1753        (*out).len = len;
1754        let p = out as *mut u8;
1755        stamp_elem_type(p, ELEM_TYPE_F64);
1756        Some(p)
1757    }
1758}
1759
1760/// Stride-1 consecutive differences (`out[i] = src[i+1] - src[i]`) over a
1761/// v2 F64 typed array. Returns a fresh v2 `TypedArray<f64>` of length
1762/// `view.len - 1` (empty for `len < 2`). SIMD-accelerated via `f64x4` for
1763/// sufficiently large inputs (PC.2).
1764///
1765/// Returns `None` for non-F64 element types.
1766pub fn diff_f64(view: &V2TypedArrayView) -> Option<*mut u8> {
1767    use wide::f64x4;
1768
1769    if view.elem_type != V2ElemType::F64 {
1770        return None;
1771    }
1772    let len = view.len;
1773    if len < 2 {
1774        let out = TypedArray::<f64>::with_capacity(0);
1775        unsafe {
1776            (*out).len = 0;
1777            let p = out as *mut u8;
1778            stamp_elem_type(p, ELEM_TYPE_F64);
1779            return Some(p);
1780        }
1781    }
1782
1783    let out_len = len - 1;
1784    let out = TypedArray::<f64>::with_capacity(out_len);
1785    unsafe {
1786        let src_arr = view.ptr as *const TypedArray<f64>;
1787        let src = (*src_arr).data as *const f64;
1788        let dst = (*out).data as *mut f64;
1789
1790        if out_len >= SIMD_UNARY_THRESHOLD {
1791            let mut i: usize = 0;
1792            // While we can still load `src[i+1 .. i+5]`, step 4 at a time.
1793            while i + 4 < (len as usize) {
1794                let prev = f64x4::from([
1795                    *src.add(i),
1796                    *src.add(i + 1),
1797                    *src.add(i + 2),
1798                    *src.add(i + 3),
1799                ]);
1800                let next = f64x4::from([
1801                    *src.add(i + 1),
1802                    *src.add(i + 2),
1803                    *src.add(i + 3),
1804                    *src.add(i + 4),
1805                ]);
1806                let d = next - prev;
1807                let arr = d.to_array();
1808                *dst.add(i) = arr[0];
1809                *dst.add(i + 1) = arr[1];
1810                *dst.add(i + 2) = arr[2];
1811                *dst.add(i + 3) = arr[3];
1812                i += 4;
1813            }
1814            // Scalar tail: remaining `out_len - i` differences.
1815            for j in i..(out_len as usize) {
1816                *dst.add(j) = *src.add(j + 1) - *src.add(j);
1817            }
1818        } else {
1819            for i in 0..(out_len as usize) {
1820                *dst.add(i) = *src.add(i + 1) - *src.add(i);
1821            }
1822        }
1823
1824        (*out).len = out_len;
1825        let p = out as *mut u8;
1826        stamp_elem_type(p, ELEM_TYPE_F64);
1827        Some(p)
1828    }
1829}
1830
1831// ═══════════════════════════════════════════════════════════════════════════
1832// J.5a non-blocking primitives (2026-05-24)
1833//
1834// Per `docs/cluster-audits/v0.3-j4-rest-reaudit.md` §6 J.5a row: kind-generic
1835// reverse / concat / slice / take / drop primitives over the existing 14-arm
1836// `V2ElemType` dispatch shape. Each primitive mirrors the `clone_array`
1837// scaffold (`v2_array_detect.rs:1429-1670`): allocate a fresh `TypedArray<T>`
1838// with the same element type, copy the relevant range, retain per-element for
1839// heap-element kinds (String / Decimal / TypedObject), stamp the result's
1840// element-type byte, return the raw pointer.
1841//
1842// Refusal #10 binding (re-audit §6): `flatten` is NOT implemented here —
1843// the outer carrier shape (`TypedArray<*const TypedArray<T>>`) requires the
1844// J.5d tuple-carrier-class architectural decision (§3 of the re-audit). The
1845// `handle_flatten_v2` site remains surface-and-stop pending that gate.
1846// ═══════════════════════════════════════════════════════════════════════════
1847
1848/// Produce a reversed copy of `view`. Kind-generic over the 14 `V2ElemType`
1849/// variants. For heap-element variants (String / Decimal / TypedObject) the
1850/// new array's pointers share the same heap targets as the source; each is
1851/// retained per-element so both arrays own valid shares.
1852///
1853/// Allocator + stamp shape mirrors `clone_array`; only the per-element copy
1854/// loop differs (reverse iteration).
1855pub fn reverse_array(view: &V2TypedArrayView) -> *mut u8 {
1856    // Helper: copy `Copy` scalar elements in reverse order.
1857    #[inline]
1858    unsafe fn copy_reverse_scalar<T: Copy>(
1859        src_data: *const T,
1860        dst_data: *mut T,
1861        len: usize,
1862    ) {
1863        if len == 0 || src_data.is_null() || dst_data.is_null() {
1864            return;
1865        }
1866        for i in 0..len {
1867            unsafe {
1868                *dst_data.add(i) = *src_data.add(len - 1 - i);
1869            }
1870        }
1871    }
1872
1873    match view.elem_type {
1874        V2ElemType::F64 => {
1875            let new_arr = TypedArray::<f64>::with_capacity(view.len);
1876            unsafe {
1877                let src = view.ptr as *const TypedArray<f64>;
1878                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1879                (*new_arr).len = view.len;
1880                let p = new_arr as *mut u8;
1881                stamp_elem_type(p, ELEM_TYPE_F64);
1882                p
1883            }
1884        }
1885        V2ElemType::I64 => {
1886            let new_arr = TypedArray::<i64>::with_capacity(view.len);
1887            unsafe {
1888                let src = view.ptr as *const TypedArray<i64>;
1889                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1890                (*new_arr).len = view.len;
1891                let p = new_arr as *mut u8;
1892                stamp_elem_type(p, ELEM_TYPE_I64);
1893                p
1894            }
1895        }
1896        V2ElemType::I32 => {
1897            let new_arr = TypedArray::<i32>::with_capacity(view.len);
1898            unsafe {
1899                let src = view.ptr as *const TypedArray<i32>;
1900                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1901                (*new_arr).len = view.len;
1902                let p = new_arr as *mut u8;
1903                stamp_elem_type(p, ELEM_TYPE_I32);
1904                p
1905            }
1906        }
1907        V2ElemType::Bool => {
1908            let new_arr = TypedArray::<u8>::with_capacity(view.len);
1909            unsafe {
1910                let src = view.ptr as *const TypedArray<u8>;
1911                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1912                (*new_arr).len = view.len;
1913                let p = new_arr as *mut u8;
1914                stamp_elem_type(p, ELEM_TYPE_BOOL);
1915                p
1916            }
1917        }
1918        V2ElemType::I8 => {
1919            let new_arr = TypedArray::<i8>::with_capacity(view.len);
1920            unsafe {
1921                let src = view.ptr as *const TypedArray<i8>;
1922                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1923                (*new_arr).len = view.len;
1924                let p = new_arr as *mut u8;
1925                stamp_elem_type(p, ELEM_TYPE_I8);
1926                p
1927            }
1928        }
1929        V2ElemType::U8 => {
1930            let new_arr = TypedArray::<u8>::with_capacity(view.len);
1931            unsafe {
1932                let src = view.ptr as *const TypedArray<u8>;
1933                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1934                (*new_arr).len = view.len;
1935                let p = new_arr as *mut u8;
1936                stamp_elem_type(p, ELEM_TYPE_U8);
1937                p
1938            }
1939        }
1940        V2ElemType::I16 => {
1941            let new_arr = TypedArray::<i16>::with_capacity(view.len);
1942            unsafe {
1943                let src = view.ptr as *const TypedArray<i16>;
1944                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1945                (*new_arr).len = view.len;
1946                let p = new_arr as *mut u8;
1947                stamp_elem_type(p, ELEM_TYPE_I16);
1948                p
1949            }
1950        }
1951        V2ElemType::U16 => {
1952            let new_arr = TypedArray::<u16>::with_capacity(view.len);
1953            unsafe {
1954                let src = view.ptr as *const TypedArray<u16>;
1955                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1956                (*new_arr).len = view.len;
1957                let p = new_arr as *mut u8;
1958                stamp_elem_type(p, ELEM_TYPE_U16);
1959                p
1960            }
1961        }
1962        V2ElemType::U32 => {
1963            let new_arr = TypedArray::<u32>::with_capacity(view.len);
1964            unsafe {
1965                let src = view.ptr as *const TypedArray<u32>;
1966                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1967                (*new_arr).len = view.len;
1968                let p = new_arr as *mut u8;
1969                stamp_elem_type(p, ELEM_TYPE_U32);
1970                p
1971            }
1972        }
1973        V2ElemType::F32 => {
1974            let new_arr = TypedArray::<f32>::with_capacity(view.len);
1975            unsafe {
1976                let src = view.ptr as *const TypedArray<f32>;
1977                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1978                (*new_arr).len = view.len;
1979                let p = new_arr as *mut u8;
1980                stamp_elem_type(p, ELEM_TYPE_F32);
1981                p
1982            }
1983        }
1984        V2ElemType::Char => {
1985            let new_arr = TypedArray::<char>::with_capacity(view.len);
1986            unsafe {
1987                let src = view.ptr as *const TypedArray<char>;
1988                copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
1989                (*new_arr).len = view.len;
1990                let p = new_arr as *mut u8;
1991                stamp_elem_type(p, ELEM_TYPE_CHAR);
1992                p
1993            }
1994        }
1995        V2ElemType::String => {
1996            let new_arr = TypedArray::<*const StringObj>::with_capacity(view.len);
1997            unsafe {
1998                let src = view.ptr as *const TypedArray<*const StringObj>;
1999                let src_data = (*src).data;
2000                let dst_data = (*new_arr).data;
2001                let len = view.len as usize;
2002                if len > 0 && !src_data.is_null() && !dst_data.is_null() {
2003                    for i in 0..len {
2004                        let elem = *src_data.add(len - 1 - i);
2005                        v2_retain(&(*elem).header);
2006                        *dst_data.add(i) = elem;
2007                    }
2008                }
2009                (*new_arr).len = view.len;
2010                let p = new_arr as *mut u8;
2011                stamp_elem_type(p, ELEM_TYPE_STRING);
2012                p
2013            }
2014        }
2015        V2ElemType::Decimal => {
2016            let new_arr = TypedArray::<*const DecimalObj>::with_capacity(view.len);
2017            unsafe {
2018                let src = view.ptr as *const TypedArray<*const DecimalObj>;
2019                let src_data = (*src).data;
2020                let dst_data = (*new_arr).data;
2021                let len = view.len as usize;
2022                if len > 0 && !src_data.is_null() && !dst_data.is_null() {
2023                    for i in 0..len {
2024                        let elem = *src_data.add(len - 1 - i);
2025                        v2_retain(&(*elem).header);
2026                        *dst_data.add(i) = elem;
2027                    }
2028                }
2029                (*new_arr).len = view.len;
2030                let p = new_arr as *mut u8;
2031                stamp_elem_type(p, ELEM_TYPE_DECIMAL);
2032                p
2033            }
2034        }
2035        V2ElemType::TypedObject => {
2036            let new_arr =
2037                TypedArray::<*const TypedObjectStorage>::with_capacity(view.len);
2038            unsafe {
2039                let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
2040                let src_data = (*src).data;
2041                let dst_data = (*new_arr).data;
2042                let len = view.len as usize;
2043                if len > 0 && !src_data.is_null() && !dst_data.is_null() {
2044                    for i in 0..len {
2045                        let elem = *src_data.add(len - 1 - i);
2046                        v2_retain(&(*elem).header);
2047                        *dst_data.add(i) = elem;
2048                    }
2049                }
2050                (*new_arr).len = view.len;
2051                let p = new_arr as *mut u8;
2052                stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
2053                p
2054            }
2055        }
2056    }
2057}
2058
2059/// Concatenate two v2 typed arrays of the same element type. Returns the
2060/// raw pointer to a freshly-allocated `TypedArray<T>` with `a.len + b.len`
2061/// elements. Returns `Err` on element-type mismatch (per ADR-006 §2.7.5
2062/// stamp-at-compile-time: mixed-kind concat is structurally rejected, no
2063/// coercion).
2064///
2065/// Kind-generic over the 14 `V2ElemType` variants. Same retain discipline
2066/// as `clone_array` for heap-element variants.
2067pub fn concat_arrays(
2068    a: &V2TypedArrayView,
2069    b: &V2TypedArrayView,
2070) -> Result<*mut u8, &'static str> {
2071    if a.elem_type != b.elem_type {
2072        return Err("concat_arrays: element type mismatch");
2073    }
2074    let total_len = a
2075        .len
2076        .checked_add(b.len)
2077        .ok_or("concat_arrays: result length overflow")?;
2078
2079    // Helper: copy two source ranges into a fresh scalar buffer.
2080    #[inline]
2081    unsafe fn copy_two_scalar<T: Copy>(
2082        a_data: *const T,
2083        a_len: usize,
2084        b_data: *const T,
2085        b_len: usize,
2086        dst_data: *mut T,
2087    ) {
2088        if dst_data.is_null() {
2089            return;
2090        }
2091        if a_len > 0 && !a_data.is_null() {
2092            unsafe { std::ptr::copy_nonoverlapping(a_data, dst_data, a_len) };
2093        }
2094        if b_len > 0 && !b_data.is_null() {
2095            unsafe { std::ptr::copy_nonoverlapping(b_data, dst_data.add(a_len), b_len) };
2096        }
2097    }
2098
2099    let result = match a.elem_type {
2100        V2ElemType::F64 => unsafe {
2101            let new_arr = TypedArray::<f64>::with_capacity(total_len);
2102            let a_arr = a.ptr as *const TypedArray<f64>;
2103            let b_arr = b.ptr as *const TypedArray<f64>;
2104            copy_two_scalar(
2105                (*a_arr).data,
2106                a.len as usize,
2107                (*b_arr).data,
2108                b.len as usize,
2109                (*new_arr).data,
2110            );
2111            (*new_arr).len = total_len;
2112            let p = new_arr as *mut u8;
2113            stamp_elem_type(p, ELEM_TYPE_F64);
2114            p
2115        },
2116        V2ElemType::I64 => unsafe {
2117            let new_arr = TypedArray::<i64>::with_capacity(total_len);
2118            let a_arr = a.ptr as *const TypedArray<i64>;
2119            let b_arr = b.ptr as *const TypedArray<i64>;
2120            copy_two_scalar(
2121                (*a_arr).data,
2122                a.len as usize,
2123                (*b_arr).data,
2124                b.len as usize,
2125                (*new_arr).data,
2126            );
2127            (*new_arr).len = total_len;
2128            let p = new_arr as *mut u8;
2129            stamp_elem_type(p, ELEM_TYPE_I64);
2130            p
2131        },
2132        V2ElemType::I32 => unsafe {
2133            let new_arr = TypedArray::<i32>::with_capacity(total_len);
2134            let a_arr = a.ptr as *const TypedArray<i32>;
2135            let b_arr = b.ptr as *const TypedArray<i32>;
2136            copy_two_scalar(
2137                (*a_arr).data,
2138                a.len as usize,
2139                (*b_arr).data,
2140                b.len as usize,
2141                (*new_arr).data,
2142            );
2143            (*new_arr).len = total_len;
2144            let p = new_arr as *mut u8;
2145            stamp_elem_type(p, ELEM_TYPE_I32);
2146            p
2147        },
2148        V2ElemType::Bool => unsafe {
2149            let new_arr = TypedArray::<u8>::with_capacity(total_len);
2150            let a_arr = a.ptr as *const TypedArray<u8>;
2151            let b_arr = b.ptr as *const TypedArray<u8>;
2152            copy_two_scalar(
2153                (*a_arr).data,
2154                a.len as usize,
2155                (*b_arr).data,
2156                b.len as usize,
2157                (*new_arr).data,
2158            );
2159            (*new_arr).len = total_len;
2160            let p = new_arr as *mut u8;
2161            stamp_elem_type(p, ELEM_TYPE_BOOL);
2162            p
2163        },
2164        V2ElemType::I8 => unsafe {
2165            let new_arr = TypedArray::<i8>::with_capacity(total_len);
2166            let a_arr = a.ptr as *const TypedArray<i8>;
2167            let b_arr = b.ptr as *const TypedArray<i8>;
2168            copy_two_scalar(
2169                (*a_arr).data,
2170                a.len as usize,
2171                (*b_arr).data,
2172                b.len as usize,
2173                (*new_arr).data,
2174            );
2175            (*new_arr).len = total_len;
2176            let p = new_arr as *mut u8;
2177            stamp_elem_type(p, ELEM_TYPE_I8);
2178            p
2179        },
2180        V2ElemType::U8 => unsafe {
2181            let new_arr = TypedArray::<u8>::with_capacity(total_len);
2182            let a_arr = a.ptr as *const TypedArray<u8>;
2183            let b_arr = b.ptr as *const TypedArray<u8>;
2184            copy_two_scalar(
2185                (*a_arr).data,
2186                a.len as usize,
2187                (*b_arr).data,
2188                b.len as usize,
2189                (*new_arr).data,
2190            );
2191            (*new_arr).len = total_len;
2192            let p = new_arr as *mut u8;
2193            stamp_elem_type(p, ELEM_TYPE_U8);
2194            p
2195        },
2196        V2ElemType::I16 => unsafe {
2197            let new_arr = TypedArray::<i16>::with_capacity(total_len);
2198            let a_arr = a.ptr as *const TypedArray<i16>;
2199            let b_arr = b.ptr as *const TypedArray<i16>;
2200            copy_two_scalar(
2201                (*a_arr).data,
2202                a.len as usize,
2203                (*b_arr).data,
2204                b.len as usize,
2205                (*new_arr).data,
2206            );
2207            (*new_arr).len = total_len;
2208            let p = new_arr as *mut u8;
2209            stamp_elem_type(p, ELEM_TYPE_I16);
2210            p
2211        },
2212        V2ElemType::U16 => unsafe {
2213            let new_arr = TypedArray::<u16>::with_capacity(total_len);
2214            let a_arr = a.ptr as *const TypedArray<u16>;
2215            let b_arr = b.ptr as *const TypedArray<u16>;
2216            copy_two_scalar(
2217                (*a_arr).data,
2218                a.len as usize,
2219                (*b_arr).data,
2220                b.len as usize,
2221                (*new_arr).data,
2222            );
2223            (*new_arr).len = total_len;
2224            let p = new_arr as *mut u8;
2225            stamp_elem_type(p, ELEM_TYPE_U16);
2226            p
2227        },
2228        V2ElemType::U32 => unsafe {
2229            let new_arr = TypedArray::<u32>::with_capacity(total_len);
2230            let a_arr = a.ptr as *const TypedArray<u32>;
2231            let b_arr = b.ptr as *const TypedArray<u32>;
2232            copy_two_scalar(
2233                (*a_arr).data,
2234                a.len as usize,
2235                (*b_arr).data,
2236                b.len as usize,
2237                (*new_arr).data,
2238            );
2239            (*new_arr).len = total_len;
2240            let p = new_arr as *mut u8;
2241            stamp_elem_type(p, ELEM_TYPE_U32);
2242            p
2243        },
2244        V2ElemType::F32 => unsafe {
2245            let new_arr = TypedArray::<f32>::with_capacity(total_len);
2246            let a_arr = a.ptr as *const TypedArray<f32>;
2247            let b_arr = b.ptr as *const TypedArray<f32>;
2248            copy_two_scalar(
2249                (*a_arr).data,
2250                a.len as usize,
2251                (*b_arr).data,
2252                b.len as usize,
2253                (*new_arr).data,
2254            );
2255            (*new_arr).len = total_len;
2256            let p = new_arr as *mut u8;
2257            stamp_elem_type(p, ELEM_TYPE_F32);
2258            p
2259        },
2260        V2ElemType::Char => unsafe {
2261            let new_arr = TypedArray::<char>::with_capacity(total_len);
2262            let a_arr = a.ptr as *const TypedArray<char>;
2263            let b_arr = b.ptr as *const TypedArray<char>;
2264            copy_two_scalar(
2265                (*a_arr).data,
2266                a.len as usize,
2267                (*b_arr).data,
2268                b.len as usize,
2269                (*new_arr).data,
2270            );
2271            (*new_arr).len = total_len;
2272            let p = new_arr as *mut u8;
2273            stamp_elem_type(p, ELEM_TYPE_CHAR);
2274            p
2275        },
2276        V2ElemType::String => unsafe {
2277            let new_arr = TypedArray::<*const StringObj>::with_capacity(total_len);
2278            let a_arr = a.ptr as *const TypedArray<*const StringObj>;
2279            let b_arr = b.ptr as *const TypedArray<*const StringObj>;
2280            let dst_data = (*new_arr).data;
2281            let a_data = (*a_arr).data;
2282            let b_data = (*b_arr).data;
2283            if !dst_data.is_null() {
2284                if a.len > 0 && !a_data.is_null() {
2285                    for i in 0..(a.len as usize) {
2286                        let elem = *a_data.add(i);
2287                        v2_retain(&(*elem).header);
2288                        *dst_data.add(i) = elem;
2289                    }
2290                }
2291                if b.len > 0 && !b_data.is_null() {
2292                    let off = a.len as usize;
2293                    for i in 0..(b.len as usize) {
2294                        let elem = *b_data.add(i);
2295                        v2_retain(&(*elem).header);
2296                        *dst_data.add(off + i) = elem;
2297                    }
2298                }
2299            }
2300            (*new_arr).len = total_len;
2301            let p = new_arr as *mut u8;
2302            stamp_elem_type(p, ELEM_TYPE_STRING);
2303            p
2304        },
2305        V2ElemType::Decimal => unsafe {
2306            let new_arr = TypedArray::<*const DecimalObj>::with_capacity(total_len);
2307            let a_arr = a.ptr as *const TypedArray<*const DecimalObj>;
2308            let b_arr = b.ptr as *const TypedArray<*const DecimalObj>;
2309            let dst_data = (*new_arr).data;
2310            let a_data = (*a_arr).data;
2311            let b_data = (*b_arr).data;
2312            if !dst_data.is_null() {
2313                if a.len > 0 && !a_data.is_null() {
2314                    for i in 0..(a.len as usize) {
2315                        let elem = *a_data.add(i);
2316                        v2_retain(&(*elem).header);
2317                        *dst_data.add(i) = elem;
2318                    }
2319                }
2320                if b.len > 0 && !b_data.is_null() {
2321                    let off = a.len as usize;
2322                    for i in 0..(b.len as usize) {
2323                        let elem = *b_data.add(i);
2324                        v2_retain(&(*elem).header);
2325                        *dst_data.add(off + i) = elem;
2326                    }
2327                }
2328            }
2329            (*new_arr).len = total_len;
2330            let p = new_arr as *mut u8;
2331            stamp_elem_type(p, ELEM_TYPE_DECIMAL);
2332            p
2333        },
2334        V2ElemType::TypedObject => unsafe {
2335            let new_arr =
2336                TypedArray::<*const TypedObjectStorage>::with_capacity(total_len);
2337            let a_arr = a.ptr as *const TypedArray<*const TypedObjectStorage>;
2338            let b_arr = b.ptr as *const TypedArray<*const TypedObjectStorage>;
2339            let dst_data = (*new_arr).data;
2340            let a_data = (*a_arr).data;
2341            let b_data = (*b_arr).data;
2342            if !dst_data.is_null() {
2343                if a.len > 0 && !a_data.is_null() {
2344                    for i in 0..(a.len as usize) {
2345                        let elem = *a_data.add(i);
2346                        v2_retain(&(*elem).header);
2347                        *dst_data.add(i) = elem;
2348                    }
2349                }
2350                if b.len > 0 && !b_data.is_null() {
2351                    let off = a.len as usize;
2352                    for i in 0..(b.len as usize) {
2353                        let elem = *b_data.add(i);
2354                        v2_retain(&(*elem).header);
2355                        *dst_data.add(off + i) = elem;
2356                    }
2357                }
2358            }
2359            (*new_arr).len = total_len;
2360            let p = new_arr as *mut u8;
2361            stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
2362            p
2363        },
2364    };
2365    Ok(result)
2366}
2367
2368/// Allocate a fresh `TypedArray<T>` containing the elements of `view` from
2369/// `start` (inclusive) to `end` (exclusive), clamped to `[0, view.len]`.
2370/// Kind-generic. Empty / out-of-order ranges produce an empty result array
2371/// (mirrors Rust's `slice::get(start..end)` clamping rather than panicking).
2372///
2373/// Shared internal worker for `slice_array` / `take_array` / `drop_array_n`.
2374fn copy_range_to_new_array(
2375    view: &V2TypedArrayView,
2376    start: u32,
2377    end: u32,
2378) -> *mut u8 {
2379    // Clamp the range to `[0, view.len]` and compute out_len.
2380    let start = start.min(view.len);
2381    let end = end.min(view.len);
2382    let out_len = end.saturating_sub(start);
2383    let s = start as usize;
2384    let n = out_len as usize;
2385
2386    // Helper: scalar copy of `view.data[start..start+n]` into the fresh
2387    // buffer.
2388    #[inline]
2389    unsafe fn copy_scalar_range<T: Copy>(
2390        src_data: *const T,
2391        dst_data: *mut T,
2392        start: usize,
2393        len: usize,
2394    ) {
2395        if len == 0 || src_data.is_null() || dst_data.is_null() {
2396            return;
2397        }
2398        unsafe {
2399            std::ptr::copy_nonoverlapping(src_data.add(start), dst_data, len);
2400        }
2401    }
2402
2403    match view.elem_type {
2404        V2ElemType::F64 => unsafe {
2405            let new_arr = TypedArray::<f64>::with_capacity(out_len);
2406            let src = view.ptr as *const TypedArray<f64>;
2407            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2408            (*new_arr).len = out_len;
2409            let p = new_arr as *mut u8;
2410            stamp_elem_type(p, ELEM_TYPE_F64);
2411            p
2412        },
2413        V2ElemType::I64 => unsafe {
2414            let new_arr = TypedArray::<i64>::with_capacity(out_len);
2415            let src = view.ptr as *const TypedArray<i64>;
2416            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2417            (*new_arr).len = out_len;
2418            let p = new_arr as *mut u8;
2419            stamp_elem_type(p, ELEM_TYPE_I64);
2420            p
2421        },
2422        V2ElemType::I32 => unsafe {
2423            let new_arr = TypedArray::<i32>::with_capacity(out_len);
2424            let src = view.ptr as *const TypedArray<i32>;
2425            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2426            (*new_arr).len = out_len;
2427            let p = new_arr as *mut u8;
2428            stamp_elem_type(p, ELEM_TYPE_I32);
2429            p
2430        },
2431        V2ElemType::Bool => unsafe {
2432            let new_arr = TypedArray::<u8>::with_capacity(out_len);
2433            let src = view.ptr as *const TypedArray<u8>;
2434            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2435            (*new_arr).len = out_len;
2436            let p = new_arr as *mut u8;
2437            stamp_elem_type(p, ELEM_TYPE_BOOL);
2438            p
2439        },
2440        V2ElemType::I8 => unsafe {
2441            let new_arr = TypedArray::<i8>::with_capacity(out_len);
2442            let src = view.ptr as *const TypedArray<i8>;
2443            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2444            (*new_arr).len = out_len;
2445            let p = new_arr as *mut u8;
2446            stamp_elem_type(p, ELEM_TYPE_I8);
2447            p
2448        },
2449        V2ElemType::U8 => unsafe {
2450            let new_arr = TypedArray::<u8>::with_capacity(out_len);
2451            let src = view.ptr as *const TypedArray<u8>;
2452            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2453            (*new_arr).len = out_len;
2454            let p = new_arr as *mut u8;
2455            stamp_elem_type(p, ELEM_TYPE_U8);
2456            p
2457        },
2458        V2ElemType::I16 => unsafe {
2459            let new_arr = TypedArray::<i16>::with_capacity(out_len);
2460            let src = view.ptr as *const TypedArray<i16>;
2461            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2462            (*new_arr).len = out_len;
2463            let p = new_arr as *mut u8;
2464            stamp_elem_type(p, ELEM_TYPE_I16);
2465            p
2466        },
2467        V2ElemType::U16 => unsafe {
2468            let new_arr = TypedArray::<u16>::with_capacity(out_len);
2469            let src = view.ptr as *const TypedArray<u16>;
2470            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2471            (*new_arr).len = out_len;
2472            let p = new_arr as *mut u8;
2473            stamp_elem_type(p, ELEM_TYPE_U16);
2474            p
2475        },
2476        V2ElemType::U32 => unsafe {
2477            let new_arr = TypedArray::<u32>::with_capacity(out_len);
2478            let src = view.ptr as *const TypedArray<u32>;
2479            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2480            (*new_arr).len = out_len;
2481            let p = new_arr as *mut u8;
2482            stamp_elem_type(p, ELEM_TYPE_U32);
2483            p
2484        },
2485        V2ElemType::F32 => unsafe {
2486            let new_arr = TypedArray::<f32>::with_capacity(out_len);
2487            let src = view.ptr as *const TypedArray<f32>;
2488            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2489            (*new_arr).len = out_len;
2490            let p = new_arr as *mut u8;
2491            stamp_elem_type(p, ELEM_TYPE_F32);
2492            p
2493        },
2494        V2ElemType::Char => unsafe {
2495            let new_arr = TypedArray::<char>::with_capacity(out_len);
2496            let src = view.ptr as *const TypedArray<char>;
2497            copy_scalar_range((*src).data, (*new_arr).data, s, n);
2498            (*new_arr).len = out_len;
2499            let p = new_arr as *mut u8;
2500            stamp_elem_type(p, ELEM_TYPE_CHAR);
2501            p
2502        },
2503        V2ElemType::String => unsafe {
2504            let new_arr = TypedArray::<*const StringObj>::with_capacity(out_len);
2505            let src = view.ptr as *const TypedArray<*const StringObj>;
2506            let src_data = (*src).data;
2507            let dst_data = (*new_arr).data;
2508            if n > 0 && !src_data.is_null() && !dst_data.is_null() {
2509                for i in 0..n {
2510                    let elem = *src_data.add(s + i);
2511                    v2_retain(&(*elem).header);
2512                    *dst_data.add(i) = elem;
2513                }
2514            }
2515            (*new_arr).len = out_len;
2516            let p = new_arr as *mut u8;
2517            stamp_elem_type(p, ELEM_TYPE_STRING);
2518            p
2519        },
2520        V2ElemType::Decimal => unsafe {
2521            let new_arr = TypedArray::<*const DecimalObj>::with_capacity(out_len);
2522            let src = view.ptr as *const TypedArray<*const DecimalObj>;
2523            let src_data = (*src).data;
2524            let dst_data = (*new_arr).data;
2525            if n > 0 && !src_data.is_null() && !dst_data.is_null() {
2526                for i in 0..n {
2527                    let elem = *src_data.add(s + i);
2528                    v2_retain(&(*elem).header);
2529                    *dst_data.add(i) = elem;
2530                }
2531            }
2532            (*new_arr).len = out_len;
2533            let p = new_arr as *mut u8;
2534            stamp_elem_type(p, ELEM_TYPE_DECIMAL);
2535            p
2536        },
2537        V2ElemType::TypedObject => unsafe {
2538            let new_arr =
2539                TypedArray::<*const TypedObjectStorage>::with_capacity(out_len);
2540            let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
2541            let src_data = (*src).data;
2542            let dst_data = (*new_arr).data;
2543            if n > 0 && !src_data.is_null() && !dst_data.is_null() {
2544                for i in 0..n {
2545                    let elem = *src_data.add(s + i);
2546                    v2_retain(&(*elem).header);
2547                    *dst_data.add(i) = elem;
2548                }
2549            }
2550            (*new_arr).len = out_len;
2551            let p = new_arr as *mut u8;
2552            stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
2553            p
2554        },
2555    }
2556}
2557
2558/// `arr.slice(start, end)` — bounded copy of `[start..end)`. `start` and
2559/// `end` are clamped to `[0, view.len]`; if `start >= end`, the result is
2560/// empty. Mirrors Rust's `slice::get(range)` clamping behaviour.
2561#[inline]
2562pub fn slice_array(view: &V2TypedArrayView, start: u32, end: u32) -> *mut u8 {
2563    copy_range_to_new_array(view, start, end)
2564}
2565
2566/// `arr.take(n)` — first `n` elements. `n` clamped to `[0, view.len]`.
2567#[inline]
2568pub fn take_array(view: &V2TypedArrayView, n: u32) -> *mut u8 {
2569    copy_range_to_new_array(view, 0, n)
2570}
2571
2572/// `arr.drop(n)` — all elements except the first `n`. `n` clamped to
2573/// `[0, view.len]`. Named `drop_array_n` to avoid collision with
2574/// `TypedArray::drop_array` (the allocator destructor).
2575#[inline]
2576pub fn drop_array_n(view: &V2TypedArrayView, n: u32) -> *mut u8 {
2577    copy_range_to_new_array(view, n, view.len)
2578}
2579
2580// ── R8 W4 J.5b HOF-builder primitives (2026-05-24) ──────────────────────────
2581//
2582// `where` / `select` / `take_while` / `skip_while` need to build a fresh
2583// `TypedArray<T>` whose element kind is determined either by the input
2584// (filter ops: same as input view.elem_type) or by the closure's return
2585// kind (select). The two helpers below are the kind-mapping +
2586// empty-allocator pieces; the per-op two-pass scan-then-allocate driver
2587// lives in `objects/array_query.rs` because it must call the closure via
2588// `VirtualMachine::call_value_immediate_nb` (§2.7.11 / Q12 ABI), which is
2589// not reachable from this leaf module.
2590//
2591// Supervisor D3 binding (2026-05-24): structured error on closure-return
2592// kind mismatch; NO coercion (forbidden per CLAUDE.md §Type System Rules);
2593// NO heterogeneous `Array<Any>` carrier (Shape has no `any` type per
2594// CLAUDE.md "No `any` type"). First invocation establishes the closure-
2595// return kind; subsequent mismatch surfaces `VMError::RuntimeError` with a
2596// structured message naming the expected / got kinds + the offending index.
2597
2598/// Map a `NativeKind` to its corresponding `V2ElemType` for HOF-builder
2599/// output-array allocation. Returns `None` for kinds with no monomorphized
2600/// `TypedArray<T>` carrier (e.g. `Null`, `Ptr(Closure)`, generic
2601/// `Ptr(HeapKind::HashMap)`). Pairs with `allocate_empty_typed_array` +
2602/// `push_element` to build a result array element-by-element.
2603///
2604/// The mapping mirrors `V2ElemType::elem_kind` in reverse (§2.7.7 / Q9
2605/// kind ↔ elem-type bijection over the 14 stamped carriers). Anything not
2606/// in the bijection is unsupported as an output-array element kind —
2607/// callers (e.g. `select`) surface a structured `RuntimeError` rather than
2608/// fabricating a Bool-default carrier (forbidden per ADR-006 §2.7.14).
2609#[inline]
2610pub fn native_kind_to_v2_elem_type(kind: NativeKind) -> Option<V2ElemType> {
2611    match kind {
2612        NativeKind::Float64 => Some(V2ElemType::F64),
2613        NativeKind::Int64 => Some(V2ElemType::I64),
2614        NativeKind::Int32 => Some(V2ElemType::I32),
2615        NativeKind::Bool => Some(V2ElemType::Bool),
2616        NativeKind::Int8 => Some(V2ElemType::I8),
2617        NativeKind::UInt8 => Some(V2ElemType::U8),
2618        NativeKind::Int16 => Some(V2ElemType::I16),
2619        NativeKind::UInt16 => Some(V2ElemType::U16),
2620        NativeKind::UInt32 => Some(V2ElemType::U32),
2621        NativeKind::Float32 => Some(V2ElemType::F32),
2622        NativeKind::Char => Some(V2ElemType::Char),
2623        NativeKind::StringV2 => Some(V2ElemType::String),
2624        NativeKind::DecimalV2 => Some(V2ElemType::Decimal),
2625        NativeKind::Ptr(HeapKind::TypedObject) => Some(V2ElemType::TypedObject),
2626        _ => None,
2627    }
2628}
2629
2630/// Allocate an empty `TypedArray<T>` for the given `V2ElemType`,
2631/// initialised with `capacity` slots, length zero, and stamped with the
2632/// matching element-type discriminant byte (§2.7.5 producer-side stamp).
2633/// Returns the raw `*mut u8` carrier pointer for wrapping into a
2634/// `Ptr(HeapKind::TypedArray)` `KindedSlot`. Subsequent `push_element`
2635/// calls grow the array.
2636///
2637/// Used by the HOF-builder handlers (`where` / `select` / `take_while` /
2638/// `skip_while`) for output-array allocation after the closure-return
2639/// kind is established (select) or known statically (filter ops, where
2640/// the input view's elem_type is the output's elem_type).
2641pub fn allocate_empty_typed_array(elem_type: V2ElemType, capacity: u32) -> *mut u8 {
2642    unsafe {
2643        let p: *mut u8 = match elem_type {
2644            V2ElemType::F64 => TypedArray::<f64>::with_capacity(capacity) as *mut u8,
2645            V2ElemType::I64 => TypedArray::<i64>::with_capacity(capacity) as *mut u8,
2646            V2ElemType::I32 => TypedArray::<i32>::with_capacity(capacity) as *mut u8,
2647            V2ElemType::Bool => TypedArray::<u8>::with_capacity(capacity) as *mut u8,
2648            V2ElemType::I8 => TypedArray::<i8>::with_capacity(capacity) as *mut u8,
2649            V2ElemType::U8 => TypedArray::<u8>::with_capacity(capacity) as *mut u8,
2650            V2ElemType::I16 => TypedArray::<i16>::with_capacity(capacity) as *mut u8,
2651            V2ElemType::U16 => TypedArray::<u16>::with_capacity(capacity) as *mut u8,
2652            V2ElemType::U32 => TypedArray::<u32>::with_capacity(capacity) as *mut u8,
2653            V2ElemType::F32 => TypedArray::<f32>::with_capacity(capacity) as *mut u8,
2654            V2ElemType::Char => TypedArray::<char>::with_capacity(capacity) as *mut u8,
2655            V2ElemType::String => {
2656                TypedArray::<*const StringObj>::with_capacity(capacity) as *mut u8
2657            }
2658            V2ElemType::Decimal => {
2659                TypedArray::<*const DecimalObj>::with_capacity(capacity) as *mut u8
2660            }
2661            V2ElemType::TypedObject => {
2662                TypedArray::<*const TypedObjectStorage>::with_capacity(capacity) as *mut u8
2663            }
2664        };
2665        let stamp_byte: u8 = match elem_type {
2666            V2ElemType::F64 => ELEM_TYPE_F64,
2667            V2ElemType::I64 => ELEM_TYPE_I64,
2668            V2ElemType::I32 => ELEM_TYPE_I32,
2669            V2ElemType::Bool => ELEM_TYPE_BOOL,
2670            V2ElemType::I8 => ELEM_TYPE_I8,
2671            V2ElemType::U8 => ELEM_TYPE_U8,
2672            V2ElemType::I16 => ELEM_TYPE_I16,
2673            V2ElemType::U16 => ELEM_TYPE_U16,
2674            V2ElemType::U32 => ELEM_TYPE_U32,
2675            V2ElemType::F32 => ELEM_TYPE_F32,
2676            V2ElemType::Char => ELEM_TYPE_CHAR,
2677            V2ElemType::String => ELEM_TYPE_STRING,
2678            V2ElemType::Decimal => ELEM_TYPE_DECIMAL,
2679            V2ElemType::TypedObject => ELEM_TYPE_TYPED_OBJECT,
2680        };
2681        stamp_elem_type(p, stamp_byte);
2682        p
2683    }
2684}
2685
2686// ═══════════════════════════════════════════════════════════════════════════
2687// R8 W4 J.5c (2026-05-24) — deep-equality primitives per supervisor D2.
2688//
2689// `eq_element(a, b, elem_type) -> bool` is a generic value-equality body
2690// with an internal 14-arm dispatch on `V2ElemType` (one arm per element
2691// kind landed in this module). Matches the existing primitives-layer ABI
2692// shape (mirrors `write_element` / `push_element`): the host opcode /
2693// handler passes the (bits, kind) pair of the needle plus the receiver's
2694// element type; the primitive performs the per-kind compare and returns
2695// a plain `bool`.
2696//
2697// `position_of(view, needle_bits, needle_kind) -> Option<u32>` is the
2698// per-op driver for `Array.indexOf(value)` (the `Some(i)` arm projects to
2699// the result; `None` projects to `-1`).
2700//
2701// `contains_element(view, needle_bits, needle_kind) -> bool` is the
2702// per-op driver for `Array.includes(value)`.
2703//
2704// Supervisor D2 binding (2026-05-24): generic eq_element with internal
2705// kind dispatch. REFUSED on sight: MethodFnV2 trait dispatch (forbidden
2706// MethodFn bridge pattern per CLAUDE.md §Renames-to-refuse). REFUSED on
2707// sight: dynamic-fallback / Bool-default on unknown / unsupported kinds —
2708// the kind-mismatch path returns `false` structurally (no element is
2709// equal to a value of a different carrier shape) without fabricating
2710// equality on bits, mirroring the strict §2.7.5 producer-stamp + §2.7.14
2711// no-Bool-default discipline.
2712//
2713// Refcount discipline: `eq_element` only READS bits, never retains or
2714// releases. Per-element reads inside `position_of` / `contains_element`
2715// do NOT use `read_element` (which would retain the element header on
2716// every iteration for the heap-element kinds and then immediately drop);
2717// instead they walk the underlying `TypedArray<T>` buffer directly via
2718// `get_unchecked` and compare against the needle via `eq_element` — the
2719// receiver's per-element shares are NOT touched, and the needle's share
2720// is owned by the caller (the dispatch shell `args[1]`). This keeps the
2721// includes / indexOf hot path allocation-free for the scalar arms.
2722// ═══════════════════════════════════════════════════════════════════════════
2723
2724/// Per-kind value equality for the `(bits, kind)` carrier shape.
2725///
2726/// Returns `true` iff `(a_bits, kind)` and `(b_bits, kind)` denote the
2727/// same value under the kind's semantics. Returns `false` when either
2728/// pointer is null for a heap-element kind (defensive — the dispatch
2729/// shell rejects null receivers earlier, but the primitive must stay
2730/// sound under any input).
2731///
2732/// Float semantics: bitwise equality (NOT `==`). This is the
2733/// IEEE-754-pure choice — `NaN == NaN` is `false` under `==` but `true`
2734/// under bitwise compare. The choice matches the `includes` / `indexOf`
2735/// observable: a NaN element pushed into the array IS findable via the
2736/// same NaN bit pattern (the array doesn't lose track of its own
2737/// elements). The user can still write a custom predicate via
2738/// `find(|x| x != x)` when IEEE compare is desired.
2739///
2740/// String / Decimal: deref through the v2-raw `StringObj` / `DecimalObj`
2741/// carrier and compare content (`StringObj::as_str` → `&str` equality;
2742/// `DecimalObj::value` → `Decimal` `PartialEq`).
2743///
2744/// TypedObject: deep field-by-field comparison. The two objects are
2745/// equal iff (a) they share the same `schema_id`, (b) they share the
2746/// same per-field `NativeKind` table, and (c) every per-field slot
2747/// compares equal under the slot's `NativeKind` via a recursive
2748/// `eq_element` call on the slot bits. Per ADR-006 §2.7.16 typed-Arc
2749/// dispatch-label receiver-recovery: the comparison reads the storage
2750/// directly via `&*(p as *const TypedObjectStorage)` (no Box-wrap
2751/// reinterpret).
2752///
2753/// # Safety
2754/// The caller must uphold the §2.7.5 producer-side stamp invariant —
2755/// for heap-element kinds (`StringV2` / `DecimalV2` /
2756/// `Ptr(HeapKind::TypedObject)`), the bits must be either `0` (null) or
2757/// a live carrier pointer of the matching `T`. The `bits == 0` early-out
2758/// is the defensive bound; otherwise the deref is sound under the
2759/// construction-side contract upheld by every producer in this module.
2760#[inline]
2761pub fn eq_element(a_bits: u64, b_bits: u64, elem_type: V2ElemType) -> bool {
2762    match elem_type {
2763        // Scalar arms: bitwise equality on the slot's significant bits.
2764        // For width-narrowed scalars (I8 / U8 / I16 / U16 / I32 / U32 /
2765        // Bool / F32 / Char) the producer zero/sign-extends into the
2766        // 8-byte slot via `as u64`; comparing the full 8 bytes is sound
2767        // (the unused high bits agree by construction).
2768        V2ElemType::F64 => a_bits == b_bits,
2769        V2ElemType::I64 => a_bits == b_bits,
2770        V2ElemType::I32 => a_bits == b_bits,
2771        V2ElemType::Bool => a_bits == b_bits,
2772        V2ElemType::I8 => a_bits == b_bits,
2773        V2ElemType::U8 => a_bits == b_bits,
2774        V2ElemType::I16 => a_bits == b_bits,
2775        V2ElemType::U16 => a_bits == b_bits,
2776        V2ElemType::U32 => a_bits == b_bits,
2777        V2ElemType::F32 => a_bits == b_bits,
2778        V2ElemType::Char => a_bits == b_bits,
2779        // Heap-element arms: deref and compare content.
2780        V2ElemType::String => {
2781            if a_bits == 0 || b_bits == 0 {
2782                return a_bits == b_bits;
2783            }
2784            let a_ptr = a_bits as usize as *const StringObj;
2785            let b_ptr = b_bits as usize as *const StringObj;
2786            if a_ptr == b_ptr {
2787                return true;
2788            }
2789            unsafe { StringObj::as_str(a_ptr) == StringObj::as_str(b_ptr) }
2790        }
2791        V2ElemType::Decimal => {
2792            if a_bits == 0 || b_bits == 0 {
2793                return a_bits == b_bits;
2794            }
2795            let a_ptr = a_bits as usize as *const DecimalObj;
2796            let b_ptr = b_bits as usize as *const DecimalObj;
2797            if a_ptr == b_ptr {
2798                return true;
2799            }
2800            unsafe { DecimalObj::value(a_ptr) == DecimalObj::value(b_ptr) }
2801        }
2802        V2ElemType::TypedObject => {
2803            if a_bits == 0 || b_bits == 0 {
2804                return a_bits == b_bits;
2805            }
2806            let a_ptr = a_bits as usize as *const TypedObjectStorage;
2807            let b_ptr = b_bits as usize as *const TypedObjectStorage;
2808            if a_ptr == b_ptr {
2809                return true;
2810            }
2811            // SAFETY: per the construction-side contract for the
2812            // `*const TypedObjectStorage` v2-raw carrier (Wave 2 Agent D1,
2813            // ADR-006 §2.3 typed-Arc dispatch-label receiver-recovery):
2814            // a non-null `*const TypedObjectStorage` slot bits value is
2815            // a live carrier (HeapHeader at offset 0). The borrow is
2816            // bounded to this scope; no share is retained or released.
2817            unsafe { typed_object_deep_eq(&*a_ptr, &*b_ptr) }
2818        }
2819    }
2820}
2821
2822/// Deep equality for two `TypedObjectStorage` instances.
2823///
2824/// Returns `true` iff (a) `schema_id` matches, (b) `slots.len()` matches,
2825/// (c) `field_kinds` table matches (this is the production-time
2826/// per-schema invariant — same schema_id → identical field_kinds slice),
2827/// and (d) every slot compares equal via `eq_element` dispatching on the
2828/// per-field `NativeKind`.
2829///
2830/// Per ADR-005 §1 single-discriminator + ADR-006 §2.7.6 / Q8 carrier-
2831/// API-bound: the per-field `NativeKind` is the discriminator; the
2832/// recursion maps `NativeKind` back to `V2ElemType` for the per-field
2833/// `eq_element` call (the field families covered are the ones the v2-raw
2834/// `TypedArray<T>` element-storage layer supports — scalar primitives +
2835/// String / Decimal / nested TypedObject). Other `NativeKind` variants
2836/// (e.g. `Ptr(HeapKind::HashMap)` field) surface-and-stop with `false` —
2837/// the §2.7.14 no-Bool-default discipline is preserved by being the
2838/// strict-equal answer, not a fabricated truth value.
2839///
2840/// # Safety
2841/// `a` / `b` must be live `&TypedObjectStorage` borrows bounded to the
2842/// caller's scope.
2843unsafe fn typed_object_deep_eq(
2844    a: &TypedObjectStorage,
2845    b: &TypedObjectStorage,
2846) -> bool {
2847    if a.schema_id != b.schema_id {
2848        return false;
2849    }
2850    if a.slots.len() != b.slots.len() {
2851        return false;
2852    }
2853    // field_kinds is `Arc<[NativeKind]>` shared per-schema; equal
2854    // schema_ids guarantee equal field_kinds in production. The
2855    // length-then-elementwise compare below is defensive.
2856    if a.field_kinds.len() != b.field_kinds.len() {
2857        return false;
2858    }
2859    for (k1, k2) in a.field_kinds.iter().zip(b.field_kinds.iter()) {
2860        if k1 != k2 {
2861            return false;
2862        }
2863    }
2864    for i in 0..a.slots.len() {
2865        let bits_a = a.slots[i].raw();
2866        let bits_b = b.slots[i].raw();
2867        let kind = a.field_kinds[i];
2868        // Map the per-field NativeKind back to the V2ElemType the
2869        // primitive dispatches on. Fields whose kind lies outside the
2870        // supported families return `false` (the strict, no-Bool-default
2871        // answer per §2.7.14): a TypedObject with a HashMap-valued field
2872        // compares unequal under deep-equality at the structural layer
2873        // until that field's comparison primitive lands; a future
2874        // amendment can extend this map without changing the call shape.
2875        let field_elem = match kind {
2876            NativeKind::Float64 => Some(V2ElemType::F64),
2877            NativeKind::Int64 => Some(V2ElemType::I64),
2878            NativeKind::Int32 => Some(V2ElemType::I32),
2879            NativeKind::Int16 => Some(V2ElemType::I16),
2880            NativeKind::Int8 => Some(V2ElemType::I8),
2881            NativeKind::UInt8 => Some(V2ElemType::U8),
2882            NativeKind::UInt16 => Some(V2ElemType::U16),
2883            NativeKind::UInt32 => Some(V2ElemType::U32),
2884            NativeKind::Float32 => Some(V2ElemType::F32),
2885            NativeKind::Char => Some(V2ElemType::Char),
2886            NativeKind::Bool => Some(V2ElemType::Bool),
2887            NativeKind::StringV2 => Some(V2ElemType::String),
2888            NativeKind::DecimalV2 => Some(V2ElemType::Decimal),
2889            NativeKind::Ptr(HeapKind::TypedObject) => Some(V2ElemType::TypedObject),
2890            // Null-as-sentinel field: equal iff both slots agree on the
2891            // null tag. The §2.7.5 stamp guarantees the per-slot
2892            // discriminator already filtered non-null bits before reach.
2893            NativeKind::Null => {
2894                if bits_a != bits_b {
2895                    return false;
2896                }
2897                continue;
2898            }
2899            // NativeKind::String (Arc<String> carrier) — deref via Arc
2900            // raw pointer and compare content. NOT covered by V2ElemType
2901            // (that variant is the StringV2 v2-raw carrier); handled
2902            // inline here.
2903            NativeKind::String => {
2904                if bits_a == bits_b {
2905                    continue;
2906                }
2907                if bits_a == 0 || bits_b == 0 {
2908                    return false;
2909                }
2910                let s_a = unsafe { &*(bits_a as usize as *const String) };
2911                let s_b = unsafe { &*(bits_b as usize as *const String) };
2912                if s_a != s_b {
2913                    return false;
2914                }
2915                continue;
2916            }
2917            // Other heap-kinded fields (HashMap / Deque / TraitObject /
2918            // Channel / TypedArray / etc.): strict no-Bool-default —
2919            // compare equal only when the raw pointer bits agree
2920            // (identity equality). The structurally-typed deep-equality
2921            // for these field shapes is out of J.5c scope; extending
2922            // this match without a Bool-default fallback is the correct
2923            // forward path when a future driver needs them.
2924            _ => {
2925                if bits_a != bits_b {
2926                    return false;
2927                }
2928                continue;
2929            }
2930        };
2931        if let Some(et) = field_elem {
2932            if !eq_element(bits_a, bits_b, et) {
2933                return false;
2934            }
2935        }
2936    }
2937    true
2938}
2939
2940/// Return the first index `i` where `view[i] == needle` under the
2941/// element-type's equality, or `None` if no element matches.
2942///
2943/// The needle's bits must be the value of kind matching `view.elem_type`
2944/// — the dispatch shell at `handle_index_of_v2` enforces the
2945/// kind-precondition (returns `-1` on mismatch without invoking this
2946/// primitive, mirroring the JS semantics that `[1,2,3].indexOf("1")` is
2947/// `-1` not an error).
2948///
2949/// Reads the underlying `TypedArray<T>` buffer directly via
2950/// `get_unchecked` (no `read_element` indirection) — no per-iteration
2951/// retain/release on heap-element kinds.
2952///
2953/// # Safety
2954/// Caller must guarantee `view` is a live `V2TypedArrayView` (produced
2955/// by `as_v2_typed_array`) and `needle_bits` is a value of `view.elem_type`
2956/// per the §2.7.5 producer-stamp contract.
2957#[inline]
2958pub fn position_of(view: &V2TypedArrayView, needle_bits: u64) -> Option<u32> {
2959    let n = view.len;
2960    if n == 0 {
2961        return None;
2962    }
2963    // Per-element pointer read path for heap-element kinds avoids the
2964    // per-iteration `v2_retain` work that `read_element` does — we only
2965    // need the bits, not a fresh share.
2966    macro_rules! scan_scalar {
2967        ($t:ty, $to_bits:expr) => {{
2968            let arr = view.ptr as *const TypedArray<$t>;
2969            for i in 0..n {
2970                let v = unsafe { TypedArray::<$t>::get_unchecked(arr, i) };
2971                if $to_bits(v) == needle_bits {
2972                    return Some(i);
2973                }
2974            }
2975            None
2976        }};
2977    }
2978    match view.elem_type {
2979        V2ElemType::F64 => scan_scalar!(f64, |v: f64| v.to_bits()),
2980        V2ElemType::I64 => scan_scalar!(i64, |v: i64| v as u64),
2981        V2ElemType::I32 => scan_scalar!(i32, |v: i32| (v as i64) as u64),
2982        V2ElemType::Bool => scan_scalar!(u8, |v: u8| (v != 0) as u64),
2983        V2ElemType::I8 => scan_scalar!(i8, |v: i8| (v as i64) as u64),
2984        V2ElemType::U8 => scan_scalar!(u8, |v: u8| v as u64),
2985        V2ElemType::I16 => scan_scalar!(i16, |v: i16| (v as i64) as u64),
2986        V2ElemType::U16 => scan_scalar!(u16, |v: u16| v as u64),
2987        V2ElemType::U32 => scan_scalar!(u32, |v: u32| v as u64),
2988        V2ElemType::F32 => scan_scalar!(f32, |v: f32| v.to_bits() as u64),
2989        V2ElemType::Char => scan_scalar!(char, |v: char| v as u32 as u64),
2990        V2ElemType::String => unsafe {
2991            let arr = view.ptr as *const TypedArray<*const StringObj>;
2992            for i in 0..n {
2993                let elem_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, i);
2994                if eq_element(elem_ptr as u64, needle_bits, V2ElemType::String) {
2995                    return Some(i);
2996                }
2997            }
2998            None
2999        },
3000        V2ElemType::Decimal => unsafe {
3001            let arr = view.ptr as *const TypedArray<*const DecimalObj>;
3002            for i in 0..n {
3003                let elem_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, i);
3004                if eq_element(elem_ptr as u64, needle_bits, V2ElemType::Decimal) {
3005                    return Some(i);
3006                }
3007            }
3008            None
3009        },
3010        V2ElemType::TypedObject => unsafe {
3011            let arr = view.ptr as *const TypedArray<*const TypedObjectStorage>;
3012            for i in 0..n {
3013                let elem_ptr = TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, i);
3014                if eq_element(elem_ptr as u64, needle_bits, V2ElemType::TypedObject) {
3015                    return Some(i);
3016                }
3017            }
3018            None
3019        }
3020    }
3021}
3022
3023// ── R8 W4 J.5f sort/orderBy/thenBy primitives (2026-05-24) ──────────────────
3024//
3025// Kind-generic permutation + natural-ordering compare primitives backing the
3026// `array_sort.rs` sort / orderBy / thenBy handlers (supervisor D4 v0.3 scope:
3027// basic sort + orderBy + thenBy; relational joins + groupBy → v0.4).
3028//
3029// `permute_array` copies elements at the given indices from `view` into a
3030// fresh `TypedArray<T>` of the SAME element kind — output elem_type =
3031// input elem_type. Heap-element kinds (`String`/`Decimal`/`TypedObject`)
3032// bump the per-elem refcount once per stored slot (matches the
3033// `copy_range_to_new_array` heap-arm contract; the J.5b `allocate_empty +
3034// per-element push_element` pattern works too but allocates one share per
3035// push, which over-counts when the same index appears twice in an
3036// `indices` slice; the explicit retain-per-stored-slot here is the
3037// canonical refcount discipline for the permute case).
3038//
3039// `cmp_element_natural` compares two `(bits, kind)` pairs read from the
3040// SAME view (so the discriminator on both sides is the view's elem_type)
3041// per the supervisor D4 v0.3 natural-ordering matrix:
3042//   - scalar (F64/F32/I64/I32/I16/I8/U32/U16/U8): direct `<`/`==`
3043//   - Bool: false < true
3044//   - Char: codepoint order
3045//   - String: lexicographic compare
3046//   - Decimal: rust_decimal::Decimal::cmp
3047//   - TypedObject: SURFACE — natural ordering on heap aggregates needs an
3048//     ADR-006-amendment-level decision (`Ord` trait + per-field projection).
3049//     Refused per supervisor D3 + ADR-006 §2.7.14 (no fabricated default).
3050//
3051// NaN handling: `f64::total_cmp` / `f32::total_cmp` — NaN is the largest
3052// element (Rust precedent). No silent NaN-skip / NaN-equals fabrication.
3053
3054/// Compare two elements of the SAME view by natural ordering.
3055///
3056/// Returns `Some(Ordering)` on success; `None` if the element kind has no
3057/// canonical natural ordering at v0.3 (`TypedObject` SURFACE per supervisor
3058/// D3 + ADR-006 §2.7.14 — Bool-default refused). Callers surface a
3059/// structured `RuntimeError` when `None` is returned.
3060///
3061/// `bits_a` and `bits_b` are read via `read_element(view, i)` and refer to
3062/// the same `view.elem_type` (the SAME-view discipline). For heap-element
3063/// reads (`StringV2` / `DecimalV2`), the returned `(bits, kind)` carries
3064/// the raw `*const StringObj/DecimalObj` pointer — the comparison here
3065/// dereferences but does not retain or release the pointer (the caller's
3066/// share remains live for the comparator's borrow).
3067#[inline]
3068pub fn cmp_element_natural(
3069    view: &V2TypedArrayView,
3070    bits_a: u64,
3071    bits_b: u64,
3072) -> Option<std::cmp::Ordering> {
3073    use std::cmp::Ordering;
3074    match view.elem_type {
3075        V2ElemType::F64 => Some(f64::from_bits(bits_a).total_cmp(&f64::from_bits(bits_b))),
3076        V2ElemType::F32 => Some(
3077            f32::from_bits(bits_a as u32).total_cmp(&f32::from_bits(bits_b as u32)),
3078        ),
3079        V2ElemType::I64 => Some((bits_a as i64).cmp(&(bits_b as i64))),
3080        V2ElemType::I32 => Some((bits_a as u32 as i32).cmp(&(bits_b as u32 as i32))),
3081        V2ElemType::I16 => Some((bits_a as u16 as i16).cmp(&(bits_b as u16 as i16))),
3082        V2ElemType::I8 => Some((bits_a as u8 as i8).cmp(&(bits_b as u8 as i8))),
3083        V2ElemType::U32 => Some((bits_a as u32).cmp(&(bits_b as u32))),
3084        V2ElemType::U16 => Some((bits_a as u16).cmp(&(bits_b as u16))),
3085        V2ElemType::U8 => Some((bits_a as u8).cmp(&(bits_b as u8))),
3086        V2ElemType::Bool => {
3087            // false (0) < true (non-zero) — fold any non-zero to true for
3088            // ordering parity with Rust `bool::cmp`.
3089            let a = bits_a != 0;
3090            let b = bits_b != 0;
3091            Some(a.cmp(&b))
3092        }
3093        V2ElemType::Char => {
3094            // Codepoint order — `read_element` produces a valid codepoint
3095            // per the V2ElemType::Char arm; cmp on the raw u32 produces the
3096            // same ordering as `char::cmp`.
3097            Some((bits_a as u32).cmp(&(bits_b as u32)))
3098        }
3099        V2ElemType::String => unsafe {
3100            let a_ptr = bits_a as usize as *const StringObj;
3101            let b_ptr = bits_b as usize as *const StringObj;
3102            if a_ptr.is_null() || b_ptr.is_null() {
3103                return None;
3104            }
3105            let a = StringObj::as_str(a_ptr);
3106            let b = StringObj::as_str(b_ptr);
3107            Some(a.cmp(b))
3108        },
3109        V2ElemType::Decimal => unsafe {
3110            let a_ptr = bits_a as usize as *const DecimalObj;
3111            let b_ptr = bits_b as usize as *const DecimalObj;
3112            if a_ptr.is_null() || b_ptr.is_null() {
3113                return None;
3114            }
3115            let a = DecimalObj::value(a_ptr);
3116            let b = DecimalObj::value(b_ptr);
3117            Some(a.cmp(&b))
3118        },
3119        V2ElemType::TypedObject => {
3120            // SURFACE per supervisor D3 + ADR-006 §2.7.14: natural ordering
3121            // on heap aggregates (TypedObject) requires an Ord-trait
3122            // mechanism + per-field projection decision. v0.4 territory.
3123            // Bool-default refused; return None and let caller surface the
3124            // structured RuntimeError naming the elem_type.
3125            None
3126        }
3127        _ => {
3128            // Defensive: any other elem_type without natural ordering.
3129            // Returns None; caller surfaces structured RuntimeError.
3130            #[allow(unreachable_patterns)]
3131            None
3132        }
3133    }
3134}
3135
3136/// Materialize a permutation of `view` into a fresh `TypedArray<T>` of the
3137/// same element kind. `indices[i]` selects the source element at position
3138/// `i` of the output. Indices out of range `[0, view.len)` are skipped
3139/// (defensive — callers should validate up front; out-of-range indices
3140/// indicate a sort comparator bug, not a user-input edge).
3141///
3142/// Refcount discipline for heap-element kinds (`String` / `Decimal` /
3143/// `TypedObject`): each stored slot receives one `v2_retain` on the
3144/// per-element header so the output array independently owns its shares.
3145/// This matches the `copy_range_to_new_array` heap-arm contract — the
3146/// caller's input view share is unaffected by this function (read-only).
3147///
3148/// Used by `array_sort::handle_sort_v2` / `handle_order_by_v2` /
3149/// `handle_then_by_v2` to materialize the sorted permutation. Kind-generic
3150/// across all 14 monomorphized `TypedArray<T>` carriers per supervisor D4
3151/// v0.3 scope.
3152pub fn permute_array(view: &V2TypedArrayView, indices: &[u32]) -> *mut u8 {
3153    let out_len = indices.len() as u32;
3154
3155    /// Helper: scalar permute — read source[indices[i]] and write to
3156    /// dst[i]. Out-of-range indices are skipped silently (sort
3157    /// comparator bugs surface elsewhere); the corresponding output
3158    /// slot is left at its `with_capacity` initial bit pattern. Output
3159    /// length is set to the number of successfully written slots so
3160    /// the output never claims an uninitialized slot is valid.
3161    ///
3162    /// Returns the actual write count (== indices.len() in the
3163    /// well-formed case where every index is in `[0, src_len)`).
3164    #[inline]
3165    unsafe fn permute_scalar<T: Copy>(
3166        src_data: *const T,
3167        dst_data: *mut T,
3168        src_len: u32,
3169        indices: &[u32],
3170    ) -> u32 {
3171        if src_data.is_null() || dst_data.is_null() {
3172            return 0;
3173        }
3174        let mut w: u32 = 0;
3175        for &idx in indices {
3176            if idx >= src_len {
3177                continue;
3178            }
3179            unsafe {
3180                let v = *src_data.add(idx as usize);
3181                *dst_data.add(w as usize) = v;
3182            }
3183            w += 1;
3184        }
3185        w
3186    }
3187
3188    match view.elem_type {
3189        V2ElemType::F64 => unsafe {
3190            let new_arr = TypedArray::<f64>::with_capacity(out_len);
3191            let src = view.ptr as *const TypedArray<f64>;
3192            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3193            (*new_arr).len = written;
3194            let p = new_arr as *mut u8;
3195            stamp_elem_type(p, ELEM_TYPE_F64);
3196            p
3197        },
3198        V2ElemType::I64 => unsafe {
3199            let new_arr = TypedArray::<i64>::with_capacity(out_len);
3200            let src = view.ptr as *const TypedArray<i64>;
3201            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3202            (*new_arr).len = written;
3203            let p = new_arr as *mut u8;
3204            stamp_elem_type(p, ELEM_TYPE_I64);
3205            p
3206        },
3207        V2ElemType::I32 => unsafe {
3208            let new_arr = TypedArray::<i32>::with_capacity(out_len);
3209            let src = view.ptr as *const TypedArray<i32>;
3210            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3211            (*new_arr).len = written;
3212            let p = new_arr as *mut u8;
3213            stamp_elem_type(p, ELEM_TYPE_I32);
3214            p
3215        },
3216        V2ElemType::Bool => unsafe {
3217            let new_arr = TypedArray::<u8>::with_capacity(out_len);
3218            let src = view.ptr as *const TypedArray<u8>;
3219            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3220            (*new_arr).len = written;
3221            let p = new_arr as *mut u8;
3222            stamp_elem_type(p, ELEM_TYPE_BOOL);
3223            p
3224        },
3225        V2ElemType::I8 => unsafe {
3226            let new_arr = TypedArray::<i8>::with_capacity(out_len);
3227            let src = view.ptr as *const TypedArray<i8>;
3228            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3229            (*new_arr).len = written;
3230            let p = new_arr as *mut u8;
3231            stamp_elem_type(p, ELEM_TYPE_I8);
3232            p
3233        },
3234        V2ElemType::U8 => unsafe {
3235            let new_arr = TypedArray::<u8>::with_capacity(out_len);
3236            let src = view.ptr as *const TypedArray<u8>;
3237            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3238            (*new_arr).len = written;
3239            let p = new_arr as *mut u8;
3240            stamp_elem_type(p, ELEM_TYPE_U8);
3241            p
3242        },
3243        V2ElemType::I16 => unsafe {
3244            let new_arr = TypedArray::<i16>::with_capacity(out_len);
3245            let src = view.ptr as *const TypedArray<i16>;
3246            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3247            (*new_arr).len = written;
3248            let p = new_arr as *mut u8;
3249            stamp_elem_type(p, ELEM_TYPE_I16);
3250            p
3251        },
3252        V2ElemType::U16 => unsafe {
3253            let new_arr = TypedArray::<u16>::with_capacity(out_len);
3254            let src = view.ptr as *const TypedArray<u16>;
3255            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3256            (*new_arr).len = written;
3257            let p = new_arr as *mut u8;
3258            stamp_elem_type(p, ELEM_TYPE_U16);
3259            p
3260        },
3261        V2ElemType::U32 => unsafe {
3262            let new_arr = TypedArray::<u32>::with_capacity(out_len);
3263            let src = view.ptr as *const TypedArray<u32>;
3264            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3265            (*new_arr).len = written;
3266            let p = new_arr as *mut u8;
3267            stamp_elem_type(p, ELEM_TYPE_U32);
3268            p
3269        },
3270        V2ElemType::F32 => unsafe {
3271            let new_arr = TypedArray::<f32>::with_capacity(out_len);
3272            let src = view.ptr as *const TypedArray<f32>;
3273            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3274            (*new_arr).len = written;
3275            let p = new_arr as *mut u8;
3276            stamp_elem_type(p, ELEM_TYPE_F32);
3277            p
3278        },
3279        V2ElemType::Char => unsafe {
3280            let new_arr = TypedArray::<char>::with_capacity(out_len);
3281            let src = view.ptr as *const TypedArray<char>;
3282            let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
3283            (*new_arr).len = written;
3284            let p = new_arr as *mut u8;
3285            stamp_elem_type(p, ELEM_TYPE_CHAR);
3286            p
3287        },
3288        V2ElemType::String => unsafe {
3289            let new_arr = TypedArray::<*const StringObj>::with_capacity(out_len);
3290            let src = view.ptr as *const TypedArray<*const StringObj>;
3291            let src_data = (*src).data;
3292            let dst_data = (*new_arr).data;
3293            let mut w: u32 = 0;
3294            if !src_data.is_null() && !dst_data.is_null() {
3295                for &idx in indices {
3296                    if idx >= view.len {
3297                        continue;
3298                    }
3299                    let elem = *src_data.add(idx as usize);
3300                    v2_retain(&(*elem).header);
3301                    *dst_data.add(w as usize) = elem;
3302                    w += 1;
3303                }
3304            }
3305            (*new_arr).len = w;
3306            let p = new_arr as *mut u8;
3307            stamp_elem_type(p, ELEM_TYPE_STRING);
3308            p
3309        },
3310        V2ElemType::Decimal => unsafe {
3311            let new_arr = TypedArray::<*const DecimalObj>::with_capacity(out_len);
3312            let src = view.ptr as *const TypedArray<*const DecimalObj>;
3313            let src_data = (*src).data;
3314            let dst_data = (*new_arr).data;
3315            let mut w: u32 = 0;
3316            if !src_data.is_null() && !dst_data.is_null() {
3317                for &idx in indices {
3318                    if idx >= view.len {
3319                        continue;
3320                    }
3321                    let elem = *src_data.add(idx as usize);
3322                    v2_retain(&(*elem).header);
3323                    *dst_data.add(w as usize) = elem;
3324                    w += 1;
3325                }
3326            }
3327            (*new_arr).len = w;
3328            let p = new_arr as *mut u8;
3329            stamp_elem_type(p, ELEM_TYPE_DECIMAL);
3330            p
3331        },
3332        V2ElemType::TypedObject => unsafe {
3333            let new_arr =
3334                TypedArray::<*const TypedObjectStorage>::with_capacity(out_len);
3335            let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
3336            let src_data = (*src).data;
3337            let dst_data = (*new_arr).data;
3338            let mut w: u32 = 0;
3339            if !src_data.is_null() && !dst_data.is_null() {
3340                for &idx in indices {
3341                    if idx >= view.len {
3342                        continue;
3343                    }
3344                    let elem = *src_data.add(idx as usize);
3345                    v2_retain(&(*elem).header);
3346                    *dst_data.add(w as usize) = elem;
3347                    w += 1;
3348                }
3349            }
3350            (*new_arr).len = w;
3351            let p = new_arr as *mut u8;
3352            stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
3353            p
3354        },
3355    }
3356}
3357
3358/// Return `true` iff any element of `view` equals `needle_bits` under
3359/// the element-type's equality.
3360///
3361/// Thin wrapper over `position_of` — present as a named primitive so the
3362/// per-op driver naming in `array_query.rs` (`handle_includes_v2`)
3363/// matches the surface comment (J.5 territory: per-kind value-equality
3364/// `v2_array_detect::contains_element` primitive).
3365#[inline]
3366pub fn contains_element(view: &V2TypedArrayView, needle_bits: u64) -> bool {
3367    position_of(view, needle_bits).is_some()
3368}
3369
3370#[cfg(test)]
3371mod tests {
3372    use super::*;
3373
3374    /// Build the kinded `(bits, kind)` pair for a v2 typed array pointer
3375    /// (the shape `v2_handlers/array.rs` push: raw ptr bits +
3376    /// `Ptr(HeapKind::TypedArray)` per r5c-2-β-CKPT-C).
3377    #[inline]
3378    fn ptr_pair(ptr: *mut u8) -> (u64, NativeKind) {
3379        (ptr as usize as u64, NativeKind::Ptr(HeapKind::TypedArray))
3380    }
3381
3382    #[test]
3383    fn test_stamp_and_read_elem_type_f64() {
3384        let arr = TypedArray::<f64>::with_capacity(0);
3385        unsafe {
3386            stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
3387            let byte = read_elem_type_byte(arr as *const u8);
3388            assert_eq!(byte, ELEM_TYPE_F64);
3389            TypedArray::drop_array(arr);
3390        }
3391    }
3392
3393    #[test]
3394    fn test_as_v2_typed_array_recognizes_stamped_f64() {
3395        let arr = TypedArray::<f64>::with_capacity(4);
3396        unsafe {
3397            TypedArray::push(arr, 1.5);
3398            TypedArray::push(arr, 2.5);
3399            stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
3400        }
3401        let (bits, kind) = ptr_pair(arr as *mut u8);
3402        let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
3403        assert_eq!(view.elem_type, V2ElemType::F64);
3404        assert_eq!(view.len, 2);
3405        unsafe {
3406            TypedArray::drop_array(arr);
3407        }
3408    }
3409
3410    #[test]
3411    fn test_read_element_i64_indices() {
3412        let arr = TypedArray::<i64>::from_slice(&[10, 20, 30]);
3413        unsafe {
3414            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3415        }
3416        let (bits, kind) = ptr_pair(arr as *mut u8);
3417        let view = as_v2_typed_array(bits, kind).unwrap();
3418        assert_eq!(read_element(&view, 0), Some((10u64, NativeKind::Int64)));
3419        assert_eq!(read_element(&view, 1), Some((20u64, NativeKind::Int64)));
3420        assert_eq!(read_element(&view, 2), Some((30u64, NativeKind::Int64)));
3421        assert!(read_element(&view, 3).is_none());
3422        unsafe {
3423            TypedArray::drop_array(arr);
3424        }
3425    }
3426
3427    #[test]
3428    fn test_clone_array_i64() {
3429        let arr = TypedArray::<i64>::from_slice(&[100, 200, 300]);
3430        unsafe {
3431            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3432        }
3433        let (bits, kind) = ptr_pair(arr as *mut u8);
3434        let view = as_v2_typed_array(bits, kind).unwrap();
3435        let cloned_ptr = clone_array(&view);
3436        let (cb, ck) = ptr_pair(cloned_ptr);
3437        let cloned_view = as_v2_typed_array(cb, ck).expect("clone should be detectable");
3438        assert_eq!(cloned_view.elem_type, V2ElemType::I64);
3439        assert_eq!(cloned_view.len, 3);
3440        assert_eq!(read_element(&cloned_view, 0), Some((100u64, NativeKind::Int64)));
3441        unsafe {
3442            TypedArray::<i64>::drop_array(cloned_ptr as *mut TypedArray<i64>);
3443            TypedArray::drop_array(arr);
3444        }
3445    }
3446
3447    // ──────────────────────────────────────────────────────────────────────
3448    // Wave 2 Agent A1 (2026-05-14) — F32 + Char round-trip smokes.
3449    // ──────────────────────────────────────────────────────────────────────
3450
3451    #[test]
3452    fn test_stamp_and_read_elem_type_f32_char() {
3453        let arr_f32 = TypedArray::<f32>::with_capacity(0);
3454        let arr_char = TypedArray::<char>::with_capacity(0);
3455        unsafe {
3456            stamp_elem_type(arr_f32 as *mut u8, ELEM_TYPE_F32);
3457            stamp_elem_type(arr_char as *mut u8, ELEM_TYPE_CHAR);
3458            assert_eq!(read_elem_type_byte(arr_f32 as *const u8), ELEM_TYPE_F32);
3459            assert_eq!(read_elem_type_byte(arr_char as *const u8), ELEM_TYPE_CHAR);
3460            TypedArray::drop_array(arr_f32);
3461            TypedArray::drop_array(arr_char);
3462        }
3463    }
3464
3465    #[test]
3466    fn test_as_v2_typed_array_recognizes_stamped_f32() {
3467        let arr = TypedArray::<f32>::with_capacity(4);
3468        unsafe {
3469            TypedArray::push(arr, 1.5_f32);
3470            TypedArray::push(arr, 2.5_f32);
3471            stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32);
3472        }
3473        let (bits, kind) = ptr_pair(arr as *mut u8);
3474        let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
3475        assert_eq!(view.elem_type, V2ElemType::F32);
3476        assert_eq!(view.len, 2);
3477        unsafe { TypedArray::drop_array(arr); }
3478    }
3479
3480    #[test]
3481    fn test_as_v2_typed_array_recognizes_stamped_char() {
3482        let arr = TypedArray::<char>::with_capacity(4);
3483        unsafe {
3484            TypedArray::push(arr, 'A');
3485            TypedArray::push(arr, '☃');
3486            stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR);
3487        }
3488        let (bits, kind) = ptr_pair(arr as *mut u8);
3489        let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
3490        assert_eq!(view.elem_type, V2ElemType::Char);
3491        assert_eq!(view.len, 2);
3492        unsafe { TypedArray::drop_array(arr); }
3493    }
3494
3495    #[test]
3496    fn test_read_element_f32() {
3497        let arr = TypedArray::<f32>::from_slice(&[1.5_f32, 2.25_f32, 3.0_f32]);
3498        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
3499        let (bits, kind) = ptr_pair(arr as *mut u8);
3500        let view = as_v2_typed_array(bits, kind).unwrap();
3501        let r0 = read_element(&view, 0).unwrap();
3502        let r1 = read_element(&view, 1).unwrap();
3503        let r2 = read_element(&view, 2).unwrap();
3504        assert_eq!(r0.1, NativeKind::Float32);
3505        assert_eq!(f32::from_bits(r0.0 as u32), 1.5_f32);
3506        assert_eq!(f32::from_bits(r1.0 as u32), 2.25_f32);
3507        assert_eq!(f32::from_bits(r2.0 as u32), 3.0_f32);
3508        assert!(read_element(&view, 3).is_none());
3509        unsafe { TypedArray::drop_array(arr); }
3510    }
3511
3512    #[test]
3513    fn test_read_element_char() {
3514        let arr = TypedArray::<char>::from_slice(&['h', 'i', '!']);
3515        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
3516        let (bits, kind) = ptr_pair(arr as *mut u8);
3517        let view = as_v2_typed_array(bits, kind).unwrap();
3518        for (i, expected) in ['h', 'i', '!'].iter().enumerate() {
3519            let (b, k) = read_element(&view, i as u32).unwrap();
3520            assert_eq!(k, NativeKind::Char);
3521            assert_eq!(char::from_u32(b as u32).unwrap(), *expected);
3522        }
3523        assert!(read_element(&view, 3).is_none());
3524        unsafe { TypedArray::drop_array(arr); }
3525    }
3526
3527    #[test]
3528    fn test_push_element_f32() {
3529        let arr = TypedArray::<f32>::with_capacity(4);
3530        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
3531        let (bits, kind) = ptr_pair(arr as *mut u8);
3532        let view = as_v2_typed_array(bits, kind).unwrap();
3533        push_element(&view, (1.5_f32).to_bits() as u64, NativeKind::Float32).unwrap();
3534        // Refresh view to see the new len.
3535        let view = as_v2_typed_array(bits, kind).unwrap();
3536        let (b, k) = read_element(&view, 0).unwrap();
3537        assert_eq!(k, NativeKind::Float32);
3538        assert_eq!(f32::from_bits(b as u32), 1.5_f32);
3539        unsafe { TypedArray::drop_array(arr); }
3540    }
3541
3542    #[test]
3543    fn test_push_element_char() {
3544        let arr = TypedArray::<char>::with_capacity(4);
3545        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
3546        let (bits, kind) = ptr_pair(arr as *mut u8);
3547        let view = as_v2_typed_array(bits, kind).unwrap();
3548        push_element(&view, 'Z' as u32 as u64, NativeKind::Char).unwrap();
3549        let view = as_v2_typed_array(bits, kind).unwrap();
3550        let (b, _) = read_element(&view, 0).unwrap();
3551        assert_eq!(char::from_u32(b as u32).unwrap(), 'Z');
3552        unsafe { TypedArray::drop_array(arr); }
3553    }
3554
3555    #[test]
3556    fn test_clone_array_f32() {
3557        let arr = TypedArray::<f32>::from_slice(&[1.0_f32, 2.0_f32, 3.0_f32]);
3558        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
3559        let (bits, kind) = ptr_pair(arr as *mut u8);
3560        let view = as_v2_typed_array(bits, kind).unwrap();
3561        let cloned = clone_array(&view);
3562        let (cb, ck) = ptr_pair(cloned);
3563        let cv = as_v2_typed_array(cb, ck).unwrap();
3564        assert_eq!(cv.elem_type, V2ElemType::F32);
3565        assert_eq!(cv.len, 3);
3566        unsafe {
3567            TypedArray::<f32>::drop_array(cloned as *mut TypedArray<f32>);
3568            TypedArray::drop_array(arr);
3569        }
3570    }
3571
3572    #[test]
3573    fn test_clone_array_char() {
3574        let arr = TypedArray::<char>::from_slice(&['a', 'b', 'c']);
3575        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
3576        let (bits, kind) = ptr_pair(arr as *mut u8);
3577        let view = as_v2_typed_array(bits, kind).unwrap();
3578        let cloned = clone_array(&view);
3579        let (cb, ck) = ptr_pair(cloned);
3580        let cv = as_v2_typed_array(cb, ck).unwrap();
3581        assert_eq!(cv.elem_type, V2ElemType::Char);
3582        assert_eq!(cv.len, 3);
3583        unsafe {
3584            TypedArray::<char>::drop_array(cloned as *mut TypedArray<char>);
3585            TypedArray::drop_array(arr);
3586        }
3587    }
3588
3589    #[test]
3590    fn test_non_pointer_value_returns_none() {
3591        // Wrong kind: integer literal, not a pointer.
3592        assert!(as_v2_typed_array(42u64, NativeKind::Int64).is_none());
3593
3594        // Wrong kind: float bits.
3595        assert!(as_v2_typed_array(3.14_f64.to_bits(), NativeKind::Float64).is_none());
3596
3597        // Wrong kind: bool.
3598        assert!(as_v2_typed_array(1u64, NativeKind::Bool).is_none());
3599
3600        // r5c-2-β-CKPT-C: a genuine scalar `u64` (kind = `NativeKind::UInt64`)
3601        // is NOT the typed-array carrier. The function rejects it on the kind
3602        // alone — the bits (here `u64::MAX`) are never dereferenced as a
3603        // pointer. This is the regression guard for the SIGSEGV in
3604        // `let x: u64 = 18446744073709551615; print(x)`.
3605        assert!(as_v2_typed_array(u64::MAX, NativeKind::UInt64).is_none());
3606        assert!(as_v2_typed_array(1000u64, NativeKind::UInt64).is_none());
3607        assert!(as_v2_typed_array(0u64, NativeKind::UInt64).is_none());
3608
3609        // Right kind but null pointer.
3610        assert!(
3611            as_v2_typed_array(0u64, NativeKind::Ptr(HeapKind::TypedArray)).is_none()
3612        );
3613    }
3614
3615    /// r5c-2-β-CKPT-C: the kind track itself is the carrier discriminator.
3616    /// The IDENTICAL pointer bits are recognised as a typed array under the
3617    /// `Ptr(HeapKind::TypedArray)` carrier kind and REJECTED under the
3618    /// scalar `UInt64` kind — `as_v2_typed_array` never inspects the value
3619    /// of the bits to guess whether it is a pointer (no low-address /
3620    /// is_heap heuristic).
3621    #[test]
3622    fn test_kind_track_discriminates_array_carrier_from_scalar() {
3623        let arr = TypedArray::<i64>::from_slice(&[100, 200]);
3624        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64) };
3625        let bits = arr as usize as u64;
3626
3627        // Same bits, array carrier kind → detected.
3628        let view = as_v2_typed_array(bits, NativeKind::Ptr(HeapKind::TypedArray))
3629            .expect("array carrier kind must detect the typed array");
3630        assert_eq!(view.elem_type, V2ElemType::I64);
3631        assert_eq!(view.len, 2);
3632
3633        // Same bits, scalar `UInt64` kind → rejected on the kind alone.
3634        assert!(
3635            as_v2_typed_array(bits, NativeKind::UInt64).is_none(),
3636            "a scalar-u64 kind must NOT be treated as the array carrier"
3637        );
3638
3639        unsafe { TypedArray::<i64>::drop_array(arr) };
3640    }
3641
3642    // ──────────────────────────────────────────────────────────────────────
3643    // Wave 2 Agent A2 (2026-05-14) — String + Decimal heap-element round-trip
3644    // smokes. Per audit §3.2 S2-prime + §4.1.B.4 migration recipe:
3645    // `TypedArray<*const StringObj/DecimalObj>` element-read retains the
3646    // per-element header before pushing the slot bits with NativeKind::
3647    // StringV2 / DecimalV2 (Agent B's Round 1 carrier-shape variants).
3648    // ──────────────────────────────────────────────────────────────────────
3649
3650    #[test]
3651    fn test_stamp_and_read_elem_type_string_decimal() {
3652        let arr_string = TypedArray::<*const StringObj>::with_capacity(0);
3653        let arr_decimal = TypedArray::<*const DecimalObj>::with_capacity(0);
3654        unsafe {
3655            stamp_elem_type(arr_string as *mut u8, ELEM_TYPE_STRING);
3656            stamp_elem_type(arr_decimal as *mut u8, ELEM_TYPE_DECIMAL);
3657            assert_eq!(read_elem_type_byte(arr_string as *const u8), ELEM_TYPE_STRING);
3658            assert_eq!(read_elem_type_byte(arr_decimal as *const u8), ELEM_TYPE_DECIMAL);
3659            TypedArray::<*const StringObj>::drop_array_heap(arr_string);
3660            TypedArray::<*const DecimalObj>::drop_array_heap(arr_decimal);
3661        }
3662    }
3663
3664    #[test]
3665    fn test_as_v2_typed_array_recognizes_stamped_string() {
3666        let arr = TypedArray::<*const StringObj>::with_capacity(4);
3667        unsafe {
3668            let s = StringObj::new("hello");
3669            TypedArray::push(arr, s as *const StringObj);
3670            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
3671        }
3672        let (bits, kind) = ptr_pair(arr as *mut u8);
3673        let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
3674        assert_eq!(view.elem_type, V2ElemType::String);
3675        assert_eq!(view.len, 1);
3676        unsafe { TypedArray::<*const StringObj>::drop_array_heap(arr); }
3677    }
3678
3679    #[test]
3680    fn test_as_v2_typed_array_recognizes_stamped_decimal() {
3681        use rust_decimal::Decimal;
3682        use rust_decimal::prelude::FromPrimitive;
3683        let arr = TypedArray::<*const DecimalObj>::with_capacity(4);
3684        unsafe {
3685            let d = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
3686            TypedArray::push(arr, d as *const DecimalObj);
3687            stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
3688        }
3689        let (bits, kind) = ptr_pair(arr as *mut u8);
3690        let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
3691        assert_eq!(view.elem_type, V2ElemType::Decimal);
3692        assert_eq!(view.len, 1);
3693        unsafe { TypedArray::<*const DecimalObj>::drop_array_heap(arr); }
3694    }
3695
3696    #[test]
3697    fn test_read_element_string_retains_share() {
3698        use shape_value::v2::refcount::v2_get_refcount;
3699        unsafe {
3700            let arr = TypedArray::<*const StringObj>::with_capacity(4);
3701            let s = StringObj::new("greetings");
3702            // Initial refcount: 1 (from `StringObj::new`).
3703            assert_eq!(v2_get_refcount(&(*s).header), 1);
3704            TypedArray::push(arr, s as *const StringObj);
3705            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
3706            let (bits, kind) = ptr_pair(arr as *mut u8);
3707            let view = as_v2_typed_array(bits, kind).unwrap();
3708            // read_element retains: refcount goes 1 → 2.
3709            let (read_bits, read_kind) = read_element(&view, 0).unwrap();
3710            assert_eq!(read_kind, NativeKind::StringV2);
3711            assert_eq!(read_bits, s as u64);
3712            assert_eq!(v2_get_refcount(&(*s).header), 2);
3713            // Release the read share (simulates the StringV2 arm in
3714            // drop_with_kind dropping the slot).
3715            <StringObj as HeapElement>::release_elem(s);
3716            assert_eq!(v2_get_refcount(&(*s).header), 1);
3717            // drop_array_heap releases the array's share → free.
3718            TypedArray::<*const StringObj>::drop_array_heap(arr);
3719        }
3720    }
3721
3722    #[test]
3723    fn test_read_element_decimal_retains_share() {
3724        use rust_decimal::Decimal;
3725        use rust_decimal::prelude::FromPrimitive;
3726        use shape_value::v2::refcount::v2_get_refcount;
3727        unsafe {
3728            let arr = TypedArray::<*const DecimalObj>::with_capacity(4);
3729            let d = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
3730            assert_eq!(v2_get_refcount(&(*d).header), 1);
3731            TypedArray::push(arr, d as *const DecimalObj);
3732            stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
3733            let (bits, kind) = ptr_pair(arr as *mut u8);
3734            let view = as_v2_typed_array(bits, kind).unwrap();
3735            let (read_bits, read_kind) = read_element(&view, 0).unwrap();
3736            assert_eq!(read_kind, NativeKind::DecimalV2);
3737            assert_eq!(read_bits, d as u64);
3738            assert_eq!(v2_get_refcount(&(*d).header), 2);
3739            <DecimalObj as HeapElement>::release_elem(d);
3740            assert_eq!(v2_get_refcount(&(*d).header), 1);
3741            TypedArray::<*const DecimalObj>::drop_array_heap(arr);
3742        }
3743    }
3744
3745    #[test]
3746    fn test_push_element_string_kind_mismatch_refused() {
3747        // The architectural surface only accepts NativeKind::StringV2 — the
3748        // Q25.A SUPERSEDED #3 mixed-migration forbidden pattern. Pushing
3749        // legacy `NativeKind::String` (Phase-2c `Arc<String>` carrier) at
3750        // this layer would silently corrupt the buffer (the bits are an Arc,
3751        // not a *const StringObj). The arm returns Err structurally.
3752        let arr = TypedArray::<*const StringObj>::with_capacity(4);
3753        unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING); }
3754        let (bits, kind) = ptr_pair(arr as *mut u8);
3755        let view = as_v2_typed_array(bits, kind).unwrap();
3756        // Pretend we have an Arc<String> bit pattern with the legacy
3757        // NativeKind::String — this is the cross-tier mismatch.
3758        let result = push_element(&view, 0xDEAD_BEEF, NativeKind::String);
3759        assert!(result.is_err());
3760        let err = result.unwrap_err();
3761        assert!(err.contains("StringV2"), "expected error to cite StringV2, got: {}", err);
3762        unsafe { TypedArray::<*const StringObj>::drop_array_heap(arr); }
3763    }
3764
3765    #[test]
3766    fn test_clone_array_string_retains_each_element() {
3767        use shape_value::v2::refcount::v2_get_refcount;
3768        unsafe {
3769            let arr = TypedArray::<*const StringObj>::with_capacity(2);
3770            let s1 = StringObj::new("foo");
3771            let s2 = StringObj::new("bar");
3772            TypedArray::push(arr, s1 as *const StringObj);
3773            TypedArray::push(arr, s2 as *const StringObj);
3774            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
3775            // Each element starts at refcount 1.
3776            assert_eq!(v2_get_refcount(&(*s1).header), 1);
3777            assert_eq!(v2_get_refcount(&(*s2).header), 1);
3778            let (bits, kind) = ptr_pair(arr as *mut u8);
3779            let view = as_v2_typed_array(bits, kind).unwrap();
3780            let cloned = clone_array(&view);
3781            // Both originals now have refcount 2 (one share per array).
3782            assert_eq!(v2_get_refcount(&(*s1).header), 2);
3783            assert_eq!(v2_get_refcount(&(*s2).header), 2);
3784            let (cb, ck) = ptr_pair(cloned);
3785            let cv = as_v2_typed_array(cb, ck).unwrap();
3786            assert_eq!(cv.elem_type, V2ElemType::String);
3787            assert_eq!(cv.len, 2);
3788            // Drop the clone — refcounts drop back to 1.
3789            TypedArray::<*const StringObj>::drop_array_heap(cloned as *mut TypedArray<*const StringObj>);
3790            assert_eq!(v2_get_refcount(&(*s1).header), 1);
3791            assert_eq!(v2_get_refcount(&(*s2).header), 1);
3792            // Drop the original — frees both StringObj allocations.
3793            TypedArray::<*const StringObj>::drop_array_heap(arr);
3794        }
3795    }
3796
3797    #[test]
3798    fn test_pop_element_string_transfers_share() {
3799        use shape_value::v2::refcount::v2_get_refcount;
3800        unsafe {
3801            let arr = TypedArray::<*const StringObj>::with_capacity(2);
3802            let s = StringObj::new("popme");
3803            TypedArray::push(arr, s as *const StringObj);
3804            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
3805            assert_eq!(v2_get_refcount(&(*s).header), 1);
3806            let (bits, kind) = ptr_pair(arr as *mut u8);
3807            let view = as_v2_typed_array(bits, kind).unwrap();
3808            // pop transfers the array's share to the caller (no retain).
3809            let (popped_bits, popped_kind) = pop_element(&view).unwrap();
3810            assert_eq!(popped_kind, NativeKind::StringV2);
3811            assert_eq!(popped_bits, s as u64);
3812            // Refcount unchanged at 1 (the share moved).
3813            assert_eq!(v2_get_refcount(&(*s).header), 1);
3814            // Release the caller-side share via HeapElement → free.
3815            <StringObj as HeapElement>::release_elem(s);
3816            TypedArray::<*const StringObj>::drop_array_heap(arr);
3817        }
3818    }
3819
3820    // ──────────────────────────────────────────────────────────────────────
3821    // R8 W3 J.5a (2026-05-24) — reverse / concat / slice / take / drop /
3822    // skip primitive round-trip smokes.
3823    //
3824    // Each primitive shares the `clone_array` scaffold (per-V2ElemType
3825    // allocator + stamp + retain). The tests exercise the most common
3826    // element kinds (I64, F64) for empirical confirmation that the
3827    // result array has the expected length, stamped element type, and
3828    // element values in the expected order; heap-element retain paths
3829    // (String) are also smoke-tested via `reverse_array` to guard the
3830    // per-element `v2_retain` discipline.
3831    // ──────────────────────────────────────────────────────────────────────
3832
3833    #[test]
3834    fn test_reverse_array_i64() {
3835        unsafe {
3836            let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
3837            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3838            let (bits, kind) = ptr_pair(arr as *mut u8);
3839            let view = as_v2_typed_array(bits, kind).unwrap();
3840            let new_ptr = reverse_array(&view);
3841            let new_view =
3842                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3843                    .unwrap();
3844            assert_eq!(new_view.elem_type, V2ElemType::I64);
3845            assert_eq!(new_view.len, 5);
3846            let new_arr = new_ptr as *const TypedArray<i64>;
3847            let data = (*new_arr).data;
3848            assert_eq!(*data.add(0), 5);
3849            assert_eq!(*data.add(1), 4);
3850            assert_eq!(*data.add(2), 3);
3851            assert_eq!(*data.add(3), 2);
3852            assert_eq!(*data.add(4), 1);
3853            TypedArray::<i64>::drop_array(arr);
3854            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
3855        }
3856    }
3857
3858    #[test]
3859    fn test_reverse_array_empty() {
3860        unsafe {
3861            let arr = TypedArray::<i64>::with_capacity(0);
3862            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3863            let (bits, kind) = ptr_pair(arr as *mut u8);
3864            let view = as_v2_typed_array(bits, kind).unwrap();
3865            let new_ptr = reverse_array(&view);
3866            let new_view =
3867                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3868                    .unwrap();
3869            assert_eq!(new_view.len, 0);
3870            TypedArray::<i64>::drop_array(arr);
3871            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
3872        }
3873    }
3874
3875    #[test]
3876    fn test_reverse_array_string_retains_each_element() {
3877        use shape_value::v2::refcount::v2_get_refcount;
3878        unsafe {
3879            let s1 = StringObj::new("a");
3880            let s2 = StringObj::new("b");
3881            let arr = TypedArray::<*const StringObj>::with_capacity(2);
3882            TypedArray::push(arr, s1 as *const StringObj);
3883            TypedArray::push(arr, s2 as *const StringObj);
3884            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
3885            assert_eq!(v2_get_refcount(&(*s1).header), 1);
3886            assert_eq!(v2_get_refcount(&(*s2).header), 1);
3887
3888            let (bits, kind) = ptr_pair(arr as *mut u8);
3889            let view = as_v2_typed_array(bits, kind).unwrap();
3890            let new_ptr = reverse_array(&view);
3891
3892            // After reverse, both s1 and s2 have refcount 2 — once owned by
3893            // the source array, once by the new reversed array.
3894            assert_eq!(v2_get_refcount(&(*s1).header), 2);
3895            assert_eq!(v2_get_refcount(&(*s2).header), 2);
3896
3897            let new_view =
3898                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3899                    .unwrap();
3900            assert_eq!(new_view.elem_type, V2ElemType::String);
3901            assert_eq!(new_view.len, 2);
3902            let new_arr = new_ptr as *const TypedArray<*const StringObj>;
3903            let data = (*new_arr).data;
3904            assert_eq!(*data.add(0), s2 as *const StringObj);
3905            assert_eq!(*data.add(1), s1 as *const StringObj);
3906
3907            TypedArray::<*const StringObj>::drop_array_heap(arr);
3908            TypedArray::<*const StringObj>::drop_array_heap(
3909                new_ptr as *mut TypedArray<*const StringObj>,
3910            );
3911            // Both StringObj allocations are now freed by `drop_array_heap`.
3912        }
3913    }
3914
3915    #[test]
3916    fn test_concat_arrays_i64() {
3917        unsafe {
3918            let a = TypedArray::<i64>::from_slice(&[1, 2]);
3919            let b = TypedArray::<i64>::from_slice(&[3, 4, 5]);
3920            stamp_elem_type(a as *mut u8, ELEM_TYPE_I64);
3921            stamp_elem_type(b as *mut u8, ELEM_TYPE_I64);
3922            let view_a = as_v2_typed_array(a as u64, NativeKind::Ptr(HeapKind::TypedArray))
3923                .unwrap();
3924            let view_b = as_v2_typed_array(b as u64, NativeKind::Ptr(HeapKind::TypedArray))
3925                .unwrap();
3926            let new_ptr = concat_arrays(&view_a, &view_b).unwrap();
3927            let new_view =
3928                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3929                    .unwrap();
3930            assert_eq!(new_view.elem_type, V2ElemType::I64);
3931            assert_eq!(new_view.len, 5);
3932            let new_arr = new_ptr as *const TypedArray<i64>;
3933            let data = (*new_arr).data;
3934            assert_eq!(*data.add(0), 1);
3935            assert_eq!(*data.add(1), 2);
3936            assert_eq!(*data.add(2), 3);
3937            assert_eq!(*data.add(3), 4);
3938            assert_eq!(*data.add(4), 5);
3939            TypedArray::<i64>::drop_array(a);
3940            TypedArray::<i64>::drop_array(b);
3941            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
3942        }
3943    }
3944
3945    #[test]
3946    fn test_concat_arrays_kind_mismatch() {
3947        unsafe {
3948            let a = TypedArray::<i64>::from_slice(&[1, 2]);
3949            let b = TypedArray::<f64>::from_slice(&[3.0, 4.0]);
3950            stamp_elem_type(a as *mut u8, ELEM_TYPE_I64);
3951            stamp_elem_type(b as *mut u8, ELEM_TYPE_F64);
3952            let view_a = as_v2_typed_array(a as u64, NativeKind::Ptr(HeapKind::TypedArray))
3953                .unwrap();
3954            let view_b = as_v2_typed_array(b as u64, NativeKind::Ptr(HeapKind::TypedArray))
3955                .unwrap();
3956            assert!(concat_arrays(&view_a, &view_b).is_err());
3957            TypedArray::<i64>::drop_array(a);
3958            TypedArray::<f64>::drop_array(b);
3959        }
3960    }
3961
3962    #[test]
3963    fn test_slice_array_i64() {
3964        unsafe {
3965            let arr = TypedArray::<i64>::from_slice(&[10, 20, 30, 40, 50]);
3966            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3967            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3968                .unwrap();
3969            let new_ptr = slice_array(&view, 1, 4);
3970            let new_view =
3971                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3972                    .unwrap();
3973            assert_eq!(new_view.elem_type, V2ElemType::I64);
3974            assert_eq!(new_view.len, 3);
3975            let new_arr = new_ptr as *const TypedArray<i64>;
3976            let data = (*new_arr).data;
3977            assert_eq!(*data.add(0), 20);
3978            assert_eq!(*data.add(1), 30);
3979            assert_eq!(*data.add(2), 40);
3980            TypedArray::<i64>::drop_array(arr);
3981            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
3982        }
3983    }
3984
3985    #[test]
3986    fn test_slice_array_clamps_oversize_end() {
3987        unsafe {
3988            let arr = TypedArray::<i64>::from_slice(&[10, 20, 30]);
3989            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
3990            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3991                .unwrap();
3992            // end > len → clamp to len; result is `[30]`.
3993            let new_ptr = slice_array(&view, 2, 100);
3994            let new_view =
3995                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
3996                    .unwrap();
3997            assert_eq!(new_view.len, 1);
3998            let new_arr = new_ptr as *const TypedArray<i64>;
3999            assert_eq!(*(*new_arr).data.add(0), 30);
4000            TypedArray::<i64>::drop_array(arr);
4001            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4002        }
4003    }
4004
4005    #[test]
4006    fn test_slice_array_inverted_range_empty() {
4007        unsafe {
4008            let arr = TypedArray::<i64>::from_slice(&[1, 2, 3]);
4009            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4010            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4011                .unwrap();
4012            // start > end → empty result (no panic).
4013            let new_ptr = slice_array(&view, 5, 2);
4014            let new_view =
4015                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4016                    .unwrap();
4017            assert_eq!(new_view.len, 0);
4018            TypedArray::<i64>::drop_array(arr);
4019            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4020        }
4021    }
4022
4023    #[test]
4024    fn test_take_array_i64() {
4025        unsafe {
4026            let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
4027            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4028            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4029                .unwrap();
4030            let new_ptr = take_array(&view, 2);
4031            let new_view =
4032                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4033                    .unwrap();
4034            assert_eq!(new_view.len, 2);
4035            let new_arr = new_ptr as *const TypedArray<i64>;
4036            assert_eq!(*(*new_arr).data.add(0), 1);
4037            assert_eq!(*(*new_arr).data.add(1), 2);
4038            TypedArray::<i64>::drop_array(arr);
4039            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4040        }
4041    }
4042
4043    #[test]
4044    fn test_take_array_n_exceeds_len() {
4045        unsafe {
4046            let arr = TypedArray::<i64>::from_slice(&[1, 2]);
4047            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4048            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4049                .unwrap();
4050            // n > len → clamped to len.
4051            let new_ptr = take_array(&view, 100);
4052            let new_view =
4053                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4054                    .unwrap();
4055            assert_eq!(new_view.len, 2);
4056            TypedArray::<i64>::drop_array(arr);
4057            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4058        }
4059    }
4060
4061    #[test]
4062    fn test_drop_array_n_i64() {
4063        unsafe {
4064            let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
4065            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4066            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4067                .unwrap();
4068            let new_ptr = drop_array_n(&view, 2);
4069            let new_view =
4070                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4071                    .unwrap();
4072            assert_eq!(new_view.len, 3);
4073            let new_arr = new_ptr as *const TypedArray<i64>;
4074            assert_eq!(*(*new_arr).data.add(0), 3);
4075            assert_eq!(*(*new_arr).data.add(1), 4);
4076            assert_eq!(*(*new_arr).data.add(2), 5);
4077            TypedArray::<i64>::drop_array(arr);
4078            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4079        }
4080    }
4081
4082    #[test]
4083    fn test_drop_array_n_exceeds_len_yields_empty() {
4084        unsafe {
4085            let arr = TypedArray::<i64>::from_slice(&[1, 2]);
4086            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4087            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4088                .unwrap();
4089            let new_ptr = drop_array_n(&view, 100);
4090            let new_view =
4091                as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4092                    .unwrap();
4093            assert_eq!(new_view.len, 0);
4094            TypedArray::<i64>::drop_array(arr);
4095            TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
4096        }
4097    }
4098
4099    // ──────────────────────────────────────────────────────────────────────
4100    // R8 W4 J.5c (2026-05-24) — eq_element + position_of + contains_element
4101    // value-equality round-trip smokes per supervisor D2.
4102    //
4103    // Per-kind dispatch verified empirically for scalar I64 / F64 / Bool +
4104    // heap-element String / Decimal. TypedObject deep-equality covered via
4105    // schema_id mismatch (negative) + nil-payload positive (degenerate
4106    // schema → equal). NaN float bitwise compare verified for the
4107    // `[NaN, 1.0].indexOf(NaN)` edge case.
4108    // ──────────────────────────────────────────────────────────────────────
4109
4110    #[test]
4111    fn test_eq_element_scalar_i64() {
4112        assert!(eq_element(42, 42, V2ElemType::I64));
4113        assert!(!eq_element(42, 43, V2ElemType::I64));
4114        // Negative numbers (sign-extension into the u64 slot).
4115        assert!(eq_element((-5i64) as u64, (-5i64) as u64, V2ElemType::I64));
4116        assert!(!eq_element((-5i64) as u64, (5i64) as u64, V2ElemType::I64));
4117    }
4118
4119    #[test]
4120    fn test_eq_element_scalar_f64_bitwise() {
4121        assert!(eq_element(1.5f64.to_bits(), 1.5f64.to_bits(), V2ElemType::F64));
4122        assert!(!eq_element(1.5f64.to_bits(), 2.5f64.to_bits(), V2ElemType::F64));
4123        // IEEE bitwise: NaN bits == NaN bits is TRUE under eq_element
4124        // (this matches the includes/indexOf observable that an array
4125        // containing NaN can find its own NaN element).
4126        let nan = f64::NAN.to_bits();
4127        assert!(eq_element(nan, nan, V2ElemType::F64));
4128    }
4129
4130    #[test]
4131    fn test_eq_element_scalar_bool() {
4132        assert!(eq_element(1, 1, V2ElemType::Bool));
4133        assert!(eq_element(0, 0, V2ElemType::Bool));
4134        assert!(!eq_element(1, 0, V2ElemType::Bool));
4135    }
4136
4137    #[test]
4138    fn test_eq_element_string_content() {
4139        unsafe {
4140            let s1 = StringObj::new("hello");
4141            let s2 = StringObj::new("hello"); // distinct alloc, same content
4142            let s3 = StringObj::new("world");
4143            assert!(eq_element(s1 as u64, s2 as u64, V2ElemType::String));
4144            assert!(!eq_element(s1 as u64, s3 as u64, V2ElemType::String));
4145            // null-defensive
4146            assert!(eq_element(0, 0, V2ElemType::String));
4147            assert!(!eq_element(s1 as u64, 0, V2ElemType::String));
4148            // identity short-circuit
4149            assert!(eq_element(s1 as u64, s1 as u64, V2ElemType::String));
4150            StringObj::drop(s1);
4151            StringObj::drop(s2);
4152            StringObj::drop(s3);
4153        }
4154    }
4155
4156    #[test]
4157    fn test_eq_element_decimal_content() {
4158        use rust_decimal::Decimal;
4159        use rust_decimal::prelude::FromPrimitive;
4160        unsafe {
4161            let d1 = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
4162            let d2 = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
4163            let d3 = DecimalObj::new(Decimal::from_f64(2.71).unwrap());
4164            assert!(eq_element(d1 as u64, d2 as u64, V2ElemType::Decimal));
4165            assert!(!eq_element(d1 as u64, d3 as u64, V2ElemType::Decimal));
4166            DecimalObj::drop(d1);
4167            DecimalObj::drop(d2);
4168            DecimalObj::drop(d3);
4169        }
4170    }
4171
4172    #[test]
4173    fn test_position_of_i64() {
4174        unsafe {
4175            let arr = TypedArray::<i64>::from_slice(&[10, 20, 30, 20, 40]);
4176            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4177            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4178                .unwrap();
4179            assert_eq!(position_of(&view, 10u64), Some(0));
4180            assert_eq!(position_of(&view, 20u64), Some(1)); // first match
4181            assert_eq!(position_of(&view, 30u64), Some(2));
4182            assert_eq!(position_of(&view, 99u64), None);
4183            TypedArray::<i64>::drop_array(arr);
4184        }
4185    }
4186
4187    #[test]
4188    fn test_position_of_empty_returns_none() {
4189        unsafe {
4190            let arr = TypedArray::<i64>::with_capacity(0);
4191            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4192            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4193                .unwrap();
4194            assert_eq!(position_of(&view, 0u64), None);
4195            TypedArray::<i64>::drop_array(arr);
4196        }
4197    }
4198
4199    #[test]
4200    fn test_contains_element_i64() {
4201        unsafe {
4202            let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
4203            stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
4204            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4205                .unwrap();
4206            assert!(contains_element(&view, 3u64));
4207            assert!(!contains_element(&view, 99u64));
4208            TypedArray::<i64>::drop_array(arr);
4209        }
4210    }
4211
4212    #[test]
4213    fn test_position_of_f64_nan_bitwise() {
4214        unsafe {
4215            let arr = TypedArray::<f64>::from_slice(&[1.0, f64::NAN, 2.0]);
4216            stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
4217            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4218                .unwrap();
4219            // NaN findable by its own bit pattern.
4220            assert_eq!(position_of(&view, f64::NAN.to_bits()), Some(1));
4221            assert_eq!(position_of(&view, (1.0f64).to_bits()), Some(0));
4222            assert_eq!(position_of(&view, (99.0f64).to_bits()), None);
4223            TypedArray::<f64>::drop_array(arr);
4224        }
4225    }
4226
4227    #[test]
4228    fn test_position_of_string_content() {
4229        unsafe {
4230            let s1 = StringObj::new("a");
4231            let s2 = StringObj::new("b");
4232            let s3 = StringObj::new("c");
4233            let arr = TypedArray::<*const StringObj>::with_capacity(3);
4234            TypedArray::push(arr, s1 as *const StringObj);
4235            TypedArray::push(arr, s2 as *const StringObj);
4236            TypedArray::push(arr, s3 as *const StringObj);
4237            stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
4238            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4239                .unwrap();
4240            // Needle is a SEPARATELY-allocated StringObj with the same
4241            // content as s2 — content-equality must find it (not pointer
4242            // identity).
4243            let needle = StringObj::new("b");
4244            assert_eq!(position_of(&view, needle as u64), Some(1));
4245            // Non-matching content.
4246            let other = StringObj::new("zzz");
4247            assert_eq!(position_of(&view, other as u64), None);
4248            assert!(contains_element(&view, needle as u64));
4249            assert!(!contains_element(&view, other as u64));
4250            TypedArray::<*const StringObj>::drop_array_heap(arr);
4251            StringObj::drop(needle);
4252            StringObj::drop(other);
4253        }
4254    }
4255
4256    #[test]
4257    fn test_position_of_decimal_content() {
4258        use rust_decimal::Decimal;
4259        use rust_decimal::prelude::FromPrimitive;
4260        unsafe {
4261            let d1 = DecimalObj::new(Decimal::from_f64(1.5).unwrap());
4262            let d2 = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
4263            let arr = TypedArray::<*const DecimalObj>::with_capacity(2);
4264            TypedArray::push(arr, d1 as *const DecimalObj);
4265            TypedArray::push(arr, d2 as *const DecimalObj);
4266            stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
4267            let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
4268                .unwrap();
4269            let needle = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
4270            assert_eq!(position_of(&view, needle as u64), Some(1));
4271            let other = DecimalObj::new(Decimal::from_f64(9.9).unwrap());
4272            assert_eq!(position_of(&view, other as u64), None);
4273            TypedArray::<*const DecimalObj>::drop_array_heap(arr);
4274            DecimalObj::drop(needle);
4275            DecimalObj::drop(other);
4276        }
4277    }
4278
4279    #[test]
4280    fn test_position_of_typed_object_schema_mismatch() {
4281        use shape_value::slot::ValueSlot;
4282        use std::sync::Arc;
4283        unsafe {
4284            // Two TypedObjectStorages with the SAME field layout (empty)
4285            // but different schema_ids — deep-eq must return inequality.
4286            let kinds_a: Arc<[NativeKind]> = Arc::from(vec![].into_boxed_slice());
4287            let kinds_b: Arc<[NativeKind]> = Arc::from(vec![].into_boxed_slice());
4288            let obj_a = TypedObjectStorage::_new(
4289                1,
4290                vec![].into_boxed_slice() as Box<[ValueSlot]>,
4291                0,
4292                kinds_a,
4293            );
4294            let obj_b = TypedObjectStorage::_new(
4295                2,
4296                vec![].into_boxed_slice() as Box<[ValueSlot]>,
4297                0,
4298                kinds_b,
4299            );
4300            assert!(!eq_element(
4301                obj_a as u64,
4302                obj_b as u64,
4303                V2ElemType::TypedObject
4304            ));
4305            TypedObjectStorage::_drop(obj_a);
4306            TypedObjectStorage::_drop(obj_b);
4307        }
4308    }
4309
4310    #[test]
4311    fn test_position_of_typed_object_same_schema_equal_fields() {
4312        use shape_value::slot::ValueSlot;
4313        use std::sync::Arc;
4314        unsafe {
4315            // Two TypedObjectStorages with the same schema_id + same single
4316            // i64 field value (42) — deep-eq must return TRUE.
4317            let kinds: Arc<[NativeKind]> = Arc::from(vec![NativeKind::Int64].into_boxed_slice());
4318            let obj_a = TypedObjectStorage::_new(
4319                7,
4320                vec![ValueSlot::from_raw(42u64)].into_boxed_slice(),
4321                0,
4322                kinds.clone(),
4323            );
4324            let obj_b = TypedObjectStorage::_new(
4325                7,
4326                vec![ValueSlot::from_raw(42u64)].into_boxed_slice(),
4327                0,
4328                kinds.clone(),
4329            );
4330            let obj_c = TypedObjectStorage::_new(
4331                7,
4332                vec![ValueSlot::from_raw(99u64)].into_boxed_slice(),
4333                0,
4334                kinds.clone(),
4335            );
4336            assert!(eq_element(
4337                obj_a as u64,
4338                obj_b as u64,
4339                V2ElemType::TypedObject
4340            ));
4341            assert!(!eq_element(
4342                obj_a as u64,
4343                obj_c as u64,
4344                V2ElemType::TypedObject
4345            ));
4346            // Identity short-circuit.
4347            assert!(eq_element(
4348                obj_a as u64,
4349                obj_a as u64,
4350                V2ElemType::TypedObject
4351            ));
4352            // Null-defensive.
4353            assert!(!eq_element(0, obj_a as u64, V2ElemType::TypedObject));
4354            assert!(eq_element(0, 0, V2ElemType::TypedObject));
4355            TypedObjectStorage::_drop(obj_a);
4356            TypedObjectStorage::_drop(obj_b);
4357            TypedObjectStorage::_drop(obj_c);
4358        }
4359    }
4360}