ndstruct/
csl.rs

1//! CSL (Compressed Sparse Line).
2//!
3//! A generalization of the [`CSC`]/[`CSR`] structures for N dimensions. Beware that this structure
4//! doesn't make any distinction of what is a `column` or a `row` because the order of the elements
5//! is up to the caller.
6//!
7//! [`CSC`]: en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_(CSC_or_CCS)
8//! [`CSR`]: en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)
9
10mod csl_error;
11mod csl_line_constructor;
12mod csl_line_iter;
13#[cfg(feature = "rayon")]
14mod csl_rayon;
15#[cfg(feature = "rand")]
16mod csl_rnd;
17pub(crate) mod csl_utils;
18
19use crate::utils::{are_in_ascending_order, are_in_upper_bound, has_duplicates, max_nnz, windows2};
20#[cfg(feature = "alloc")]
21use alloc::vec::Vec;
22use cl_aux::{ArrayWrapper, Clear, Push, SingleTypeStorage, Truncate, WithCapacity};
23use core::ops::Range;
24pub use csl_error::*;
25pub use csl_line_constructor::*;
26pub use csl_line_iter::*;
27
28/// CSL backed by a static array.
29pub type CslArray<DATA, const D: usize, const N: usize, const O: usize> =
30  Csl<[DATA; N], [usize; N], [usize; O], D>;
31/// CSL backed by a mutable slice
32pub type CslMut<'data, DATA, const D: usize> =
33  Csl<&'data mut [DATA], &'data [usize], &'data [usize], D>;
34/// CSL backed by a slice
35pub type CslRef<'data, DATA, const D: usize> =
36  Csl<&'data [DATA], &'data [usize], &'data [usize], D>;
37/// CSL backed by a dynamic vector.
38#[cfg(feature = "alloc")]
39pub type CslVec<DATA, const D: usize> = Csl<Vec<DATA>, Vec<usize>, Vec<usize>, D>;
40
41/// Base structure for all CSL* variants.
42///
43/// It is possible to define your own fancy CSL, e.g., `Csl<
44///   Vec<num_bigint::BigNum, 32>,
45///   arrayvec::ArrayVec<[usize; 32]>,
46///   smallvec::SmallVec<[usize; 321]>,
47///   123
48/// >`.
49///
50/// # Types
51///
52/// * `DA`: Dimensions Array
53/// * `DS`: Data SingleTypeStorage
54/// * `IS`: Indices SingleTypeStorage
55/// * `OS`: Offsets SingleTypeStorage
56#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
57#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
58pub struct Csl<DS, IS, OS, const D: usize> {
59  pub(crate) data: DS,
60  pub(crate) dims: ArrayWrapper<usize, D>,
61  pub(crate) indcs: IS,
62  pub(crate) offs: OS,
63}
64
65impl<DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
66where
67  DS: WithCapacity<Error = cl_aux::Error, Input = usize>,
68  IS: WithCapacity<Error = cl_aux::Error, Input = usize>,
69  OS: WithCapacity<Error = cl_aux::Error, Input = usize>,
70{
71  /// Creates an empty instance with initial capacity.
72  ///
73  /// For storages involving solely arrays, all arguments will be discarted.
74  ///
75  /// # Arguments
76  ///
77  /// * `nnz`: Number of Non-Zero elements
78  /// * `nolp1`: Number Of Lines Plus 1, i.e., the dimensions product
79  /// (without the innermost dimension) plus 1
80  ///
81  /// # Example
82  #[cfg_attr(feature = "alloc", doc = "```rust")]
83  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
84  /// use ndstruct::csl::CslVec;
85  /// let dims = [11, 10, 1];
86  /// let nolp1 = dims.iter().rev().skip(1).product::<usize>() + 1;
87  /// let nnz = 2;
88  /// let _ = CslVec::<i32, 3>::with_capacity(nnz, nolp1);
89  /// ```
90  #[inline]
91  pub fn with_capacity(nnz: usize, nolp1: usize) -> Self {
92    Self {
93      data: DS::with_capacity(nnz),
94      dims: <_>::default(),
95      indcs: IS::with_capacity(nnz),
96      offs: OS::with_capacity(nolp1),
97    }
98  }
99}
100
101impl<DS, IS, OS, const D: usize> Csl<DS, IS, OS, D> {
102  /// The definitions of all dimensions.
103  ///
104  /// # Example
105  ///
106  /// ```rust
107  /// use ndstruct::doc_tests::csl_array_4;
108  /// assert_eq!(csl_array_4().dims(), &[2, 3, 4, 5]);
109  /// ```
110  #[inline]
111  pub fn dims(&self) -> &[usize; D] {
112    &self.dims
113  }
114}
115
116impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
117where
118  DS: AsRef<[DATA]> + SingleTypeStorage<Item = DATA>,
119  IS: AsRef<[usize]>,
120  OS: AsRef<[usize]>,
121{
122  /// Creates a valid CSL instance.
123  ///
124  /// The compressed fields are a bit complex and unless you really know what you are doing, this
125  /// method shouldn't probably be used directly. Please, try to consider using [`#constructor`]
126  /// instead.
127  ///
128  /// # Arguments
129  ///
130  /// * `dims`: Array of dimensions
131  /// * `data`: Data collection
132  /// * `indcs`: Indices of each data item
133  /// * `offs`: Offset of each innermost line
134  ///
135  /// # Example
136  #[cfg_attr(feature = "alloc", doc = "```rust")]
137  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
138  /// use ndstruct::csl::{CslArray, CslVec};
139  /// // Sparse array ([8, _, _, _, _, 9, _, _, _, _])
140  /// let mut _sparse_array = CslArray::new([10], [8.0, 9.0], [0, 5], [0, 2]);
141  /// // A bunch of nothing for your overflow needs
142  /// let mut _over_nine: ndstruct::Result<CslVec<(), 9001>>;
143  /// _over_nine = CslVec::new([0; 9001], vec![], vec![], vec![]);
144  /// ```
145  #[inline]
146  pub fn new(dims: [usize; D], data: DS, indcs: IS, offs: OS) -> crate::Result<Self> {
147    let data_ref = data.as_ref();
148    let indcs_ref = indcs.as_ref();
149    let offs_ref = offs.as_ref();
150
151    let innermost_dim_is_zero = {
152      let mut iter = dims.iter().copied();
153      for dim in &mut iter {
154        if dim != 0 {
155          break;
156        }
157      }
158      iter.any(|v| v == 0)
159    };
160    if innermost_dim_is_zero {
161      return Err(CslError::InnermostDimsZero.into());
162    }
163
164    if data_ref.len() != indcs_ref.len() {
165      return Err(CslError::DiffDataIndcsLength.into());
166    }
167
168    if !are_in_ascending_order(offs_ref, |a, b| [a, b]) {
169      return Err(CslError::InvalidOffsetsOrder.into());
170    }
171
172    let data_indcs_length_greater_than_dims_length = {
173      let max_nnz = max_nnz(&dims);
174      data_ref.len() > max_nnz || indcs_ref.len() > max_nnz
175    };
176    if data_indcs_length_greater_than_dims_length {
177      return Err(CslError::DataIndcsLengthGreaterThanDimsLength.into());
178    }
179
180    if let Some(last) = dims.last() {
181      let are_in_upper_bound = are_in_upper_bound(indcs_ref, last);
182      if !are_in_upper_bound {
183        return Err(CslError::IndcsGreaterThanEqualDimLength.into());
184      }
185      if offs_ref.len() != csl_utils::correct_offs_len(&dims)? {
186        return Err(CslError::InvalidOffsetsLength.into());
187      }
188    }
189
190    let Some(first_off) = offs_ref.first().copied() else {
191      return Ok(Self { data, dims: dims.into(), indcs, offs });
192    };
193
194    if let Some(last_ref) = offs_ref.last() {
195      if let Some(last) = last_ref.checked_sub(first_off) {
196        if last != data_ref.len() || last != indcs_ref.len() {
197          return Err(CslError::LastOffsetDifferentNnz.into());
198        }
199      }
200    }
201
202    let has_duplicated_indices = windows2(offs_ref).any(|[a, b]| {
203      let fun = || {
204        let first = a.checked_sub(first_off)?;
205        let last = b.checked_sub(first_off)?;
206        indcs_ref.get(first..last)
207      };
208      if let Some(indcs_slice) = fun() {
209        has_duplicates(indcs_slice)
210      } else {
211        false
212      }
213    });
214    if has_duplicated_indices {
215      return Err(CslError::DuplicatedIndices.into());
216    }
217
218    Ok(Self { data, dims: dims.into(), indcs, offs })
219  }
220
221  /// The data that is being stored.
222  ///
223  /// # Example
224  ///
225  /// ```rust
226  /// use ndstruct::doc_tests::csl_array_4;
227  /// assert_eq!(csl_array_4().data(), &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
228  /// ```
229  #[inline]
230  pub fn data(&self) -> &[DATA] {
231    self.data.as_ref()
232  }
233
234  /// Indices (indcs) of a line, i.e., indices of the innermost dimension.
235  ///
236  /// # Example
237  ///
238  /// ```rust
239  /// use ndstruct::doc_tests::csl_array_4;
240  /// assert_eq!(csl_array_4().indcs(), &[0, 3, 1, 3, 4, 2, 2, 4, 2]);
241  /// ```
242  #[inline]
243  pub fn indcs(&self) -> &[usize] {
244    self.indcs.as_ref()
245  }
246
247  /// Any immutable line reference determined by `indcs`. The innermost dimension is ignored.
248  ///
249  /// # Examples
250  ///
251  /// ```rust
252  /// use ndstruct::{csl::CslRef, doc_tests::csl_array_4};
253  /// let csl = csl_array_4();
254  /// assert_eq!(csl.line([0, 0, 2, 0]), CslRef::new([5], &[][..], &[][..], &[3, 3][..]).ok());
255  /// assert_eq!(csl.line([0, 1, 0, 0]), CslRef::new([5], &[6][..], &[2][..], &[5, 6][..]).ok());
256  /// ```
257  #[inline]
258  pub fn line(&self, indcs: [usize; D]) -> Option<CslRef<'_, DATA, 1>> {
259    csl_utils::line(self, indcs)
260  }
261
262  /// Number of NonZero elements.
263  ///
264  /// # Example
265  ///
266  /// ```rust
267  /// use ndstruct::doc_tests::csl_array_4;
268  /// assert_eq!(csl_array_4().nnz(), 9);
269  /// ```
270  #[inline]
271  pub fn nnz(&self) -> usize {
272    self.data.as_ref().len()
273  }
274
275  /// The joining of two consecutives offsets (offs) represent the starting and ending points of a
276  /// line in the `data` and `indcs` slices.
277  ///
278  /// # Example
279  ///
280  /// ```rust
281  /// use ndstruct::doc_tests::csl_array_4;
282  /// assert_eq!(
283  ///   csl_array_4().offs(),
284  ///   &[0, 2, 3, 3, 5, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
285  /// );
286  /// ```
287  #[inline]
288  pub fn offs(&self) -> &[usize] {
289    self.offs.as_ref()
290  }
291
292  /// Iterator that returns immutable line references of the outermost dimension
293  ///
294  /// # Examples
295  ///
296  /// ```rust
297  /// # fn main() -> ndstruct::Result<()> {
298  /// use ndstruct::{csl::CslRef, doc_tests::csl_array_4};
299  /// let csl = csl_array_4();
300  /// let sub_csl = csl.sub_dim(0..3).unwrap();
301  /// let mut iter = sub_csl.outermost_line_iter()?;
302  /// assert_eq!(
303  ///   iter.next(),
304  ///   CslRef::new([1, 4, 5], &[1, 2, 3, 4, 5][..], &[0, 3, 1, 3, 4][..], &[0, 2, 3, 3, 5][..]).ok()
305  /// );
306  /// assert_eq!(iter.next(), CslRef::new([1, 4, 5], &[6][..], &[2][..], &[5, 6, 6, 6, 6][..]).ok());
307  /// assert_eq!(
308  ///   iter.next(),
309  ///   CslRef::new([1, 4, 5], &[7, 8][..], &[2, 4][..], &[6, 7, 8, 8, 8][..]).ok()
310  /// );
311  /// assert_eq!(iter.next(), None);
312  /// # Ok(()) }
313  #[inline]
314  pub fn outermost_line_iter(&self) -> crate::Result<CslLineIterRef<'_, DATA, D>> {
315    CslLineIterRef::new(self.dims.0, self.data.as_ref(), self.indcs.as_ref(), self.offs.as_ref())
316  }
317
318  /// Parallel iterator that returns all immutable line references of the current dimension
319  /// using `rayon`.
320  ///
321  /// # Examples
322  #[cfg_attr(all(feature = "alloc", feature = "rayon"), doc = "```rust")]
323  #[cfg_attr(not(all(feature = "alloc", feature = "rayon")), doc = "```ignore")]
324  /// # fn main() -> ndstruct::Result<()> {
325  /// use ndstruct::doc_tests::csl_array_4;
326  /// use rayon::prelude::*;
327  /// let csl = csl_array_4();
328  /// let outermost_rayon_iter = csl.outermost_line_rayon_iter()?;
329  /// outermost_rayon_iter.enumerate().for_each(|(idx, csl_ref)| {
330  ///   assert_eq!(csl_ref, csl.outermost_line_iter().unwrap().nth(idx).unwrap());
331  /// });
332  /// # Ok(()) }
333  /// ```
334  #[cfg(feature = "rayon")]
335  #[inline]
336  pub fn outermost_line_rayon_iter(
337    &self,
338  ) -> crate::Result<crate::ParallelIteratorWrapper<CslLineIterRef<'_, DATA, D>>> {
339    Ok(crate::ParallelIteratorWrapper(self.outermost_line_iter()?))
340  }
341
342  /// Retrieves an immutable reference of any sub dimension.
343  ///
344  /// # Arguments
345  ///
346  /// * `range`: Starting and ending of the desired dimension
347  ///
348  /// # Example
349  ///
350  /// ```rust
351  /// use ndstruct::{csl::CslRef, doc_tests::csl_array_4};
352  /// let csl = csl_array_4();
353  /// // The last cuboid
354  /// assert_eq!(
355  ///   csl.sub_dim(1..2),
356  ///   CslRef::new([1, 3, 4, 5], &[9][..], &[2][..], &[8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9][..])
357  ///     .ok()
358  /// );
359  /// // The last 2 matrices of the first cuboid;
360  /// assert_eq!(
361  ///   csl.sub_dim(1..3),
362  ///   CslRef::new([2, 4, 5], &[6, 7, 8][..], &[2, 2, 4][..], &[5, 6, 6, 6, 6, 7, 8, 8, 8][..]).ok()
363  /// );
364  /// ```
365  #[inline]
366  pub fn sub_dim<const TD: usize>(&self, range: Range<usize>) -> Option<CslRef<'_, DATA, TD>> {
367    csl_utils::sub_dim(self, range)
368  }
369
370  /// Retrieves an immutable reference of a single data value.
371  ///
372  /// # Arguments
373  ///
374  /// * `indcs`: Indices of all dimensions
375  ///
376  /// # Example
377  ///
378  /// ```rust
379  /// use ndstruct::doc_tests::csl_array_4;
380  /// let csl = csl_array_4();
381  /// assert_eq!(csl.value([1, 0, 2, 2]), Some(&9));
382  /// let line = csl.line([0, 0, 3, 0]).unwrap();
383  /// assert_eq!(line.value([3]), Some(&4));
384  /// ```
385  #[inline]
386  pub fn value(&self, indcs: [usize; D]) -> Option<&DATA> {
387    let idx = csl_utils::data_idx(self, indcs)?;
388    self.data.as_ref().get(idx)
389  }
390}
391
392impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
393where
394  DS: AsMut<[DATA]> + AsRef<[DATA]> + SingleTypeStorage<Item = DATA>,
395  IS: AsRef<[usize]>,
396  OS: AsRef<[usize]>,
397{
398  /// Clears all values and dimensions.
399  ///
400  /// # Example
401  #[cfg_attr(feature = "alloc", doc = "```rust")]
402  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
403  /// use ndstruct::{csl::CslVec, doc_tests::csl_vec_4};
404  /// let mut csl = csl_vec_4();
405  /// csl.clear();
406  /// assert_eq!(csl, CslVec::default());
407  /// ```
408  #[inline]
409  pub fn clear(&mut self)
410  where
411    DS: Clear,
412    IS: Clear,
413    OS: Clear,
414  {
415    self.dims = <_>::default();
416    self.data.clear();
417    self.indcs.clear();
418    self.offs.clear();
419  }
420
421  /// See [`CslLineConstructor`](CslLineConstructor) for more information.
422  #[inline]
423  pub fn constructor(&mut self) -> crate::Result<CslLineConstructor<'_, DS, IS, OS, D>>
424  where
425    DS: Push<DATA>,
426    IS: Push<usize>,
427    OS: Push<usize>,
428  {
429    CslLineConstructor::new(self)
430  }
431
432  /// Mutable version of [`data`](#method.data).
433  #[inline]
434  pub fn data_mut(&mut self) -> &mut [DATA] {
435    self.data.as_mut()
436  }
437
438  /// Mutable version of [`line`](#method.line).
439  #[inline]
440  pub fn line_mut(&mut self, indcs: [usize; D]) -> Option<CslMut<'_, DATA, 1>> {
441    csl_utils::line_mut(self, indcs)
442  }
443
444  /// Mutable version of [`outermost_line_iter`](#method.outermost_line_iter).
445  #[inline]
446  pub fn outermost_line_iter_mut(&mut self) -> crate::Result<CslLineIterMut<'_, DATA, D>> {
447    CslLineIterMut::new(self.dims.0, self.data.as_mut(), self.indcs.as_ref(), self.offs.as_ref())
448  }
449
450  /// Mutable version of [`outermost_line_rayon_iter`](#method.outermost_line_rayon_iter).
451  #[cfg(feature = "rayon")]
452  #[inline]
453  pub fn outermost_line_rayon_iter_mut(
454    &mut self,
455  ) -> crate::Result<crate::ParallelIteratorWrapper<CslLineIterMut<'_, DATA, D>>> {
456    Ok(crate::ParallelIteratorWrapper(self.outermost_line_iter_mut()?))
457  }
458
459  /// Mutable version of [`sub_dim`](#method.sub_dim).
460  #[inline]
461  pub fn sub_dim_mut<const TD: usize>(
462    &mut self,
463    range: Range<usize>,
464  ) -> Option<CslMut<'_, DATA, TD>> {
465    csl_utils::sub_dim_mut(self, range)
466  }
467
468  /// Intra-swap a single data value.
469  ///
470  /// # Arguments
471  ///
472  /// * `a`: First set of indices
473  /// * `b`: Second set of indices
474  ///
475  /// # Example
476  #[cfg_attr(feature = "alloc", doc = "```rust")]
477  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
478  /// use ndstruct::doc_tests::csl_vec_4;
479  /// let mut csl = csl_vec_4();
480  /// csl.swap_value([0, 0, 0, 0], [1, 0, 2, 2]);
481  /// assert_eq!(csl.data(), &[9, 2, 3, 4, 5, 6, 7, 8, 1]);
482  /// ```
483  #[inline]
484  pub fn swap_value(&mut self, a: [usize; D], b: [usize; D]) -> bool {
485    if let Some(a_idx) = csl_utils::data_idx(self, a) {
486      if let Some(b_idx) = csl_utils::data_idx(self, b) {
487        self.data.as_mut().swap(a_idx, b_idx);
488        return true;
489      }
490    }
491    false
492  }
493
494  /// Truncates all values in the exactly exclusive line defined by `indcs`. The last index is ignored.
495  ///
496  /// # Example
497  #[cfg_attr(feature = "alloc", doc = "```rust")]
498  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
499  /// use ndstruct::{csl::CslVec, doc_tests::csl_vec_4};
500  /// let mut csl = csl_vec_4();
501  /// csl.truncate([0, 0, 3, 0]);
502  /// assert_eq!(
503  ///   Ok(csl),
504  ///   CslVec::new([0, 0, 4, 5], vec![1, 2, 3], vec![0, 3, 1], vec![0, 2, 3, 3, 3])
505  /// );
506  /// ```
507  #[inline]
508  pub fn truncate(&mut self, indcs: [usize; D])
509  where
510    DS: Truncate<Input = usize>,
511    IS: Truncate<Input = usize>,
512    OS: AsMut<[usize]> + Truncate<Input = usize>,
513  {
514    let Some([offs_indcs, values]) = csl_utils::line_offs(&self.dims, &indcs, self.offs.as_ref())
515    else {
516      return;
517    };
518    let cut_point = values.start;
519    self.data.truncate(cut_point);
520    self.indcs.truncate(cut_point);
521    self.offs.truncate(offs_indcs.end);
522    let iter = indcs.iter().zip(self.dims.iter_mut()).rev().skip(1).rev();
523    iter.filter(|&(a, _)| *a == 0).for_each(|(_, b)| *b = 0);
524    let before_last = if let Some(rslt) = self.offs.as_ref().get(offs_indcs.end.saturating_sub(2)) {
525      *rslt
526    } else {
527      return;
528    };
529    if let Some(rslt) = self.offs.as_mut().get_mut(offs_indcs.end.saturating_sub(1)) {
530      *rslt = before_last;
531    }
532  }
533
534  /// Mutable version of [`value`](#method.value).
535  #[inline]
536  pub fn value_mut(&mut self, indcs: [usize; D]) -> Option<&mut DATA> {
537    let idx = csl_utils::data_idx(self, indcs)?;
538    self.data.as_mut().get_mut(idx)
539  }
540}
541
542#[cfg(feature = "rand")]
543impl<DATA, DS, IS, OS, const D: usize> Csl<DS, IS, OS, D>
544where
545  DS: AsMut<[DATA]> + AsRef<[DATA]> + Default + Push<DATA> + SingleTypeStorage<Item = DATA>,
546  IS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<usize>,
547  OS: AsMut<[usize]> + AsRef<[usize]> + Default + Push<usize>,
548{
549  /// Creates a new random and valid instance delimited by the passed arguments.
550  ///
551  /// # Arguments
552  ///
553  /// * `dims`: Array of dimensions
554  /// * `nnz`: Number of Non-Zero elements
555  /// * `rng`: `rand::Rng` trait
556  /// * `cb`: Callback to control data creation
557  ///
558  /// # Example
559  #[cfg_attr(feature = "alloc", doc = "```rust")]
560  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
561  /// use ndstruct::csl::CslVec;
562  /// use rand::{Rng, rngs::mock::StepRng};
563  /// let mut rng = StepRng::new(0, 1);
564  /// let dims = [1, 2, 3];
565  /// let mut _random: ndstruct::Result<CslVec<u8, 3>>;
566  /// _random = CslVec::new_controlled_random_rand(dims, 9, &mut rng, |r, _| r.gen());
567  /// ```
568  #[inline]
569  pub fn new_controlled_random_rand<R>(
570    dims: [usize; D],
571    nnz: usize,
572    rng: &mut R,
573    cb: impl FnMut(&mut R, [usize; D]) -> DATA,
574  ) -> crate::Result<Self>
575  where
576    R: rand::Rng,
577  {
578    let mut csl = Csl { dims: dims.into(), ..Default::default() };
579    csl_rnd::CslRnd::new(&mut csl, nnz, rng)?.fill(cb).ok_or(crate::Error::UnknownError)?;
580    Self::new(csl.dims.0, csl.data, csl.indcs, csl.offs)
581  }
582
583  /// Creates a new random and valid instance.
584  ///
585  /// # Arguments
586  ///
587  /// * `rng`: `rand::Rng` trait
588  /// * `upper_bound`: The maximum allowed exclusive dimension
589  ///
590  /// # Example
591  ///
592  /// # Example
593  #[cfg_attr(feature = "alloc", doc = "```rust")]
594  #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
595  /// # fn main() -> ndstruct::Result<()> {
596  /// use ndstruct::csl::CslVec;
597  /// use rand::{rngs::mock::StepRng, seq::SliceRandom};
598  /// let mut rng = StepRng::new(0, 1);
599  /// let upper_bound = 5;
600  /// let random: ndstruct::Result<CslVec<u8, 8>>;
601  /// random = CslVec::new_random_rand(&mut rng, upper_bound);
602  /// assert!(random?.dims().choose(&mut rng).unwrap() < &upper_bound);
603  /// # Ok(()) }
604  #[inline]
605  pub fn new_random_rand<R>(rng: &mut R, upper_bound: usize) -> crate::Result<Self>
606  where
607    R: rand::Rng,
608    rand::distributions::Standard: rand::distributions::Distribution<DATA>,
609  {
610    let dims = crate::utils::valid_random_dims(rng, upper_bound);
611    let max_nnz = max_nnz(&dims);
612    let nnz = if max_nnz == 0 { 0 } else { rng.gen_range(0..max_nnz) };
613    Self::new_controlled_random_rand(dims, nnz, rng, |r, _| r.gen())
614  }
615}