#![allow(
// Serde
clippy::integer_arithmetic
)]
mod coo_error;
mod coo_utils;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use cl_traits::Storage;
pub use coo_error::*;
use coo_utils::*;
pub type CooArray<DATA, const D: usize, const DT: usize> = Coo<[([usize; D], DATA); DT], D>;
pub type CooMut<'a, DATA, const D: usize> = Coo<&'a mut [([usize; D], DATA)], D>;
pub type CooRef<'a, DATA, const D: usize> = Coo<&'a [([usize; D], DATA)], D>;
#[cfg(feature = "alloc")]
pub type CooVec<DATA, const D: usize> = Coo<Vec<([usize; D], DATA)>, D>;
#[cfg_attr(feature = "with-serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Coo<DS, const D: usize> {
pub(crate) data: DS,
#[cfg_attr(feature = "with-serde", serde(with = "serde_big_array::BigArray"))]
pub(crate) dims: [usize; D],
}
impl<DS, const D: usize> Coo<DS, D> {
#[inline]
pub fn dims(&self) -> &[usize; D] {
&self.dims
}
}
impl<DATA, DS, const D: usize> Coo<DS, D>
where
DS: AsRef<[<DS as Storage>::Item]> + Storage<Item = ([usize; D], DATA)>,
{
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
#[inline]
pub fn new(dims: [usize; D], data: DS) -> crate::Result<Self> {
if !crate::utils::are_in_ascending_order(data.as_ref(), |a, b| [&a.0, &b.0]) {
return Err(CooError::InvalidIndcsOrder.into());
}
let has_invalid_indcs = !data.as_ref().iter().all(|&(indcs, _)| {
indcs.iter().zip(dims.iter()).all(
|(data_idx, dim)| {
if dim == &0 {
true
} else {
data_idx < dim
}
},
)
});
if has_invalid_indcs {
return Err(CooError::InvalidIndcs.into());
}
if !does_not_have_duplicates_sorted(data.as_ref(), |a, b| a.0[..] != b.0[..]) {
return Err(CooError::DuplicatedIndices.into());
}
Ok(Self { data, dims })
}
#[inline]
pub fn data(&self) -> &[([usize; D], DATA)] {
self.data.as_ref()
}
#[inline]
pub fn value(&self, indcs: [usize; D]) -> Option<&DATA> {
value(indcs, self.data.as_ref())
}
}
impl<DATA, DS, const D: usize> Coo<DS, D>
where
DS: AsMut<[<DS as Storage>::Item]> + Storage<Item = ([usize; D], DATA)>,
{
#[inline]
pub fn value_mut(&mut self, indcs: [usize; D]) -> Option<&mut DATA> {
value_mut(indcs, self.data.as_mut())
}
}
#[cfg(feature = "with-rand")]
impl<DATA, DS, const D: usize> Coo<DS, D>
where
DS: AsMut<[<DS as Storage>::Item]>
+ AsRef<[<DS as Storage>::Item]>
+ Default
+ Storage<Item = ([usize; D], DATA)>
+ cl_traits::CapacityUpperBound
+ cl_traits::Push<Input = <DS as Storage>::Item>,
{
#[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,
mut cb: F,
) -> crate::Result<Self>
where
F: FnMut(&mut R, &[usize; D]) -> DATA,
R: rand::Rng,
{
use rand::distributions::Distribution;
if nnz > crate::utils::max_nnz(&dims) {
return Err(CooError::NnzGreaterThanMaximumNnz.into());
}
let mut data: DS = Default::default();
if nnz > data.as_ref().len() {
return Err(crate::Error::InsufficientCapacity);
}
for _ in 0..nnz {
let indcs: [usize; D] = cl_traits::create_array(|idx| {
let dim = *dims.get(idx).unwrap_or(&0);
if dim == 0 {
0
} else {
rand::distributions::Uniform::from(0..dim).sample(rng)
}
});
if data.as_ref().iter().all(|value| value.0 != indcs) {
#[allow(
// Capacity was already checked
clippy::let_underscore_must_use
)]
let _ = data.push({
let element = cb(rng, &indcs);
(indcs, element)
});
}
}
data.as_mut().sort_unstable_by(|a, b| a.0.cmp(&b.0));
Coo::new(dims, data)
}
#[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 = crate::utils::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, const D: usize> Default for Coo<DS, D>
where
DS: Default,
{
#[inline]
fn default() -> Self {
Self { data: DS::default(), dims: cl_traits::default_array() }
}
}