use faer::sparse::SymbolicSparseColMatRef;
use faer_traits::Index;
use super::Ic0Error;
#[derive(Debug, Clone)]
pub struct SymbolicIc0<I> {
pub(crate) dim: usize,
pub(crate) a_nnz: usize,
pub(crate) diag_pos: Vec<usize>,
pub(crate) l_col_ptr: Vec<I>,
pub(crate) l_row_idx: Vec<I>,
pub(crate) row_col_ptr: Vec<usize>,
pub(crate) row_col_idx: Vec<I>,
pub(crate) row_lpos: Vec<usize>,
}
impl<I: Index> SymbolicIc0<I> {
pub fn try_new(pattern: SymbolicSparseColMatRef<'_, I>) -> Result<Self, Ic0Error> {
let nrows = pattern.nrows();
let ncols = pattern.ncols();
if nrows != ncols {
return Err(Ic0Error::NonSquareMatrix { nrows, ncols });
}
let n = nrows;
let mut diag_pos = Vec::with_capacity(n);
let mut l_col_ptr: Vec<I> = Vec::with_capacity(n + 1);
l_col_ptr.push(I::truncate(0));
let mut a_nnz = 0usize;
let mut l_running = 0usize;
for j in 0..n {
let row_idx = pattern.row_idx_of_col_raw(j);
let col_len = row_idx.len();
a_nnz += col_len;
let mut diag_idx: Option<usize> = None;
let mut prev: Option<usize> = None;
for (k, raw) in row_idx.iter().enumerate() {
let r = raw.zx();
if let Some(p) = prev
&& r <= p
{
return Err(Ic0Error::UnsortedRowIndices { col: j });
}
if r == j {
diag_idx = Some(k);
}
prev = Some(r);
}
let d = diag_idx.ok_or(Ic0Error::MissingDiagonal { col: j })?;
diag_pos.push(d);
l_running += col_len - d;
l_col_ptr.push(I::truncate(l_running));
}
let mut l_row_idx: Vec<I> = Vec::with_capacity(l_running);
for (j, &d) in diag_pos.iter().enumerate() {
let row_idx = pattern.row_idx_of_col_raw(j);
l_row_idx.extend_from_slice(&row_idx[d..]);
}
debug_assert_eq!(l_row_idx.len(), l_running);
let mut row_col_ptr = vec![0usize; n + 1];
for k in 0..n {
let lk_start = l_col_ptr[k].zx() + 1;
let lk_end = l_col_ptr[k + 1].zx();
for raw_row in &l_row_idx[lk_start..lk_end] {
let i = raw_row.zx();
row_col_ptr[i + 1] += 1;
}
}
for i in 0..n {
row_col_ptr[i + 1] += row_col_ptr[i];
}
let total = row_col_ptr[n];
let mut row_col_idx: Vec<I> = vec![I::truncate(0); total];
let mut row_lpos: Vec<usize> = vec![0usize; total];
let mut fill = vec![0usize; n];
for k in 0..n {
let lk_start = l_col_ptr[k].zx() + 1;
let lk_end = l_col_ptr[k + 1].zx();
for (offset, raw_row) in l_row_idx[lk_start..lk_end].iter().enumerate() {
let pos = lk_start + offset;
let i = raw_row.zx();
let dst = row_col_ptr[i] + fill[i];
row_col_idx[dst] = I::truncate(k);
row_lpos[dst] = pos;
fill[i] += 1;
}
}
Ok(Self {
dim: n,
a_nnz,
diag_pos,
l_col_ptr,
l_row_idx,
row_col_ptr,
row_col_idx,
row_lpos,
})
}
#[inline]
pub fn dim(&self) -> usize {
self.dim
}
#[inline]
pub fn a_nnz(&self) -> usize {
self.a_nnz
}
#[inline]
pub fn l_nnz(&self) -> usize {
self.l_row_idx.len()
}
}