rsparse/
lib.rs

1//! <span style="color:#c45508">rs</span>parse
2//!
3//! A Rust library for solving sparse linear systems using direct methods.
4//!
5//!
6//! ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/rlado/rsparse/rust.yml) [![Crates.io](https://img.shields.io/crates/d/rsparse)](https://crates.io/crates/rsparse) [![Crates.io](https://img.shields.io/crates/v/rsparse)](https://crates.io/crates/rsparse)
7//!
8//! ---
9//!
10//! ## Data structures
11//! - CSC matrix (`Sprs`)
12//! - Triplet matrix (`Trpl`)
13//!
14//! ## Features
15//! - Convert from dense `[Vec<f64>]` or `Vec<Vec<64>>` matrix to CSC sparse matrix `Sprs`
16//! - Convert from sparse to dense `Vec<Vec<f64>>`
17//! - Convert from a triplet format matrix `Trpl` to CSC `Sprs`
18//! - Sparse matrix addition [C=A+B]
19//! - Sparse matrix multiplication [C=A*B]
20//! - Transpose sparse matrices
21//! - Solve sparse linear systems
22//!
23//! ### Solvers
24//! - **lsolve**: Solve a lower triangular system. Solves Lx=b where x and b are dense.
25//! - **ltsolve**: Solve L’x=b where x and b are dense.
26//! - **usolve**: Solve an upper triangular system. Solves Ux=b where x and b are dense
27//! - **utsolve**: Solve U’x=b where x and b are dense
28//! - **cholsol**: A\b solver using Cholesky factorization. Where A is a defined positive `Sprs` matrix and b is a dense vector
29//! - **lusol**: A\b solver using LU factorization. Where A is a square `Sprs` matrix and b is a dense vector
30//! - **qrsol**: A\b solver using QR factorization. Where A is a rectangular `Sprs` matrix and b is a dense vector
31//!
32//! ## Examples
33//! ### Basic matrix operations
34//! ```rust
35//! // Create a CSC sparse matrix A
36//! let a = rsparse::data::Sprs{
37//!     // Maximum number of entries
38//!     nzmax: 5,
39//!     // number of rows
40//!     m: 3,
41//!     // number of columns
42//!     n: 3,
43//!     // Values
44//!     x: vec![1., 9., 9., 2., 9.],
45//!     // Indices
46//!     i: vec![1, 2, 2, 0, 2],
47//!     // Pointers
48//!     p: vec![0, 2, 3, 5]
49//! };
50//!
51//! // Import the same matrix from a dense structure
52//! let mut a2 = rsparse::data::Sprs::new_from_vec(
53//!     &[
54//!         vec![0., 0., 2.],
55//!         vec![1., 0., 0.],
56//!         vec![9., 9., 9.]
57//!     ]
58//! );
59//!
60//! // Check if they are the same
61//! assert_eq!(a.nzmax, a2.nzmax);
62//! assert_eq!(a.m,a2.m);
63//! assert_eq!(a.n,a2.n);
64//! assert_eq!(a.x,a2.x);
65//! assert_eq!(a.i,a2.i);
66//! assert_eq!(a.p,a2.p);
67//!
68//! // Transform A to dense and print result
69//! println!("\nA");
70//! print_matrix(&a.to_dense());
71//!
72//! // Transpose A
73//! let at = rsparse::transpose(&a);
74//! // Transform to dense and print result
75//! println!("\nAt");
76//! print_matrix(&at.to_dense());
77//!
78//! // B = A + A'
79//! let b = &a + &at;
80//! // Transform to dense and print result
81//! println!("\nB");
82//! print_matrix(&b.to_dense());
83//!
84//! // C = A * B
85//! let c = &a * &b;
86//! // Transform to dense and print result
87//! println!("\nC");
88//! print_matrix(&c.to_dense());
89//!
90//! fn print_matrix(vec: &[Vec<f64>]) {
91//!     for row in vec {
92//!         println!("{:?}", row);
93//!     }
94//! }
95//! ```
96//!
97//! Output:
98//!
99//! ```result
100//! A
101//! 0   0   2
102//! 1   0   0
103//! 9   9   9
104//!
105//! At
106//! 0   1   9
107//! 0   0   9
108//! 2   0   9
109//!
110//! B
111//! 0   1   11
112//! 1   0   9
113//! 11  9   18
114//!
115//! C
116//! 22  18  36
117//! 0   1   11
118//! 108 90  342
119//! ```
120//!
121//!
122//! ### Solve a linear system
123//! ```rust
124//! // Arbitrary A matrix (dense)
125//! let a = [
126//!     vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
127//!     vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
128//!     vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
129//!     vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
130//!     vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
131//!     vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
132//!     vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
133//!     vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
134//! ];
135//!
136//! // Convert A to sparse
137//! let mut a_sparse = rsparse::data::Sprs::new_from_vec(&a);
138//!
139//! // Generate arbitrary b vector
140//! let mut b = [
141//!     0.4377,
142//!     0.7328,
143//!     0.1227,
144//!     0.1817,
145//!     0.2634,
146//!     0.6876,
147//!     0.8711,
148//!     0.4201
149//! ];
150//!
151//! // Known solution:
152//! /*
153//!     0.264678,
154//!     -1.228118,
155//!     -0.035452,
156//!     -0.676711,
157//!     -0.066194,
158//!     0.761495,
159//!     1.852384,
160//!     -0.282992
161//! */
162//!
163//! // A*x=b -> solve for x -> place x in b
164//! rsparse::lusol(&a_sparse, &mut b, 1, 1e-6);
165//! println!("\nX");
166//! println!("{:?}", &b);
167//! ```
168//!
169//! Output:
170//!
171//! ```result
172//! X
173//! [0.2646806068156303, -1.2280777288645675, -0.035491404094236435, -0.6766064748053932, -0.06619898266432682, 0.7615102544801993, 1.8522970972589123, -0.2830302118359591]
174//! ```
175//!
176//! ## Sources
177//! - Davis, T. (2006). Direct Methods for Sparse Linear Systems. Society for Industrial and Applied Mathematics. [https://doi.org/10.1137/1.9780898718881](https://doi.org/10.1137/1.9780898718881)
178//! - [CSparse](https://people.math.sc.edu/Burkardt/c_src/csparse/csparse.html): A Concise Sparse Matrix Package in C
179//!
180//! MIT License
181//! Copyright (c) 2023 Ricard Lado
182
183pub mod data;
184use data::{Nmrc, Sprs, Symb};
185
186#[derive(Copy, Clone, Debug)]
187pub enum Error {
188    /// Cholesky factorization failed (not positive definite)
189    NotPositiveDefinite,
190    /// LU factorization failed (no pivot found)
191    NoPivot,
192}
193
194impl std::fmt::Display for Error {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        match self {
197            Self::NoPivot => write!(f, "Could not find a pivot"),
198            Self::NotPositiveDefinite => write!(f, "Could not complete Cholesky factorization. Please provide a positive definite matrix"),
199        }
200    }
201}
202
203impl std::error::Error for Error {}
204
205// --- Public functions --------------------------------------------------------
206
207/// C = alpha * A + beta * B
208///
209/// # Example:
210/// ```rust
211/// let a = [
212///     vec![2., 2., 4., 4., 1.],
213///     vec![3., 4., 5., 8., 3.],
214///     vec![2., 6., 3., 9., 3.],
215///     vec![5., 7., 6., 7., 1.],
216///     vec![7., 1., 8., 9., 2.],
217/// ];
218/// let mut a_sparse = rsparse::data::Sprs::new();
219/// a_sparse.from_vec(&a);
220///
221/// let b = [
222///     vec![8., 8., 6., 6., 2.],
223///     vec![4., 9., 7., 5., 9.],
224///     vec![2., 3., 8., 4., 1.],
225///     vec![4., 7., 6., 8., 9.],
226///     vec![9., 1., 8., 7., 1.],
227/// ];
228/// let mut b_sparse = rsparse::data::Sprs::new();
229/// b_sparse.from_vec(&b);
230///
231/// let r = [
232///     vec![10., 10., 10., 10., 3.],
233///     vec![7., 13., 12., 13., 12.],
234///     vec![4., 9., 11., 13., 4.],
235///     vec![9., 14., 12., 15., 10.],
236///     vec![16., 2., 16., 16., 3.],
237/// ];
238/// let mut r_sparse = rsparse::data::Sprs::new();
239/// r_sparse.from_vec(&r);
240///
241/// // Check as dense
242/// assert_eq!(rsparse::add(&a_sparse, &b_sparse, 1., 1.).to_dense(), r);
243/// ```
244///
245pub fn add(a: &Sprs, b: &Sprs, alpha: f64, beta: f64) -> Sprs {
246    let mut nz = 0;
247    let m = a.m;
248    let n = b.n;
249    let anz = a.p[a.n] as usize;
250    let bnz = b.p[n] as usize;
251    let mut w = vec![0; m];
252    let mut x = vec![0.0; m];
253    let mut c = Sprs::zeros(m, n, anz + bnz);
254
255    for j in 0..n {
256        c.p[j] = nz as isize; // column j of C starts here
257        nz = scatter(a, j, alpha, &mut w[..], &mut x[..], j + 1, &mut c, nz); // alpha*A(:,j)
258        nz = scatter(b, j, beta, &mut w[..], &mut x[..], j + 1, &mut c, nz); // beta*B(:,j)
259
260        for p in c.p[j] as usize..nz {
261            c.x[p] = x[c.i[p]];
262        }
263    }
264    c.p[n] = nz as isize; // finalize the last column of C
265
266    c.quick_trim();
267
268    c
269}
270
271/// L = chol (A, [Pinv parent cp]), Pinv is optional
272///
273/// See: `schol(...)`
274///
275pub fn chol(a: &Sprs, s: &mut Symb) -> Result<Nmrc, Error> {
276    let mut top;
277    let mut d;
278    let mut lki;
279    let mut i;
280    let n = a.n;
281
282    let mut n_mat = Nmrc::new();
283    let mut w = vec![0; 3 * n];
284    let ws = n; // pointer of w
285    let wc = 2 * n; // pointer of w
286    let mut x = vec![0.; n];
287
288    let c = match s.pinv {
289        Some(_) => symperm(a, &s.pinv),
290        None => a.clone(),
291    };
292    n_mat.l = Sprs::zeros(n, n, s.cp[n] as usize);
293    for k in 0..n {
294        // --- Nonzero pattern of L(k,:) ------------------------------------
295        w[wc + k] = s.cp[k]; // column k of L starts here
296        n_mat.l.p[k] = w[wc + k];
297        x[k] = 0.; // x (0:k) is now zero
298        w[k] = k as isize; // mark node k as visited
299        top = ereach(&c, k, &s.parent[..], ws, &mut w[..], &mut x[..], n); // find row k of L
300        d = x[k]; // d = C(k,k)
301        x[k] = 0.; // clear workspace for k+1st iteration
302
303        // --- Triangular solve ---------------------------------------------
304        while top < n {
305            // solve L(0:k-1,0:k-1) * x = C(:,k)
306            i = w[ws + top] as usize; // s [top..n-1] is pattern of L(k,:)
307            lki = x[i] / n_mat.l.x[n_mat.l.p[i] as usize]; // L(k,i) = x (i) / L(i,i)
308            x[i] = 0.; // clear workspace for k+1st iteration
309            for p in (n_mat.l.p[i] + 1)..w[wc + i] as isize {
310                x[n_mat.l.i[p as usize]] -= n_mat.l.x[p as usize] * lki;
311            }
312            d -= lki * lki; // d = d - L(k,i)*L(k,i)
313            let p = w[wc + i] as usize;
314            w[wc + i] += 1;
315            n_mat.l.i[p] = k; // store L(k,i) in column i
316            n_mat.l.x[p] = lki;
317
318            // increment statement
319            top += 1;
320        }
321        // --- Compute L(k,k) -----------------------------------------------
322        if d <= 0. {
323            // not pos def
324            return Err(Error::NotPositiveDefinite);
325        }
326        let p = w[wc + k];
327        w[wc + k] += 1;
328        n_mat.l.i[p as usize] = k; // store L(k,k) = sqrt (d) in column k
329        n_mat.l.x[p as usize] = f64::powf(d, 0.5);
330    }
331    n_mat.l.p[n] = s.cp[n]; // finalize L
332
333    Ok(n_mat)
334}
335
336/// A\b solver using Cholesky factorization.
337///
338/// x=A\b where A is symmetric positive definite; b overwritten with solution
339///
340/// # Parameters:
341///
342/// Input, i8 ORDER:
343/// - -1:natural,
344/// - 0:Cholesky,
345/// - 1:LU,
346/// - 2:QR
347///
348/// # Example:
349/// ```rust
350/// let c = [
351///     vec![5.0, 0.0, 0.0, 0.0, 0.0],
352///     vec![0.0, 5.0, 0.0, 0.0, 0.017856],
353///     vec![0.0, 0.0, 5.0, 0.0, 0.0],
354///     vec![0.0, 0.0, 0.0, 5.0, 0.479746],
355///     vec![0.0, 0.017856, 0.0, 0.479746, 5.0]
356/// ];
357/// let mut c_sparse = rsparse::data::Sprs::new();
358/// c_sparse.from_vec(&c);
359///
360/// let mut b = [
361///     0.2543,
362///     0.8143,
363///     0.2435,
364///     0.9293,
365///     0.3500
366/// ];
367///
368/// rsparse::cholsol(&mut c_sparse, &mut b, 0);
369/// println!("\nX");
370/// println!("{:?}", &b);
371/// ```
372///
373pub fn cholsol(a: &Sprs, b: &mut [f64], order: i8) -> Result<(), Error> {
374    let n = a.n;
375    let mut s = schol(a, order); // ordering and symbolic analysis
376    let n_mat = chol(a, &mut s)?; // numeric Cholesky factorization
377    let mut x = vec![0.; n];
378
379    ipvec(n, &s.pinv, b, &mut x[..]); // x = P*b
380    lsolve(&n_mat.l, &mut x); // x = L\x
381    ltsolve(&n_mat.l, &mut x); // x = L'\x
382    pvec(n, &s.pinv, &x[..], &mut b[..]); // b = P'*x
383
384    Ok(())
385}
386
387/// Generalized A times X Plus Y
388///
389/// r = A * x + y
390///
391/// # Example
392/// ```rust
393/// let a = [
394///     vec![0., 0., 2.],
395///     vec![1., 0., 0.],
396///     vec![9., 9., 9.]
397/// ];
398/// let mut a_sparse = rsparse::data::Sprs::new();
399/// a_sparse.from_vec(&a);
400///
401/// let x = vec![1., 2., 3.];
402/// let y = vec![3., 2., 1.];
403///
404/// assert_eq!(rsparse::gaxpy(&a_sparse, &x, &y), vec![9., 3., 55.]);
405/// ```
406///
407pub fn gaxpy(a_mat: &Sprs, x: &[f64], y: &[f64]) -> Vec<f64> {
408    let mut r = y.to_vec();
409    for (j, &x_j) in x.iter().enumerate().take(a_mat.n) {
410        for p in a_mat.p[j]..a_mat.p[j + 1] {
411            let p_u = p as usize;
412            r[a_mat.i[p_u]] += a_mat.x[p_u] * x_j;
413        }
414    }
415
416    r
417}
418
419/// Solves a lower triangular system. Solves Lx=b. Where x and b are dense.
420///
421/// The lsolve function assumes that the diagonal entry of L is always present
422/// and is the first entry in each column. Otherwise, the row indices in each
423/// column of L can appear in any order.
424///
425/// On input, X contains the right hand side, and on output, the solution.
426///
427/// # Example:
428/// ```rust
429/// let l = [
430///     vec![1.0000,  0.,      0.,       0.,       0.,       0.,       0.,       0.,       0.,       0.],
431///     vec![0.4044,  1.0000,  0.,       0.,       0.,       0.,       0.,       0.,       0.,       0.],
432///     vec![0.3465,  0.0122,  1.0000,   0.,       0.,       0.,       0.,       0.,       0.,       0.],
433///     vec![0.7592, -0.3591, -0.1154,   1.0000,   0.,       0.,       0.,       0.,       0.,       0.],
434///     vec![0.6868,  0.1135,  0.2113,   0.6470,   1.0000,   0.,       0.,       0.,       0.,       0.],
435///     vec![0.7304, -0.1453,  0.1755,   0.0585,  -0.7586,   1.0000,   0.,       0.,       0.,       0.],
436///     vec![0.8362,  0.0732,  0.7601,  -0.1107,   0.1175,  -0.5406,   1.0000,   0.,       0.,       0.],
437///     vec![0.0390,  0.8993,  0.3428,   0.1639,   0.4246,  -0.5861,   0.7790,   1.0000,   0.,       0.],
438///     vec![0.8079, -0.4437,  0.8271,   0.2583,  -0.2238,   0.0544,   0.2360,  -0.7387,   1.0000,   0.],
439///     vec![0.1360,  0.9532, -0.1212,  -0.1943,   0.4311,   0.1069,   0.3717,   0.7176,  -0.6053,   1.0000]
440/// ];
441/// let mut l_sparse = rsparse::data::Sprs::new();
442/// l_sparse.from_vec(&l);
443///
444/// let mut b = [
445///     0.8568,
446///     0.3219,
447///     0.9263,
448///     0.4635,
449///     0.8348,
450///     0.1339,
451///     0.8444,
452///     0.7000,
453///     0.7947,
454///     0.5552
455/// ];
456///
457/// rsparse::lsolve(&l_sparse, &mut b);
458/// ```
459///
460pub fn lsolve(l: &Sprs, x: &mut [f64]) {
461    for j in 0..l.n {
462        x[j] /= l.x[l.p[j] as usize];
463        for p in (l.p[j] + 1) as usize..l.p[j + 1] as usize {
464            x[l.i[p]] -= l.x[p] * x[j];
465        }
466    }
467}
468
469/// Solves L'x=b. Where x and b are dense.
470///
471/// On input, X contains the right hand side, and on output, the solution.
472///
473/// # Example:
474/// ```rust
475/// let l = [
476///     vec![1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
477///     vec![0.3376, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
478///     vec![0.8260, 0.2762, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000],
479///     vec![0.5710, 0.1764, 0.5430, 1.0000, 0.0000, 0.0000, 0.0000],
480///     vec![0.9194, 0.3583, 0.6850, 0.6594, 1.0000, 0.0000, 0.0000],
481///     vec![0.2448, 0.5015, -0.2830, 0.2239, 0.4723, 1.0000, 0.0000],
482///     vec![0.2423, 0.2332, -0.8355, 0.7522, -0.3700, 0.1985, 1.0000]
483/// ];
484/// let mut l_sparse = rsparse::data::Sprs::new();
485/// l_sparse.from_vec(&l);
486///
487/// let mut b = [
488///     0.444841,
489///     0.528773,
490///     0.988345,
491///     0.097749,
492///     0.996166,
493///     0.068040,
494///     0.844511
495/// ];
496///
497/// rsparse::ltsolve(&l_sparse, &mut b);
498/// ```
499
500///
501pub fn ltsolve(l: &Sprs, x: &mut [f64]) {
502    for j in (0..l.n).rev() {
503        for p in (l.p[j] + 1) as usize..l.p[j + 1] as usize {
504            x[j] -= l.x[p] * x[l.i[p]];
505        }
506        x[j] /= l.x[l.p[j] as usize];
507    }
508}
509
510/// (L,U,Pinv) = lu(A, (Q lnz unz)). lnz and unz can be guess
511///
512/// See: `sqr(...)`
513///
514pub fn lu(a: &Sprs, s: &mut Symb, tol: f64) -> Result<Nmrc, Error> {
515    let n = a.n;
516    let mut col;
517    let mut top;
518    let mut ipiv;
519    let mut a_f;
520    let mut t;
521    let mut pivot;
522    let mut x = vec![0.; n];
523    let mut xi = vec![0; 2 * n];
524    let mut n_mat = Nmrc {
525        l: Sprs::zeros(n, n, s.lnz), // initial L and U
526        u: Sprs::zeros(n, n, s.unz),
527        pinv: Some(vec![0; n]),
528        b: Vec::new(),
529    };
530
531    x[0..n].fill(0.); // clear workspace
532    n_mat.pinv.as_mut().unwrap()[0..n].fill(-1); // no rows pivotal yet
533    n_mat.l.p[0..n + 1].fill(0); // no cols of L yet
534
535    s.lnz = 0;
536    s.unz = 0;
537    for k in 0..n {
538        // compute L(:,k) and U(:,k)
539        // --- Triangular solve ---------------------------------------------
540        n_mat.l.p[k] = s.lnz as isize; // L(:,k) starts here
541        n_mat.u.p[k] = s.unz as isize; // L(:,k) starts here
542
543        // Resize L and U
544        if s.lnz + n > n_mat.l.nzmax {
545            let nsz = 2 * n_mat.l.nzmax + n;
546            n_mat.l.nzmax = nsz;
547            n_mat.l.i.resize(nsz, 0);
548            n_mat.l.x.resize(nsz, 0.);
549        }
550        if s.unz + n > n_mat.u.nzmax {
551            let nsz = 2 * n_mat.u.nzmax + n;
552            n_mat.u.nzmax = nsz;
553            n_mat.u.i.resize(nsz, 0);
554            n_mat.u.x.resize(nsz, 0.);
555        }
556
557        col = s.q.as_ref().map_or(k, |q| q[k] as usize);
558        top = splsolve(&mut n_mat.l, a, col, &mut xi[..], &mut x[..], &n_mat.pinv); // x = L\A(:,col)
559
560        // --- Find pivot ---------------------------------------------------
561        ipiv = -1;
562        a_f = -1.;
563        for &i in xi[top..n].iter() {
564            let i = i as usize;
565            if n_mat.pinv.as_ref().unwrap()[i] < 0 {
566                // row i is not pivotal
567                t = f64::abs(x[i]);
568                if t > a_f {
569                    a_f = t; // largest pivot candidate so far
570                    ipiv = i as isize;
571                }
572            } else {
573                // x(i) is the entry U(Pinv[i],k)
574                n_mat.u.i[s.unz] = n_mat.pinv.as_ref().unwrap()[i] as usize;
575                n_mat.u.x[s.unz] = x[i];
576                s.unz += 1;
577            }
578        }
579        if ipiv == -1 || a_f <= 0. {
580            return Err(Error::NoPivot);
581        }
582        if n_mat.pinv.as_ref().unwrap()[col] < 0 && f64::abs(x[col]) >= a_f * tol {
583            ipiv = col as isize;
584        }
585
586        // --- Divide by pivot ----------------------------------------------
587        pivot = x[ipiv as usize]; // the chosen pivot
588        n_mat.u.i[s.unz] = k; // last entry in U(:,k) is U(k,k)
589        n_mat.u.x[s.unz] = pivot;
590        s.unz += 1;
591        n_mat.pinv.as_mut().unwrap()[ipiv as usize] = k as isize; // ipiv is the kth pivot row
592        n_mat.l.i[s.lnz] = ipiv as usize; // first entry in L(:,k) is L(k,k) = 1
593        n_mat.l.x[s.lnz] = 1.;
594        s.lnz += 1;
595        for &i in xi[top..n].iter() {
596            let i = i as usize;
597            if n_mat.pinv.as_ref().unwrap()[i] < 0 {
598                // x(i) is an entry in L(:,k)
599                n_mat.l.i[s.lnz] = i; // save unpermuted row in L
600                n_mat.l.x[s.lnz] = x[i] / pivot; // scale pivot column
601                s.lnz += 1
602            }
603            x[i] = 0.; // x [0..n-1] = 0 for next k
604        }
605    }
606    // --- Finalize L and U -------------------------------------------------
607    n_mat.l.p[n] = s.lnz as isize;
608    n_mat.u.p[n] = s.unz as isize;
609    // fix row indices of L for final Pinv
610    for p in 0..s.lnz {
611        n_mat.l.i[p] = n_mat.pinv.as_ref().unwrap()[n_mat.l.i[p]] as usize;
612    }
613    n_mat.l.quick_trim();
614    n_mat.u.quick_trim();
615
616    Ok(n_mat)
617}
618
619/// A\b solver using LU factorization.
620///
621/// x=A\b where A is asymmetric; b (dense) overwritten with solution
622///
623/// Input, i8 ORDER:
624/// - -1:natural,
625/// - 0:Cholesky,
626/// - 1:LU,
627/// - 2:QR
628///
629/// # Example:
630/// ```rust
631/// // Arbitrary A matrix (dense)
632/// let a = [
633///     vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
634///     vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
635///     vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
636///     vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
637///     vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
638///     vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
639///     vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
640///     vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
641/// ];
642///
643/// // Convert A to sparse
644/// let mut a_sparse = rsparse::data::Sprs::new();
645/// a_sparse.from_vec(&a);
646///
647/// // Generate arbitrary b vector
648/// let mut b = [
649///     0.4377,
650///     0.7328,
651///     0.1227,
652///     0.1817,
653///     0.2634,
654///     0.6876,
655///     0.8711,
656///     0.4201
657/// ];
658///
659/// // A*x=b -> solve for x -> place x in b
660/// rsparse::lusol(&a_sparse, &mut b, 1, 1e-6);
661/// println!("\nX");
662/// println!("{:?}", &b);
663/// ```
664
665///
666pub fn lusol(a: &Sprs, b: &mut [f64], order: i8, tol: f64) -> Result<(), Error> {
667    let mut x = vec![0.; a.n];
668    let mut s;
669    s = sqr(a, order, false); // ordering and symbolic analysis
670    let n = lu(a, &mut s, tol)?; // numeric LU factorization
671
672    ipvec(a.n, &n.pinv, b, &mut x[..]); // x = P*b
673    lsolve(&n.l, &mut x); // x = L\x
674    usolve(&n.u, &mut x[..]); // x = U\x
675    ipvec(a.n, &s.q, &x[..], &mut b[..]); // b = Q*x
676    Ok(())
677}
678
679/// C = A * B
680///
681/// # Example
682/// ```rust
683/// let a = [
684///     vec![0., 0., 2.],
685///     vec![1., 0., 0.],
686///     vec![9., 9., 9.]
687/// ];
688/// let mut a_sparse = rsparse::data::Sprs::new();
689/// a_sparse.from_vec(&a);
690///
691/// let b = [
692///     vec![0., 0., 2.],
693///     vec![1., 0., 0.],
694///     vec![9., 1., 9.]
695/// ];
696/// let mut b_sparse = rsparse::data::Sprs::new();
697/// b_sparse.from_vec(&b);
698///
699/// let c = rsparse::multiply(&a_sparse, &b_sparse);
700///
701/// assert_eq!(
702///     c.to_dense(),
703///     vec![vec![18., 2., 18.], vec![0., 0., 2.], vec![90., 9., 99.]]
704/// );
705/// ```
706///
707pub fn multiply(a: &Sprs, b: &Sprs) -> Sprs {
708    let mut nz = 0;
709    let mut w = vec![0; a.m];
710    let mut x = vec![0.0; a.m];
711    let mut c = Sprs::zeros(a.m, b.n, 2 * (a.p[a.n] + b.p[b.n]) as usize + a.m);
712
713    for j in 0..b.n {
714        if nz + a.m > c.nzmax {
715            // change the max # of entries of C
716            let nsz = 2 * c.nzmax + a.m;
717            c.nzmax = nsz;
718            c.i.resize(nsz, 0);
719            c.x.resize(nsz, 0.);
720        }
721        c.p[j] = nz as isize; // column j of C starts here
722        for p in b.p[j]..b.p[j + 1] {
723            nz = scatter(
724                a,
725                b.i[p as usize],
726                b.x[p as usize],
727                &mut w[..],
728                &mut x[..],
729                j + 1,
730                &mut c,
731                nz,
732            );
733        }
734        for p in c.p[j] as usize..nz {
735            c.x[p] = x[c.i[p]];
736        }
737    }
738    c.p[b.n] = nz as isize;
739    c.quick_trim();
740
741    c
742}
743
744/// Computes the 1-norm of a sparse matrix
745///
746/// 1-norm of a sparse matrix = max (sum (abs (A))), largest column sum
747///
748/// # Example:
749/// ```rust
750/// let a = [
751///     vec![0.947046, 0.107385, 0.414713, 0.829759, 0.184515, 0.915179],
752///     vec![0.731729, 0.256865, 0.57665, 0.808786, 0.975115, 0.853119],
753///     vec![0.241559, 0.76349, 0.561508, 0.726358, 0.418349, 0.089947],
754///     vec![0.056867, 0.612998, 0.933199, 0.834696, 0.831912, 0.077548],
755///     vec![0.080079, 0.350149, 0.930013, 0.482766, 0.808863, 0.152294],
756///     vec![0.486605, 0.215417, 0.446327, 0.737579, 0.141593, 0.472575]
757/// ];
758///
759/// let mut a_sparse = rsparse::data::Sprs::new();
760/// a_sparse.from_vec(&a);
761///
762/// assert!(f64::abs(rsparse::norm(&a_sparse) - 4.4199) < 1e-3);
763/// ```
764///
765pub fn norm(a: &Sprs) -> f64 {
766    let mut norm_r = 0.;
767    for j in 0..a.n {
768        let mut s = 0.;
769        for p in a.p[j] as usize..a.p[j + 1] as usize {
770            s += a.x[p].abs();
771        }
772        norm_r = f64::max(norm_r, s);
773    }
774
775    norm_r
776}
777
778/// Sparse QR factorization (V,beta,p,R) = qr (A)
779///
780/// See: `sqr(...)`
781///
782pub fn qr(a: &Sprs, s: &Symb) -> Nmrc {
783    let mut p1;
784    let mut top;
785    let mut col;
786    let mut i;
787
788    let m = a.m;
789    let n = a.n;
790    let mut vnz = s.lnz;
791    let mut rnz = s.unz;
792
793    let mut v = Sprs::zeros(s.m2, n, vnz); // equivalent to n_mat.l
794    let mut r = Sprs::zeros(s.m2, n, rnz); // equivalent to n_mat.u
795
796    let leftmost_p = m + n; // pointer of s.pinv
797    let mut w = vec![0; s.m2 + n];
798    let ws = s.m2; // pointer of w // size n
799    let mut x = vec![0.; s.m2];
800    let mut n_mat = Nmrc::new();
801    let mut beta = vec![0.; n]; // equivalent to n_mat.b
802
803    w[0..s.m2].fill(-1); // clear w, to mark nodes
804    rnz = 0;
805    vnz = 0;
806    for k in 0..n {
807        // compute V and R
808        r.p[k] = rnz as isize; // R(:,k) starts here
809        v.p[k] = vnz as isize; // V(:,k) starts here
810        p1 = vnz;
811        w[k] = k as isize; // add V(k,k) to pattern of V
812        v.i[vnz] = k;
813        vnz += 1;
814        top = n;
815        col = s.q.as_ref().map_or(k, |q| q[k] as usize);
816
817        for p in a.p[col]..a.p[col + 1] {
818            // find R(:,k) pattern
819            i = s.pinv.as_ref().unwrap()[leftmost_p + a.i[p as usize]]; // i = min(find(A(i,Q)))
820            let mut len = 0;
821            while w[i as usize] != k as isize {
822                // traverse up to k
823                w[ws + len] = i;
824                len += 1;
825                w[i as usize] = k as isize;
826                // increment statement
827                i = s.parent[i as usize];
828            }
829            for j in 1..(len + 1) {
830                top -= 1;
831                w[ws + top] = w[ws + len - j]; // push path on stack
832            }
833            i = s.pinv.as_ref().unwrap()[a.i[p as usize]]; // i = permuted row of A(:,col)
834            x[i as usize] = a.x[p as usize]; // x (i) = A(.,col)
835            if i > k as isize && w[i as usize] < k as isize {
836                // pattern of V(:,k) = x (k+1:m)
837                v.i[vnz] = i as usize; // add i to pattern of V(:,k)
838                vnz += 1;
839                w[i as usize] = k as isize;
840            }
841        }
842        for p in top..n {
843            // for each i in pattern of R(:,k)
844            i = w[ws + p]; // R(i,k) is nonzero
845            happly(&v, i as usize, beta[i as usize], &mut x[..]); // apply (V(i),Beta(i)) to x
846            r.i[rnz] = i as usize; // R(i,k) = x(i)
847            r.x[rnz] = x[i as usize];
848            rnz += 1;
849            x[i as usize] = 0.;
850            if s.parent[i as usize] == k as isize {
851                vnz = scatter_no_x(i as usize, &mut w[..], k, &mut v, vnz);
852            }
853        }
854        for p in p1..vnz {
855            // gather V(:,k) = x
856            v.x[p] = x[v.i[p]];
857            x[v.i[p]] = 0.;
858        }
859        r.i[rnz] = k; // R(k,k) = norm (x)
860        r.x[rnz] = house(&mut v.x[..], Some(p1), &mut beta[..], Some(k), vnz - p1); // [v,beta]=house(x)
861        rnz += 1;
862    }
863    r.p[n] = rnz as isize; // finalize R
864    v.p[n] = vnz as isize; // finalize V
865
866    n_mat.l = v;
867    n_mat.u = r;
868    n_mat.b = beta;
869
870    n_mat
871}
872
873/// A\b solver using QR factorization.
874///
875/// x=A\b where A can be rectangular; b overwritten with solution
876///
877/// # Parameters:
878///
879/// Input, i8 ORDER:
880/// - -1:natural,
881/// - 0:Cholesky,
882/// - 1:LU,
883/// - 2:QR
884///
885/// # Example:
886/// ```rust
887/// // Arbitrary A matrix (dense)
888/// let a = [
889///     vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
890///     vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
891///     vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
892///     vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
893///     vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
894///     vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
895///     vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
896///     vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
897/// ];
898///
899/// // Convert A to sparse
900/// let mut a_sparse = rsparse::data::Sprs::new();
901/// a_sparse.from_vec(&a);
902///
903/// // Generate arbitrary b vector
904/// let mut b = [
905///     0.4377,
906///     0.7328,
907///     0.1227,
908///     0.1817,
909///     0.2634,
910///     0.6876,
911///     0.8711,
912///     0.4201
913/// ];
914///
915/// // A*x=b -> solve for x -> place x in b
916/// rsparse::qrsol(&a_sparse, &mut b, 2);
917/// println!("\nX");
918/// println!("{:?}", &b);
919/// ```
920///
921pub fn qrsol(a: &Sprs, b: &mut [f64], order: i8) {
922    let n = a.n;
923    let m = a.m;
924
925    if m >= n {
926        let s = sqr(a, order, true); // ordering and symbolic analysis
927        let n_mat = qr(a, &s); // numeric QR factorization
928        let mut x = vec![0.; s.m2];
929
930        ipvec(m, &s.pinv, b, &mut x[..]); // x(0:m-1) = P*b(0:m-1)
931        for k in 0..n {
932            // apply Householder refl. to x
933            happly(&n_mat.l, k, n_mat.b[k], &mut x[..]);
934        }
935        usolve(&n_mat.u, &mut x[..]); // x = R\x
936        ipvec(n, &s.q, &x[..], &mut b[..]); // b(0:n-1) = Q*x (permutation)
937    } else {
938        let at = transpose(a); // Ax=b is underdetermined
939        let s = sqr(&at, order, true); // ordering and symbolic analysis
940        let n_mat = qr(&at, &s); // numeric QR factorization of A'
941        let mut x = vec![0.; s.m2];
942
943        pvec(m, &s.q, b, &mut x[..]); // x(0:m-1) = Q'*b (permutation)
944        utsolve(&n_mat.u, &mut x[..]); // x = R'\x
945        for k in (0..m).rev() {
946            happly(&n_mat.l, k, n_mat.b[k], &mut x[..]);
947        }
948        pvec(n, &s.pinv, &x[..], &mut b[..]); // b (0:n-1) = P'*x
949    }
950}
951
952/// Ordering and symbolic analysis for a Cholesky factorization
953///
954/// # Parameters:
955///
956/// Input, i8 ORDER:
957/// - -1:natural,
958/// - 0:Cholesky,
959/// - 1:LU,
960/// - 2:QR
961///
962pub fn schol(a: &Sprs, order: i8) -> Symb {
963    let n = a.n;
964    let mut s = Symb::new(); // allocate symbolic analysis
965    let p = amd(a, order); // P = amd(A+A'), or natural
966    s.pinv = pinvert(&p, n); // find inverse permutation
967    drop(p);
968    let c_mat = symperm(a, &s.pinv); // C = spones(triu(A(P,P)))
969    s.parent = etree(&c_mat, false); // find e tree of C
970    let post = post(n, &s.parent[..]); // postorder the etree
971    let mut c = counts(&c_mat, &s.parent[..], &post[..], false); // find column counts of chol(C)
972    drop(post);
973    drop(c_mat);
974    s.cp = vec![0; n + 1]; // find column pointers for L
975    s.unz = cumsum(&mut s.cp[..], &mut c[..], n);
976    s.lnz = s.unz;
977    drop(c);
978
979    s
980}
981
982/// Scalar plus sparse matrix. C = alpha + A
983///
984/// # Example:
985/// ```rust
986/// let a = [
987///     vec![8., 8., 6., 6., 2.],
988///     vec![4., 9., 7., 5., 9.],
989///     vec![2., 3., 8., 4., 1.],
990///     vec![4., 7., 6., 8., 9.],
991///     vec![9., 1., 8., 7., 1.],
992/// ];
993/// let mut a_sparse = rsparse::data::Sprs::new();
994/// a_sparse.from_vec(&a);
995///
996/// let r = [
997///     vec![10., 10., 8., 8., 4.],
998///     vec![6., 11., 9., 7., 11.],
999///     vec![4., 5., 10., 6., 3.],
1000///     vec![6., 9., 8., 10., 11.],
1001///     vec![11., 3., 10., 9., 3.],
1002/// ];
1003/// let mut r_sparse = rsparse::data::Sprs::new();
1004/// r_sparse.from_vec(&r);
1005///
1006/// // Add 2
1007/// assert_eq!(
1008///     rsparse::scpmat(2., &a_sparse).to_dense(),
1009///     r_sparse.to_dense()
1010/// );
1011/// ```
1012///
1013pub fn scpmat(alpha: f64, a: &Sprs) -> Sprs {
1014    let mut c = Sprs::new();
1015    c.m = a.m;
1016    c.n = a.n;
1017    c.nzmax = a.nzmax;
1018    c.p = a.p.clone();
1019    c.i = a.i.clone();
1020    c.x = a.x.iter().map(|x| x + alpha).collect();
1021
1022    c
1023}
1024
1025/// Scalar times sparse matrix. C = alpha * A
1026///
1027/// # Example:
1028/// ```rust
1029/// let a = [
1030///     vec![8., 8., 6., 6., 2.],
1031///     vec![4., 9., 7., 5., 9.],
1032///     vec![2., 3., 8., 4., 1.],
1033///     vec![4., 7., 6., 8., 9.],
1034///     vec![9., 1., 8., 7., 1.],
1035/// ];
1036/// let mut a_sparse = rsparse::data::Sprs::new();
1037/// a_sparse.from_vec(&a);
1038///
1039/// let r = [
1040///     vec![16., 16., 12., 12., 4.],
1041///     vec![8., 18., 14., 10., 18.],
1042///     vec![4., 6., 16., 8., 2.],
1043///     vec![8., 14., 12., 16., 18.],
1044///     vec![18., 2., 16., 14., 2.],
1045/// ];
1046/// let mut r_sparse = rsparse::data::Sprs::new();
1047/// r_sparse.from_vec(&r);
1048///
1049/// // Multiply a by 2
1050/// assert_eq!(
1051///     rsparse::scxmat(2., &a_sparse).to_dense(),
1052///     r_sparse.to_dense()
1053/// );
1054/// ```
1055///
1056pub fn scxmat(alpha: f64, a: &Sprs) -> Sprs {
1057    let mut c = Sprs::new();
1058    c.m = a.m;
1059    c.n = a.n;
1060    c.nzmax = a.nzmax;
1061    c.p = a.p.clone();
1062    c.i = a.i.clone();
1063    c.x = a.x.iter().map(|x| x * alpha).collect();
1064
1065    c
1066}
1067
1068/// Print a sparse matrix
1069///
1070pub fn sprs_print(a: &Sprs, brief: bool) {
1071    let m = a.m;
1072    let n = a.n;
1073    let nzmax = a.nzmax;
1074
1075    println!(
1076        "{}-by-{}, nzmax: {} nnz: {}, 1-norm: {}",
1077        m,
1078        n,
1079        nzmax,
1080        a.p[n],
1081        norm(a)
1082    );
1083    for j in 0..n {
1084        println!(
1085            "      col {} : locations {} to {}",
1086            j,
1087            a.p[j],
1088            a.p[j + 1] - 1
1089        );
1090        for p in a.p[j]..a.p[j + 1] {
1091            println!("            {} : {}", a.i[p as usize], a.x[p as usize]);
1092            if brief && p > 20 {
1093                println!("  ...");
1094                return;
1095            }
1096        }
1097    }
1098}
1099
1100/// Symbolic analysis for QR or LU
1101///
1102/// Input, i8 ORDER:
1103/// - -1:natural,
1104/// - 0:Cholesky,
1105/// - 1:LU,
1106/// - 2:QR
1107///
1108pub fn sqr(a: &Sprs, order: i8, qr: bool) -> Symb {
1109    let mut s = Symb::new();
1110    let pst;
1111
1112    s.q = amd(a, order); // fill-reducing ordering
1113    if qr {
1114        // QR symbolic analysis
1115        let c = if order >= 0 {
1116            permute(a, &None, &s.q)
1117        } else {
1118            a.clone()
1119        };
1120        s.parent = etree(&c, true); // etree of C'*C, where C=A(:,Q)
1121        pst = post(a.n, &s.parent[..]);
1122        s.cp = counts(&c, &s.parent[..], &pst[..], true); // col counts chol(C'*C)
1123        s.pinv = vcount(&c, &s.parent[..], &mut s.m2, &mut s.lnz);
1124        s.unz = 0;
1125        for k in 0..a.n {
1126            s.unz += s.cp[k] as usize;
1127        }
1128    } else {
1129        s.unz = 4 * a.p[a.n] as usize + a.n; // for LU factorization only
1130        s.lnz = s.unz; // guess nnz(L) and nnz(U)
1131    }
1132
1133    s
1134}
1135
1136/// C = A'
1137///
1138/// The algorithm for transposing a sparse matrix (C = A^T) it can be viewed not
1139/// just as a linear algebraic function but as a method for converting a
1140/// compressed-column sparse matrix into a compressed-row sparse matrix as well.
1141/// The algorithm computes the row counts of A, computes the cumulative sum to
1142/// obtain the row pointers, and then iterates over each nonzero entry in A,
1143/// placing the entry in its appropriate row vector. If the resulting sparse
1144/// matrix C is interpreted as a matrix in compressed-row form, then C is equal
1145/// to A, just in a different format. If C is viewed as a compressed-column
1146/// matrix, then C contains A^T.
1147///
1148/// # Example
1149/// ```rust
1150/// let a = [
1151///     vec![2.1615, 2.0044, 2.1312, 0.8217, 2.2074],
1152///     vec![2.2828, 1.9089, 1.9295, 0.9412, 2.0017],
1153///     vec![2.2156, 1.8776, 1.9473, 1.0190, 1.8352],
1154///     vec![1.0244, 0.8742, 0.9177, 0.7036, 0.7551],
1155///     vec![2.0367, 1.5642, 1.4313, 0.8668, 1.7571],
1156/// ];
1157/// let mut a_sparse = rsparse::data::Sprs::new();
1158/// a_sparse.from_vec(&a);
1159///
1160/// assert_eq!(
1161///     rsparse::transpose(&a_sparse).to_dense(),
1162///     vec![
1163///         vec![2.1615, 2.2828, 2.2156, 1.0244, 2.0367],
1164///         vec![2.0044, 1.9089, 1.8776, 0.8742, 1.5642],
1165///         vec![2.1312, 1.9295, 1.9473, 0.9177, 1.4313],
1166///         vec![0.8217, 0.9412, 1.0190, 0.7036, 0.8668],
1167///         vec![2.2074, 2.0017, 1.8352, 0.7551, 1.7571]
1168///     ]
1169/// )
1170/// ```
1171///
1172pub fn transpose(a: &Sprs) -> Sprs {
1173    let mut q;
1174    let mut w = vec![0; a.m];
1175    let mut c = Sprs::zeros(a.n, a.m, a.p[a.n] as usize);
1176
1177    for p in 0..a.p[a.n] as usize {
1178        w[a.i[p]] += 1; // row counts
1179    }
1180    cumsum(&mut c.p[..], &mut w[..], a.m); // row pointers
1181    for j in 0..a.n {
1182        for p in a.p[j] as usize..a.p[j + 1] as usize {
1183            q = w[a.i[p]] as usize;
1184            c.i[q] = j; // place A(i,j) as entry C(j,i)
1185            c.x[q] = a.x[p];
1186            w[a.i[p]] += 1;
1187        }
1188    }
1189
1190    c
1191}
1192
1193/// Solves an upper triangular system. Solves U*x=b.
1194///
1195/// Solve Ux=b where x and b are dense. x=b on input, solution on output.
1196///
1197/// # Example:
1198/// ```rust
1199/// let u = [
1200///     vec![0.7824, 0.4055, 0.0827, 0.9534, 0.9713, 0.1418, 0.0781],
1201///     vec![0.0, 0.7766, 0.2981, 0.2307, -0.3172, 0.6819, 0.5979],
1202///     vec![0.0, 0.0, 0.2986, -0.5576, 0.5928, -0.2759, -0.1672],
1203///     vec![0.0, 0.0, 0.0, 0.6393, -0.4245, 0.1277, 0.5842],
1204///     vec![0.0, 0.0, 0.0, 0.0, -1.277, 1.1435, 1.0631],
1205///     vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.2096, 0.7268],
1206///     vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.4574]
1207/// ];
1208/// let mut u_sparse = rsparse::data::Sprs::new();
1209/// u_sparse.from_vec(&u);
1210///
1211/// let mut b = [
1212///     0.189772,
1213///     0.055761,
1214///     0.030676,
1215///     0.181620,
1216///     0.526924,
1217///     0.744179,
1218///     0.078005
1219/// ];
1220///
1221/// rsparse::usolve(&u_sparse, &mut b);
1222/// ```
1223///
1224pub fn usolve(u: &Sprs, x: &mut [f64]) {
1225    for j in (0..u.n).rev() {
1226        x[j] /= u.x[(u.p[j + 1] - 1) as usize];
1227        for p in u.p[j]..u.p[j + 1] - 1 {
1228            x[u.i[p as usize]] -= u.x[p as usize] * x[j];
1229        }
1230    }
1231}
1232
1233/// Solves U'x=b where x and b are dense.
1234///
1235/// x=b on input, solution on output.
1236///
1237/// # Example:
1238/// ```rust
1239/// let u = [
1240///     vec![0.9842, 0.1720, 0.9948, 0.2766, 0.4560, 0.1462, 0.8124],
1241///     vec![0.0000, 0.6894, 0.1043, 0.4486, 0.5217, 0.7157, 0.4132],
1242///     vec![0.0000, 0.0000, -0.5500, -0.2340, 0.0822, 0.2176, -0.1996],
1243///     vec![0.0000, 0.0000, 0.0000, 0.6554, -0.1564, -0.0287, 0.2107],
1244///     vec![0.0000, 0.0000, 0.0000, 0.0000, -0.4127, -0.4652, -0.6993],
1245///     vec![0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.6881, 0.3037],
1246///     vec![0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, -0.7740]
1247/// ];
1248/// let mut u_sparse = rsparse::data::Sprs::new();
1249/// u_sparse.from_vec(&u);
1250///
1251/// let mut b = [
1252///     0.444841,
1253///     0.528773,
1254///     0.988345,
1255///     0.097749,
1256///     0.996166,
1257///     0.068040,
1258///     0.844511
1259/// ];
1260///
1261/// rsparse::utsolve(&u_sparse, &mut b);
1262/// ```
1263
1264///
1265pub fn utsolve(u: &Sprs, x: &mut [f64]) {
1266    for j in 0..u.n {
1267        for p in u.p[j] as usize..(u.p[j + 1] - 1) as usize {
1268            x[j] -= u.x[p] * x[u.i[p]];
1269        }
1270        x[j] /= u.x[(u.p[j + 1] - 1) as usize];
1271    }
1272}
1273
1274// --- Private functions -------------------------------------------------------
1275
1276/// amd(...) carries out the approximate minimum degree algorithm.
1277/// p = amd(A+A') if symmetric is true, or amd(A'A) otherwise
1278/// # Parameters:
1279///
1280/// Input, i8 ORDER:
1281/// - -1:natural,
1282/// - 0:Cholesky,
1283/// - 1:LU,
1284/// - 2:QR
1285///
1286fn amd(a: &Sprs, order: i8) -> Option<Vec<isize>> {
1287    let mut dense;
1288    let mut c;
1289    let mut nel = 0;
1290    let mut mindeg = 0;
1291    let mut elenk;
1292    let mut nvk;
1293    let mut p;
1294    let mut p2;
1295    let mut pk1;
1296    let mut pk2;
1297    let mut e;
1298    let mut pj;
1299    let mut ln;
1300    let mut nvi;
1301    let mut i;
1302    let mut mark_v;
1303    let mut lemax = 0;
1304    let mut eln;
1305    let mut wnvi;
1306    let mut p1;
1307    let mut pn;
1308    let mut h;
1309    let mut d;
1310    let mut dext;
1311    let mut p3;
1312    let mut p4;
1313    let mut j;
1314    let mut nvj;
1315    let mut jlast;
1316
1317    // --- Construct matrix C -----------------------------------------------
1318    if order < 0 {
1319        return None;
1320    }
1321
1322    let mut at = transpose(a); // compute A'
1323    let m = a.m;
1324    let n = a.n;
1325    dense = std::cmp::max(16, (10. * f32::sqrt(n as f32)) as isize); // find dense threshold
1326    dense = std::cmp::min((n - 2) as isize, dense);
1327
1328    if order == 0 && n == m {
1329        c = add(a, &at, 0., 0.); // C = A+A'
1330    } else if order == 1 {
1331        p2 = 0; // drop dense columns from AT
1332        for j in 0..m {
1333            p = at.p[j]; // column j of AT starts here
1334            at.p[j] = p2; // new column j starts here
1335            if at.p[j + 1] - p > dense {
1336                continue; // skip dense col j
1337            }
1338            while p < at.p[j + 1] {
1339                at.i[p2 as usize] = at.i[p as usize];
1340                p2 += 1;
1341                p += 1;
1342            }
1343        }
1344        at.p[m] = p2; // finalize AT
1345        let a2 = transpose(&at); // A2 = AT'
1346        c = multiply(&at, &a2); // C=A'*A with no dense rows
1347    } else {
1348        c = multiply(&at, a); // C=A'*A
1349    }
1350    drop(at);
1351
1352    let mut p_v = vec![0; n + 1]; // allocate result
1353    let mut ww = vec![0; 8 * (n + 1)]; // get workspace
1354                                       // offsets of ww (pointers in csparse)
1355    let len = 0; // of ww
1356    let nv = n + 1; // of ww
1357    let next = 2 * (n + 1); // of ww
1358    let head = 3 * (n + 1); // of ww
1359    let elen = 4 * (n + 1); // of ww
1360    let degree = 5 * (n + 1); // of ww
1361    let w = 6 * (n + 1); // of ww
1362    let hhead = 7 * (n + 1); // of ww
1363    let last = 0; // of p_v // use P as workspace for last
1364
1365    fkeep(&mut c, &diag); // drop diagonal entries
1366    let mut cnz = c.p[n];
1367    // change the max # of entries of C
1368    let nsz = cnz as usize + cnz as usize / 5 + 2 * n;
1369    c.nzmax = nsz;
1370    c.i.resize(nsz, 0);
1371    c.x.resize(nsz, 0.);
1372
1373    // --- Initialize quotient graph ----------------------------------------
1374    for k in 0..n {
1375        ww[len + k] = c.p[k + 1] - c.p[k];
1376    }
1377    ww[len + n] = 0;
1378    for i in 0..=n {
1379        ww[head + i] = -1; // degree list i is empty
1380        p_v[last + i] = -1;
1381        ww[next + i] = -1;
1382        ww[hhead + i] = -1; // hash list i is empty
1383        ww[nv + i] = 1; // node i is just one node
1384        ww[w + i] = 1; // node i is alive
1385        ww[elen + i] = 0; // Ek of node i is empty
1386        ww[degree + i] = ww[len + i]; // degree of node i
1387    }
1388    mark_v = wclear(0, 0, &mut ww[..], w, n); // clear w ALERT!!! C implementation passes w (pointer to ww)
1389    ww[elen + n] = -2; // n is a dead element
1390    c.p[n] = -1; // n is a root of assembly tree
1391    ww[w + n] = 0; // n is a dead element
1392
1393    // --- Initialize degree lists ------------------------------------------
1394    for i in 0..n {
1395        let d = ww[degree + i];
1396        if d == 0 {
1397            // node i is empty
1398            ww[elen + i] = -2; // element i is dead
1399            nel += 1;
1400            c.p[i] = -1; // i is a root of assembly tree
1401            ww[w + i] = 0;
1402        } else if d > dense {
1403            // node i is dense
1404            ww[nv + i] = 0; // absorb i into element n
1405            ww[elen + i] = -1; // node i is dead
1406            nel += 1;
1407            c.p[i] = flip(n as isize);
1408            ww[nv + n] += 1;
1409        } else {
1410            if ww[(head as isize + d) as usize] != -1 {
1411                let wt = ww[(head as isize + d) as usize];
1412                p_v[(last as isize + wt) as usize] = i as isize;
1413            }
1414            ww[next + i] = ww[(head as isize + d) as usize]; // put node i in degree list d
1415            ww[(head as isize + d) as usize] = i as isize;
1416        }
1417    }
1418
1419    while nel < n {
1420        // while (selecting pivots) do
1421        // --- Select node of minimum approximate degree --------------------
1422        let mut k;
1423        loop {
1424            k = ww[head + mindeg];
1425            if !(mindeg < n && k == -1) {
1426                break;
1427            }
1428            mindeg += 1;
1429        }
1430
1431        if ww[(next as isize + k) as usize] != -1 {
1432            let wt = ww[(next as isize + k) as usize];
1433            p_v[(last as isize + wt) as usize] = -1;
1434        }
1435        ww[head + mindeg] = ww[(next as isize + k) as usize]; // remove k from degree list
1436        elenk = ww[(elen as isize + k) as usize]; // elenk = |Ek|
1437        nvk = ww[(nv as isize + k) as usize]; // # of nodes k represents
1438        nel += nvk as usize; // nv[k] nodes of A eliminated
1439
1440        // --- Garbage collection -------------------------------------------
1441        if elenk > 0 && (cnz + mindeg as isize) as usize >= c.nzmax {
1442            for j in 0..n {
1443                p = c.p[j];
1444                if p >= 0 {
1445                    // j is a live node or element
1446                    c.p[j] = c.i[p as usize] as isize; // save first entry of object
1447                    c.i[p as usize] = flip(j as isize) as usize; // first entry is now FLIP(j)
1448                }
1449            }
1450            let mut q = 0;
1451            p = 0;
1452            while p < cnz {
1453                // scan all of memory
1454                let j = flip(c.i[p as usize] as isize);
1455                p += 1;
1456                if j >= 0 {
1457                    // found object j
1458                    c.i[q] = c.p[j as usize] as usize; // restore first entry of object
1459                    c.p[j as usize] = q as isize; // new pointer to object j
1460                    q += 1;
1461                    for _ in 0..ww[(len as isize + j) as usize] - 1 {
1462                        c.i[q] = c.i[p as usize];
1463                        q += 1;
1464                        p += 1;
1465                    }
1466                }
1467            }
1468            cnz = q as isize; // Ci [cnz...nzmax-1] now free
1469        }
1470        // --- Construct new element ----------------------------------------
1471        let mut dk = 0;
1472
1473        ww[(nv as isize + k) as usize] = -nvk; // flag k as in Lk
1474        p = c.p[k as usize];
1475        if elenk == 0 {
1476            // do in place if elen[k] == 0
1477            pk1 = p;
1478        } else {
1479            pk1 = cnz;
1480        }
1481        pk2 = pk1;
1482        for k1 in 1..=(elenk + 1) as usize {
1483            if k1 > elenk as usize {
1484                e = k; // search the nodes in k
1485                pj = p; // list of nodes starts at Ci[pj]
1486                ln = ww[(len as isize + k) as usize] - elenk; // length of list of nodes in k
1487            } else {
1488                e = c.i[p as usize] as isize; // search the nodes in e
1489                p += 1;
1490                pj = c.p[e as usize];
1491                ln = ww[(len as isize + e) as usize]; // length of list of nodes in e
1492            }
1493            for _ in 1..=ln {
1494                i = c.i[pj as usize] as isize;
1495                pj += 1;
1496                nvi = ww[(nv as isize + i) as usize];
1497                if nvi <= 0 {
1498                    continue; // node i dead, or seen
1499                }
1500                dk += nvi; // degree[Lk] += size of node i
1501                ww[(nv as isize + i) as usize] = -nvi; // negate nv[i] to denote i in Lk
1502                c.i[pk2 as usize] = i as usize; // place i in Lk
1503                pk2 += 1;
1504                if ww[(next as isize + i) as usize] != -1 {
1505                    let wt = ww[(next as isize + i) as usize];
1506                    p_v[(last as isize + wt) as usize] = p_v[last + i as usize];
1507                }
1508                if p_v[(last as isize + i) as usize] != -1 {
1509                    // remove i from degree list
1510                    let wt = p_v[(last as isize + i) as usize];
1511                    ww[(next as isize + wt) as usize] = ww[(next as isize + i) as usize];
1512                } else {
1513                    let wt = ww[degree + i as usize];
1514                    ww[(head as isize + wt) as usize] = ww[next + i as usize];
1515                }
1516            }
1517            if e != k {
1518                c.p[e as usize] = flip(k); // absorb e into k
1519                ww[(w as isize + e) as usize] = 0; // e is now a dead element
1520            }
1521        }
1522        if elenk != 0 {
1523            cnz = pk2; // Ci [cnz...nzmax] is free
1524        }
1525        ww[(degree as isize + k) as usize] = dk; // external degree of k - |Lk\i|
1526        c.p[k as usize] = pk1; // element k is in Ci[pk1..pk2-1]
1527        ww[(len as isize + k) as usize] = pk2 - pk1;
1528        ww[(elen as isize + k) as usize] = -2; // k is now an element
1529
1530        // --- Find set differences -----------------------------------------
1531        mark_v = wclear(mark_v, lemax, &mut ww[..], w, n); // clear w if necessary
1532        for pk in pk1..pk2 {
1533            // scan1: find |Le\Lk|
1534            i = c.i[pk as usize] as isize;
1535            eln = ww[(elen as isize + i) as usize];
1536            if eln <= 0 {
1537                continue; // skip if elen[i] empty
1538            }
1539            nvi = -ww[(nv as isize + i) as usize]; // nv [i] was negated
1540            wnvi = mark_v - nvi;
1541            for p in c.p[i as usize] as usize..=(c.p[i as usize] + eln - 1) as usize {
1542                // scan Ei
1543                e = c.i[p] as isize;
1544                if ww[(w as isize + e) as usize] >= mark_v {
1545                    ww[(w as isize + e) as usize] -= nvi; // decrement |Le\Lk|
1546                } else if ww[(w as isize + e) as usize] != 0 {
1547                    // ensure e is a live element
1548                    ww[(w as isize + e) as usize] = ww[(degree as isize + e) as usize] + wnvi;
1549                    // 1st time e seen in scan 1
1550                }
1551            }
1552        }
1553
1554        // --- Degree update ------------------------------------------------
1555        for pk in pk1..pk2 {
1556            // scan2: degree update
1557            i = c.i[pk as usize] as isize; // consider node i in Lk
1558            p1 = c.p[i as usize];
1559            p2 = p1 + ww[(elen as isize + i) as usize] - 1;
1560            pn = p1;
1561            h = 0;
1562            d = 0;
1563            for p in p1..=p2 {
1564                // scan Ei
1565                e = c.i[p as usize] as isize;
1566                if ww[(w as isize + e) as usize] != 0 {
1567                    // e is an unabsorbed element
1568                    dext = ww[(w as isize + e) as usize] - mark_v; // dext = |Le\Lk|
1569                    if dext > 0 {
1570                        d += dext; // sum up the set differences
1571                        c.i[pn as usize] = e as usize; // keep e in Ei
1572                        pn += 1;
1573                        h += e as usize; // compute the hash of node i
1574                    } else {
1575                        c.p[e as usize] = flip(k); // aggressive absorb. e->k
1576                        ww[(w as isize + e) as usize] = 0; // e is a dead element
1577                    }
1578                }
1579            }
1580            ww[(elen as isize + i) as usize] = pn - p1 + 1; // elen[i] = |Ei|
1581            p3 = pn;
1582            p4 = p1 + ww[(len as isize + i) as usize];
1583            for p in p2 + 1..p4 {
1584                // prune edges in Ai
1585                j = c.i[p as usize] as isize;
1586                nvj = ww[(nv as isize + j) as usize];
1587                if nvj <= 0 {
1588                    continue; // node j dead or in Lk
1589                }
1590                d += nvj; // degree(i) += |j|
1591                c.i[pn as usize] = j as usize; // place j in node list of i
1592                pn += 1;
1593                h += j as usize; // compute hash for node i
1594            }
1595            if d == 0 {
1596                // check for mass elimination
1597                c.p[i as usize] = flip(k); // absorb i into k
1598                nvi = -ww[(nv as isize + i) as usize];
1599                dk -= nvi; // |Lk| -= |i|
1600                nvk += nvi; // |k| += nv[i]
1601                nel += nvi as usize;
1602                ww[(nv as isize + i) as usize] = 0;
1603                ww[(elen as isize + i) as usize] = -1; // node i is dead
1604            } else {
1605                ww[(degree as isize + i) as usize] =
1606                    std::cmp::min(ww[(degree as isize + i) as usize], d); // update degree(i)
1607                c.i[pn as usize] = c.i[p3 as usize]; // move first node to end
1608                c.i[p3 as usize] = c.i[p1 as usize]; // move 1st el. to end of Ei
1609                c.i[p1 as usize] = k as usize; // add k as 1st element in of Ei
1610                ww[(len as isize + i) as usize] = pn - p1 + 1; // new len of adj. list of node i
1611                h %= n; // finalize hash of i
1612                ww[(next as isize + i) as usize] = ww[hhead + h]; // place i in hash bucket
1613                ww[hhead + h] = i;
1614                p_v[(last as isize + i) as usize] = h as isize; // save hash of i in last[i]
1615            }
1616        } // scan2 is done
1617        ww[(degree as isize + k) as usize] = dk; // finalize |Lk|
1618        lemax = std::cmp::max(lemax, dk);
1619        mark_v = wclear(mark_v + lemax, lemax, &mut ww[..], w, n); // clear w
1620
1621        // --- Supernode detection ------------------------------------------
1622        for pk in pk1..pk2 {
1623            i = c.i[pk as usize] as isize;
1624            if ww[(nv as isize + i) as usize] >= 0 {
1625                continue; // skip if i is dead
1626            }
1627            h = p_v[(last as isize + i) as usize] as usize; // scan hash bucket of node i
1628            i = ww[hhead + h];
1629            ww[hhead + h] = -1; // hash bucket will be empty
1630
1631            while i != -1 && ww[(next as isize + i) as usize] != -1 {
1632                ln = ww[(len as isize + i) as usize];
1633                eln = ww[(elen as isize + i) as usize];
1634                for p in c.p[i as usize] + 1..=c.p[i as usize] + ln - 1 {
1635                    ww[w + c.i[p as usize]] = mark_v;
1636                }
1637                jlast = i;
1638
1639                let mut ok;
1640                j = ww[(next as isize + i) as usize];
1641                while j != -1 {
1642                    // compare i with all j
1643                    ok = ww[(len as isize + j) as usize] == ln
1644                        && ww[(elen as isize + j) as usize] == eln;
1645
1646                    p = c.p[j as usize] + 1;
1647                    while ok && p < c.p[j as usize] + ln {
1648                        if ww[w + c.i[p as usize]] != mark_v {
1649                            // compare i and j
1650                            ok = false;
1651                        }
1652
1653                        p += 1;
1654                    }
1655                    if ok {
1656                        // i and j are identical
1657                        c.p[j as usize] = flip(i); // absorb j into i
1658                        ww[(nv as isize + i) as usize] += ww[(nv as isize + j) as usize];
1659                        ww[(nv as isize + j) as usize] = 0;
1660                        ww[(elen as isize + j) as usize] = -1; // node j is dead
1661                        j = ww[(next as isize + j) as usize]; // delete j from hash bucket
1662                        ww[(next as isize + jlast) as usize] = j;
1663                    } else {
1664                        jlast = j; // j and i are different
1665                        j = ww[(next as isize + j) as usize];
1666                    }
1667                }
1668
1669                // increment while loop
1670                i = ww[(next as isize + i) as usize];
1671                mark_v += 1;
1672            }
1673        }
1674
1675        // --- Finalize new element------------------------------------------
1676        p = pk1;
1677        for pk in pk1..pk2 {
1678            // finalize Lk
1679            i = c.i[pk as usize] as isize;
1680            nvi = -ww[(nv as isize + i) as usize];
1681            if nvi <= 0 {
1682                continue; // skip if i is dead
1683            }
1684            ww[(nv as isize + i) as usize] = nvi; // restore nv[i]
1685            d = ww[(degree as isize + i) as usize] + dk - nvi; // compute external degree(i)
1686            d = std::cmp::min(d, n as isize - nel as isize - nvi);
1687            if ww[(head as isize + d) as usize] != -1 {
1688                let wt = ww[(head as isize + d) as usize];
1689                p_v[(last as isize + wt) as usize] = i;
1690            }
1691            ww[(next as isize + i) as usize] = ww[(head as isize + d) as usize]; // put i back in degree list
1692            p_v[(last as isize + i) as usize] = -1;
1693            ww[(head as isize + d) as usize] = i;
1694            mindeg = std::cmp::min(mindeg, d as usize); // find new minimum degree
1695            ww[(degree as isize + i) as usize] = d;
1696            c.i[p as usize] = i as usize; // place i in Lk
1697            p += 1;
1698        }
1699        ww[(nv as isize + k) as usize] = nvk; // # nodes absorbed into k
1700        ww[(len as isize + k) as usize] = p - pk1;
1701        if ww[(len as isize + k) as usize] == 0 {
1702            // length of adj list of element k
1703            c.p[k as usize] = -1; // k is a root of the tree
1704            ww[(w as isize + k) as usize] = 0; // k is now a dead element
1705        }
1706        if elenk != 0 {
1707            cnz = p; // free unused space in Lk
1708        }
1709    }
1710
1711    // --- Post-ordering ----------------------------------------------------
1712    for i in 0..n {
1713        c.p[i] = flip(c.p[i]); // fix assembly tree
1714    }
1715    for j in 0..=n {
1716        ww[head + j] = -1;
1717    }
1718    for j in (0..=n).rev() {
1719        // place unordered nodes in lists
1720        if ww[nv + j] > 0 {
1721            continue; // skip if j is an element
1722        }
1723        ww[next + j] = ww[(head as isize + c.p[j]) as usize]; // place j in list of its parent
1724        ww[(head as isize + c.p[j]) as usize] = j as isize;
1725    }
1726    for e in (0..=n).rev() {
1727        // place elements in lists
1728        if ww[nv + e] <= 0 {
1729            continue; // skip unless e is an element
1730        }
1731        if c.p[e] != -1 {
1732            ww[next + e] = ww[(head as isize + c.p[e]) as usize]; // place e in list of its parent
1733            ww[(head as isize + c.p[e]) as usize] = e as isize;
1734        }
1735    }
1736    let mut k = 0;
1737    for i in 0..=n {
1738        // postorder the assembly tree
1739        if c.p[i] == -1 {
1740            k = tdfs(i as isize, k, &mut ww[..], head, next, &mut p_v[..], w); // Note that CSparse passes the pointers of ww
1741        }
1742    }
1743
1744    Some(p_v)
1745}
1746
1747/// process edge (j,i) of the matrix
1748///
1749fn cedge(
1750    j: isize,
1751    i: isize,
1752    w: &mut [isize],
1753    first: usize,
1754    maxfirst: usize,
1755    delta_colcount: &mut [isize],
1756    prevleaf: usize,
1757    ancestor: usize,
1758) {
1759    let mut q;
1760    let mut s;
1761    let mut sparent;
1762
1763    if i <= j || w[(first as isize + j) as usize] <= w[(maxfirst as isize + i) as usize] {
1764        return;
1765    }
1766    w[(maxfirst as isize + i) as usize] = w[(first as isize + j) as usize]; // update max first[j] seen so far
1767    let jprev = w[(prevleaf as isize + i) as usize]; // j is a leaf of the ith subtree
1768    delta_colcount[j as usize] += 1; // A(i,j) is in the skeleton matrix
1769    if jprev != -1 {
1770        // q = least common ancestor of jprev and j
1771        q = jprev;
1772        while q != w[(ancestor as isize + q) as usize] {
1773            // increment
1774            q = w[(ancestor as isize + q) as usize];
1775        }
1776        s = jprev;
1777        while s != q {
1778            sparent = w[(ancestor as isize + s) as usize]; // path compression
1779            w[(ancestor as isize + s) as usize] = q;
1780            // increment
1781            s = sparent;
1782        }
1783        delta_colcount[q as usize] -= 1; // decrement to account for overlap in q
1784    }
1785    w[(prevleaf as isize + i) as usize] = j; // j is now previous leaf of ith subtree
1786}
1787
1788/// colcount = column counts of LL'=A or LL'=A'A, given parent & post ordering
1789///
1790fn counts(a: &Sprs, parent: &[isize], post: &[isize], ata: bool) -> Vec<isize> {
1791    let m = a.m;
1792    let n = a.n;
1793
1794    let s = if ata { 4 * n + n + m + 1 } else { 4 * n };
1795
1796    let mut w = vec![0; s]; // get workspace
1797    let first = 3 * n; // pointer for w
1798    let ancestor = 0; // pointer for w
1799    let maxfirst = n; // pointer for w
1800    let prevleaf = 2 * n; // pointer for w
1801    let head = 4 * n; // pointer for w
1802    let next = 5 * n + 1; // pointer for w
1803    let mut delta_colcount = vec![0; n]; // allocate result || CSparse: delta = colcount = cs_malloc (n, sizeof (int)) ;
1804    let mut j;
1805    let mut k;
1806
1807    let at = transpose(a);
1808    w[0..s].fill(-1); // clear workspace [0..s-1]
1809    for (k, &p) in post.iter().enumerate() {
1810        // find first [j]
1811        j = p;
1812        delta_colcount[j as usize] = match w[(first as isize + j) as usize] {
1813            -1 => 1,
1814            _ => 0,
1815        };
1816        while j != -1 && w[(first as isize + j) as usize] == -1 {
1817            w[(first as isize + j) as usize] = k as isize;
1818
1819            // increment
1820            j = parent[j as usize];
1821        }
1822    }
1823
1824    if ata {
1825        for k in 0..n {
1826            w[post[k] as usize] = k as isize; // invert post
1827        }
1828        for i in 0..m {
1829            k = n; // k = least post-ordered column in row i
1830            for p in at.p[i]..at.p[i + 1] {
1831                k = std::cmp::min(k, w[at.i[p as usize]] as usize);
1832            }
1833            w[next + i] = w[head + k]; // place row i in link list k
1834            w[head + k] = i as isize;
1835        }
1836    }
1837    for i in 0..n {
1838        w[ancestor + i] = i as isize; // each node in its own set
1839    }
1840    for k in 0..n {
1841        j = post[k]; // j is the kth node in post-ordered etree
1842        if parent[j as usize] != -1 {
1843            delta_colcount[parent[j as usize] as usize] -= 1; // j is not a root
1844        }
1845        if ata {
1846            let mut ii = w[head + k];
1847            while ii != -1 {
1848                for p in at.p[ii as usize]..at.p[ii as usize + 1] {
1849                    cedge(
1850                        j,
1851                        at.i[p as usize] as isize,
1852                        &mut w[..],
1853                        first,
1854                        maxfirst,
1855                        &mut delta_colcount[..],
1856                        prevleaf,
1857                        ancestor,
1858                    );
1859                }
1860
1861                // increment
1862                ii = w[(next as isize + ii) as usize];
1863            }
1864        } else {
1865            for p in at.p[j as usize]..at.p[j as usize + 1] {
1866                cedge(
1867                    j,
1868                    at.i[p as usize] as isize,
1869                    &mut w[..],
1870                    first,
1871                    maxfirst,
1872                    &mut delta_colcount[..],
1873                    prevleaf,
1874                    ancestor,
1875                );
1876            }
1877        }
1878        if parent[j as usize] != -1 {
1879            w[(ancestor as isize + j) as usize] = parent[j as usize];
1880        }
1881    }
1882    for j in 0..n {
1883        // sum up delta's of each child
1884        if parent[j] != -1 {
1885            delta_colcount[parent[j] as usize] += delta_colcount[j];
1886        }
1887    }
1888
1889    delta_colcount
1890}
1891
1892/// p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c
1893///
1894fn cumsum(p: &mut [isize], c: &mut [isize], n: usize) -> usize {
1895    let mut nz = 0;
1896    for (p_i, c_i) in p.iter_mut().zip(c.iter_mut()).take(n) {
1897        *p_i = nz;
1898        nz += *c_i;
1899        *c_i = *p_i;
1900    }
1901    p[n] = nz;
1902
1903    nz as usize
1904}
1905
1906/// depth-first-search of the graph of a matrix, starting at node j
1907/// if pstack_i is used for pstack=xi[pstack_i]
1908///
1909fn dfs(
1910    j: usize,
1911    l: &mut Sprs,
1912    top: usize,
1913    xi: &mut [isize],
1914    pstack_i: &usize,
1915    pinv: &Option<Vec<isize>>,
1916) -> usize {
1917    let mut i;
1918    let mut j = j;
1919    let mut jnew;
1920    let mut head = 0;
1921    let mut done;
1922    let mut p2;
1923    let mut top = top;
1924
1925    xi[0] = j as isize; // initialize the recursion stack
1926    while head >= 0 {
1927        j = xi[head as usize] as usize; // get j from the top of the recursion stack
1928        if pinv.is_some() {
1929            jnew = pinv.as_ref().unwrap()[j];
1930        } else {
1931            jnew = j as isize;
1932        }
1933        if !marked(&l.p[..], j) {
1934            mark(&mut l.p[..], j); // mark node j as visited
1935            if jnew < 0 {
1936                xi[pstack_i + head as usize] = 0;
1937            } else {
1938                xi[pstack_i + head as usize] = unflip(l.p[jnew as usize]);
1939            }
1940        }
1941        done = true; // node j done if no unvisited neighbors
1942        if jnew < 0 {
1943            p2 = 0;
1944        } else {
1945            p2 = unflip(l.p[(jnew + 1) as usize]);
1946        }
1947        for p in xi[pstack_i + head as usize]..p2 {
1948            // examine all neighbors of j
1949            i = l.i[p as usize]; // consider neighbor node i
1950            if marked(&l.p[..], i) {
1951                continue; // skip visited node i
1952            }
1953            xi[pstack_i + head as usize] = p; // pause depth-first search of node j
1954            head += 1;
1955            xi[head as usize] = i as isize; // start dfs at node i
1956            done = false; // node j is not done
1957            break; // break, to start dfs (i)
1958        }
1959        if done {
1960            // depth-first search at node j is done
1961            head -= 1; // remove j from the recursion stack
1962            top -= 1;
1963            xi[top] = j as isize; // and place in the output stack
1964        }
1965    }
1966
1967    top
1968}
1969
1970/// keep off-diagonal entries; drop diagonal entries
1971///
1972fn diag(i: isize, j: isize, _: f64) -> bool {
1973    i != j
1974}
1975
1976/// compute nonzero pattern of L(k,:)
1977///
1978fn ereach(
1979    a: &Sprs,
1980    k: usize,
1981    parent: &[isize],
1982    s: usize,
1983    w: &mut [isize],
1984    x: &mut [f64],
1985    top: usize,
1986) -> usize {
1987    let mut top = top;
1988    let mut i;
1989    let mut len;
1990    for p in a.p[k]..a.p[k + 1] {
1991        // get pattern of L(k,:)
1992        i = a.i[p as usize] as isize; // A(i,k) is nonzero
1993        if i > k as isize {
1994            continue; // only use upper triangular part of A
1995        }
1996        x[i as usize] = a.x[p as usize]; // x(i) = A(i,k)
1997        len = 0;
1998        while w[i as usize] != k as isize {
1999            // traverse up etree
2000            w[s + len] = i; // L(k,i) is nonzero
2001            len += 1;
2002            w[i as usize] = k as isize; // mark i as visited
2003
2004            // increment statement
2005            i = parent[i as usize];
2006        }
2007
2008        for j in 1..(len + 1) {
2009            top -= 1;
2010            w[s + top] = w[s + len - j]; // push path on stack
2011        }
2012    }
2013
2014    top // s [top..n-1] contains pattern of L(k,:)
2015}
2016
2017/// compute the etree of A (using triu(A), or A'A without forming A'A
2018///
2019fn etree(a: &Sprs, ata: bool) -> Vec<isize> {
2020    let mut parent = vec![0; a.n];
2021    let mut i;
2022    let mut inext;
2023
2024    let mut w = if ata {
2025        vec![0; a.n + a.m]
2026    } else {
2027        vec![0; a.n]
2028    };
2029
2030    let ancestor = 0;
2031    let prev = ancestor + a.n;
2032
2033    if ata {
2034        w[prev..prev + a.m].fill(-1);
2035    }
2036
2037    for k in 0..a.n {
2038        parent[k] = -1; // node k has no parent yet
2039        w[ancestor + k] = -1; // nor does k have an ancestor
2040        for p in a.p[k] as usize..a.p[k + 1] as usize {
2041            if ata {
2042                i = w[prev + a.i[p]];
2043            } else {
2044                i = a.i[p] as isize;
2045            }
2046            while i != -1 && i < k as isize {
2047                // traverse from i to k
2048                inext = w[(ancestor as isize + i) as usize]; // inext = ancestor of i
2049                w[(ancestor as isize + i) as usize] = k as isize; // path compression
2050                if inext == -1 {
2051                    parent[i as usize] = k as isize; // no anc., parent is k
2052                }
2053
2054                // increment
2055                i = inext
2056            }
2057            if ata {
2058                w[prev + a.i[p]] = k as isize;
2059            }
2060        }
2061    }
2062
2063    parent
2064}
2065
2066/// drop entries for which fkeep(A(i,j)) is false; return nz if OK, else -1
2067///
2068fn fkeep(a: &mut Sprs, f: &dyn Fn(isize, isize, f64) -> bool) -> isize {
2069    let mut p;
2070    let mut nz = 0;
2071    let n = a.n;
2072    for j in 0..n {
2073        p = a.p[j]; // get current location of col j
2074        a.p[j] = nz; // record new location of col j
2075        while p < a.p[j + 1] {
2076            if f(a.i[p as usize] as isize, j as isize, a.x[p as usize]) {
2077                // a.x always exists
2078                a.x[nz as usize] = a.x[p as usize]; // keep a(i,j)
2079                a.i[nz as usize] = a.i[p as usize];
2080                nz += 1;
2081            }
2082            p += 1;
2083        }
2084    }
2085    a.p[n] = nz;
2086
2087    nz
2088}
2089
2090/// apply the ith Householder vector to x
2091///
2092fn happly(v: &Sprs, i: usize, beta: f64, x: &mut [f64]) {
2093    let mut tau = 0.;
2094
2095    for p in v.p[i]..v.p[i + 1] {
2096        // tau = v'*x
2097        tau += v.x[p as usize] * x[v.i[p as usize]];
2098    }
2099    tau *= beta; // tau = beta*(v'*x)
2100    for p in v.p[i]..v.p[i + 1] {
2101        // x = x - v*tau
2102        x[v.i[p as usize]] -= v.x[p as usize] * tau;
2103    }
2104}
2105
2106/// create a Householder reflection [v,beta,s]=house(x), overwrite x with v,
2107/// where (I-beta*v*v')*x = s*x.  See Algo 5.1.1, Golub & Van Loan, 3rd ed.
2108///
2109fn house(
2110    x: &mut [f64],
2111    xp: Option<usize>,
2112    beta: &mut [f64],
2113    betap: Option<usize>,
2114    n: usize,
2115) -> f64 {
2116    let s;
2117    let xp = xp.unwrap_or(0);
2118    let betap = betap.unwrap_or(0);
2119
2120    let sigma: f64 = (1..n).map(|i| x[i + xp] * x[i + xp]).sum();
2121    // FIXME: float comparison with bit-wise not equals
2122    if sigma != 0. {
2123        s = (x[xp] * x[xp] + sigma).sqrt(); // s = norm (x)
2124        x[xp] = if x[xp] <= 0. {
2125            x[xp] - s
2126        } else {
2127            -sigma / (x[xp] + s)
2128        };
2129        beta[betap] = (-s * x[xp]).recip();
2130    } else {
2131        s = x[xp].abs(); // s = |x(0)|
2132        beta[betap] = if x[xp] <= 0. { 2. } else { 0. };
2133        x[xp] = 1.;
2134    }
2135
2136    s
2137}
2138
2139/// x(P) = b, for dense vectors x and b; P=None denotes identity
2140///
2141fn ipvec(n: usize, p: &Option<Vec<isize>>, b: &[f64], x: &mut [f64]) {
2142    for k in 0..n {
2143        if p.is_some() {
2144            x[p.as_ref().unwrap()[k] as usize] = b[k];
2145        } else {
2146            x[k] = b[k];
2147        }
2148    }
2149}
2150
2151/// C = A(P,Q) where P and Q are permutations of 0..m-1 and 0..n-1
2152///
2153fn permute(a: &Sprs, pinv: &Option<Vec<isize>>, q: &Option<Vec<isize>>) -> Sprs {
2154    let mut j;
2155    let mut nz = 0;
2156    let mut c = Sprs::zeros(a.m, a.n, a.p[a.n] as usize);
2157
2158    for k in 0..a.n {
2159        c.p[k] = nz as isize; // column k of C is column Q[k] of A
2160        if q.is_some() {
2161            j = q.as_ref().unwrap()[k] as usize;
2162        } else {
2163            j = k;
2164        }
2165        for p in a.p[j] as usize..a.p[j + 1] as usize {
2166            c.x[nz] = a.x[p]; // row i of A is row Pinv[i] of C
2167            if pinv.is_some() {
2168                c.i[nz] = pinv.as_ref().unwrap()[a.i[p]] as usize;
2169            } else {
2170                c.i[nz] = a.i[p];
2171            }
2172            nz += 1;
2173        }
2174    }
2175    c.p[a.n] = nz as isize;
2176
2177    c
2178}
2179
2180/// Pinv = P', or P = Pinv'
2181///
2182fn pinvert(p: &Option<Vec<isize>>, n: usize) -> Option<Vec<isize>> {
2183    // pinv
2184    if p.is_none() {
2185        // p = None denotes identity
2186        return None;
2187    }
2188
2189    let mut pinv = vec![0; n]; // allocate result
2190    for k in 0..n {
2191        pinv[p.as_ref().unwrap()[k] as usize] = k as isize; // invert the permutation
2192    }
2193
2194    Some(pinv)
2195}
2196
2197/// post order a forest
2198///
2199fn post(n: usize, parent: &[isize]) -> Vec<isize> {
2200    let mut k = 0;
2201    let mut post = vec![0; n]; // allocate result
2202    let mut w = vec![0; 3 * n]; // 3*n workspace
2203    let head = 0; // pointer for w
2204    let next = head + n; // pointer for w
2205    let stack = head + 2 * n; // pointer for w
2206
2207    for j in 0..n {
2208        w[head + j] = -1; // empty link lists
2209    }
2210    for j in (0..n).rev() {
2211        // traverse nodes in reverse order
2212        if parent[j] == -1 {
2213            continue; // j is a root
2214        }
2215        w[next + j] = w[(head as isize + parent[j]) as usize]; // add j to list of its parent
2216        w[(head as isize + parent[j]) as usize] = j as isize;
2217    }
2218    for (j, par) in parent.iter().enumerate().take(n) {
2219        if *par != -1 {
2220            continue; // skip j if it is not a root
2221        }
2222        k = tdfs(j as isize, k, &mut w[..], head, next, &mut post[..], stack);
2223    }
2224
2225    post
2226}
2227
2228/// x = b(P), for dense vectors x and b; P=None denotes identity
2229///
2230fn pvec(n: usize, p: &Option<Vec<isize>>, b: &[f64], x: &mut [f64]) {
2231    for (k, x_k) in x.iter_mut().enumerate().take(n) {
2232        *x_k = match p {
2233            Some(p) => b[p[k] as usize],
2234            None => b[k],
2235        };
2236    }
2237}
2238
2239/// xi [top...n-1] = nodes reachable from graph of L*P' via nodes in B(:,k).
2240/// xi [n...2n-1] used as workspace.
2241///
2242fn reach(l: &mut Sprs, b: &Sprs, k: usize, xi: &mut [isize], pinv: &Option<Vec<isize>>) -> usize {
2243    let mut top = l.n;
2244
2245    for p in b.p[k] as usize..b.p[k + 1] as usize {
2246        if !marked(&l.p[..], b.i[p]) {
2247            // start a dfs at unmarked node i
2248            let n = l.n;
2249            top = dfs(b.i[p], l, top, &mut xi[..], &n, pinv);
2250        }
2251    }
2252    for i in xi.iter().take(l.n).skip(top) {
2253        mark(&mut l.p[..], *i as usize); // restore L
2254    }
2255
2256    top
2257}
2258
2259/// x = x + beta * A(:,j), where x is a dense vector and A(:,j) is sparse
2260///
2261fn scatter(
2262    a: &Sprs,
2263    j: usize,
2264    beta: f64,
2265    w: &mut [isize],
2266    x: &mut [f64],
2267    mark: usize,
2268    c: &mut Sprs,
2269    nz: usize,
2270) -> usize {
2271    let mut i;
2272    let mut nzo = nz;
2273    for p in a.p[j] as usize..a.p[j + 1] as usize {
2274        i = a.i[p]; // A(i,j) is nonzero
2275        if w[i] < mark as isize {
2276            w[i] = mark as isize; // i is new entry in column j
2277            c.i[nzo] = i; // add i to pattern of C(:,j)
2278            nzo += 1;
2279            x[i] = beta * a.x[p]; // x(i) = beta*A(i,j)
2280        } else {
2281            x[i] += beta * a.x[p]; // i exists in C(:,j) already
2282        }
2283    }
2284
2285    nzo
2286}
2287
2288/// beta * A(:,j), where A(:,j) is sparse. For QR decomposition
2289///
2290fn scatter_no_x(j: usize, w: &mut [isize], mark: usize, c: &mut Sprs, nz: usize) -> usize {
2291    let mut i;
2292    let mut nzo = nz;
2293    for p in c.p[j] as usize..c.p[j + 1] as usize {
2294        i = c.i[p]; // A(i,j) is nonzero
2295        if w[i] < mark as isize {
2296            w[i] = mark as isize; // i is new entry in column j
2297            c.i[nzo] = i; // add i to pattern of C(:,j)
2298            nzo += 1;
2299        }
2300    }
2301
2302    nzo
2303}
2304
2305/// Solve Lx=b(:,k), leaving pattern in xi[top..n-1], values scattered in x.
2306///
2307fn splsolve(
2308    l: &mut Sprs,
2309    b: &Sprs,
2310    k: usize,
2311    xi: &mut [isize],
2312    x: &mut [f64],
2313    pinv: &Option<Vec<isize>>,
2314) -> usize {
2315    let mut jnew;
2316    let top = reach(l, b, k, &mut xi[..], pinv); // xi[top..n-1]=Reach(B(:,k))
2317
2318    for p in top..l.n {
2319        x[xi[p] as usize] = 0.; // clear x
2320    }
2321    for p in b.p[k] as usize..b.p[k + 1] as usize {
2322        x[b.i[p]] = b.x[p]; // scatter B
2323    }
2324    for j in xi.iter().take(l.n).skip(top) {
2325        let j_u = *j as usize; // x(j) is nonzero
2326        jnew = match pinv {
2327            Some(pinv) => pinv[j_u], // j is column jnew of L
2328            None => *j,              // j is column jnew of L
2329        };
2330        if jnew < 0 {
2331            continue; // column jnew is empty
2332        }
2333        for p in (l.p[jnew as usize] + 1) as usize..l.p[jnew as usize + 1] as usize {
2334            x[l.i[p]] -= l.x[p] * x[j_u]; // x(i) -= L(i,j) * x(j)
2335        }
2336    }
2337
2338    top // return top of stack
2339}
2340
2341/// C = A(p,p) where A and C are symmetric the upper part stored, Pinv not P
2342///
2343fn symperm(a: &Sprs, pinv: &Option<Vec<isize>>) -> Sprs {
2344    let n = a.n;
2345    let mut i;
2346    let mut i2;
2347    let mut j2;
2348    let mut q;
2349    let mut c = Sprs::zeros(n, n, a.p[n] as usize);
2350    let mut w = vec![0; n];
2351
2352    for j in 0..n {
2353        //count entries in each column of C
2354        j2 = pinv.as_ref().map_or(j, |pinv| pinv[j] as usize); // column j of A is column j2 of C
2355
2356        for p in a.p[j]..a.p[j + 1] {
2357            i = a.i[p as usize];
2358            if i > j {
2359                continue; // skip lower triangular part of A
2360            }
2361            i2 = pinv.as_ref().map_or(i, |pinv| pinv[i] as usize); // row i of A is row i2 of C
2362            w[std::cmp::max(i2, j2)] += 1; // column count of C
2363        }
2364    }
2365    cumsum(&mut c.p[..], &mut w[..], n); // compute column pointers of C
2366    for j in 0..n {
2367        j2 = pinv.as_ref().map_or(j, |pinv| pinv[j] as usize); // column j of A is column j2 of C
2368        for p in a.p[j]..a.p[j + 1] {
2369            i = a.i[p as usize];
2370            if i > j {
2371                continue; // skip lower triangular part of A
2372            }
2373            i2 = pinv.as_ref().map_or(i, |pinv| pinv[i] as usize); // row i of A is row i2 of C
2374            q = w[std::cmp::max(i2, j2)] as usize;
2375            w[std::cmp::max(i2, j2)] += 1;
2376            c.i[q] = std::cmp::min(i2, j2);
2377            c.x[q] = a.x[p as usize];
2378        }
2379    }
2380
2381    c
2382}
2383
2384/// depth-first search and postorder of a tree rooted at node j (for fn amd())
2385///
2386fn tdfs(
2387    j: isize,
2388    k: isize,
2389    ww: &mut [isize],
2390    head: usize,
2391    next: usize,
2392    post: &mut [isize],
2393    stack: usize,
2394) -> isize {
2395    let mut i;
2396    let mut p;
2397    let mut top = 0;
2398    let mut k = k;
2399
2400    ww[stack] = j; // place j on the stack
2401    while top >= 0 {
2402        // while (stack is not empty)
2403        p = ww[(stack as isize + top) as usize]; // p = top of stack
2404        i = ww[(head as isize + p) as usize]; // i = youngest child of p
2405        match i {
2406            -1 => {
2407                top -= 1; // p has no unordered children left
2408                post[k as usize] = p; // node p is the kth post-ordered node
2409                k += 1;
2410            }
2411            _ => {
2412                ww[(head as isize + p) as usize] = ww[(next as isize + i) as usize]; // remove i from children of p
2413                top += 1;
2414                ww[(stack as isize + top) as usize] = i; // start dfs on child node i
2415            }
2416        }
2417    }
2418
2419    k
2420}
2421
2422/// compute vnz, Pinv, leftmost, m2 from A and parent
2423///
2424fn vcount(a: &Sprs, parent: &[isize], m2: &mut usize, vnz: &mut usize) -> Option<Vec<isize>> {
2425    let n = a.n;
2426    let m = a.m;
2427
2428    let mut pinv: Vec<isize> = vec![0; 2 * m + n];
2429    let leftmost = m + n; // pointer of pinv
2430    let mut w = vec![0; m + 3 * n];
2431
2432    // pointers for w
2433    let next = 0;
2434    let head = m;
2435    let tail = m + n;
2436    let nque = m + 2 * n;
2437
2438    w[head..head + n].fill(-1); // queue k is empty
2439    w[tail..tail + n].fill(-1);
2440    w[nque..nque + n].fill(0);
2441    pinv[leftmost..leftmost + m].fill(-1);
2442    for k in (0..n).rev() {
2443        for p in a.p[k]..a.p[k + 1] {
2444            pinv[leftmost + a.i[p as usize]] = k as isize; // leftmost[i] = min(find(A(i,:)))
2445        }
2446    }
2447    let mut k;
2448    for i in (0..m).rev() {
2449        // scan rows in reverse order
2450        pinv[i] = -1; // row i is not yet ordered
2451        k = pinv[leftmost + i];
2452        if k == -1 {
2453            continue; // row i is empty
2454        }
2455        if w[(nque as isize + k) as usize] == 0 {
2456            w[(tail as isize + k) as usize] = i as isize; // first row in queue k
2457        }
2458        w[(nque as isize + k) as usize] += 1;
2459        w[next + i] = w[(head as isize + k) as usize]; // put i at head of queue k
2460        w[(head as isize + k) as usize] = i as isize;
2461    }
2462    *vnz = 0;
2463    *m2 = m;
2464    let mut i;
2465    for k in 0..n {
2466        // find row permutation and nnz(V)
2467        i = w[head + k]; // remove row i from queue k
2468        *vnz += 1; // count V(k,k) as nonzero
2469        if i < 0 {
2470            i = *m2 as isize; // add a fictitious row
2471            *m2 += 1;
2472        }
2473        pinv[i as usize] = k as isize; // associate row i with V(:,k)
2474        w[nque + k] -= 1;
2475        if w[nque + k] <= 0 {
2476            continue; // skip if V(k+1:m,k) is empty
2477        }
2478        *vnz += w[nque + k] as usize; // nque [k] = nnz (V(k+1:m,k))
2479        let pa = parent[k]; // move all rows to parent of k
2480        if pa != -1 {
2481            if w[(nque as isize + pa) as usize] == 0 {
2482                w[(tail as isize + pa) as usize] = w[tail + k];
2483            }
2484            let tw = w[tail + k];
2485            w[(next as isize + tw) as usize] = w[(head as isize + pa) as usize];
2486            w[(head as isize + pa) as usize] = w[(next as isize + i) as usize];
2487            w[(nque as isize + pa) as usize] += w[nque + k];
2488        }
2489    }
2490    let mut k = n;
2491    for p in pinv.iter_mut().take(m) {
2492        if *p < 0 {
2493            *p = k as isize;
2494            k += 1;
2495        }
2496    }
2497
2498    Some(pinv)
2499}
2500
2501/// clears W
2502///
2503fn wclear(mark_v: isize, lemax: isize, ww: &mut [isize], w: usize, n: usize) -> isize {
2504    let mut mark = mark_v;
2505    if mark < 2 || (mark + lemax < 0) {
2506        for k in 0..n {
2507            if ww[w + k] != 0 {
2508                ww[w + k] = 1;
2509            }
2510        }
2511        mark = 2;
2512    }
2513
2514    mark // at this point, w [0..n-1] < mark holds
2515}
2516
2517// --- Inline functions --------------------------------------------------------
2518
2519#[inline]
2520fn flip(i: isize) -> isize {
2521    -(i) - 2
2522}
2523
2524#[inline]
2525fn unflip(i: isize) -> isize {
2526    if i < 0 {
2527        flip(i)
2528    } else {
2529        i
2530    }
2531}
2532
2533#[inline]
2534fn marked(ap: &[isize], j: usize) -> bool {
2535    ap[j] < 0
2536}
2537
2538#[inline]
2539fn mark(ap: &mut [isize], j: usize) {
2540    ap[j] = flip(ap[j])
2541}