use super::btf::btf_order;
use super::amd::amd_order;
use super::types::unflip;
pub struct Symbolic {
pub p: Vec<usize>,
pub q: Vec<usize>,
pub r: Vec<usize>,
#[allow(dead_code)]
pub lnz: Vec<f64>,
}
impl Symbolic {
pub fn nblocks(&self) -> usize {
self.r.len() - 1
}
}
pub fn analyze(n: usize, col_ptr: &[i64], row_idx: &[i64]) -> Symbolic {
if n == 0 {
return Symbolic { p: Vec::new(), q: Vec::new(), r: vec![0], lnz: Vec::new() };
}
let (p_btf, mut q_btf, r, nmatch) = btf_order(n, col_ptr, row_idx);
if nmatch < n {
for q in q_btf.iter_mut() {
*q = unflip(*q);
}
}
let nblocks = r.len() - 1;
let mut p = vec![0i64; n];
let mut q = vec![0i64; n];
let mut lnz = vec![0.0f64; nblocks];
for block in 0..nblocks {
let k1 = r[block] as usize;
let k2 = r[block + 1] as usize;
let nk = k2 - k1;
let mut pinv_btf = vec![0usize; n];
for (k, &orig) in p_btf.iter().enumerate() {
pinv_btf[orig as usize] = k;
}
let pblk: Vec<i64> = if nk <= 3 {
(0..nk as i64).collect()
} else {
let mut c_col_ptr = vec![0i64; nk + 1];
let mut c_row_idx: Vec<i64> = Vec::new();
for k in 0..nk {
let oldcol = q_btf[k + k1] as usize;
for &oldrow in &row_idx[col_ptr[oldcol] as usize..col_ptr[oldcol + 1] as usize] {
let newrow = pinv_btf[oldrow as usize];
if newrow >= k1 {
c_row_idx.push((newrow - k1) as i64);
}
}
c_col_ptr[k + 1] = c_row_idx.len() as i64;
}
for k in 0..nk {
let (s, e) = (c_col_ptr[k] as usize, c_col_ptr[k + 1] as usize);
c_row_idx[s..e].sort_unstable();
}
amd_order(nk, &c_col_ptr, &c_row_idx).expect("block C is always valid by construction")
};
for k in 0..nk {
let pk = pblk[k] as usize;
q[k + k1] = q_btf[pk + k1];
p[k + k1] = p_btf[pk + k1];
}
lnz[block] = if nk <= 3 { (nk * (nk + 1) / 2) as f64 } else { (nk * 4) as f64 };
}
Symbolic {
p: p.iter().map(|&x| x as usize).collect(),
q: q.iter().map(|&x| x as usize).collect(),
r: r.iter().map(|&x| x as usize).collect(),
lnz,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn is_permutation(p: &[usize], n: usize) -> bool {
let mut sorted: Vec<usize> = p.to_vec();
sorted.sort_unstable();
sorted == (0..n).collect::<Vec<_>>()
}
#[test]
fn diagonal_matrix() {
let col_ptr = [0, 1, 2, 3];
let row_idx = [0, 1, 2];
let sym = analyze(3, &col_ptr, &row_idx);
assert!(is_permutation(&sym.p, 3));
assert!(is_permutation(&sym.q, 3));
assert_eq!(sym.r, vec![0, 1, 2, 3]);
}
#[test]
fn small_chain() {
let col_ptr = [0, 1, 3, 5, 7, 8];
let row_idx = [1, 0, 2, 1, 3, 2, 4, 3];
let sym = analyze(5, &col_ptr, &row_idx);
assert!(is_permutation(&sym.p, 5));
assert!(is_permutation(&sym.q, 5));
assert_eq!(*sym.r.last().unwrap(), 5);
assert_eq!(sym.r[0], 0);
}
#[test]
fn mesh_bigger_than_natural_threshold() {
let col_ptr = [0, 2, 4, 6, 8, 10];
let row_idx = [1, 4, 0, 2, 1, 3, 2, 4, 0, 3];
let sym = analyze(5, &col_ptr, &row_idx);
assert!(is_permutation(&sym.p, 5));
assert!(is_permutation(&sym.q, 5));
}
}