use alloc::vec;
use alloc::vec::Vec;
use crate::scalar::Scalar;
use super::sorted_csc::SortedCscMatrix;
use super::sorted_csr::SortedCsrMatrix;
use super::{CooMatrix, CscError, CscMatrix, CsrError, CsrMatrix};
pub fn coo_to_csr<T: Scalar>(coo: CooMatrix<T>) -> Result<SortedCsrMatrix<T>, CsrError> {
let (rows, cols, coo_row, coo_col, coo_val) = coo.into_raw_parts();
let nnz_in = coo_val.len();
let mut order: Vec<usize> = (0..nnz_in).collect();
order.sort_by_key(|&i| (coo_row[i], coo_col[i]));
let mut out_row: Vec<u32> = Vec::new();
let mut out_col: Vec<u32> = Vec::new();
let mut out_val: Vec<T> = Vec::new();
for &i in &order {
let r = coo_row[i];
let c = coo_col[i];
let v = coo_val[i];
let is_dup = out_row.last().copied() == Some(r) && out_col.last().copied() == Some(c);
if is_dup {
if let Some(last) = out_val.last_mut() {
*last = last.add(v);
}
} else {
out_row.push(r);
out_col.push(c);
out_val.push(v);
}
}
let expected_len = rows.checked_add(1).ok_or(CsrError::DimensionOverflow)?;
let mut row_ptr = vec![0u32; expected_len];
for &r in &out_row {
row_ptr[r as usize + 1] += 1;
}
for i in 1..=rows {
row_ptr[i] = row_ptr[i]
.checked_add(row_ptr[i - 1])
.ok_or(CsrError::DimensionOverflow)?;
}
Ok(SortedCsrMatrix::from_sorted_unchecked(CsrMatrix::new_raw(
rows, cols, row_ptr, out_col, out_val,
)))
}
pub fn csr_to_coo<T>(csr: CsrMatrix<T>) -> Result<CooMatrix<T>, CsrError> {
let (rows, cols, row_ptr, col_indices, values) = csr.into_raw_parts();
let nnz = values.len();
let mut row_indices = Vec::with_capacity(nnz);
for r in 0..rows {
let count = row_ptr[r + 1] - row_ptr[r];
let r_u32 = u32::try_from(r).map_err(|_| CsrError::DimensionOverflow)?;
for _ in 0..count {
row_indices.push(r_u32);
}
}
Ok(CooMatrix::new_raw(
rows,
cols,
row_indices,
col_indices,
values,
))
}
pub fn csr_to_csc<T: Scalar>(m: CsrMatrix<T>) -> Result<SortedCscMatrix<T>, CscError> {
let (rows, cols, row_ptr, col_indices, values) = m.into_raw_parts();
let nnz = values.len();
let mut triples: Vec<(u32, u32, T)> = Vec::with_capacity(nnz);
for r in 0..rows {
let r_u32 = u32::try_from(r).map_err(|_| CscError::DimensionOverflow)?;
for k in row_ptr[r] as usize..row_ptr[r + 1] as usize {
triples.push((col_indices[k], r_u32, values[k]));
}
}
triples.sort_by_key(|&(c, r, _)| (c, r));
let expected_len = cols.checked_add(1).ok_or(CscError::DimensionOverflow)?;
let mut col_ptr = vec![0u32; expected_len];
for &(c, _, _) in &triples {
col_ptr[c as usize + 1] += 1;
}
for i in 1..=cols {
col_ptr[i] = col_ptr[i]
.checked_add(col_ptr[i - 1])
.ok_or(CscError::DimensionOverflow)?;
}
let mut out_row: Vec<u32> = Vec::with_capacity(nnz);
let mut out_val: Vec<T> = Vec::with_capacity(nnz);
for (_, r, v) in triples {
out_row.push(r);
out_val.push(v);
}
Ok(SortedCscMatrix::from_sorted_unchecked(CscMatrix::new_raw(
rows, cols, col_ptr, out_row, out_val,
)))
}
pub fn csc_to_csr<T: Scalar>(m: CscMatrix<T>) -> Result<SortedCsrMatrix<T>, CsrError> {
let (rows, cols, col_ptr, row_indices, values) = m.into_raw_parts();
let nnz = values.len();
let mut triples: Vec<(u32, u32, T)> = Vec::with_capacity(nnz);
for c in 0..cols {
let c_u32 = u32::try_from(c).map_err(|_| CsrError::DimensionOverflow)?;
for k in col_ptr[c] as usize..col_ptr[c + 1] as usize {
triples.push((row_indices[k], c_u32, values[k]));
}
}
triples.sort_by_key(|&(r, c, _)| (r, c));
let expected_len = rows.checked_add(1).ok_or(CsrError::DimensionOverflow)?;
let mut row_ptr = vec![0u32; expected_len];
for &(r, _, _) in &triples {
row_ptr[r as usize + 1] += 1;
}
for i in 1..=rows {
row_ptr[i] = row_ptr[i]
.checked_add(row_ptr[i - 1])
.ok_or(CsrError::DimensionOverflow)?;
}
let mut out_col: Vec<u32> = Vec::with_capacity(nnz);
let mut out_val: Vec<T> = Vec::with_capacity(nnz);
for (_, c, v) in triples {
out_col.push(c);
out_val.push(v);
}
Ok(SortedCsrMatrix::from_sorted_unchecked(CsrMatrix::new_raw(
rows, cols, row_ptr, out_col, out_val,
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn coo_to_csr_rows_plus_one_overflow_is_an_error_not_a_panic() {
let coo = CooMatrix::<f64>::new(usize::MAX, 1, vec![], vec![], vec![]).unwrap();
let err = coo_to_csr(coo);
assert_eq!(err.map(|_| ()), Err(CsrError::DimensionOverflow));
}
#[test]
fn csr_to_csc_cols_plus_one_overflow_is_an_error_not_a_panic() {
let m: CsrMatrix<f64> = CsrMatrix::new_raw(0, usize::MAX, vec![0], vec![], vec![]);
let err = csr_to_csc(m);
assert_eq!(err.map(|_| ()), Err(CscError::DimensionOverflow));
}
#[test]
fn csc_to_csr_rows_plus_one_overflow_is_an_error_not_a_panic() {
let m: CscMatrix<f64> = CscMatrix::new_raw(usize::MAX, 0, vec![0], vec![], vec![]);
let err = csc_to_csr(m);
assert_eq!(err.map(|_| ()), Err(CsrError::DimensionOverflow));
}
}