Skip to main content

strided_basic/
gather_plan.rs

1//! Prepared indexed plans over raw strided value and index layouts.
2//!
3//! This module owns the generic gather, dynamic-slice/update, and scatter
4//! traversals used by the erased replay layer. It models the XLA/tenferro
5//! indexed shape vocabulary, but keeps tensor allocation, dtype promotion, and
6//! frontend error policy outside `strided-kernel`.
7
8use core::{mem::MaybeUninit, ops::Add};
9
10use crate::copy_plan::{replay_fused_raw, CopyPlan, OverwriteWriter, ReadModifyWrite};
11use crate::inline::InlineVec;
12use crate::raw_ops::{fuse_pair_layout, FusedPairLayout};
13use crate::{Exec, RawStridedMut, RawStridedRef, Result, StridedError, RAW_FUSED_RANK_LIMIT};
14
15type AxisVec<T> = InlineVec<T>;
16
17/// Gather configuration shared by generic and erased replay.
18///
19/// The fields follow the usual gather vocabulary:
20///
21/// - `start_index_map[component]` names the operand axis controlled by a
22///   component in the index vector;
23/// - `collapsed_slice_dims` names operand axes whose slice size is one and
24///   which do not appear as output window axes;
25/// - `offset_dims` names output axes that represent window offsets;
26/// - output axes not in `offset_dims` are batch axes from `start_indices`;
27/// - `index_vector_dim == start_indices_rank` represents scalar index vectors.
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct GatherSpec {
30    pub offset_dims: Vec<usize>,
31    pub collapsed_slice_dims: Vec<usize>,
32    pub start_index_map: Vec<usize>,
33    pub index_vector_dim: usize,
34    pub slice_sizes: Vec<usize>,
35}
36
37/// Index scalar types accepted by [`GatherPlan`].
38pub trait GatherIndex: Copy + Send + Sync {
39    fn to_i64(self) -> i64;
40}
41
42impl GatherIndex for i32 {
43    #[inline]
44    fn to_i64(self) -> i64 {
45        i64::from(self)
46    }
47}
48
49impl GatherIndex for i64 {
50    #[inline]
51    fn to_i64(self) -> i64 {
52        self
53    }
54}
55
56/// A compiled gather traversal for one value layout, index layout, and output layout.
57#[derive(Clone, Debug)]
58pub struct GatherPlan {
59    operand_dims: AxisVec<usize>,
60    operand_strides: AxisVec<isize>,
61    index_dims: AxisVec<usize>,
62    index_strides: AxisVec<isize>,
63    dest_dims: AxisVec<usize>,
64    dest_strides: AxisVec<isize>,
65    spec: GatherSpec,
66    replay: GatherReplay,
67    total: usize,
68}
69
70#[derive(Clone, Copy, Debug, Default)]
71struct GatherReplayAxis {
72    dest_step: isize,
73    dest_reset: isize,
74    window_step: isize,
75    window_reset: isize,
76    index_batch_step: isize,
77    index_batch_reset: isize,
78}
79
80#[derive(Clone, Debug)]
81struct GatherReplay {
82    axes: AxisVec<GatherReplayAxis>,
83    index_component_offsets: AxisVec<isize>,
84    index_operand_strides: AxisVec<isize>,
85}
86
87struct GatherReplayState {
88    coords: AxisVec<usize>,
89    dest_offset: isize,
90    window_offset: isize,
91    index_batch_offset: isize,
92}
93
94#[derive(Clone, Copy, Debug, Default)]
95struct WindowReplayAxis {
96    source_step: isize,
97    source_reset: isize,
98    dest_step: isize,
99    dest_reset: isize,
100}
101
102#[derive(Clone, Debug)]
103struct WindowReplay {
104    shape: AxisVec<usize>,
105    axes: AxisVec<WindowReplayAxis>,
106}
107
108struct WindowReplayState {
109    coords: CoordScratch,
110    source_offset: isize,
111    dest_offset: isize,
112}
113
114#[derive(Clone, Debug)]
115struct ScatterReplay {
116    batch: WindowReplay,
117    window: WindowReplay,
118    index_component_offsets: AxisVec<isize>,
119}
120
121impl ScatterReplay {
122    fn compile(
123        batch_shape: &[usize],
124        index_dims: &[usize],
125        index_strides: &[isize],
126        index_vector_dim: usize,
127        index_component_count: usize,
128        update_dims: &[usize],
129        update_strides: &[isize],
130        update_window_dims: &[usize],
131        is_update_window_dim: &[bool],
132        window_shape_updates: &[usize],
133        window_dims: &[usize],
134        dest_strides: &[isize],
135    ) -> Result<Self> {
136        let (
137            batch_source_strides,
138            batch_dest_strides,
139            window_source_strides,
140            window_dest_strides,
141            vector_stride,
142        ) = scatter_replay_strides(
143            index_dims,
144            index_strides,
145            index_vector_dim,
146            update_dims,
147            update_strides,
148            update_window_dims,
149            is_update_window_dim,
150            window_dims,
151            dest_strides,
152        );
153        let batch = WindowReplay::compile(batch_shape, &batch_source_strides, &batch_dest_strides)?;
154        let window = WindowReplay::compile(
155            window_shape_updates,
156            &window_source_strides,
157            &window_dest_strides,
158        )?;
159        let mut index_component_offsets = AxisVec::with_capacity(index_component_count);
160        for component in 0..index_component_count {
161            index_component_offsets.push(checked_offset_add(0, vector_stride, component)?);
162        }
163        Ok(Self {
164            batch,
165            window,
166            index_component_offsets,
167        })
168    }
169}
170
171fn scatter_replay_strides(
172    index_dims: &[usize],
173    index_strides: &[isize],
174    index_vector_dim: usize,
175    update_dims: &[usize],
176    update_strides: &[isize],
177    update_window_dims: &[usize],
178    is_update_window_dim: &[bool],
179    window_dims: &[usize],
180    dest_strides: &[isize],
181) -> (
182    AxisVec<isize>,
183    AxisVec<isize>,
184    AxisVec<isize>,
185    AxisVec<isize>,
186    isize,
187) {
188    let batch_source_strides = index_dims
189        .iter()
190        .zip(index_strides.iter())
191        .enumerate()
192        .filter_map(|(axis, (_, &stride))| (axis != index_vector_dim).then_some(stride))
193        .collect();
194    let batch_dest_strides = update_dims
195        .iter()
196        .zip(update_strides.iter())
197        .enumerate()
198        .filter_map(|(axis, (_, &stride))| (!is_update_window_dim[axis]).then_some(stride))
199        .collect();
200    let window_source_strides = update_window_dims
201        .iter()
202        .map(|&axis| update_strides[axis])
203        .collect();
204    let window_dest_strides = window_dims.iter().map(|&axis| dest_strides[axis]).collect();
205    let vector_stride = if index_vector_dim < index_dims.len() {
206        index_strides[index_vector_dim]
207    } else {
208        0
209    };
210    (
211        batch_source_strides,
212        batch_dest_strides,
213        window_source_strides,
214        window_dest_strides,
215        vector_stride,
216    )
217}
218
219impl WindowReplay {
220    fn compile(shape: &[usize], source_strides: &[isize], dest_strides: &[isize]) -> Result<Self> {
221        validate_layout_span(shape, source_strides)?;
222        validate_layout_span(shape, dest_strides)?;
223        let mut fused_shape: AxisVec<usize> = AxisVec::with_capacity(shape.len());
224        let mut axes: AxisVec<WindowReplayAxis> = AxisVec::with_capacity(shape.len());
225        for (axis, &dim) in shape.iter().enumerate() {
226            let source_step = source_strides[axis];
227            let dest_step = dest_strides[axis];
228            if let Some(previous_axis) = fused_shape.len().checked_sub(1) {
229                let previous_extent = fused_shape[previous_axis];
230                let previous_extent =
231                    isize::try_from(previous_extent).map_err(|_| StridedError::OffsetOverflow)?;
232                let expected_source = axes[previous_axis]
233                    .source_step
234                    .checked_mul(previous_extent)
235                    .ok_or(StridedError::OffsetOverflow)?;
236                let expected_dest = axes[previous_axis]
237                    .dest_step
238                    .checked_mul(previous_extent)
239                    .ok_or(StridedError::OffsetOverflow)?;
240                if source_step == expected_source && dest_step == expected_dest {
241                    let fused_extent = fused_shape[previous_axis]
242                        .checked_mul(dim)
243                        .ok_or(StridedError::OffsetOverflow)?;
244                    fused_shape[previous_axis] = fused_extent;
245                    axes[previous_axis].source_reset =
246                        checked_replay_reset(fused_extent, axes[previous_axis].source_step)?;
247                    axes[previous_axis].dest_reset =
248                        checked_replay_reset(fused_extent, axes[previous_axis].dest_step)?;
249                    continue;
250                }
251            }
252            fused_shape.push(dim);
253            axes.push(WindowReplayAxis {
254                source_step,
255                source_reset: checked_replay_reset(dim, source_step)?,
256                dest_step,
257                dest_reset: checked_replay_reset(dim, dest_step)?,
258            });
259        }
260        Ok(Self {
261            shape: fused_shape,
262            axes,
263        })
264    }
265
266    fn decode(
267        &self,
268        mut linear: usize,
269        source_base: isize,
270        dest_base: isize,
271    ) -> Result<WindowReplayState> {
272        let mut coords = CoordScratch::new(self.shape.len());
273        let mut source_offset = source_base;
274        let mut dest_offset = dest_base;
275        for (axis, (&dim, coord)) in self
276            .shape
277            .iter()
278            .zip(coords.as_mut_slice().iter_mut())
279            .enumerate()
280        {
281            *coord = linear % dim;
282            linear /= dim;
283            let replay_axis = self.axes[axis];
284            source_offset = checked_offset_add(source_offset, replay_axis.source_step, *coord)?;
285            dest_offset = checked_offset_add(dest_offset, replay_axis.dest_step, *coord)?;
286        }
287        Ok(WindowReplayState {
288            coords,
289            source_offset,
290            dest_offset,
291        })
292    }
293
294    #[inline]
295    fn advance(&self, state: &mut WindowReplayState) {
296        for ((coord, &dim), replay_axis) in state
297            .coords
298            .as_mut_slice()
299            .iter_mut()
300            .zip(self.shape.iter())
301            .zip(self.axes.iter())
302        {
303            let next = *coord + 1;
304            if next < dim {
305                *coord = next;
306                state.source_offset += replay_axis.source_step;
307                state.dest_offset += replay_axis.dest_step;
308                return;
309            }
310            *coord = 0;
311            state.source_offset += replay_axis.source_reset;
312            state.dest_offset += replay_axis.dest_reset;
313        }
314    }
315}
316
317/// Scatter configuration shared by generic and erased replay.
318///
319/// `ScatterPlan` implements tenferro's current additive scatter semantics:
320/// every update value is added to the selected output slot, so overlapping
321/// windows accumulate in deterministic column-major replay order.
322#[derive(Clone, Debug, Eq, PartialEq)]
323pub struct ScatterSpec {
324    pub update_window_dims: Vec<usize>,
325    pub inserted_window_dims: Vec<usize>,
326    pub scatter_dims_to_operand_dims: Vec<usize>,
327    pub index_vector_dim: usize,
328}
329
330/// A compiled fixed-window dynamic-slice traversal.
331#[derive(Clone, Debug)]
332pub struct DynamicSlicePlan {
333    operand_dims: AxisVec<usize>,
334    operand_strides: AxisVec<isize>,
335    start_dims: AxisVec<usize>,
336    start_strides: AxisVec<isize>,
337    dest_dims: AxisVec<usize>,
338    dest_strides: AxisVec<isize>,
339    slice_sizes: AxisVec<usize>,
340    total: usize,
341    /// Fused window traversal (dest strides, operand strides) replayed at the
342    /// runtime source base; `None` when its fused rank exceeds
343    /// [`RAW_FUSED_RANK_LIMIT`](crate::RAW_FUSED_RANK_LIMIT), in which case
344    /// `replay`/`execute_parallel` walk the window instead.
345    window: Option<FusedPairLayout>,
346    replay: WindowReplay,
347}
348
349/// A compiled dynamic-update-slice traversal.
350///
351/// Execution first copies `operand` into `dest`, then overwrites the clamped
352/// update window. The plan performs no allocation for ranks at most
353/// [`RAW_FUSED_RANK_LIMIT`].
354#[derive(Clone, Debug)]
355pub struct DynamicUpdateSlicePlan {
356    operand_dims: AxisVec<usize>,
357    operand_strides: AxisVec<isize>,
358    start_dims: AxisVec<usize>,
359    start_strides: AxisVec<isize>,
360    update_dims: AxisVec<usize>,
361    update_strides: AxisVec<isize>,
362    dest_dims: AxisVec<usize>,
363    dest_strides: AxisVec<isize>,
364    total: usize,
365    copy_plan: CopyPlan,
366    /// Fused update window traversal (dest strides, update strides) replayed
367    /// at the runtime destination base; `None` when its fused rank exceeds
368    /// [`RAW_FUSED_RANK_LIMIT`](crate::RAW_FUSED_RANK_LIMIT), in which case
369    /// `replay`/`execute_update_parallel` walk the window instead.
370    window: Option<FusedPairLayout>,
371    replay: WindowReplay,
372}
373
374/// A compiled additive scatter traversal.
375///
376/// Execution first copies `operand` into `dest`, then applies additive updates
377/// in deterministic column-major order. Boolean values are intentionally not
378/// supported because additive scatter has no bool semantics.
379#[derive(Clone, Debug)]
380pub struct ScatterPlan {
381    operand_dims: AxisVec<usize>,
382    operand_strides: AxisVec<isize>,
383    index_dims: AxisVec<usize>,
384    index_strides: AxisVec<isize>,
385    update_dims: AxisVec<usize>,
386    update_strides: AxisVec<isize>,
387    dest_dims: AxisVec<usize>,
388    dest_strides: AxisVec<isize>,
389    spec: ScatterSpec,
390    window_shape: AxisVec<usize>,
391    batch_elems: usize,
392    window_elems: usize,
393    copy_plan: CopyPlan,
394    replay: ScatterReplay,
395}
396
397impl GatherPlan {
398    /// Compile a gather plan for fixed operand, index, and destination layouts.
399    pub fn compile(
400        operand_dims: &[usize],
401        operand_strides: &[isize],
402        index_dims: &[usize],
403        index_strides: &[isize],
404        dest_dims: &[usize],
405        dest_strides: &[isize],
406        spec: GatherSpec,
407    ) -> Result<Self> {
408        if operand_dims.len() != operand_strides.len()
409            || index_dims.len() != index_strides.len()
410            || dest_dims.len() != dest_strides.len()
411        {
412            return Err(StridedError::StrideLengthMismatch);
413        }
414        checked_total_len(operand_dims)?;
415        checked_total_len(index_dims)?;
416        let total = checked_total_len(dest_dims)?;
417        validate_layout_span(operand_dims, operand_strides)?;
418        validate_layout_span(index_dims, index_strides)?;
419        validate_layout_span(dest_dims, dest_strides)?;
420        if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
421            return Err(StridedError::NonInjectiveOutputLayout);
422        }
423
424        let operand_rank = operand_dims.len();
425        if spec.slice_sizes.len() != operand_rank {
426            return Err(StridedError::RankMismatch(
427                spec.slice_sizes.len(),
428                operand_rank,
429            ));
430        }
431        validate_unique_axes(&spec.collapsed_slice_dims, operand_rank)?;
432        validate_unique_axes(&spec.start_index_map, operand_rank)?;
433        if spec.index_vector_dim > index_dims.len() {
434            return Err(StridedError::InvalidAxis {
435                axis: spec.index_vector_dim,
436                rank: index_dims.len() + 1,
437            });
438        }
439
440        for (axis, (&window, &dim)) in spec.slice_sizes.iter().zip(operand_dims.iter()).enumerate()
441        {
442            if window > dim {
443                return Err(StridedError::InvalidAxis {
444                    axis,
445                    rank: operand_rank,
446                });
447            }
448        }
449        for &axis in &spec.collapsed_slice_dims {
450            if spec.slice_sizes[axis] != 1 {
451                return Err(StridedError::InvalidAxis {
452                    axis,
453                    rank: operand_rank,
454                });
455            }
456        }
457
458        let index_vector_size = if spec.index_vector_dim == index_dims.len() {
459            1
460        } else {
461            index_dims[spec.index_vector_dim]
462        };
463        if index_vector_size != spec.start_index_map.len() {
464            return Err(StridedError::RankMismatch(
465                index_vector_size,
466                spec.start_index_map.len(),
467            ));
468        }
469
470        let window_dims = operand_window_dims(operand_rank, &spec.collapsed_slice_dims);
471        if spec.offset_dims.len() != window_dims.len() {
472            return Err(StridedError::RankMismatch(
473                spec.offset_dims.len(),
474                window_dims.len(),
475            ));
476        }
477
478        let batch_shape = index_batch_shape(index_dims, spec.index_vector_dim);
479        let out_rank = batch_shape.len() + spec.offset_dims.len();
480        validate_unique_axes(&spec.offset_dims, out_rank)?;
481
482        let mut out_axis_to_operand_dim: AxisVec<Option<usize>> =
483            (0..out_rank).map(|_| None).collect();
484        for (offset_axis, &out_axis) in spec.offset_dims.iter().enumerate() {
485            out_axis_to_operand_dim[out_axis] = Some(window_dims[offset_axis]);
486        }
487
488        let mut expected_dest_dims: AxisVec<usize> = AxisVec::with_capacity(out_rank);
489        let mut batch_axis = 0usize;
490        for &operand_dim in &out_axis_to_operand_dim {
491            match operand_dim {
492                Some(axis) => expected_dest_dims.push(spec.slice_sizes[axis]),
493                None => {
494                    expected_dest_dims.push(batch_shape[batch_axis]);
495                    batch_axis += 1;
496                }
497            }
498        }
499        if dest_dims != &expected_dest_dims[..] {
500            return Err(StridedError::ShapeMismatch(
501                dest_dims.to_vec(),
502                expected_dest_dims.to_vec(),
503            ));
504        }
505
506        let batch_index_strides: AxisVec<isize> = index_dims
507            .iter()
508            .zip(index_strides.iter())
509            .enumerate()
510            .filter_map(|(axis, (_, &stride))| (axis != spec.index_vector_dim).then_some(stride))
511            .collect();
512        let mut batch_axis = 0usize;
513        let mut replay_axes = AxisVec::with_capacity(out_rank);
514        for (out_axis, &operand_dim) in out_axis_to_operand_dim.iter().enumerate() {
515            let (window_step, index_batch_step) = match operand_dim {
516                Some(axis) => (operand_strides[axis], 0),
517                None => {
518                    let step = batch_index_strides[batch_axis];
519                    batch_axis += 1;
520                    (0, step)
521                }
522            };
523            replay_axes.push(GatherReplayAxis {
524                dest_step: dest_strides[out_axis],
525                dest_reset: checked_replay_reset(dest_dims[out_axis], dest_strides[out_axis])?,
526                window_step,
527                window_reset: checked_replay_reset(dest_dims[out_axis], window_step)?,
528                index_batch_step,
529                index_batch_reset: checked_replay_reset(dest_dims[out_axis], index_batch_step)?,
530            });
531        }
532
533        let vector_stride = if spec.index_vector_dim < index_dims.len() {
534            index_strides[spec.index_vector_dim]
535        } else {
536            0
537        };
538        let mut index_component_offsets = AxisVec::with_capacity(spec.start_index_map.len());
539        for component in 0..spec.start_index_map.len() {
540            index_component_offsets.push(checked_offset_add(0, vector_stride, component)?);
541        }
542
543        let index_operand_strides: AxisVec<isize> = spec
544            .start_index_map
545            .iter()
546            .map(|&axis| operand_strides[axis])
547            .collect();
548
549        Ok(Self {
550            operand_dims: operand_dims.into(),
551            operand_strides: operand_strides.into(),
552            index_dims: index_dims.into(),
553            index_strides: index_strides.into(),
554            dest_dims: dest_dims.into(),
555            dest_strides: dest_strides.into(),
556            spec,
557            replay: GatherReplay {
558                axes: replay_axes,
559                index_component_offsets,
560                index_operand_strides,
561            },
562            total,
563        })
564    }
565
566    #[inline]
567    pub fn spec(&self) -> &GatherSpec {
568        &self.spec
569    }
570
571    #[inline]
572    pub fn dest_dims(&self) -> &[usize] {
573        &self.dest_dims
574    }
575
576    /// Execute the prepared gather traversal.
577    pub fn execute<T, I>(
578        &self,
579        dest: &mut RawStridedMut<'_, T>,
580        operand: &RawStridedRef<'_, T>,
581        start_indices: &RawStridedRef<'_, I>,
582        exec: Exec<'_>,
583    ) -> Result<()>
584    where
585        T: Copy + Send + Sync,
586        I: GatherIndex,
587    {
588        self.execute_with_writer(dest, operand, start_indices, exec)
589    }
590
591    /// Execute the prepared gather into a destination whose reachable slots
592    /// may be uninitialized. Every logical destination slot is written.
593    pub(crate) fn execute_uninit<T, I>(
594        &self,
595        dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
596        operand: &RawStridedRef<'_, T>,
597        start_indices: &RawStridedRef<'_, I>,
598        exec: Exec<'_>,
599    ) -> Result<()>
600    where
601        T: Copy + Send + Sync,
602        I: GatherIndex,
603    {
604        self.execute_with_writer(dest, operand, start_indices, exec)
605    }
606
607    fn execute_with_writer<T, I, W>(
608        &self,
609        dest: &mut W,
610        operand: &RawStridedRef<'_, T>,
611        start_indices: &RawStridedRef<'_, I>,
612        exec: Exec<'_>,
613    ) -> Result<()>
614    where
615        T: Copy + Send + Sync,
616        I: GatherIndex,
617        W: OverwriteWriter<T>,
618    {
619        self.check_call(dest, operand, start_indices)?;
620        if self.total == 0 {
621            return Ok(());
622        }
623        if self.uses_rank_one_scalar_take_path() {
624            let exec = crate::threading::for_len(exec, self.total);
625            if !exec.is_serial() {
626                return self.execute_rank_one_scalar_take_parallel(
627                    dest,
628                    operand,
629                    start_indices,
630                    exec,
631                );
632            }
633            return self.execute_rank_one_scalar_take(dest, operand, start_indices);
634        }
635        let exec = crate::threading::for_len(exec, self.total);
636        if !exec.is_serial() {
637            return self.execute_parallel(dest, operand, start_indices, exec);
638        }
639
640        let mut state =
641            self.decode_replay_state(0, dest.offset(), operand.offset(), start_indices.offset())?;
642        let operand_data = operand.data();
643        let index_data = start_indices.data();
644
645        // INVARIANT: the checked decode starts at a valid logical output. Each
646        // replay axis has checked step/reset deltas, so this state remains the
647        // corresponding destination, window, and batch-index offsets.
648        for _ in 0..self.total {
649            // INVARIANT: window_offset plus every clamped mapped contribution
650            // is an operand offset for coordinates inside operand_dims; the
651            // validated operand span and RawStridedRef reachability therefore
652            // keep the fresh source offset inside the allocation.
653            let mut source_offset = state.window_offset;
654            for ((&index_component_offset, &operand_stride), &operand_dim) in self
655                .replay
656                .index_component_offsets
657                .iter()
658                .zip(self.replay.index_operand_strides.iter())
659                .zip(self.spec.start_index_map.iter())
660            {
661                let index_offset = state.index_batch_offset + index_component_offset;
662                // SAFETY: checked index layout replay and RawStridedRef
663                // reachability prove this component read is in bounds.
664                let start = unsafe { *index_data.as_ptr().offset(index_offset) }.to_i64();
665                let clamped = self.clamp_window_start(start, operand_dim);
666                source_offset += operand_stride * clamped as isize;
667            }
668
669            // SAFETY: checked replay metadata and the validated writer prove
670            // both the source and the distinct destination offset are valid.
671            let value = unsafe { *operand_data.as_ptr().offset(source_offset) };
672            unsafe { dest.write_at(state.dest_offset, value) };
673            self.advance_replay_state(&mut state);
674        }
675        Ok(())
676    }
677
678    fn uses_rank_one_scalar_take_path(&self) -> bool {
679        self.operand_dims.len() == 1
680            && self.index_dims.len() == 1
681            && self.dest_dims.len() == 1
682            && self.operand_strides[0] == 1
683            && self.index_strides[0] == 1
684            && self.dest_strides[0] == 1
685            && self.spec.offset_dims.is_empty()
686            && self.spec.collapsed_slice_dims.as_slice() == [0]
687            && self.spec.start_index_map.as_slice() == [0]
688            && self.spec.index_vector_dim == 1
689            && self.spec.slice_sizes.as_slice() == [1]
690    }
691
692    fn execute_rank_one_scalar_take<T, I, W>(
693        &self,
694        dest: &mut W,
695        operand: &RawStridedRef<'_, T>,
696        start_indices: &RawStridedRef<'_, I>,
697    ) -> Result<()>
698    where
699        T: Copy,
700        I: GatherIndex,
701        W: OverwriteWriter<T>,
702    {
703        let mut dest_offset = dest.offset();
704        let mut index_offset = start_indices.offset();
705        let operand_offset = operand.offset();
706        let operand_data = operand.data();
707        let index_data = start_indices.data();
708
709        // INVARIANT: compile and check_call validated compact rank-one layouts,
710        // so incrementing these offsets once per logical element stays within
711        // the validated allocations and cannot overflow isize.
712        for _ in 0..self.total {
713            // SAFETY: the invariant above proves all three offsets are in bounds.
714            unsafe {
715                let start = (*index_data.as_ptr().offset(index_offset)).to_i64();
716                let source_offset = operand_offset + self.clamp_window_start(start, 0) as isize;
717                dest.write_at(dest_offset, *operand_data.as_ptr().offset(source_offset));
718            }
719            dest_offset += 1;
720            index_offset += 1;
721        }
722        Ok(())
723    }
724
725    fn execute_rank_one_scalar_take_parallel<T, I, W>(
726        &self,
727        dest: &mut W,
728        operand: &RawStridedRef<'_, T>,
729        start_indices: &RawStridedRef<'_, I>,
730        exec: Exec<'_>,
731    ) -> Result<()>
732    where
733        T: Copy + Send + Sync,
734        I: GatherIndex,
735        W: OverwriteWriter<T>,
736    {
737        let dest_offset = dest.offset();
738        let operand_offset = operand.offset();
739        let index_offset = start_indices.offset();
740        // SAFETY: check_call validated the writer's complete destination layout.
741        let dest_ptr = crate::threading::SendPtr(unsafe { dest.data_ptr() });
742        let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
743        let index_ptr = crate::threading::SendPtr(start_indices.data().as_ptr() as *mut I);
744
745        crate::threading::parallel_map_reduce(
746            0..self.total,
747            exec,
748            &|range| {
749                let dest_ptr = dest_ptr.as_ptr();
750                let operand_ptr = operand_ptr.as_const();
751                let index_ptr = index_ptr.as_const();
752                // INVARIANT: the validated compact rank-one layouts map each
753                // output position to one distinct destination offset.
754                for position in range {
755                    let position = position as isize;
756                    // SAFETY: the invariant above proves the source and distinct
757                    // destination offsets are in their validated allocations.
758                    unsafe {
759                        let start = (*index_ptr.offset(index_offset + position)).to_i64();
760                        let source_offset =
761                            operand_offset + self.clamp_window_start(start, 0) as isize;
762                        dest_ptr
763                            .offset(dest_offset + position)
764                            .write(operand_ptr.offset(source_offset).read());
765                    }
766                }
767                Ok(())
768            },
769            &|left, right| left.and(right),
770        )
771    }
772
773    fn execute_parallel<T, I, W>(
774        &self,
775        dest: &mut W,
776        operand: &RawStridedRef<'_, T>,
777        start_indices: &RawStridedRef<'_, I>,
778        exec: Exec<'_>,
779    ) -> Result<()>
780    where
781        T: Copy + Send + Sync,
782        I: GatherIndex,
783        W: OverwriteWriter<T>,
784    {
785        let dest_offset_base = dest.offset();
786        let operand_offset_base = operand.offset();
787        let index_offset_base = start_indices.offset();
788        // SAFETY: the validated writer owns the destination allocation.
789        let dest_ptr = crate::threading::SendPtr(unsafe { dest.data_ptr() });
790        let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
791        let index_ptr = crate::threading::SendPtr(start_indices.data().as_ptr() as *mut I);
792
793        crate::threading::parallel_map_reduce(
794            0..self.total,
795            exec,
796            &|range| {
797                let mut state = self.decode_replay_state(
798                    range.start,
799                    dest_offset_base,
800                    operand_offset_base,
801                    index_offset_base,
802                )?;
803                let dest_ptr = dest_ptr.as_ptr();
804                let operand_ptr = operand_ptr.as_const();
805                let index_ptr = index_ptr.as_const();
806
807                // INVARIANT: this worker owns a disjoint logical range. Checked
808                // range-start decode plus checked replay deltas keeps every
809                // state at its corresponding output, and injectivity makes all
810                // writes across workers distinct.
811                for _ in range {
812                    // INVARIANT: source_offset is freshly based on the current
813                    // window offset; clamped mapped coordinates remain inside
814                    // the validated operand span and are never accumulated.
815                    let mut source_offset = state.window_offset;
816                    for ((&index_component_offset, &operand_stride), &operand_dim) in self
817                        .replay
818                        .index_component_offsets
819                        .iter()
820                        .zip(self.replay.index_operand_strides.iter())
821                        .zip(self.spec.start_index_map.iter())
822                    {
823                        let index_offset = state.index_batch_offset + index_component_offset;
824                        // SAFETY: the checked index span and this worker's
825                        // validated range-start state prove this read is valid.
826                        let start = unsafe { (*index_ptr.offset(index_offset)).to_i64() };
827                        let clamped = self.clamp_window_start(start, operand_dim);
828                        source_offset += operand_stride * clamped as isize;
829                    }
830
831                    // SAFETY: the source proof above and distinct destination
832                    // invariant justify these raw accesses.
833                    unsafe {
834                        dest_ptr
835                            .offset(state.dest_offset)
836                            .write(operand_ptr.offset(source_offset).read());
837                    }
838                    self.advance_replay_state(&mut state);
839                }
840                Ok(())
841            },
842            &|left, right| left.and(right),
843        )
844    }
845
846    fn check_call<T, I, W>(
847        &self,
848        dest: &W,
849        operand: &RawStridedRef<'_, T>,
850        start_indices: &RawStridedRef<'_, I>,
851    ) -> Result<()>
852    where
853        W: OverwriteWriter<T>,
854    {
855        if dest.dims() != &self.dest_dims[..]
856            || dest.strides() != &self.dest_strides[..]
857            || operand.dims() != &self.operand_dims[..]
858            || operand.strides() != &self.operand_strides[..]
859            || start_indices.dims() != &self.index_dims[..]
860            || start_indices.strides() != &self.index_strides[..]
861        {
862            return Err(StridedError::PlanLayoutMismatch);
863        }
864        Ok(())
865    }
866
867    fn decode_replay_state(
868        &self,
869        mut linear: usize,
870        dest_offset_base: isize,
871        operand_offset_base: isize,
872        index_offset_base: isize,
873    ) -> Result<GatherReplayState> {
874        let mut coords = AxisVec::with_capacity(self.dest_dims.len());
875        let mut dest_offset = dest_offset_base;
876        let mut window_offset = operand_offset_base;
877        let mut index_batch_offset = index_offset_base;
878        for (axis, &dim) in self.dest_dims.iter().enumerate() {
879            let coord = linear % dim;
880            linear /= dim;
881            let replay_axis = self.replay.axes[axis];
882            coords.push(coord);
883            dest_offset = checked_offset_add(dest_offset, replay_axis.dest_step, coord)?;
884            window_offset = checked_offset_add(window_offset, replay_axis.window_step, coord)?;
885            index_batch_offset =
886                checked_offset_add(index_batch_offset, replay_axis.index_batch_step, coord)?;
887        }
888        Ok(GatherReplayState {
889            coords,
890            dest_offset,
891            window_offset,
892            index_batch_offset,
893        })
894    }
895
896    #[inline]
897    fn advance_replay_state(&self, state: &mut GatherReplayState) {
898        for (axis, replay_axis) in self.replay.axes.iter().enumerate() {
899            let coord = state.coords[axis];
900            if coord + 1 < self.dest_dims[axis] {
901                state.coords[axis] = coord + 1;
902                state.dest_offset += replay_axis.dest_step;
903                state.window_offset += replay_axis.window_step;
904                state.index_batch_offset += replay_axis.index_batch_step;
905                return;
906            }
907            state.coords[axis] = 0;
908            state.dest_offset += replay_axis.dest_reset;
909            state.window_offset += replay_axis.window_reset;
910            state.index_batch_offset += replay_axis.index_batch_reset;
911        }
912    }
913
914    #[inline]
915    fn clamp_window_start(&self, start: i64, operand_dim: usize) -> usize {
916        let dim_size = self.operand_dims[operand_dim];
917        let window_size = self.spec.slice_sizes[operand_dim];
918        let max_start = dim_size.saturating_sub(window_size) as i64;
919        start.clamp(0, max_start) as usize
920    }
921}
922
923impl DynamicSlicePlan {
924    /// Compile a fixed-window dynamic-slice traversal.
925    ///
926    /// `start_*` describes a rank-1 index vector whose length equals the
927    /// operand rank. `dest_dims` must equal `slice_sizes`.
928    pub fn compile(
929        operand_dims: &[usize],
930        operand_strides: &[isize],
931        start_dims: &[usize],
932        start_strides: &[isize],
933        dest_dims: &[usize],
934        dest_strides: &[isize],
935        slice_sizes: &[usize],
936    ) -> Result<Self> {
937        if operand_dims.len() != operand_strides.len()
938            || start_dims.len() != start_strides.len()
939            || dest_dims.len() != dest_strides.len()
940        {
941            return Err(StridedError::StrideLengthMismatch);
942        }
943        if slice_sizes.len() != operand_dims.len() {
944            return Err(StridedError::RankMismatch(
945                slice_sizes.len(),
946                operand_dims.len(),
947            ));
948        }
949        validate_start_vector(start_dims, operand_dims.len())?;
950        checked_total_len(operand_dims)?;
951        checked_total_len(start_dims)?;
952        let total = checked_total_len(dest_dims)?;
953        validate_layout_span(operand_dims, operand_strides)?;
954        validate_layout_span(start_dims, start_strides)?;
955        validate_layout_span(dest_dims, dest_strides)?;
956        if dest_dims != slice_sizes {
957            return Err(StridedError::ShapeMismatch(
958                dest_dims.to_vec(),
959                slice_sizes.to_vec(),
960            ));
961        }
962        if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
963            return Err(StridedError::NonInjectiveOutputLayout);
964        }
965        validate_window_sizes(operand_dims, slice_sizes)?;
966        let replay = WindowReplay::compile(slice_sizes, operand_strides, dest_strides)?;
967
968        Ok(Self {
969            operand_dims: operand_dims.into(),
970            operand_strides: operand_strides.into(),
971            start_dims: start_dims.into(),
972            start_strides: start_strides.into(),
973            dest_dims: dest_dims.into(),
974            dest_strides: dest_strides.into(),
975            slice_sizes: slice_sizes.into(),
976            total,
977            window: fuse_pair_layout(slice_sizes, dest_strides, operand_strides),
978            replay,
979        })
980    }
981
982    /// Execute the prepared dynamic-slice traversal.
983    pub fn execute<T, I>(
984        &self,
985        dest: &mut RawStridedMut<'_, T>,
986        operand: &RawStridedRef<'_, T>,
987        starts: &RawStridedRef<'_, I>,
988        exec: Exec<'_>,
989    ) -> Result<()>
990    where
991        T: Copy + Send + Sync,
992        I: GatherIndex,
993    {
994        self.execute_with_writer(dest, operand, starts, exec)
995    }
996
997    pub(crate) fn execute_uninit<T, I>(
998        &self,
999        dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1000        operand: &RawStridedRef<'_, T>,
1001        starts: &RawStridedRef<'_, I>,
1002        exec: Exec<'_>,
1003    ) -> Result<()>
1004    where
1005        T: Copy + Send + Sync,
1006        I: GatherIndex,
1007    {
1008        self.execute_with_writer(dest, operand, starts, exec)
1009    }
1010
1011    fn execute_with_writer<T, I, W>(
1012        &self,
1013        dest: &mut W,
1014        operand: &RawStridedRef<'_, T>,
1015        starts: &RawStridedRef<'_, I>,
1016        exec: Exec<'_>,
1017    ) -> Result<()>
1018    where
1019        T: Copy + Send + Sync,
1020        I: GatherIndex,
1021        W: OverwriteWriter<T>,
1022    {
1023        self.check_call(dest, operand, starts)?;
1024        if self.total == 0 {
1025            return Ok(());
1026        }
1027        if self.uses_rank_one_contiguous_path() {
1028            return self.execute_rank_one_contiguous(dest, operand, starts, exec);
1029        }
1030        if let Some(window) = &self.window {
1031            return self.execute_fused_window(dest, operand, starts, window, exec);
1032        }
1033        let replay = &self.replay;
1034        let exec = crate::threading::for_len(exec, self.total);
1035        if !exec.is_serial() {
1036            return self.execute_parallel(dest, operand, starts, exec, replay);
1037        }
1038
1039        let mut starts_storage = CoordScratch::new(self.operand_dims.len());
1040        let clamped_starts = starts_storage.as_mut_slice();
1041        read_clamped_starts(
1042            starts,
1043            &self.operand_dims,
1044            &self.slice_sizes,
1045            clamped_starts,
1046        )?;
1047        let source_base =
1048            checked_strided_offset(operand.offset(), &self.operand_strides, clamped_starts)?;
1049        let mut state = replay.decode(0, source_base, dest.offset())?;
1050        let operand_data = operand.data();
1051
1052        // INVARIANT: compile validated the full operand/destination spans and the
1053        // replay window spans; checked bases plus checked reset deltas therefore
1054        // keep every current offset reachable without per-element offset scans.
1055        for _ in 0..self.total {
1056            // SAFETY: the invariant above proves both current offsets are valid.
1057            let value = unsafe { *operand_data.as_ptr().offset(state.source_offset) };
1058            // SAFETY: the invariant above proves this logical destination offset
1059            // is in-bounds, and the output layout is injective.
1060            unsafe { dest.write_at(state.dest_offset, value) };
1061            replay.advance(&mut state);
1062        }
1063        Ok(())
1064    }
1065
1066    /// Replay the compiled fused window from the clamped runtime source base.
1067    ///
1068    /// The window is a CopyPlan-style fused layout, so full inner runs are
1069    /// replayed with a pointer loop (slice copies for unit strides) and the
1070    /// work is split across workers above the repository threshold.
1071    fn execute_fused_window<T, I, W>(
1072        &self,
1073        dest: &mut W,
1074        operand: &RawStridedRef<'_, T>,
1075        starts: &RawStridedRef<'_, I>,
1076        window: &FusedPairLayout,
1077        exec: Exec<'_>,
1078    ) -> Result<()>
1079    where
1080        T: Copy + Send + Sync,
1081        I: GatherIndex,
1082        W: OverwriteWriter<T>,
1083    {
1084        let mut starts_storage = CoordScratch::new(self.operand_dims.len());
1085        let clamped_starts = starts_storage.as_mut_slice();
1086        read_clamped_starts(
1087            starts,
1088            &self.operand_dims,
1089            &self.slice_sizes,
1090            clamped_starts,
1091        )?;
1092        let source_base =
1093            checked_strided_offset(operand.offset(), &self.operand_strides, clamped_starts)?;
1094        let dest_base = dest.offset();
1095        // SAFETY: the validated writer owns the destination allocation.
1096        let dest_ptr = unsafe { dest.data_ptr() }.cast::<MaybeUninit<T>>();
1097        // SAFETY: `window` fuses `slice_sizes` with the checked destination
1098        // and operand strides. The clamped window `start + slice_sizes` lies
1099        // inside `operand_dims`, so every source offset from `source_base` is
1100        // an offset the validated operand view can reach; the destination has
1101        // exactly `slice_sizes` dims and its validated layout reaches every
1102        // destination offset. Compile proved the destination injective, and
1103        // the exclusive writer borrow does not overlap the shared operand.
1104        // Writing `MaybeUninit::new` is valid for both initialized and
1105        // uninitialized destination slots.
1106        unsafe {
1107            replay_fused_raw(
1108                dest_ptr,
1109                dest_base,
1110                operand.data().as_ptr(),
1111                source_base,
1112                window,
1113                |slot: &mut MaybeUninit<T>, value: T| *slot = MaybeUninit::new(value),
1114                |value: T| value,
1115                exec,
1116            );
1117        }
1118        Ok(())
1119    }
1120
1121    #[inline]
1122    fn uses_rank_one_contiguous_path(&self) -> bool {
1123        self.operand_dims.len() == 1 && self.operand_strides[0] == 1 && self.dest_strides[0] == 1
1124    }
1125
1126    fn execute_rank_one_contiguous<T, I, W>(
1127        &self,
1128        dest: &mut W,
1129        operand: &RawStridedRef<'_, T>,
1130        starts: &RawStridedRef<'_, I>,
1131        exec: Exec<'_>,
1132    ) -> Result<()>
1133    where
1134        T: Copy + Send + Sync,
1135        I: GatherIndex,
1136        W: OverwriteWriter<T>,
1137    {
1138        let mut clamped_starts = [0usize; 1];
1139        read_clamped_starts(
1140            starts,
1141            &self.operand_dims,
1142            &self.slice_sizes,
1143            &mut clamped_starts,
1144        )?;
1145        let source_start = checked_offset_add(operand.offset(), 1, clamped_starts[0])?;
1146        let source_start =
1147            usize::try_from(source_start).map_err(|_| StridedError::OffsetOverflow)?;
1148        let dest_start =
1149            usize::try_from(dest.offset()).map_err(|_| StridedError::OffsetOverflow)?;
1150        let source_end = source_start
1151            .checked_add(self.total)
1152            .ok_or(StridedError::OffsetOverflow)?;
1153        let source = operand
1154            .data()
1155            .get(source_start..source_end)
1156            .ok_or(StridedError::OffsetOverflow)?;
1157        // SAFETY: the validated writer owns the destination allocation.
1158        let dest_ptr = unsafe { dest.data_ptr() };
1159        // SAFETY: bounds were checked above and the writer owns the logical
1160        // destination storage.
1161        unsafe {
1162            // Chunked across workers above the repository threshold; each
1163            // worker writes a disjoint chunk of the checked destination run.
1164            crate::threading::copy_contiguous(
1165                source.as_ptr(),
1166                dest_ptr.add(dest_start),
1167                self.total,
1168                exec,
1169            );
1170        }
1171        Ok(())
1172    }
1173
1174    fn execute_parallel<T, I, W>(
1175        &self,
1176        dest: &mut W,
1177        operand: &RawStridedRef<'_, T>,
1178        starts: &RawStridedRef<'_, I>,
1179        exec: Exec<'_>,
1180        replay: &WindowReplay,
1181    ) -> Result<()>
1182    where
1183        T: Copy + Send + Sync,
1184        I: GatherIndex,
1185        W: OverwriteWriter<T>,
1186    {
1187        let mut clamped_starts: AxisVec<usize> = (0..self.operand_dims.len()).map(|_| 0).collect();
1188        read_clamped_starts(
1189            starts,
1190            &self.operand_dims,
1191            &self.slice_sizes,
1192            &mut clamped_starts,
1193        )?;
1194        let source_base =
1195            checked_strided_offset(operand.offset(), &self.operand_strides, &clamped_starts)?;
1196        let dest_base = dest.offset();
1197        let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
1198        // SAFETY: the validated writer owns the destination allocation.
1199        let dest_ptr = crate::threading::SendPtr(unsafe { dest.data_ptr() });
1200
1201        crate::threading::parallel_map_reduce(
1202            0..self.total,
1203            exec,
1204            &|range| {
1205                let mut state = replay.decode(range.start, source_base, dest_base)?;
1206                let operand_ptr = operand_ptr.as_const();
1207                let dest_ptr = dest_ptr.as_ptr();
1208
1209                // INVARIANT: each worker decodes one checked range start. Its
1210                // replay range is disjoint, and the injective output layout makes
1211                // all writes distinct across workers.
1212                for _ in range {
1213                    // SAFETY: checked replay bases/resets keep both offsets in
1214                    // their validated allocations.
1215                    unsafe {
1216                        dest_ptr
1217                            .offset(state.dest_offset)
1218                            .write(operand_ptr.offset(state.source_offset).read());
1219                    }
1220                    replay.advance(&mut state);
1221                }
1222                Ok(())
1223            },
1224            &|left, right| left.and(right),
1225        )
1226    }
1227
1228    fn check_call<T, I, W>(
1229        &self,
1230        dest: &W,
1231        operand: &RawStridedRef<'_, T>,
1232        starts: &RawStridedRef<'_, I>,
1233    ) -> Result<()>
1234    where
1235        W: OverwriteWriter<T>,
1236    {
1237        if dest.dims() != &self.dest_dims[..]
1238            || dest.strides() != &self.dest_strides[..]
1239            || operand.dims() != &self.operand_dims[..]
1240            || operand.strides() != &self.operand_strides[..]
1241            || starts.dims() != &self.start_dims[..]
1242            || starts.strides() != &self.start_strides[..]
1243        {
1244            return Err(StridedError::PlanLayoutMismatch);
1245        }
1246        Ok(())
1247    }
1248}
1249
1250impl DynamicUpdateSlicePlan {
1251    /// Compile a dynamic-update-slice traversal.
1252    ///
1253    /// `dest_dims` must match `operand_dims`; execution materializes
1254    /// `dest = operand` and then overwrites the clamped update window.
1255    #[allow(clippy::too_many_arguments)]
1256    pub fn compile(
1257        operand_dims: &[usize],
1258        operand_strides: &[isize],
1259        start_dims: &[usize],
1260        start_strides: &[isize],
1261        update_dims: &[usize],
1262        update_strides: &[isize],
1263        dest_dims: &[usize],
1264        dest_strides: &[isize],
1265    ) -> Result<Self> {
1266        if operand_dims.len() != operand_strides.len()
1267            || start_dims.len() != start_strides.len()
1268            || update_dims.len() != update_strides.len()
1269            || dest_dims.len() != dest_strides.len()
1270        {
1271            return Err(StridedError::StrideLengthMismatch);
1272        }
1273        if update_dims.len() != operand_dims.len() {
1274            return Err(StridedError::RankMismatch(
1275                update_dims.len(),
1276                operand_dims.len(),
1277            ));
1278        }
1279        validate_start_vector(start_dims, operand_dims.len())?;
1280        checked_total_len(operand_dims)?;
1281        checked_total_len(start_dims)?;
1282        let total = checked_total_len(update_dims)?;
1283        validate_layout_span(operand_dims, operand_strides)?;
1284        validate_layout_span(start_dims, start_strides)?;
1285        validate_layout_span(update_dims, update_strides)?;
1286        validate_layout_span(dest_dims, dest_strides)?;
1287        if dest_dims != operand_dims {
1288            return Err(StridedError::ShapeMismatch(
1289                dest_dims.to_vec(),
1290                operand_dims.to_vec(),
1291            ));
1292        }
1293        if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
1294            return Err(StridedError::NonInjectiveOutputLayout);
1295        }
1296        validate_window_sizes(operand_dims, update_dims)?;
1297        let copy_plan = CopyPlan::compile(operand_dims, dest_strides, operand_strides)?;
1298        let replay = WindowReplay::compile(update_dims, update_strides, dest_strides)?;
1299
1300        Ok(Self {
1301            operand_dims: operand_dims.into(),
1302            operand_strides: operand_strides.into(),
1303            start_dims: start_dims.into(),
1304            start_strides: start_strides.into(),
1305            update_dims: update_dims.into(),
1306            update_strides: update_strides.into(),
1307            dest_dims: dest_dims.into(),
1308            dest_strides: dest_strides.into(),
1309            total,
1310            copy_plan,
1311            window: fuse_pair_layout(update_dims, dest_strides, update_strides),
1312            replay,
1313        })
1314    }
1315
1316    /// Execute the prepared dynamic-update-slice traversal.
1317    pub fn execute<T, I>(
1318        &self,
1319        dest: &mut RawStridedMut<'_, T>,
1320        operand: &RawStridedRef<'_, T>,
1321        update: &RawStridedRef<'_, T>,
1322        starts: &RawStridedRef<'_, I>,
1323        exec: Exec<'_>,
1324    ) -> Result<()>
1325    where
1326        T: Copy + Send + Sync,
1327        I: GatherIndex,
1328    {
1329        let mut exec = exec;
1330        self.check_call(dest, operand, update, starts)?;
1331        self.copy_plan.execute(dest, operand, exec.reborrow())?;
1332        self.execute_update_with_writer(dest, update, starts, exec)
1333    }
1334
1335    /// Execute dynamic update into a destination whose reachable slots may be
1336    /// uninitialized. The copy completes before any read-modify-write access.
1337    pub(crate) fn execute_uninit<'a, T, I>(
1338        &self,
1339        dest: &'a mut RawStridedMut<'a, MaybeUninit<T>>,
1340        operand: &RawStridedRef<'_, T>,
1341        update: &RawStridedRef<'_, T>,
1342        starts: &RawStridedRef<'_, I>,
1343        exec: Exec<'_>,
1344    ) -> Result<()>
1345    where
1346        T: Copy + Send + Sync,
1347        I: GatherIndex,
1348    {
1349        self.check_call(dest, operand, update, starts)?;
1350        self.copy_plan
1351            .execute_uninit_then(dest, operand, exec, |mut receipt, exec| {
1352                self.execute_update_with_writer(&mut receipt, update, starts, exec)
1353            })?
1354    }
1355
1356    fn execute_update_with_writer<T, I, W>(
1357        &self,
1358        dest: &mut W,
1359        update: &RawStridedRef<'_, T>,
1360        starts: &RawStridedRef<'_, I>,
1361        exec: Exec<'_>,
1362    ) -> Result<()>
1363    where
1364        T: Copy + Send + Sync,
1365        I: GatherIndex,
1366        W: OverwriteWriter<T>,
1367    {
1368        if self.total == 0 {
1369            return Ok(());
1370        }
1371        if self.uses_rank_one_contiguous_path() {
1372            return self.execute_rank_one_contiguous(dest, update, starts, exec);
1373        }
1374        if let Some(window) = &self.window {
1375            return self.execute_fused_window(dest, update, starts, window, exec);
1376        }
1377        let replay = &self.replay;
1378        let exec = crate::threading::for_len(exec, self.total);
1379        if !exec.is_serial() {
1380            return self.execute_update_parallel(dest, update, starts, exec, replay);
1381        }
1382
1383        let mut starts_storage = CoordScratch::new(self.operand_dims.len());
1384        let clamped_starts = starts_storage.as_mut_slice();
1385        read_clamped_starts(
1386            starts,
1387            &self.operand_dims,
1388            &self.update_dims,
1389            clamped_starts,
1390        )?;
1391        let source_base = update.offset();
1392        let dest_base = checked_strided_offset(dest.offset(), &self.dest_strides, &clamped_starts)?;
1393        let mut state = replay.decode(0, source_base, dest_base)?;
1394        let update_data = update.data();
1395
1396        // INVARIANT: the initial CopyPlan has completed before this replay;
1397        // compile validated both window spans and the checked bases/resets keep
1398        // every update read and destination write reachable.
1399        for _ in 0..self.total {
1400            // SAFETY: checked replay metadata proves the current update read.
1401            let value = unsafe { *update_data.as_ptr().offset(state.source_offset) };
1402            // SAFETY: the copied, injective destination layout proves this write
1403            // is initialized and in-bounds.
1404            unsafe { dest.write_at(state.dest_offset, value) };
1405            replay.advance(&mut state);
1406        }
1407        Ok(())
1408    }
1409
1410    /// Replay the compiled fused update window at the clamped runtime
1411    /// destination base (see [`DynamicSlicePlan`]'s fused window replay).
1412    fn execute_fused_window<T, I, W>(
1413        &self,
1414        dest: &mut W,
1415        update: &RawStridedRef<'_, T>,
1416        starts: &RawStridedRef<'_, I>,
1417        window: &FusedPairLayout,
1418        exec: Exec<'_>,
1419    ) -> Result<()>
1420    where
1421        T: Copy + Send + Sync,
1422        I: GatherIndex,
1423        W: OverwriteWriter<T>,
1424    {
1425        let mut starts_storage = CoordScratch::new(self.operand_dims.len());
1426        let clamped_starts = starts_storage.as_mut_slice();
1427        read_clamped_starts(
1428            starts,
1429            &self.operand_dims,
1430            &self.update_dims,
1431            clamped_starts,
1432        )?;
1433        let dest_base = checked_strided_offset(dest.offset(), &self.dest_strides, clamped_starts)?;
1434        // SAFETY: the validated writer owns the destination allocation.
1435        let dest_ptr = unsafe { dest.data_ptr() }.cast::<MaybeUninit<T>>();
1436        // SAFETY: `window` fuses `update_dims` with the checked destination
1437        // and update strides. The update view has exactly `update_dims` and
1438        // its validated layout reaches every source offset; the clamped window
1439        // lies inside `dest_dims`, so every destination offset from
1440        // `dest_base` is reachable through the validated destination layout.
1441        // Compile proved the destination injective, and the exclusive writer
1442        // borrow does not overlap the shared update.
1443        unsafe {
1444            replay_fused_raw(
1445                dest_ptr,
1446                dest_base,
1447                update.data().as_ptr(),
1448                update.offset(),
1449                window,
1450                |slot: &mut MaybeUninit<T>, value: T| *slot = MaybeUninit::new(value),
1451                |value: T| value,
1452                exec,
1453            );
1454        }
1455        Ok(())
1456    }
1457
1458    #[inline]
1459    fn uses_rank_one_contiguous_path(&self) -> bool {
1460        self.operand_dims.len() == 1
1461            && self.operand_strides[0] == 1
1462            && self.update_strides[0] == 1
1463            && self.dest_strides[0] == 1
1464    }
1465
1466    fn execute_rank_one_contiguous<T, I, W>(
1467        &self,
1468        dest: &mut W,
1469        update: &RawStridedRef<'_, T>,
1470        starts: &RawStridedRef<'_, I>,
1471        exec: Exec<'_>,
1472    ) -> Result<()>
1473    where
1474        T: Copy + Send + Sync,
1475        I: GatherIndex,
1476        W: OverwriteWriter<T>,
1477    {
1478        let mut clamped_starts = [0usize; 1];
1479        read_clamped_starts(
1480            starts,
1481            &self.operand_dims,
1482            &self.update_dims,
1483            &mut clamped_starts,
1484        )?;
1485        let update_start =
1486            usize::try_from(update.offset()).map_err(|_| StridedError::OffsetOverflow)?;
1487        let dest_start = checked_offset_add(dest.offset(), 1, clamped_starts[0])?;
1488        let dest_start = usize::try_from(dest_start).map_err(|_| StridedError::OffsetOverflow)?;
1489        let update_end = update_start
1490            .checked_add(self.total)
1491            .ok_or(StridedError::OffsetOverflow)?;
1492        let update = update
1493            .data()
1494            .get(update_start..update_end)
1495            .ok_or(StridedError::OffsetOverflow)?;
1496        // SAFETY: the validated writer owns the destination allocation.
1497        let dest_ptr = unsafe { dest.data_ptr() };
1498        // SAFETY: the checked ranges are inside the destination allocation.
1499        unsafe {
1500            // Chunked across workers above the repository threshold; each
1501            // worker writes a disjoint chunk of the checked destination run.
1502            crate::threading::copy_contiguous(
1503                update.as_ptr(),
1504                dest_ptr.add(dest_start),
1505                self.total,
1506                exec,
1507            );
1508        }
1509        Ok(())
1510    }
1511
1512    fn execute_update_parallel<T, I, W>(
1513        &self,
1514        dest: &mut W,
1515        update: &RawStridedRef<'_, T>,
1516        starts: &RawStridedRef<'_, I>,
1517        exec: Exec<'_>,
1518        replay: &WindowReplay,
1519    ) -> Result<()>
1520    where
1521        T: Copy + Send + Sync,
1522        I: GatherIndex,
1523        W: OverwriteWriter<T>,
1524    {
1525        let mut clamped_starts: AxisVec<usize> = (0..self.operand_dims.len()).map(|_| 0).collect();
1526        read_clamped_starts(
1527            starts,
1528            &self.operand_dims,
1529            &self.update_dims,
1530            &mut clamped_starts,
1531        )?;
1532        let source_base = update.offset();
1533        let dest_base = checked_strided_offset(dest.offset(), &self.dest_strides, &clamped_starts)?;
1534        let update_ptr = crate::threading::SendPtr(update.data().as_ptr() as *mut T);
1535        // SAFETY: the validated writer owns the destination allocation.
1536        let dest_ptr = crate::threading::SendPtr(unsafe { dest.data_ptr() });
1537
1538        crate::threading::parallel_map_reduce(
1539            0..self.total,
1540            exec,
1541            &|range| {
1542                let mut state = replay.decode(range.start, source_base, dest_base)?;
1543                let update_ptr = update_ptr.as_const();
1544                let dest_ptr = dest_ptr.as_ptr();
1545
1546                // INVARIANT: the initial operand copy completed before this
1547                // replay; workers own disjoint ranges and injective output
1548                // layouts make their writes distinct.
1549                for _ in range {
1550                    // SAFETY: checked replay bases/resets prove both offsets are
1551                    // within the initialized update and destination allocations.
1552                    unsafe {
1553                        dest_ptr
1554                            .offset(state.dest_offset)
1555                            .write(update_ptr.offset(state.source_offset).read());
1556                    }
1557                    replay.advance(&mut state);
1558                }
1559                Ok(())
1560            },
1561            &|left, right| left.and(right),
1562        )
1563    }
1564
1565    fn check_call<T, I, W>(
1566        &self,
1567        dest: &W,
1568        operand: &RawStridedRef<'_, T>,
1569        update: &RawStridedRef<'_, T>,
1570        starts: &RawStridedRef<'_, I>,
1571    ) -> Result<()>
1572    where
1573        W: OverwriteWriter<T>,
1574    {
1575        if dest.dims() != &self.dest_dims[..]
1576            || dest.strides() != &self.dest_strides[..]
1577            || operand.dims() != &self.operand_dims[..]
1578            || operand.strides() != &self.operand_strides[..]
1579            || update.dims() != &self.update_dims[..]
1580            || update.strides() != &self.update_strides[..]
1581            || starts.dims() != &self.start_dims[..]
1582            || starts.strides() != &self.start_strides[..]
1583        {
1584            return Err(StridedError::PlanLayoutMismatch);
1585        }
1586        Ok(())
1587    }
1588}
1589
1590impl ScatterPlan {
1591    /// Compile an additive scatter traversal.
1592    #[allow(clippy::too_many_arguments)]
1593    pub fn compile(
1594        operand_dims: &[usize],
1595        operand_strides: &[isize],
1596        index_dims: &[usize],
1597        index_strides: &[isize],
1598        update_dims: &[usize],
1599        update_strides: &[isize],
1600        dest_dims: &[usize],
1601        dest_strides: &[isize],
1602        spec: ScatterSpec,
1603    ) -> Result<Self> {
1604        if operand_dims.len() != operand_strides.len()
1605            || index_dims.len() != index_strides.len()
1606            || update_dims.len() != update_strides.len()
1607            || dest_dims.len() != dest_strides.len()
1608        {
1609            return Err(StridedError::StrideLengthMismatch);
1610        }
1611        checked_total_len(operand_dims)?;
1612        checked_total_len(index_dims)?;
1613        checked_total_len(update_dims)?;
1614        if dest_dims != operand_dims {
1615            return Err(StridedError::ShapeMismatch(
1616                dest_dims.to_vec(),
1617                operand_dims.to_vec(),
1618            ));
1619        }
1620        if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
1621            return Err(StridedError::NonInjectiveOutputLayout);
1622        }
1623
1624        let operand_rank = operand_dims.len();
1625        validate_unique_axes(&spec.inserted_window_dims, operand_rank)?;
1626        validate_unique_axes(&spec.scatter_dims_to_operand_dims, operand_rank)?;
1627        if spec.index_vector_dim > index_dims.len() {
1628            return Err(StridedError::InvalidAxis {
1629                axis: spec.index_vector_dim,
1630                rank: index_dims.len() + 1,
1631            });
1632        }
1633        let index_vector_size = if spec.index_vector_dim == index_dims.len() {
1634            1
1635        } else {
1636            index_dims[spec.index_vector_dim]
1637        };
1638        if index_vector_size != spec.scatter_dims_to_operand_dims.len() {
1639            return Err(StridedError::RankMismatch(
1640                index_vector_size,
1641                spec.scatter_dims_to_operand_dims.len(),
1642            ));
1643        }
1644
1645        let batch_shape = index_batch_shape(index_dims, spec.index_vector_dim);
1646        let window_dims = operand_window_dims(operand_rank, &spec.inserted_window_dims);
1647        if spec.update_window_dims.len() != window_dims.len() {
1648            return Err(StridedError::RankMismatch(
1649                spec.update_window_dims.len(),
1650                window_dims.len(),
1651            ));
1652        }
1653
1654        let update_rank = update_dims.len();
1655        let expected_batch_rank = update_rank
1656            .checked_sub(spec.update_window_dims.len())
1657            .ok_or(StridedError::RankMismatch(
1658                spec.update_window_dims.len(),
1659                update_rank,
1660            ))?;
1661        if expected_batch_rank != batch_shape.len() {
1662            return Err(StridedError::RankMismatch(
1663                expected_batch_rank,
1664                batch_shape.len(),
1665            ));
1666        }
1667        validate_unique_axes(&spec.update_window_dims, update_rank)?;
1668
1669        let mut is_update_window_dim: AxisVec<bool> = (0..update_rank).map(|_| false).collect();
1670        for &axis in &spec.update_window_dims {
1671            is_update_window_dim[axis] = true;
1672        }
1673
1674        let mut batch_axis = 0usize;
1675        for axis in 0..update_rank {
1676            if !is_update_window_dim[axis] {
1677                if update_dims[axis] != batch_shape[batch_axis] {
1678                    return Err(StridedError::ShapeMismatch(
1679                        update_dims.to_vec(),
1680                        expected_scatter_update_shape(&batch_shape, &spec, update_dims).to_vec(),
1681                    ));
1682                }
1683                batch_axis += 1;
1684            }
1685        }
1686
1687        let mut window_shape: AxisVec<usize> = (0..operand_rank).map(|_| 1).collect();
1688        let mut window_shape_updates: AxisVec<usize> =
1689            AxisVec::with_capacity(spec.update_window_dims.len());
1690        for (pos, &update_axis) in spec.update_window_dims.iter().enumerate() {
1691            let dim = update_dims[update_axis];
1692            window_shape_updates.push(dim);
1693            window_shape[window_dims[pos]] = dim;
1694        }
1695        validate_window_sizes(operand_dims, &window_shape)?;
1696
1697        let batch_elems = checked_total_len(&batch_shape)?;
1698        let window_elems = checked_total_len(&window_shape_updates)?;
1699        let copy_plan = CopyPlan::compile(operand_dims, dest_strides, operand_strides)?;
1700        let replay = ScatterReplay::compile(
1701            &batch_shape,
1702            index_dims,
1703            index_strides,
1704            spec.index_vector_dim,
1705            spec.scatter_dims_to_operand_dims.len(),
1706            update_dims,
1707            update_strides,
1708            &spec.update_window_dims,
1709            &is_update_window_dim,
1710            &window_shape_updates,
1711            &window_dims,
1712            dest_strides,
1713        )?;
1714
1715        Ok(Self {
1716            operand_dims: operand_dims.into(),
1717            operand_strides: operand_strides.into(),
1718            index_dims: index_dims.into(),
1719            index_strides: index_strides.into(),
1720            update_dims: update_dims.into(),
1721            update_strides: update_strides.into(),
1722            dest_dims: dest_dims.into(),
1723            dest_strides: dest_strides.into(),
1724            spec,
1725            window_shape,
1726            batch_elems,
1727            window_elems,
1728            copy_plan,
1729            replay,
1730        })
1731    }
1732
1733    /// Execute the prepared additive scatter traversal.
1734    #[inline(always)]
1735    pub fn execute<T, I>(
1736        &self,
1737        dest: &mut RawStridedMut<'_, T>,
1738        operand: &RawStridedRef<'_, T>,
1739        scatter_indices: &RawStridedRef<'_, I>,
1740        updates: &RawStridedRef<'_, T>,
1741        exec: Exec<'_>,
1742    ) -> Result<()>
1743    where
1744        T: Copy + Add<Output = T> + Send + Sync,
1745        I: GatherIndex,
1746    {
1747        self.check_call(dest, operand, scatter_indices, updates)?;
1748        self.copy_plan.execute(dest, operand, exec)?;
1749        self.execute_updates(dest, scatter_indices, updates, |a, b| a + b)
1750    }
1751
1752    /// Execute additive scatter into a destination whose reachable slots may
1753    /// be uninitialized. The operand copy completes before any RMW access.
1754    pub(crate) fn execute_uninit<'a, T, I>(
1755        &self,
1756        dest: &'a mut RawStridedMut<'a, MaybeUninit<T>>,
1757        operand: &RawStridedRef<'_, T>,
1758        scatter_indices: &RawStridedRef<'_, I>,
1759        updates: &RawStridedRef<'_, T>,
1760        combine: fn(T, T) -> T,
1761        exec: Exec<'_>,
1762    ) -> Result<()>
1763    where
1764        T: Copy + Add<Output = T> + Send + Sync,
1765        I: GatherIndex,
1766    {
1767        self.check_call(dest, operand, scatter_indices, updates)?;
1768        self.copy_plan
1769            .execute_uninit_then(dest, operand, exec, |mut receipt, _| {
1770                self.execute_updates(&mut receipt, scatter_indices, updates, combine)
1771            })?
1772    }
1773
1774    #[inline(always)]
1775    fn execute_updates<T, I, W, F>(
1776        &self,
1777        dest: &mut W,
1778        scatter_indices: &RawStridedRef<'_, I>,
1779        updates: &RawStridedRef<'_, T>,
1780        combine: F,
1781    ) -> Result<()>
1782    where
1783        T: Copy + Send + Sync,
1784        I: GatherIndex,
1785        W: ReadModifyWrite<T>,
1786        F: Fn(T, T) -> T + Copy,
1787    {
1788        if self.batch_elems == 0 || self.window_elems == 0 {
1789            return Ok(());
1790        }
1791        if self.uses_rank_one_scalar_update_path() {
1792            return self.execute_rank_one_scalar_updates(dest, scatter_indices, updates, combine);
1793        }
1794        self.execute_generic_updates(dest, scatter_indices, updates, combine)
1795    }
1796
1797    #[inline(never)]
1798    fn execute_generic_updates<T, I, W, F>(
1799        &self,
1800        dest: &mut W,
1801        scatter_indices: &RawStridedRef<'_, I>,
1802        updates: &RawStridedRef<'_, T>,
1803        combine: F,
1804    ) -> Result<()>
1805    where
1806        T: Copy + Send + Sync,
1807        I: GatherIndex,
1808        W: ReadModifyWrite<T>,
1809        F: Fn(T, T) -> T + Copy,
1810    {
1811        // Overlapping additive updates are order-sensitive, so this remains a
1812        // deterministic serial replay until a combine-aware parallel plan exists.
1813        let replay = &self.replay;
1814
1815        let mut operand_base_storage = CoordScratch::new(self.operand_dims.len());
1816        let operand_base = operand_base_storage.as_mut_slice();
1817        let mut batch_state = replay
1818            .batch
1819            .decode(0, scatter_indices.offset(), updates.offset())?;
1820        let index_data = scatter_indices.data();
1821        let update_data = updates.data();
1822
1823        // INVARIANT: batch and window replay metadata was checked at compile;
1824        // the indirect component loop below is the only data-dependent lookup.
1825        for _ in 0..self.batch_elems {
1826            operand_base.fill(0);
1827            for (&component_offset, &operand_axis) in replay
1828                .index_component_offsets
1829                .iter()
1830                .zip(self.spec.scatter_dims_to_operand_dims.iter())
1831            {
1832                let index_offset = batch_state
1833                    .source_offset
1834                    .checked_add(component_offset)
1835                    .ok_or(StridedError::OffsetOverflow)?;
1836                // SAFETY: checked batch replay and component offsets keep this
1837                // indirect index read inside the validated index allocation.
1838                let start = unsafe { *index_data.as_ptr().offset(index_offset) }.to_i64();
1839                operand_base[operand_axis] = clamp_window_start(
1840                    start,
1841                    self.operand_dims[operand_axis],
1842                    self.window_shape[operand_axis],
1843                );
1844            }
1845
1846            let dest_base = checked_strided_offset(dest.offset(), dest.strides(), operand_base)?;
1847            let mut window_state = replay
1848                .window
1849                .decode(0, batch_state.dest_offset, dest_base)?;
1850            for _ in 0..self.window_elems {
1851                // SAFETY: copy completion and checked replay state prove both
1852                // the update read and initialized destination RMW are valid.
1853                let value = unsafe { *update_data.as_ptr().offset(window_state.source_offset) };
1854                unsafe { dest.add_at(window_state.dest_offset, value, combine) };
1855                replay.window.advance(&mut window_state);
1856            }
1857            replay.batch.advance(&mut batch_state);
1858        }
1859        Ok(())
1860    }
1861
1862    fn uses_rank_one_scalar_update_path(&self) -> bool {
1863        self.operand_dims.len() == 1
1864            && self.index_dims.len() == 2
1865            && self.update_dims.len() == 1
1866            && self.dest_dims.len() == 1
1867            && self.operand_strides[0] == 1
1868            && self.index_strides[0] == 1
1869            && self.update_strides[0] == 1
1870            && self.dest_strides[0] == 1
1871            && self.index_dims[1] == 1
1872            && self.spec.update_window_dims.is_empty()
1873            && self.spec.inserted_window_dims.as_slice() == [0]
1874            && self.spec.scatter_dims_to_operand_dims.as_slice() == [0]
1875            && self.spec.index_vector_dim == 1
1876            && self.window_elems == 1
1877    }
1878
1879    #[inline(always)]
1880    fn execute_rank_one_scalar_updates<T, I, W, F>(
1881        &self,
1882        dest: &mut W,
1883        scatter_indices: &RawStridedRef<'_, I>,
1884        updates: &RawStridedRef<'_, T>,
1885        combine: F,
1886    ) -> Result<()>
1887    where
1888        T: Copy,
1889        I: GatherIndex,
1890        W: ReadModifyWrite<T>,
1891        F: Fn(T, T) -> T + Copy,
1892    {
1893        let mut index_offset = scatter_indices.offset();
1894        let mut update_offset = updates.offset();
1895        let dest_offset = dest.offset();
1896        let index_data = scatter_indices.data();
1897        let update_data = updates.data();
1898        // SAFETY: the validated writer owns the destination allocation.
1899        let dest_ptr = unsafe { dest.data_ptr() };
1900
1901        // INVARIANT: compile and check_call validated compact rank-one index
1902        // and update layouts. Ordered replay preserves repeated-index semantics.
1903        for _ in 0..self.batch_elems {
1904            // SAFETY: the invariant above proves index/update reads and the
1905            // clamped destination offset are in their validated allocations.
1906            unsafe {
1907                let start = (*index_data.as_ptr().offset(index_offset)).to_i64();
1908                let output_offset =
1909                    dest_offset + clamp_window_start(start, self.operand_dims[0], 1) as isize;
1910                let output = dest_ptr.offset(output_offset);
1911                output.write(combine(
1912                    output.read(),
1913                    *update_data.as_ptr().offset(update_offset),
1914                ));
1915            }
1916            index_offset += 1;
1917            update_offset += 1;
1918        }
1919        Ok(())
1920    }
1921
1922    fn check_call<T, I, W>(
1923        &self,
1924        dest: &W,
1925        operand: &RawStridedRef<'_, T>,
1926        scatter_indices: &RawStridedRef<'_, I>,
1927        updates: &RawStridedRef<'_, T>,
1928    ) -> Result<()>
1929    where
1930        W: OverwriteWriter<T>,
1931    {
1932        if dest.dims() != &self.dest_dims[..]
1933            || dest.strides() != &self.dest_strides[..]
1934            || operand.dims() != &self.operand_dims[..]
1935            || operand.strides() != &self.operand_strides[..]
1936            || scatter_indices.dims() != &self.index_dims[..]
1937            || scatter_indices.strides() != &self.index_strides[..]
1938            || updates.dims() != &self.update_dims[..]
1939            || updates.strides() != &self.update_strides[..]
1940        {
1941            return Err(StridedError::PlanLayoutMismatch);
1942        }
1943        Ok(())
1944    }
1945}
1946
1947fn validate_unique_axes(axes: &[usize], rank: usize) -> Result<()> {
1948    // Inline boolean metadata: rank <= INLINE_RANK validation allocates
1949    // nothing, and it spills above the inline bound.
1950    let mut seen: InlineVec<bool> = InlineVec::new();
1951    seen.resize(rank, false);
1952    for &axis in axes {
1953        if axis >= rank {
1954            return Err(StridedError::InvalidAxis { axis, rank });
1955        }
1956        if seen[axis] {
1957            return Err(StridedError::InvalidAxis { axis, rank });
1958        }
1959        seen[axis] = true;
1960    }
1961    Ok(())
1962}
1963
1964fn validate_start_vector(start_dims: &[usize], operand_rank: usize) -> Result<()> {
1965    if start_dims.len() != 1 {
1966        return Err(StridedError::RankMismatch(start_dims.len(), 1));
1967    }
1968    if start_dims[0] != operand_rank {
1969        return Err(StridedError::RankMismatch(start_dims[0], operand_rank));
1970    }
1971    Ok(())
1972}
1973
1974fn validate_window_sizes(operand_dims: &[usize], window_sizes: &[usize]) -> Result<()> {
1975    if operand_dims.len() != window_sizes.len() {
1976        return Err(StridedError::RankMismatch(
1977            window_sizes.len(),
1978            operand_dims.len(),
1979        ));
1980    }
1981    for (axis, (&window, &dim)) in window_sizes.iter().zip(operand_dims.iter()).enumerate() {
1982        if window > dim {
1983            return Err(StridedError::InvalidAxis {
1984                axis,
1985                rank: operand_dims.len(),
1986            });
1987        }
1988    }
1989    Ok(())
1990}
1991
1992fn read_clamped_starts<I>(
1993    starts: &RawStridedRef<'_, I>,
1994    operand_dims: &[usize],
1995    window_sizes: &[usize],
1996    out: &mut [usize],
1997) -> Result<()>
1998where
1999    I: GatherIndex,
2000{
2001    debug_assert_eq!(operand_dims.len(), window_sizes.len());
2002    debug_assert_eq!(operand_dims.len(), out.len());
2003    for axis in 0..operand_dims.len() {
2004        let offset = checked_offset_add(starts.offset(), starts.strides()[0], axis)?;
2005        let start = unsafe { *starts.data().as_ptr().offset(offset) }.to_i64();
2006        out[axis] = clamp_window_start(start, operand_dims[axis], window_sizes[axis]);
2007    }
2008    Ok(())
2009}
2010
2011#[inline]
2012fn clamp_window_start(start: i64, dim_size: usize, window_size: usize) -> usize {
2013    let max_start = dim_size.saturating_sub(window_size) as i64;
2014    start.clamp(0, max_start) as usize
2015}
2016
2017fn expected_scatter_update_shape(
2018    batch_shape: &[usize],
2019    spec: &ScatterSpec,
2020    update_dims: &[usize],
2021) -> AxisVec<usize> {
2022    let mut expected: AxisVec<usize> = AxisVec::with_capacity(update_dims.len());
2023    let mut batch_axis = 0usize;
2024    for axis in 0..update_dims.len() {
2025        if spec.update_window_dims.contains(&axis) {
2026            expected.push(update_dims[axis]);
2027        } else {
2028            expected.push(batch_shape[batch_axis]);
2029            batch_axis += 1;
2030        }
2031    }
2032    expected
2033}
2034
2035fn operand_window_dims(rank: usize, collapsed_slice_dims: &[usize]) -> AxisVec<usize> {
2036    (0..rank)
2037        .filter(|axis| !collapsed_slice_dims.contains(axis))
2038        .collect()
2039}
2040
2041fn index_batch_shape(index_dims: &[usize], index_vector_dim: usize) -> AxisVec<usize> {
2042    if index_vector_dim == index_dims.len() {
2043        return index_dims.into();
2044    }
2045    index_dims
2046        .iter()
2047        .enumerate()
2048        .filter_map(|(axis, &dim)| (axis != index_vector_dim).then_some(dim))
2049        .collect()
2050}
2051
2052fn validate_layout_span(dims: &[usize], strides: &[isize]) -> Result<()> {
2053    if dims.len() != strides.len() {
2054        return Err(StridedError::StrideLengthMismatch);
2055    }
2056    let mut min_offset = 0isize;
2057    let mut max_offset = 0isize;
2058    for (&dim, &stride) in dims.iter().zip(strides.iter()) {
2059        let last =
2060            isize::try_from(dim.saturating_sub(1)).map_err(|_| StridedError::OffsetOverflow)?;
2061        let extent = stride
2062            .checked_mul(last)
2063            .ok_or(StridedError::OffsetOverflow)?;
2064        if extent < 0 {
2065            min_offset = min_offset
2066                .checked_add(extent)
2067                .ok_or(StridedError::OffsetOverflow)?;
2068        } else {
2069            max_offset = max_offset
2070                .checked_add(extent)
2071                .ok_or(StridedError::OffsetOverflow)?;
2072        }
2073    }
2074    Ok(())
2075}
2076
2077fn checked_replay_reset(dim: usize, step: isize) -> Result<isize> {
2078    if dim == 0 {
2079        return Ok(0);
2080    }
2081    let last = isize::try_from(dim - 1).map_err(|_| StridedError::OffsetOverflow)?;
2082    step.checked_mul(last)
2083        .and_then(isize::checked_neg)
2084        .ok_or(StridedError::OffsetOverflow)
2085}
2086
2087fn checked_total_len(dims: &[usize]) -> Result<usize> {
2088    if dims.is_empty() {
2089        return Ok(1);
2090    }
2091    dims.iter()
2092        .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
2093        .ok_or(StridedError::OffsetOverflow)
2094}
2095
2096fn checked_strided_offset(base: isize, strides: &[isize], index: &[usize]) -> Result<isize> {
2097    let mut offset = base;
2098    for (&stride, &coord) in strides.iter().zip(index.iter()) {
2099        offset = checked_offset_add(offset, stride, coord)?;
2100    }
2101    Ok(offset)
2102}
2103
2104fn checked_offset_add(base: isize, stride: isize, coord: usize) -> Result<isize> {
2105    let coord = isize::try_from(coord).map_err(|_| StridedError::OffsetOverflow)?;
2106    let scaled = stride
2107        .checked_mul(coord)
2108        .ok_or(StridedError::OffsetOverflow)?;
2109    base.checked_add(scaled).ok_or(StridedError::OffsetOverflow)
2110}
2111
2112struct CoordScratch {
2113    inline: [usize; RAW_FUSED_RANK_LIMIT],
2114    heap: Option<Vec<usize>>,
2115    len: usize,
2116}
2117
2118impl CoordScratch {
2119    fn new(len: usize) -> Self {
2120        if len <= RAW_FUSED_RANK_LIMIT {
2121            Self {
2122                inline: [0; RAW_FUSED_RANK_LIMIT],
2123                heap: None,
2124                len,
2125            }
2126        } else {
2127            Self {
2128                inline: [0; RAW_FUSED_RANK_LIMIT],
2129                heap: Some(vec![0; len]),
2130                len,
2131            }
2132        }
2133    }
2134
2135    fn as_mut_slice(&mut self) -> &mut [usize] {
2136        match &mut self.heap {
2137            Some(heap) => heap,
2138            None => &mut self.inline[..self.len],
2139        }
2140    }
2141}
2142
2143#[cfg(test)]
2144#[path = "gather_plan/tests/tests.rs"]
2145mod tests;
2146
2147#[cfg(test)]
2148#[path = "gather_plan/tests/parallel_tests.rs"]
2149mod parallel_tests;