extern crate num_traits;
#[cfg(test)]
mod tests;
use num_traits::{Bounded, Float, FromPrimitive, NumAssignOps, PrimInt, ToPrimitive};
#[derive(Debug, PartialEq, Clone)]
pub enum Marker {
Unused,
Used,
}
pub fn etree<I: PrimInt + NumAssignOps + Bounded + ToPrimitive + FromPrimitive>(
n: I,
a_p: &[I],
a_i: &[I],
work: &mut [I],
l_nz: &mut [I],
etree: &mut [Option<I>],
) -> Result<I, I> {
for i in 0..n.to_usize().unwrap() {
work[i] = I::zero();
l_nz[i] = I::zero();
etree[i] = None;
if a_p[i] == a_p[i + 1] {
return Err(I::one());
}
}
for j in 0..n.to_usize().unwrap() {
work[j] = I::from_usize(j).unwrap();
for p in a_p[j].to_usize().unwrap()..a_p[j + 1].to_usize().unwrap() {
let mut i = a_i[p].to_usize().unwrap();
if i > j {
return Err(I::one());
}
while work[i].to_usize().unwrap() != j {
if etree[i].is_none() {
etree[i] = Some(I::from_usize(j).unwrap());
}
l_nz[i] += I::one(); work[i] = I::from_usize(j).unwrap();
i = etree[i].unwrap().to_usize().unwrap();
}
}
}
let mut sum_l_nz = I::zero();
for i in 0..n.to_usize().unwrap() {
if sum_l_nz > I::max_value() - l_nz[i] {
return Err(I::from(2).unwrap());
} else {
sum_l_nz += l_nz[i];
}
}
Ok(sum_l_nz)
}
pub fn factor<F: Float + NumAssignOps, I: PrimInt + NumAssignOps + FromPrimitive + ToPrimitive>(
n: I,
a_p: &[I],
a_i: &[I],
a_x: &[F],
l_p: &mut [I],
l_i: &mut [I],
l_x: &mut [F],
d: &mut [F],
d_inv: &mut [F],
l_nz: &[I],
etree: &[Option<I>],
bwork: &mut [Marker],
iwork: &mut [I],
fwork: &mut [F],
) -> Result<I, I> {
let un = n.to_usize().unwrap();
let mut nnz_y: usize;
let mut bidx: usize;
let mut cidx: usize;
let mut next_idx: Option<usize>;
let mut nnz_e: usize;
let mut tmp_idx: usize;
let mut positive_values_in_d = I::zero();
let y_markers = bwork;
let (y_idx, iwork) = iwork.split_at_mut(un);
let (elim_buffer, iwork) = iwork.split_at_mut(un);
let (l_next_space_in_col, _) = iwork.split_at_mut(un);
let y_vals = fwork;
l_p[0] = I::zero();
for i in 0..un {
l_p[i + 1] = l_p[i] + l_nz[i];
y_markers[i] = Marker::Unused;
y_vals[i] = F::zero();
d[i] = F::zero();
l_next_space_in_col[i] = l_p[i];
}
d[0] = a_x[0];
if d[0] == F::zero() {
return Err(I::one());
}
if d[0] > F::zero() {
positive_values_in_d += I::one();
}
d_inv[0] = F::one() / d[0];
for k in 1..un {
nnz_y = 0;
tmp_idx = a_p[k + 1].to_usize().unwrap();
for i in a_p[k].to_usize().unwrap()..tmp_idx {
bidx = a_i[i].to_usize().unwrap();
if bidx == k {
d[k] = a_x[i];
continue;
}
y_vals[bidx] = a_x[i];
next_idx = Some(bidx);
if y_markers[next_idx.unwrap()] == Marker::Unused {
y_markers[next_idx.unwrap()] = Marker::Used; elim_buffer[0] = I::from(next_idx.unwrap()).unwrap(); nnz_e = 1;
next_idx = etree[bidx].map(|next| next.to_usize().unwrap());
while next_idx.is_some() && next_idx.unwrap() < k {
if y_markers[next_idx.unwrap()] == Marker::Used {
break;
}
y_markers[next_idx.unwrap()] = Marker::Used; elim_buffer[nnz_e] = I::from_usize(next_idx.unwrap()).unwrap(); nnz_e += 1;
next_idx = etree[next_idx.unwrap()].map(|next| next.to_usize().unwrap());
}
while nnz_e != 0 {
nnz_e -= 1;
y_idx[nnz_y] = elim_buffer[nnz_e];
nnz_y += 1;
}
}
}
if nnz_y > 0 {
for i in (0..=(nnz_y - 1)).rev() {
cidx = y_idx[i as usize].to_usize().unwrap();
tmp_idx = l_next_space_in_col[cidx].to_usize().unwrap();
let y_vals_cidx = y_vals[cidx];
for j in l_p[cidx].to_usize().unwrap()..tmp_idx {
y_vals[l_i[j].to_usize().unwrap()] -= l_x[j] * y_vals_cidx;
}
l_i[tmp_idx] = I::from_usize(k).unwrap();
l_x[tmp_idx] = y_vals_cidx * d_inv[cidx];
d[k] -= y_vals_cidx * l_x[tmp_idx];
l_next_space_in_col[cidx] += I::one();
y_vals[cidx] = F::zero();
y_markers[cidx] = Marker::Unused;
}
}
if d[k] == F::zero() {
return Err(I::one());
}
if d[k] > F::zero() {
positive_values_in_d += I::one();
}
d_inv[k] = F::one() / d[k];
}
Ok(positive_values_in_d)
}
pub fn solve<F: Float + NumAssignOps, I: PrimInt>(
n: I,
l_p: &[I],
l_i: &[I],
l_x: &[F],
d_inv: &[F],
x: &mut [F],
) {
lsolve(n, l_p, l_i, l_x, x);
for i in 0..n.to_usize().unwrap() {
x[i] *= d_inv[i];
}
ltsolve(n, l_p, l_i, l_x, x);
}
pub fn lsolve<F: Float + NumAssignOps, I: PrimInt>(
n: I,
l_p: &[I],
l_i: &[I],
l_x: &[F],
x: &mut [F],
) {
for i in 0..n.to_usize().unwrap() {
let val = x[i];
for j in l_p[i].to_usize().unwrap()..l_p[i + 1].to_usize().unwrap() {
x[l_i[j].to_usize().unwrap()] -= l_x[j] * val;
}
}
}
pub fn ltsolve<F: Float + NumAssignOps, I: PrimInt>(
n: I,
l_p: &[I],
l_i: &[I],
l_x: &[F],
x: &mut [F],
) {
for i in (0..=n.to_usize().unwrap() - 1).rev() {
let mut val = x[i];
for j in l_p[i].to_usize().unwrap()..l_p[i + 1].to_usize().unwrap() {
val -= l_x[j] * x[l_i[j].to_usize().unwrap()];
}
x[i] = val;
}
}
pub fn factor_solve<
F: Float + NumAssignOps,
I: PrimInt + NumAssignOps + FromPrimitive + ToPrimitive + Clone,
>(
a_n: I,
a_p: &[I],
a_i: &[I],
a_x: &[F],
b: &mut [F],
) -> Result<(), I> {
let un = a_n.to_usize().unwrap();
let l_n = a_n;
let mut etree: Vec<Option<I>> = vec![None; un];
let mut l_nz: Vec<I> = vec![I::zero(); un];
let mut l_p: Vec<I> = vec![I::zero(); un + 1];
let mut d: Vec<F> = vec![F::zero(); un];
let mut d_inv: Vec<F> = vec![F::zero(); un];
let mut iwork: Vec<I> = vec![I::zero(); 3 * un];
let mut bwork: Vec<Marker> = vec![Marker::Unused; un];
let mut fwork: Vec<F> = vec![F::zero(); un];
let sum_l_nz = crate::etree(a_n, a_p, a_i, &mut iwork, &mut l_nz, &mut etree)?;
let mut l_i: Vec<I> = vec![I::zero(); sum_l_nz.to_usize().unwrap()];
let mut l_x: Vec<F> = vec![F::zero(); sum_l_nz.to_usize().unwrap()];
factor(
a_n, a_p, a_i, a_x, &mut l_p, &mut l_i, &mut l_x, &mut d, &mut d_inv, &l_nz, &etree,
&mut bwork, &mut iwork, &mut fwork,
)?;
solve(l_n, &l_p, &l_i, &l_x, &d_inv, b);
Ok(())
}