mod csl_iter;
mod csl_line_constructor;
#[cfg(all(test, feature = "with_rand"))]
mod csl_quickcheck;
#[cfg(feature = "with_rayon")]
mod csl_rayon;
mod csl_utils;
#[cfg(feature = "with_rand")]
mod csl_rnd;
use crate::utils::{are_in_ascending_order, are_in_upper_bound, does_not_have_duplicates};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use cl_traits::{ArrayWrapper, Clear, Push, Truncate};
use core::{marker::PhantomData, ops::Range};
pub use csl_iter::*;
pub use csl_line_constructor::*;
#[cfg(feature = "with_rayon")]
pub use csl_rayon::*;
use csl_utils::*;
pub type CslArray<DATA, const DIMS: usize, const NNZ: usize, const OFFS: usize> =
Csl<DATA, ArrayWrapper<DATA, NNZ>, ArrayWrapper<usize, NNZ>, ArrayWrapper<usize, OFFS>, DIMS>;
#[cfg(feature = "with_arrayvec")]
pub type CslArrayVec<DATA, const DIMS: usize, const NNZ: usize, const OFFS: usize> = Csl<
DATA,
cl_traits::ArrayVecArrayWrapper<DATA, NNZ>,
cl_traits::ArrayVecArrayWrapper<usize, NNZ>,
cl_traits::ArrayVecArrayWrapper<usize, OFFS>,
DIMS,
>;
pub type CslMut<'a, DATA, const DIMS: usize> =
Csl<DATA, &'a mut [DATA], &'a [usize], &'a [usize], DIMS>;
pub type CslRef<'a, DATA, const DIMS: usize> =
Csl<DATA, &'a [DATA], &'a [usize], &'a [usize], DIMS>;
#[cfg(feature = "with_smallvec")]
pub type CslSmallVec<DATA, const DIMS: usize, const NNZ: usize, const OFFS: usize> = Csl<
DATA,
cl_traits::SmallVecArrayWrapper<DATA, NNZ>,
cl_traits::SmallVecArrayWrapper<usize, NNZ>,
cl_traits::SmallVecArrayWrapper<usize, OFFS>,
DIMS,
>;
#[cfg(feature = "with_staticvec")]
pub type CslStaticVec<DATA, const DIMS: usize, const NNZ: usize, const OFFS: usize> = Csl<
DATA,
staticvec::StaticVec<DATA, NNZ>,
staticvec::StaticVec<usize, NNZ>,
staticvec::StaticVec<usize, OFFS>,
DIMS,
>;
#[cfg(feature = "alloc")]
pub type CslVec<DATA, const DIMS: usize> = Csl<DATA, Vec<DATA>, Vec<usize>, Vec<usize>, DIMS>;
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Csl<DATA, DS, IS, OS, const DIMS: usize> {
pub(crate) data: DS,
pub(crate) dims: ArrayWrapper<usize, DIMS>,
pub(crate) indcs: IS,
pub(crate) offs: OS,
pub(crate) phantom: PhantomData<DATA>,
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS> {
#[inline]
pub fn dims(&self) -> &[usize; DIMS] {
&*self.dims
}
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DS: AsRef<[DATA]>,
IS: AsRef<[usize]>,
OS: AsRef<[usize]>,
{
pub fn new<ID, IDS, IIS, IOS>(
into_dims: ID,
into_data: IDS,
into_indcs: IIS,
into_offs: IOS,
) -> Self
where
ID: Into<ArrayWrapper<usize, DIMS>>,
IDS: Into<DS>,
IIS: Into<IS>,
IOS: Into<OS>,
{
let data = into_data.into();
let dims = into_dims.into();
let indcs = into_indcs.into();
let offs = into_offs.into();
let data_ref = data.as_ref();
let indcs_ref = indcs.as_ref();
let offs_ref = offs.as_ref();
assert!(
{
let mut is_valid = true;
if let Some(idx) = dims.iter().position(|dim| *dim != 0) {
is_valid = dims[idx..].iter().all(|dim| *dim != 0);
}
is_valid
},
"Innermost dimensions length must be greater than zero"
);
assert!(data_ref.len() == indcs_ref.len(), "The data length must equal the indices length");
assert!(are_in_ascending_order(&offs_ref, |a, b| [a, b]), "Offsets must be in ascending order");
assert!(
{
let max_nnz = max_nnz(&dims);
data_ref.len() <= max_nnz && indcs_ref.len() <= max_nnz
},
"The data and indices length must be equal or less than the product of all dimensions length"
);
if let Some(first) = offs_ref.get(0) {
assert!(
offs_ref.windows(2).all(|x| {
let range = x[0] - first..x[1] - first;
does_not_have_duplicates(&indcs_ref[range])
}),
"Indices of a line must be unique"
);
}
if let Some(last_ref) = offs_ref.last() {
let last = last_ref - offs_ref[0];
assert!(last == data_ref.len() && last == indcs_ref.len(), "Last offset must equal the nnz");
}
if let Some(last) = dims.last() {
let are_in_upper_bound = are_in_upper_bound(indcs_ref, last);
assert!(are_in_upper_bound, "The indices must be less than the innermost dimension length");
assert!(
offs_ref.len() == offs_len(&dims),
"Non-empty offsets length must equal the dimensions product (without the innermost \
dimension) plus one"
);
}
Self { data, dims, indcs, offs, phantom: PhantomData }
}
pub fn data(&self) -> &[DATA] {
self.data.as_ref()
}
pub fn indcs(&self) -> &[usize] {
self.indcs.as_ref()
}
pub fn line(&self, indcs: [usize; DIMS]) -> Option<CslRef<'_, DATA, 1>> {
line(self, indcs)
}
#[inline]
pub fn nnz(&self) -> usize {
self.data.as_ref().len()
}
pub fn offs(&self) -> &[usize] {
self.offs.as_ref()
}
pub fn outermost_iter(&self) -> CsIterRef<'_, DATA, DIMS> {
CsIterRef::new(&self.dims, self.data.as_ref().as_ptr(), self.indcs.as_ref(), self.offs.as_ref())
}
#[cfg(feature = "with_rayon")]
pub fn outermost_rayon_iter(&self) -> crate::ParallelIteratorWrapper<CsIterRef<'_, DATA, DIMS>> {
crate::ParallelIteratorWrapper(self.outermost_iter())
}
pub fn sub_dim<const N: usize>(&self, range: Range<usize>) -> CslRef<'_, DATA, N> {
assert!(N <= DIMS);
sub_dim(self, range)
}
pub fn value(&self, indcs: [usize; DIMS]) -> Option<&DATA> {
value(self, indcs)
}
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DS: AsMut<[DATA]> + AsRef<[DATA]>,
IS: AsRef<[usize]>,
OS: AsRef<[usize]>,
{
pub fn data_mut(&mut self) -> &mut [DATA] {
self.data.as_mut()
}
pub fn line_mut(&mut self, indcs: [usize; DIMS]) -> Option<CslMut<'_, DATA, 1>> {
line_mut(self, indcs)
}
pub fn outermost_iter_mut(&mut self) -> CslIterMut<'_, DATA, DIMS> {
CslIterMut::new(
&self.dims,
self.data.as_mut().as_mut_ptr(),
self.indcs.as_ref(),
self.offs.as_ref(),
)
}
#[cfg(feature = "with_rayon")]
pub fn outermost_rayon_iter_mut(
&mut self,
) -> crate::ParallelIteratorWrapper<CslIterMut<'_, DATA, DIMS>> {
crate::ParallelIteratorWrapper(self.outermost_iter_mut())
}
pub fn sub_dim_mut<const N: usize>(&mut self, range: Range<usize>) -> CslMut<'_, DATA, N> {
sub_dim_mut(self, range)
}
pub fn value_mut(&mut self, indcs: [usize; DIMS]) -> Option<&mut DATA> {
value_mut(self, indcs)
}
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DS: AsRef<[DATA]> + Push<Input = DATA>,
IS: AsRef<[usize]> + Push<Input = usize>,
OS: AsRef<[usize]> + Push<Input = usize>,
{
pub fn constructor(&mut self) -> CslLineConstructor<'_, DATA, DS, IS, OS, DIMS> {
CslLineConstructor::new(self)
}
}
#[cfg(feature = "with_rand")]
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DATA: Default,
DS: AsMut<[DATA]> + AsRef<[DATA]> + Default + Push<Input = DATA>,
IS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<Input = usize>,
OS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<Input = usize>,
{
pub fn new_random_with_rand<F, ID, R>(into_dims: ID, nnz: usize, rng: &mut R, cb: F) -> Self
where
F: FnMut(&mut R, [usize; DIMS]) -> DATA,
ID: Into<ArrayWrapper<usize, DIMS>>,
R: rand::Rng,
{
let dims = into_dims.into();
let mut csl = Self::default();
csl.dims = dims;
csl_rnd::CslRnd::new(&mut csl, nnz, rng).fill(cb);
Csl::new(csl.dims, csl.data, csl.indcs, csl.offs)
}
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DS: Clear,
IS: Clear,
OS: Clear,
{
pub fn clear(&mut self) {
self.dims = Default::default();
self.data.clear();
self.indcs.clear();
self.offs.clear();
}
}
impl<DATA, DS, IS, OS, const DIMS: usize> Csl<DATA, DS, IS, OS, DIMS>
where
DS: Truncate<Input = usize>,
IS: Truncate<Input = usize>,
OS: AsRef<[usize]> + Push<Input = usize> + Truncate<Input = usize>,
{
pub fn truncate(&mut self, indcs: [usize; DIMS]) {
if let Some([offs_indcs, values]) = line_offs(&self.dims, &indcs, self.offs.as_ref()) {
let cut_point = values.start + 1;
self.data.truncate(cut_point);
self.indcs.truncate(cut_point);
self.offs.truncate(offs_indcs.start + 1);
self.offs.push(*indcs.last().unwrap());
indcs.iter().zip(self.dims.iter_mut()).filter(|(a, _)| **a == 0).for_each(|(_, b)| *b = 0);
}
}
}