//! CSR (Compressed Sparse Row) matrix.
//!
//! See [Wikipedia](https://en.wikipedia.org/wiki/Sparse_matrix).

mod csr_matrix_row_constructor;
mod csr_matrix_row_iter_impls;
#[cfg(feature = "parallelization")]
mod csr_matrix_row_par_iter_impls;

pub use self::{
    csr_matrix_row_constructor::CsrMatrixRowConstructor,
    csr_matrix_row_iter_impls::{CsrMatrixRowIter, CsrMatrixRowIterMut},
};
use core::ops::Range;
use crate::{
    prelude::{Array, DynDenseStoMut, DynDenseStoRef, Matrix, StDenseStoMut, StDenseStoRef},
    utils::{are_in_upper_bound, does_not_have_duplicates},
    vec::{
        css::{CssSlice, CssSliceMut},
        VecArray,
    },
    Dim,
};

/// This structure can hold differents mixed types of indexed storages.
#[cfg_attr(
    feature = "serialization",
    derive(
        mop_common::serde_derive::Deserialize,
        mop_common::serde_derive::Serialize
    )
)]
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
pub struct CsrMatrix<DS, US> {
    data: DS,
    dim: Dim<[usize; 2]>,
    indcs: US,
    ptrs: US,
}

#[cfg(feature = "std")]
pub type CsrMatrixVec<D> = CsrMatrix<Vec<D>, Vec<usize>>;
pub type CsrMatrixVecArray<DA, UA> = CsrMatrix<VecArray<DA>, VecArray<UA>>;
pub type CsrMatrixRef<'a, D> = CsrMatrix<&'a [D], &'a [usize]>;

#[cfg(feature = "std")]
impl<D> CsrMatrixVec<D> {
    pub fn with_vec_capacity(shape: [usize; 2], nnz: usize) -> Self {
        let mut ptrs = Vec::with_capacity(shape[0] + 1);
        ptrs.push(0);
        CsrMatrix {
            data: Vec::with_capacity(nnz),
            dim: [0, shape[1]].into(),
            indcs: Vec::with_capacity(nnz),
            ptrs,
        }
    }
}

impl<DA, UA> CsrMatrixVecArray<DA, UA>
where
    DA: Array,
    UA: Array<Item = usize>,
{
    pub fn with_vec_array(cols: usize) -> Self {
        let mut ptrs_vec_array = VecArray::with_capacity();
        ptrs_vec_array.push(0);
        CsrMatrix {
            data: VecArray::with_capacity(),
            dim: [0, cols].into(),
            indcs: VecArray::with_capacity(),
            ptrs: ptrs_vec_array,
        }
    }
}

impl<DS, US> CsrMatrix<DS, US>
where
    DS: StDenseStoRef,
    US: StDenseStoRef<Item = usize>,
{
    /// Creates a new [`CsrMatrix`](CsrMatrix) from raw parameters.
    ///
    /// # Arguments
    ///
    /// * `shape` - An array containing the number of rows and columns.
    /// * `data` - The matrix data.
    /// * `indcs` - Each column index of its corresponding data.
    /// * `ptrs` - The length of each row.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::matrix::csr_matrix::CsrMatrixRef;
    /// let _ = CsrMatrixRef::new([2, 4], &[1, 2, 3], &[0, 1, 2], &[0, 3, 3]);
    /// ```
    ///
    /// # Assertions
    ///
    /// * The length of `data` must be equal the length of `indcs`.
    ///
    /// ```should_panic
    /// use mop_structs::{
    ///     matrix::csr_matrix::CsrMatrix,
    ///     vec_array
    /// };
    /// let _ = CsrMatrix::new(
    ///     [2, 4],
    ///     vec_array![[i32; 3]; 1, 2, 3],
    ///     vec_array![[usize; 3]; ],
    ///     vec_array![[usize; 3]; 0, 3, 3]
    /// );
    /// ```
    ///
    /// * The length of `data` is greater than the number of rows times the number of columns.
    ///
    /// ```should_panic
    /// use mop_structs::{
    ///     matrix::csr_matrix::CsrMatrix,
    ///     vec_array
    /// };
    /// let _ = CsrMatrix::new(
    ///     [2, 4],
    ///     vec_array![[i32; 9]; 1, 2, 3, 4, 5, 6, 7, 8, 9],
    ///     vec_array![[usize; 9]; 0, 1, 2, 3, 0, 1, 2, 3, 0],
    ///     vec_array![[usize; 9]; 0, 4, 8]
    /// );
    /// ```
    ///
    /// * The length of `ptrs` must be equal the number of rows plus 1.
    ///
    /// ```should_panic
    /// use mop_structs::{
    ///     matrix::csr_matrix::CsrMatrix,
    ///     vec_array
    /// };
    /// let _ = CsrMatrix::new(
    ///     [2, 4],
    ///     vec_array![[i32; 3]; 1, 2, 3],
    ///     vec_array![[usize; 5]; 0, 1, 2],
    ///     vec_array![[usize; 5]; 0, 3, 3, 3, 3]
    /// );
    /// ```
    ///
    /// * Column indices must be less than the number of columns.
    ///
    /// ```should_panic
    /// use mop_structs::matrix::csr_matrix::CsrMatrixRef;
    /// let _ = CsrMatrixRef::new([2, 4], &[1, 2, 3], &[0, 1, 9], &[0, 3, 3]);
    /// ```
    ///
    /// * Column indices of a row must be unique.
    ///
    /// ```should_panic
    /// use mop_structs::matrix::csr_matrix::CsrMatrixRef;
    /// let _ = CsrMatrixRef::new([2, 4], &[1, 2, 3], &[0, 0, 0], &[0, 3, 3]);
    /// ```
    ///
    /// * Rows length must end with the nnz.
    ///
    /// ```should_panic
    /// use mop_structs::matrix::csr_matrix::CsrMatrixRef;
    /// let _ = CsrMatrixRef::new([2, 4], &[1, 2, 3], &[0, 1, 2], &[0, 3, 9]);
    /// ```
    ///
    /// * Rows length must be in ascending order.
    ///
    /// ```should_panic
    ///  use mop_structs::matrix::csr_matrix::CsrMatrixRef;
    ///  let _ = CsrMatrixRef::new([3, 4], &[1, 2, 3, 4], &[0, 0, 0, 1], &[0, 1, 0, 4]);
    /// ```
    pub fn new(shape: [usize; 2], data: DS, indcs: US, ptrs: US) -> Self {
        let dim: Dim<[usize; 2]> = shape.into();
        assert!(
            data.as_slice().len() == indcs.as_slice().len(),
            "The length of `data` must be equal the length of `indcs`."
        );
        assert!(
            data.as_slice().len() <= dim.rows() * dim.cols(),
            "The length of `data` must be less than the number of rows times the \
             number of columns."
        );
        assert!(
            ptrs.as_slice().len() == dim.rows() + 1,
            "The length of `ptrs` must be equal the number of rows plus 1."
        );
        assert!(
            are_in_upper_bound(indcs.as_slice(), &dim.cols()),
            "Column indices must be less than the number of columns."
        );
        assert!(
            ptrs.as_slice()
                .windows(2)
                .all(|x| does_not_have_duplicates(&indcs.as_slice()[x[0]..x[1]])),
            "Column indices of a row must be unique."
        );
        assert!(
            *ptrs.as_slice().last().unwrap() == data.as_slice().len(),
            "Rows length must end with the nnz."
        );
        assert!(
            ptrs.as_slice().windows(2).all(|x| x[0] <= x[1])
            "Rows length must be in ascending order."
        );
        unsafe { Self::new_unchecked(shape, data, indcs, ptrs) }
    }

    /// A faster and unsafe version of [`new`](#method.new).
    pub unsafe fn new_unchecked(shape: [usize; 2], data: DS, indcs: US, ptrs: US) -> Self {
        CsrMatrix {
            data,
            dim: shape.into(),
            indcs,
            ptrs,
        }
    }

    /// Converts the inner storage to a generic immutable slice storage.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     matrix::csr_matrix::CsrMatrixRef
    /// };
    /// assert_eq!(
    ///     csr_matrix_vec_array().as_ref(),
    ///     CsrMatrixRef::new(
    ///         [4, 5],
    ///         &[1, 2, 3, 4, 5],
    ///         &[0, 2, 1, 3, 4],
    ///         &[0, 2, 4, 4, 5]
    ///     )
    /// );
    /// ```
    pub fn as_ref(&self) -> CsrMatrixRef<'_, DS::Item> {
        CsrMatrix::new(
            [self.dim.rows(), self.dim.cols()],
            self.data.as_slice(),
            self.indcs.as_slice(),
            self.ptrs.as_slice(),
        )
    }

    /// Returns an immutable reference to the data elements.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let a = csr_matrix_vec_array();
    /// assert_eq!(a.data(), &[1, 2, 3, 4, 5]);
    /// ```
    pub fn data(&self) -> &[DS::Item] {
        self.data.as_slice()
    }

    /// Returns an immutable reference to the column indices of the data elements.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let a = csr_matrix_vec_array();
    /// assert_eq!(a.indcs(), &[0, 2, 1, 3, 4]);
    /// ```
    pub fn indcs(&self) -> &[usize] {
        &self.indcs.as_slice()
    }

    /// Returns the Number of NonZero elements.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// assert_eq!(csr_matrix_vec_array().nnz(), 5);
    /// ```
    #[inline]
    pub fn nnz(&self) -> usize {
        self.data().len()
    }

    /// Returns an immutable reference to the rows length.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let a = csr_matrix_vec_array();
    /// assert_eq!(a.ptrs(), &[0, 2, 4, 4, 5]);
    /// ```
    pub fn ptrs(&self) -> &[usize] {
        &self.ptrs.as_slice()
    }

    /// Returns an immutable reference to the row at the `row_idx` row.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     vec::css::CssSlice
    /// };
    /// assert_eq!(
    ///     csr_matrix_vec_array().row(0),
    ///     CssSlice::new(5, &[1, 2], &[0, 2])
    /// );
    /// ```
    ///
    /// # Assertions
    ///
    /// * `row_idx` must be less than the number of rows.
    ///
    /// ```should_panic
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let _ = csr_matrix_vec_array().row(10);
    /// ```
    pub fn row(&self, row_idx: usize) -> CssSlice<'_, DS::Item> {
        let range = self.ptr_range_of_row(row_idx);
        CssSlice::new(
            self.dim.cols(),
            &self.data.as_slice()[range.clone()],
            &self.indcs.as_slice()[range],
        )
    }

    /// An iterator that returns immutable references of all rows.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     vec::css::CssSlice
    /// };
    /// let mut a = csr_matrix_vec_array();
    /// let mut li = a.row_iter();
    /// li.next();
    /// assert_eq!(li.next(), Some(CssSlice::new(5, &[3, 4], &[1, 3])));
    /// li.next();
    /// li.next();
    /// assert_eq!(li.next(), None);
    /// ```
    pub fn row_iter(&self) -> CsrMatrixRowIter<'_, DS::Item> {
        CsrMatrixRowIter::new(
            [self.rows(), self.cols()],
            self.data().as_ptr(),
            self.indcs(),
            self.ptrs(),
        )
    }

    /// An parallel iterator that returns immutable references of all rows.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     prelude::*
    /// };
    /// let a = csr_matrix_vec_array();
    /// a.row_par_iter().enumerate().for_each(|(idx, x)| assert_eq!(x, a.row(idx)));
    /// ```
    #[cfg(feature = "parallelization")]
    pub fn row_par_iter(
        &self,
    ) -> crate::utils::ParallelIteratorWrapper<CsrMatrixRowIter<'_, DS::Item>> {
        crate::utils::ParallelIteratorWrapper(self.row_iter())
    }

    /// If any, returns an immutable reference to the element at the `row_idx` row and
    /// `col_idx` column.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let a = csr_matrix_vec_array();
    /// assert_eq!(a.value(0, 0), Some(&1));
    /// assert_eq!(a.value(1, 0), None);
    /// ```
    ///
    /// # Assertions
    ///
    /// * `row_idx` must be less than the number of rows and `col_idx` must be less
    /// than the number of columns.
    ///
    /// ```should_panic
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let _ = csr_matrix_vec_array().value(10, 10);
    /// ```
    pub fn value(&self, row_idx: usize, col_idx: usize) -> Option<&DS::Item> {
        self.data_idx_of_coords(row_idx, col_idx)
            .map(|x| &self.data()[x])
    }

    #[inline]
    fn data_idx_of_coords(&self, row_idx: usize, col_idx: usize) -> Option<usize> {
        assert!(row_idx < self.rows() && col_idx < self.cols());
        let row_start_ptr = self.ptrs()[row_idx];
        let row_end_ptr = self.ptrs()[row_idx + 1];
        if let Ok(x) = self.indcs()[row_start_ptr..row_end_ptr].binary_search(&col_idx) {
            Some(row_start_ptr + x)
        } else {
            None
        }
    }

    #[inline]
    fn ptr_range_of_row(&self, row_idx: usize) -> Range<usize> {
        assert!(
            row_idx < self.dim.rows(),
            "`row_idx` must be less than the number of rows."
        );
        let lower_bound = self.ptrs()[row_idx] - self.ptrs()[0];
        let upper_bound = self.ptrs()[row_idx + 1] - self.ptrs()[0];
        lower_bound..upper_bound
    }
}

impl<DS, US> CsrMatrix<DS, US>
where
    DS: StDenseStoMut,
    US: StDenseStoRef<Item = usize>,
{
    /// Returns an mutable reference to the data elements.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let mut a = csr_matrix_vec_array();
    /// assert_eq!(a.data_mut(), &mut [1, 2, 3, 4, 5]);
    /// ```
    pub fn data_mut(&mut self) -> &mut [DS::Item] {
        self.data.as_mut_slice()
    }

    /// In a single borrow of `Self`, returns borrows for the most imporant inner
    /// parts (&mut data, &indices and &pointers).
    pub fn parts_with_data_mut(&mut self) -> (&mut [DS::Item], &[usize], &[usize]) {
        (
            self.data.as_mut_slice(),
            self.indcs.as_slice(),
            self.ptrs.as_slice(),
        )
    }

    /// Returns an mutable reference to the row at the `row_idx` position.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     vec::css::CssSliceMut
    /// };
    /// assert_eq!(
    ///     csr_matrix_vec_array().row_mut(1),
    ///     CssSliceMut::new(5, &mut [3, 4], &[1, 3])
    /// );
    /// ```
    ///
    /// # Assertions
    ///
    /// * `row_idx` must be less than the number of rows.
    ///
    /// ```should_panic
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let _ = csr_matrix_vec_array().row_mut(10);
    /// ```
    pub fn row_mut(&mut self, row_idx: usize) -> CssSliceMut<'_, DS::Item> {
        let range = self.ptr_range_of_row(row_idx);
        CssSliceMut::new(
            self.dim.cols(),
            &mut self.data.as_mut_slice()[range.clone()],
            &self.indcs.as_slice()[range],
        )
    }

    /// An iterator that returns mutable references of all rows.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     vec::css::CssSliceMut
    /// };
    /// let mut a = csr_matrix_vec_array();
    /// let mut li = a.row_iter_mut();
    /// li.next();
    /// assert_eq!(li.next(), Some(CssSliceMut::new(5, &mut [3, 4], &[1, 3])));
    /// li.next();
    /// li.next();
    /// assert_eq!(li.next(), None);
    /// ```
    pub fn row_iter_mut(&mut self) -> CsrMatrixRowIterMut<'_, DS::Item> {
        CsrMatrixRowIterMut::new(
            [self.rows(), self.cols()],
            self.data_mut().as_mut_ptr(),
            self.indcs(),
            self.ptrs(),
        )
    }

    /// An parallel iterator that returns mutable references of all rows.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     prelude::*
    /// };
    /// let mut a = csr_matrix_vec_array();
    /// a.row_par_iter_mut().for_each(|mut a| a.data_mut().iter_mut().for_each(|b| *b += 2));
    /// assert_eq!(a.data(), &[3, 4, 5, 6, 7]);
    /// ```
    #[cfg(feature = "parallelization")]
    pub fn row_par_iter_mut(
        &mut self,
    ) -> crate::utils::ParallelIteratorWrapper<CsrMatrixRowIterMut<'_, DS::Item>> {
        crate::utils::ParallelIteratorWrapper(self.row_iter_mut())
    }

    pub fn split_at_mut(
        &mut self,
        row_idx: usize,
    ) -> (CssSliceMut<'_, DS::Item>, CssSliceMut<'_, DS::Item>) {
        assert!(row_idx <= self.rows());
        let split_at = self.ptrs()[row_idx];
        let (first_data, second_data) = self.data.as_mut_slice().split_at_mut(split_at);
        let first = CssSliceMut::new(
            self.dim.cols(),
            first_data,
            &self.indcs.as_slice()[0..split_at],
        );
        let second = CssSliceMut::new(
            self.dim.cols(),
            second_data,
            &self.indcs.as_slice()[split_at..],
        );
        (first, second)
    }

    /// If any, returns an mutable reference to the element at the `row_idx` row and
    /// `col_idx` column.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let mut a = csr_matrix_vec_array();
    /// assert_eq!(a.value_mut(0, 0), Some(&mut 1));
    /// assert_eq!(a.value_mut(1, 0), None);
    /// ```
    ///
    /// # Assertions
    ///
    /// * `row_idx` must be less than the number of rows and `col_idx` must be less
    /// than the number of columns.
    ///
    /// ```should_panic
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// let _ = csr_matrix_vec_array().value_mut(10, 10);
    /// ```
    pub fn value_mut(&mut self, row_idx: usize, col_idx: usize) -> Option<&mut DS::Item> {
        if let Some(x) = self.data_idx_of_coords(row_idx, col_idx) {
            Some(&mut self.data_mut()[x])
        } else {
            None
        }
    }
}

impl<DS, US> CsrMatrix<DS, US>
where
    DS: DynDenseStoRef,
    US: DynDenseStoRef<Item = usize>,
{
    /// Returns the current rows capacity.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::doc_tests::csr_matrix_vec_array;
    /// assert_eq!(csr_matrix_vec_array().rows_capacity(), 4);
    /// ```
    #[inline]
    pub fn rows_capacity(&self) -> usize {
        self.ptrs.capacity() - 1
    }
}

impl<DS, US> CsrMatrix<DS, US>
where
    DS: DynDenseStoMut,
    US: DynDenseStoMut<Item = usize>,
{
    /// Removes all values and sets the number of rows to zero but doesn't modify the
    /// number of columns.
    ///
    /// Note that this method has no effect on the allocated capacity.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::csr_matrix_vec_array,
    ///     matrix::csr_matrix::CsrMatrixRef,
    ///     prelude::*
    /// };
    /// let mut a = csr_matrix_vec_array();
    /// a.clear();
    /// assert_eq!(a.as_ref(), CsrMatrixRef::new(
    ///     [0, a.cols()],
    ///     &[],
    ///     &[],
    ///     &[0]
    /// ));
    /// ```
    pub fn clear(&mut self) {
        self.data.clear();
        self.indcs.clear();
        self.ptrs.clear();
        self.ptrs.push(0);
        *self.dim.rows_mut() = 0;
    }

    pub fn extend(&mut self, other: &Self)
    where
        DS::Item: Copy,
        US::Item: Copy,
    {
        assert!(
            self.cols() == other.cols(),
            "The number of columns of `Self` must be equal the number of columns of `other`."
        );
        let mut current_ptr = *self.ptrs.as_slice().last().unwrap();
        let mut last_other_ptr = *other.ptrs().first().unwrap();
        other.ptrs().iter().skip(1).for_each(|&x| {
            current_ptr += x - last_other_ptr;
            last_other_ptr = x;
            self.ptrs.push(current_ptr);
        });
        self.data.extend(other.data());
        self.indcs.extend(other.indcs());
        *self.dim.rows_mut() += other.rows();
    }

    /// See [`CsrMatrixRowConstructor`](CsrMatrixRowConstructor) for more information.
    pub fn row_constructor(&mut self) -> CsrMatrixRowConstructor<'_, DS, US> {
        CsrMatrixRowConstructor::new(
            &mut self.dim,
            &mut self.data,
            &mut self.indcs,
            &mut self.ptrs,
        )
    }

    pub fn truncate(&mut self, until_row_idx: usize) {
        self.data.truncate(until_row_idx);
        self.indcs.truncate(until_row_idx);
        self.ptrs.truncate(until_row_idx + 1);
        *self.dim.rows_mut() = until_row_idx;
    }
}

impl<DS, US> Matrix for CsrMatrix<DS, US> {
    /// The number of columns.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{doc_tests::csr_matrix_vec_array, prelude::*};
    /// assert_eq!(csr_matrix_vec_array().cols(), 5);
    /// ```
    #[inline]
    fn cols(&self) -> usize {
        self.dim.cols()
    }

    /// The number of rows.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{doc_tests::csr_matrix_vec_array, prelude::*};
    /// assert_eq!(csr_matrix_vec_array().rows(), 4);
    /// ```
    #[inline]
    fn rows(&self) -> usize {
        self.dim.rows()
    }
}

#[cfg(feature = "testing")]
impl<T> mop_common::quickcheck::Arbitrary for CsrMatrix<Vec<T>, Vec<usize>>
where
    T: mop_common::quickcheck::Arbitrary,
{
    #[inline]
    fn arbitrary<G: mop_common::quickcheck::Gen>(g: &mut G) -> Self {
        let size = g.size();
        let cols = g.gen_range(0, size);
        let rows = g.gen_range(0, size);
        let mut unique_indcs = core::collections::HashSet::new();

        let ptrs: Vec<usize> = (0..1)
            .chain((0..rows).scan(0, |state, _| {
                *state += g.gen_range(0, cols + 1);
                Some(*state)
            }))
            .collect();

        let nnz = *ptrs.last().unwrap();
        let mut indcs = Vec::with_capacity(nnz);
        ptrs.windows(2).for_each(|x| {
            (0..)
                .map(|_| g.gen_range(0, cols))
                .filter(|y| unique_indcs.insert(*y))
                .take(x[1] - x[0])
                .for_each(|y| indcs.push(y));
            unique_indcs.clear();
        });

        Self::new(
            [rows, cols],
            (0..nnz)
                .map(|_| mop_common::quickcheck::Arbitrary::arbitrary(g))
                .collect(),
            indcs,
            ptrs,
        )
    }
}
