use std::iter::FusedIterator;
use itertools::izip;
use super::IndicesIterator;
use crate::iterators::indices_iterator::IndicesIntoIterator;
use crate::{ArrayIndicesTinyVec, ArraySubset, IndexerError};
#[derive(Clone)]
pub struct ContiguousIndices {
subset_contiguous_start: ArraySubset,
contiguous_elements: u64,
}
impl ContiguousIndices {
pub fn new(subset: ArraySubset, array_shape: &[u64]) -> Result<Self, IndexerError> {
if subset.dimensionality() != array_shape.len() {
return Err(IndexerError::new_incompatible_dimensionality(
subset.dimensionality(),
array_shape.len(),
));
}
if std::iter::zip(subset.end_exc(), array_shape).any(|(end, shape)| end > *shape) {
return Err(IndexerError::new_oob(
subset.end_exc(),
array_shape.to_vec(),
));
}
if subset.is_empty() {
if std::iter::zip(subset.start().iter(), array_shape)
.any(|(start, shape)| start >= shape)
{
return Err(IndexerError::new_oob(
subset.start().to_vec(),
array_shape.to_vec(),
));
}
return Ok(Self {
subset_contiguous_start: subset,
contiguous_elements: 0,
});
}
let mut contiguous = true;
let mut contiguous_elements = 1;
let mut shape_out: Vec<u64> = Vec::with_capacity(array_shape.len());
let subset_start = subset.start();
let subset_shape = subset.shape();
for (&subset_start, &subset_size, &array_size, shape_out_i) in izip!(
subset_start.iter().rev(),
subset_shape.iter().rev(),
array_shape.iter().rev(),
shape_out.spare_capacity_mut().iter_mut().rev(),
) {
if contiguous {
contiguous_elements *= subset_size;
shape_out_i.write(1);
contiguous = subset_start == 0 && subset_size == array_size;
} else {
shape_out_i.write(subset_size);
}
}
unsafe { shape_out.set_len(array_shape.len()) };
let ranges = subset_start
.iter()
.zip(shape_out)
.map(|(&st, sh)| st..(st + sh));
let subset_contiguous_start = ArraySubset::from(ranges);
Ok(Self {
subset_contiguous_start,
contiguous_elements,
})
}
#[must_use]
pub fn len(&self) -> usize {
self.subset_contiguous_start.num_elements_usize()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn contiguous_elements(&self) -> u64 {
self.contiguous_elements
}
#[must_use]
pub fn contiguous_elements_usize(&self) -> usize {
usize::try_from(self.contiguous_elements).unwrap()
}
#[must_use]
pub fn iter(&self) -> ContiguousIndicesIterator<'_> {
<&Self as IntoIterator>::into_iter(self)
}
}
impl<'a> IntoIterator for &'a ContiguousIndices {
type Item = (ArrayIndicesTinyVec, u64);
type IntoIter = ContiguousIndicesIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
let n_elements = self.subset_contiguous_start.num_elements_usize();
ContiguousIndicesIterator {
inner: IndicesIterator {
subset: &self.subset_contiguous_start,
range: 0..n_elements,
},
contiguous_elements: self.contiguous_elements,
}
}
}
impl IntoIterator for ContiguousIndices {
type Item = (ArrayIndicesTinyVec, u64);
type IntoIter = ContiguousIndicesIntoIterator;
fn into_iter(self) -> Self::IntoIter {
let n_elements = self.subset_contiguous_start.num_elements_usize();
ContiguousIndicesIntoIterator {
inner: IndicesIntoIterator {
subset: self.subset_contiguous_start,
range: 0..n_elements,
},
contiguous_elements: self.contiguous_elements,
}
}
}
pub struct ContiguousIndicesIterator<'a> {
inner: IndicesIterator<'a>,
contiguous_elements: u64,
}
pub struct ContiguousIndicesIntoIterator {
inner: IndicesIntoIterator,
contiguous_elements: u64,
}
macro_rules! impl_contiguous_indices_iterator {
($iterator_type:ty) => {
impl $iterator_type {
#[must_use]
pub fn contiguous_elements(&self) -> u64 {
self.contiguous_elements
}
#[must_use]
pub fn contiguous_elements_usize(&self) -> usize {
usize::try_from(self.contiguous_elements).unwrap()
}
}
impl Iterator for $iterator_type {
type Item = (ArrayIndicesTinyVec, u64);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|i| (i, self.contiguous_elements()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl DoubleEndedIterator for $iterator_type {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner
.next_back()
.map(|i| (i, self.contiguous_elements()))
}
}
impl ExactSizeIterator for $iterator_type {}
impl FusedIterator for $iterator_type {}
};
}
impl_contiguous_indices_iterator!(ContiguousIndicesIterator<'_>);
impl_contiguous_indices_iterator!(ContiguousIndicesIntoIterator);