use crate::application::view::ArrayView;
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::Layout;
#[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;
}
}
pub struct Windows<'a, T, const N: usize> {
data: &'a [T],
base_layout: Layout<N>,
window_shape: [usize; N],
counts: [usize; N],
front: usize,
back: usize,
front_index: [usize; N],
front_offset: usize,
back_index: [usize; N],
back_offset: usize,
}
impl<'a, T, const N: usize> Windows<'a, T, N> {
pub(crate) fn new(view: &ArrayView<'a, T, N>, window_shape: [usize; N]) -> Result<Self> {
let shape = view.shape();
let mut counts = [0usize; N];
for i in 0..N {
if window_shape[i] == 0 {
return Err(LetoError::StorageError {
reason: format!("window extent on axis {i} must be non-zero"),
});
}
if window_shape[i] > shape[i] {
return Err(LetoError::StorageError {
reason: format!(
"window extent {} on axis {i} exceeds array extent {}",
window_shape[i], shape[i]
),
});
}
counts[i] = shape[i] - window_shape[i] + 1;
}
let total: usize = counts.iter().product();
let (back_index, back_offset) = if total > 0 {
let mut idx = [0usize; N];
for (i, item) in idx.iter_mut().enumerate() {
*item = counts[i] - 1;
}
let offset = view
.layout()
.offset_of(idx)
.expect("invariant: last window start is valid");
(idx, offset)
} else {
([0usize; N], view.layout().offset)
};
Ok(Self {
data: view.data(),
base_layout: view.layout(),
window_shape,
counts,
front: 0,
back: total,
front_index: [0usize; N],
front_offset: view.layout().offset,
back_index,
back_offset,
})
}
}
impl<'a, T, const N: usize> Iterator for Windows<'a, T, N> {
type Item = ArrayView<'a, T, N>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
let layout = Layout::new(
self.window_shape,
self.base_layout.strides,
self.front_offset,
);
odometer_step(
&mut self.front_index,
&self.counts,
&self.base_layout.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> DoubleEndedIterator for Windows<'a, T, N> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
self.back -= 1;
let layout = Layout::new(
self.window_shape,
self.base_layout.strides,
self.back_offset,
);
odometer_step_back(
&mut self.back_index,
&self.counts,
&self.base_layout.strides,
&mut self.back_offset,
);
Some(ArrayView::new(layout, self.data))
}
}
impl<'a, T, const N: usize> ExactSizeIterator for Windows<'a, T, N> {}