#![allow(
// Serde
clippy::integer_arithmetic
)]
mod csl_error;
mod csl_line_constructor;
mod csl_line_iter;
#[cfg(feature = "with-rayon")]
mod csl_rayon;
#[cfg(feature = "with-rand")]
mod csl_rnd;
mod csl_utils;
use crate::utils::{are_in_ascending_order, are_in_upper_bound, has_duplicates, max_nnz, windows2};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use cl_traits::{Clear, Push, Storage, Truncate, WithCapacity};
use core::ops::Range;
#[cfg(feature = "with-rayon")]
pub use csl_rayon::*;
use csl_utils::*;
pub use {csl_error::*, csl_line_constructor::*, csl_line_iter::*};
pub type CslArray<DATA, const D: usize, const N: usize, const O: usize> =
Csl<[DATA; N], [usize; N], [usize; O], D>;
pub type CslMut<'a, DATA, const D: usize> = Csl<&'a mut [DATA], &'a [usize], &'a [usize], D>;
pub type CslRef<'a, DATA, const D: usize> = Csl<&'a [DATA], &'a [usize], &'a [usize], D>;
#[cfg(feature = "alloc")]
pub type CslVec<DATA, const D: usize> = Csl<Vec<DATA>, Vec<usize>, Vec<usize>, D>;
#[cfg_attr(feature = "with-serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Csl<DS, IS, OS, const D: usize> {
pub(crate) data: DS,
#[cfg_attr(feature = "with-serde", serde(with = "serde_big_array::BigArray"))]
pub(crate) dims: [usize; D],
pub(crate) indcs: IS,
pub(crate) offs: OS,
}
impl<DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
where
DS: WithCapacity<Input = usize>,
IS: WithCapacity<Input = usize>,
OS: WithCapacity<Input = usize>,
{
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn with_capacity(nnz: usize, nolp1: usize) -> Self {
Self {
data: DS::with_capacity(nnz),
dims: cl_traits::default_array(),
indcs: IS::with_capacity(nnz),
offs: OS::with_capacity(nolp1),
}
}
}
impl<DS, IS, OS, const D: usize> Csl<DS, IS, OS, D> {
#[inline]
pub fn dims(&self) -> &[usize; D] {
&self.dims
}
}
impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
where
DS: AsRef<[DATA]> + Storage<Item = DATA>,
IS: AsRef<[usize]>,
OS: AsRef<[usize]>,
{
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn new(dims: [usize; D], data: DS, indcs: IS, offs: OS) -> crate::Result<Self> {
let data_ref = data.as_ref();
let indcs_ref = indcs.as_ref();
let offs_ref = offs.as_ref();
let innermost_dim_is_zero = {
let mut iter = dims.iter().copied();
for dim in &mut iter {
if dim != 0 {
break;
}
}
iter.any(|v| v == 0)
};
if innermost_dim_is_zero {
return Err(CslError::InnermostDimsZero.into());
}
if data_ref.len() != indcs_ref.len() {
return Err(CslError::DiffDataIndcsLength.into());
}
if !are_in_ascending_order(offs_ref, |a, b| [a, b]) {
return Err(CslError::InvalidOffsetsOrder.into());
}
let data_indcs_length_greater_than_dims_length = {
let max_nnz = max_nnz(&dims);
data_ref.len() > max_nnz || indcs_ref.len() > max_nnz
};
if data_indcs_length_greater_than_dims_length {
return Err(CslError::DataIndcsLengthGreaterThanDimsLength.into());
}
if let Some(last) = dims.last() {
let are_in_upper_bound = are_in_upper_bound(indcs_ref, last);
if !are_in_upper_bound {
return Err(CslError::IndcsGreaterThanEqualDimLength.into());
}
if offs_ref.len() != correct_offs_len(&dims)? {
return Err(CslError::InvalidOffsetsLength.into());
}
}
let first_off = if let Some(r) = offs_ref.first().copied() {
r
} else {
return Ok(Self { data, dims, indcs, offs });
};
if let Some(last_ref) = offs_ref.last() {
if let Some(last) = last_ref.checked_sub(first_off) {
if last != data_ref.len() || last != indcs_ref.len() {
return Err(CslError::LastOffsetDifferentNnz.into());
}
}
}
let has_duplicated_indices = windows2(offs_ref).any(|[a, b]| {
let fun = || {
let first = a.checked_sub(first_off)?;
let last = b.checked_sub(first_off)?;
indcs_ref.get(first..last)
};
if let Some(indcs) = fun() {
has_duplicates(indcs)
} else {
false
}
});
if has_duplicated_indices {
return Err(CslError::DuplicatedIndices.into());
}
Ok(Self { data, dims, indcs, offs })
}
#[inline]
pub fn data(&self) -> &[DATA] {
self.data.as_ref()
}
#[inline]
pub fn indcs(&self) -> &[usize] {
self.indcs.as_ref()
}
#[inline]
pub fn line(&self, indcs: [usize; D]) -> Option<CslRef<'_, DATA, 1>> {
line(self, indcs)
}
#[inline]
pub fn nnz(&self) -> usize {
self.data.as_ref().len()
}
#[inline]
pub fn offs(&self) -> &[usize] {
self.offs.as_ref()
}
#[inline]
pub fn outermost_line_iter(&self) -> crate::Result<CslLineIterRef<'_, DATA, D>> {
CslLineIterRef::new(self.dims, self.data.as_ref(), self.indcs.as_ref(), self.offs.as_ref())
}
#[cfg_attr(all(feature = "alloc", feature = "with-rayon"), doc = "```rust")]
#[cfg_attr(not(all(feature = "alloc", feature = "with-rayon")), doc = "```ignore")]
#[cfg(feature = "with-rayon")]
#[inline]
pub fn outermost_line_rayon_iter(
&self,
) -> crate::Result<crate::ParallelIteratorWrapper<CslLineIterRef<'_, DATA, D>>> {
Ok(crate::ParallelIteratorWrapper(self.outermost_line_iter()?))
}
#[inline]
pub fn sub_dim<const TD: usize>(&self, range: Range<usize>) -> Option<CslRef<'_, DATA, TD>> {
sub_dim(self, range)
}
#[inline]
pub fn value(&self, indcs: [usize; D]) -> Option<&DATA> {
let idx = data_idx(self, indcs)?;
self.data.as_ref().get(idx)
}
}
impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
where
DS: AsMut<[DATA]> + AsRef<[DATA]> + Storage<Item = DATA>,
IS: AsRef<[usize]>,
OS: AsRef<[usize]>,
{
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn clear(&mut self)
where
DS: Clear,
IS: Clear,
OS: Clear,
{
self.dims = cl_traits::default_array();
let _ = self.data.clear();
let _ = self.indcs.clear();
let _ = self.offs.clear();
}
#[inline]
pub fn constructor(&mut self) -> crate::Result<CslLineConstructor<'_, DS, IS, OS, D>>
where
DS: Push<Input = DATA>,
IS: Push<Input = usize>,
OS: Push<Input = usize>,
{
CslLineConstructor::new(self)
}
#[inline]
pub fn data_mut(&mut self) -> &mut [DATA] {
self.data.as_mut()
}
#[inline]
pub fn line_mut(&mut self, indcs: [usize; D]) -> Option<CslMut<'_, DATA, 1>> {
line_mut(self, indcs)
}
#[inline]
pub fn outermost_line_iter_mut(&mut self) -> crate::Result<CslLineIterMut<'_, DATA, D>> {
CslLineIterMut::new(self.dims, self.data.as_mut(), self.indcs.as_ref(), self.offs.as_ref())
}
#[cfg(feature = "with-rayon")]
#[inline]
pub fn outermost_line_rayon_iter_mut(
&mut self,
) -> crate::Result<crate::ParallelIteratorWrapper<CslLineIterMut<'_, DATA, D>>> {
Ok(crate::ParallelIteratorWrapper(self.outermost_line_iter_mut()?))
}
#[inline]
pub fn sub_dim_mut<const TD: usize>(
&mut self,
range: Range<usize>,
) -> Option<CslMut<'_, DATA, TD>> {
sub_dim_mut(self, range)
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn swap_value(&mut self, a: [usize; D], b: [usize; D]) -> bool {
if let Some(a_idx) = data_idx(self, a) {
if let Some(b_idx) = data_idx(self, b) {
self.data.as_mut().swap(a_idx, b_idx);
return true;
}
}
false
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn truncate(&mut self, indcs: [usize; D])
where
DS: Truncate<Input = usize>,
IS: Truncate<Input = usize>,
OS: AsMut<[usize]> + Truncate<Input = usize>,
{
let [offs_indcs, values] = if let Some(r) = line_offs(&self.dims, &indcs, self.offs.as_ref()) {
r
} else {
return;
};
let cut_point = values.start;
let _ = self.data.truncate(cut_point);
let _ = self.indcs.truncate(cut_point);
let _ = self.offs.truncate(offs_indcs.end);
let iter = indcs.iter().zip(self.dims.iter_mut()).rev().skip(1).rev();
iter.filter(|&(a, _)| *a == 0).for_each(|(_, b)| *b = 0);
let before_last = if let Some(rslt) = self.offs.as_ref().get(offs_indcs.end.saturating_sub(2)) {
*rslt
} else {
return;
};
if let Some(rslt) = self.offs.as_mut().get_mut(offs_indcs.end.saturating_sub(1)) {
*rslt = before_last;
}
}
#[inline]
pub fn value_mut(&mut self, indcs: [usize; D]) -> Option<&mut DATA> {
let idx = data_idx(self, indcs)?;
self.data.as_mut().get_mut(idx)
}
}
#[cfg(feature = "with-rand")]
impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
where
DS: AsMut<[DATA]> + AsRef<[DATA]> + Default + Push<Input = DATA> + Storage<Item = DATA>,
IS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<Input = usize>,
OS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<Input = usize>,
{
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn new_controlled_random_rand<F, R>(
dims: [usize; D],
nnz: usize,
rng: &mut R,
cb: F,
) -> crate::Result<Self>
where
F: FnMut(&mut R, [usize; D]) -> DATA,
R: rand::Rng,
{
let mut csl = Csl { dims, ..Default::default() };
csl_rnd::CslRnd::new(&mut csl, nnz, rng)?.fill(cb).ok_or(crate::Error::UnknownError)?;
Self::new(csl.dims, csl.data, csl.indcs, csl.offs)
}
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn new_random_rand<R>(rng: &mut R, upper_bound: usize) -> crate::Result<Self>
where
R: rand::Rng,
rand::distributions::Standard: rand::distributions::Distribution<DATA>,
{
let dims = crate::utils::valid_random_dims(rng, upper_bound);
let max_nnz = max_nnz(&dims);
let nnz = if max_nnz == 0 { 0 } else { rng.gen_range(0..max_nnz) };
Self::new_controlled_random_rand(dims, nnz, rng, |rng, _| rng.gen())
}
}
impl<DS, IS, OS, const D: usize> Default for Csl<DS, IS, OS, D>
where
DS: Default,
IS: Default,
OS: Default,
{
#[inline]
fn default() -> Self {
Self {
data: Default::default(),
dims: cl_traits::default_array(),
indcs: Default::default(),
offs: Default::default(),
}
}
}