use crate::csl::{manage_last_offset, Csl};
use cl_traits::{Push, Storage};
use core::fmt;
#[derive(Debug, PartialEq)]
pub struct CslLineConstructor<'a, DS, IS, OS, const D: usize> {
csl: &'a mut Csl<DS, IS, OS, D>,
curr_dim_idx: usize,
last_off: usize,
}
impl<'a, DATA, DS, IS, OS, const D: usize> CslLineConstructor<'a, DS, IS, OS, D>
where
DS: AsRef<[DATA]> + Push<Input = DATA> + Storage<Item = DATA>,
IS: AsRef<[usize]> + Push<Input = usize>,
OS: AsRef<[usize]> + Push<Input = usize>,
{
#[inline]
pub(crate) fn new(csl: &'a mut Csl<DS, IS, OS, D>) -> crate::Result<Self> {
if D == 0 {
return Err(CslLineConstructorError::EmptyDimension.into());
}
let curr_dim_idx = if let Some(idx) = csl.dims.iter().copied().position(|x| x != 0) {
idx
} else {
csl.dims.len()
};
let last_off = manage_last_offset(&mut csl.offs)?;
Ok(Self { csl, curr_dim_idx, last_off })
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn next_outermost_dim(mut self, len: usize) -> crate::Result<Self> {
self.curr_dim_idx =
self.curr_dim_idx.checked_sub(1).ok_or(CslLineConstructorError::DimsOverflow)?;
*self.curr_dim() = len;
Ok(self)
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn push_empty_line(self) -> crate::Result<Self> {
let _ = self.csl.offs.push(self.last_off).map_err(|_err| crate::Error::InsufficientCapacity)?;
Ok(self)
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn push_line<DI>(mut self, di: DI) -> crate::Result<Self>
where
DI: Iterator<Item = (usize, DATA)>,
{
let nnz_iter = 1..self.last_dim().saturating_add(1);
let off_iter = self.last_off.saturating_add(1)..;
let mut iter = off_iter.zip(nnz_iter.zip(di));
let mut last_off = self.last_off;
let mut nnz = 0;
let mut push = |curr_last_off, curr_nnz, idx, value| {
let _ = self.csl.indcs.push(idx).map_err(|_err| crate::Error::InsufficientCapacity)?;
let _ = self.csl.data.push(value).map_err(|_err| crate::Error::InsufficientCapacity)?;
nnz = curr_nnz;
last_off = curr_last_off;
Ok::<(), crate::Error>(())
};
let mut last_line_idx = if let Some((curr_last_off, (curr_nnz, (idx, value)))) = iter.next() {
push(curr_last_off, curr_nnz, idx, value)?;
idx
} else {
return self.push_empty_line();
};
for (curr_last_off, (curr_nnz, (idx, value))) in iter {
if idx <= last_line_idx {
return Err(CslLineConstructorError::UnsortedIndices.into());
}
push(curr_last_off, curr_nnz, idx, value)?;
last_line_idx = idx;
}
if nnz == 0 {
return self.push_empty_line();
}
let _ = self.csl.offs.push(last_off).map_err(|_err| crate::Error::InsufficientCapacity)?;
self.last_off = last_off;
Ok(self)
}
#[allow(
// self.curr_dim_idx always points to a valid reference
clippy::unwrap_used
)]
#[inline]
fn curr_dim(&mut self) -> &mut usize {
self.csl.dims.get_mut(self.curr_dim_idx).unwrap()
}
#[allow(
// Constructor doesn't contain empty dimensions
clippy::unwrap_used
)]
#[inline]
fn last_dim(&mut self) -> usize {
*self.csl.dims.last().unwrap()
}
}
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub enum CslLineConstructorError {
DimsOverflow,
UnsortedIndices,
EmptyDimension,
MaxNumOfLines,
}
impl fmt::Display for CslLineConstructorError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
Self::DimsOverflow => "DimsOverflow",
Self::UnsortedIndices => "UnsortedIndices",
Self::EmptyDimension => "EmptyDimension",
Self::MaxNumOfLines => "MaxNumOfLines",
};
write!(f, "{}", s)
}
}
#[cfg(feature = "std")]
impl std::error::Error for CslLineConstructorError {}