use crate::application::view::{ArrayView, ArrayViewMut};
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::Layout;
use crate::domain::remove_axis::RemoveAxis;
#[inline]
fn odometer_step<const N: usize>(
index: &mut [usize; N],
shape: &[usize; N],
strides: &[isize; N],
offset: &mut usize,
) {
for i in (0..N).rev() {
index[i] += 1;
if index[i] < shape[i] {
*offset = (*offset as isize + strides[i]) as usize;
break;
}
*offset = (*offset as isize - (shape[i] - 1) as isize * strides[i]) as usize;
index[i] = 0;
}
}
#[inline]
fn odometer_step_back<const N: usize>(
index: &mut [usize; N],
shape: &[usize; N],
strides: &[isize; N],
offset: &mut usize,
) {
for i in (0..N).rev() {
if index[i] > 0 {
index[i] -= 1;
*offset = (*offset as isize - strides[i]) as usize;
break;
}
*offset = (*offset as isize + (shape[i] - 1) as isize * strides[i]) as usize;
index[i] = shape[i] - 1;
}
}
#[inline]
fn lane_geometry<R, const N: usize, const M: usize>(
shape: [usize; N],
strides: [isize; N],
offset: usize,
axis: usize,
marker: R,
) -> Result<(Layout<M>, usize, isize)>
where
R: RemoveAxis<N, SmallerShape = [usize; M], SmallerStrides = [isize; M]>,
{
let reduced_shape = marker.remove_shape(shape, axis)?;
let reduced_strides = marker.remove_strides(strides, axis)?;
let complement = Layout::from_parts_unchecked(reduced_shape, reduced_strides, offset);
Ok((complement, shape[axis], strides[axis]))
}
pub struct Lanes<'a, T, const N: usize, const M: usize> {
data: &'a [T],
complement: Layout<M>,
axis_len: usize,
axis_stride: isize,
front: usize,
back: usize,
front_index: [usize; M],
front_offset: usize,
back_index: [usize; M],
back_offset: usize,
}
impl<'a, T, const N: usize, const M: usize> Lanes<'a, T, N, M> {
pub(crate) fn new<R>(view: &ArrayView<'a, T, N>, axis: usize, marker: R) -> Result<Self>
where
R: RemoveAxis<N, SmallerShape = [usize; M], SmallerStrides = [isize; M]>,
{
if axis >= N {
return Err(LetoError::StorageError {
reason: format!("Axis {axis} out of bounds for rank {N}"),
});
}
view.layout().validate_storage_len(view.data().len())?;
let (complement, axis_len, axis_stride) =
lane_geometry(view.shape(), view.strides(), view.offset(), axis, marker)?;
let back = complement.size();
let (back_index, back_offset) = if back > 0 {
let mut idx = [0usize; M];
for (i, item) in idx.iter_mut().enumerate() {
*item = complement.shape()[i] - 1;
}
let offset = complement
.offset_of(idx)
.expect("invariant: last index is valid");
(idx, offset)
} else {
([0usize; M], complement.offset())
};
Ok(Self {
data: view.data(),
complement,
axis_len,
axis_stride,
front: 0,
back,
front_index: [0usize; M],
front_offset: complement.offset(),
back_index,
back_offset,
})
}
}
impl<'a, T, const N: usize, const M: usize> Iterator for Lanes<'a, T, N, M> {
type Item = ArrayView<'a, T, 1>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
let layout =
Layout::from_parts_unchecked([self.axis_len], [self.axis_stride], self.front_offset);
odometer_step(
&mut self.front_index,
&self.complement.shape(),
&self.complement.strides(),
&mut self.front_offset,
);
self.front += 1;
Some(ArrayView::new(layout, self.data))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.back - self.front;
(remaining, Some(remaining))
}
}
impl<'a, T, const N: usize, const M: usize> DoubleEndedIterator for Lanes<'a, T, N, M> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
self.back -= 1;
let layout =
Layout::from_parts_unchecked([self.axis_len], [self.axis_stride], self.back_offset);
odometer_step_back(
&mut self.back_index,
&self.complement.shape(),
&self.complement.strides(),
&mut self.back_offset,
);
Some(ArrayView::new(layout, self.data))
}
}
impl<'a, T, const N: usize, const M: usize> ExactSizeIterator for Lanes<'a, T, N, M> {}
pub struct LanesMut<'a, T, const N: usize, const M: usize> {
ptr: *mut T,
complement: Layout<M>,
axis_len: usize,
axis_stride: isize,
front: usize,
back: usize,
front_index: [usize; M],
front_offset: usize,
_marker: std::marker::PhantomData<&'a mut [T]>,
}
impl<'a, T, const N: usize, const M: usize> LanesMut<'a, T, N, M> {
pub(crate) fn new<R>(view: ArrayViewMut<'a, T, N>, axis: usize, marker: R) -> Result<Self>
where
R: RemoveAxis<N, SmallerShape = [usize; M], SmallerStrides = [isize; M]>,
{
if axis >= N {
return Err(LetoError::StorageError {
reason: format!("Axis {axis} out of bounds for rank {N}"),
});
}
view.layout().validate_storage_len(view.len)?;
if !view.layout().is_injective()? {
return Err(LetoError::StorageError {
reason: "lane mutable iterator requires an injective (non-aliasing) layout"
.to_string(),
});
}
let (complement, axis_len, axis_stride) =
lane_geometry(view.shape(), view.strides(), view.offset(), axis, marker)?;
let back = complement.size();
let ptr = view.ptr.as_ptr();
Ok(Self {
ptr,
complement,
axis_len,
axis_stride,
front: 0,
back,
front_index: [0usize; M],
front_offset: complement.offset(),
_marker: std::marker::PhantomData,
})
}
}
impl<'a, T, const N: usize, const M: usize> Iterator for LanesMut<'a, T, N, M> {
type Item = ArrayViewMut<'a, T, 1>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
let layout =
Layout::from_parts_unchecked([self.axis_len], [self.axis_stride], self.front_offset);
odometer_step(
&mut self.front_index,
&self.complement.shape(),
&self.complement.strides(),
&mut self.front_offset,
);
self.front += 1;
let (min_offset, max_offset) = layout.min_max_offsets();
let span_len = max_offset - min_offset + 1;
let adjusted_layout = Layout::from_parts_unchecked(
layout.shape(),
layout.strides(),
layout.offset() - min_offset,
);
unsafe {
Some(ArrayViewMut {
layout: adjusted_layout,
ptr: std::ptr::NonNull::new_unchecked(self.ptr.add(min_offset)),
len: span_len,
window_shared: span_len != self.axis_len,
_marker: std::marker::PhantomData,
})
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.back - self.front;
(remaining, Some(remaining))
}
}
impl<'a, T, const N: usize, const M: usize> ExactSizeIterator for LanesMut<'a, T, N, M> {}