//------------------------------------------------------------------------------
// BTF/Source/btf_order: permute a matrix to block triangular form
//------------------------------------------------------------------------------

// BTF, Copyright (c) 2004-2022, University of Florida.  All Rights Reserved.
// Author: Timothy A. Davis.
// SPDX-License-Identifier: LGPL-2.1+

//------------------------------------------------------------------------------

/* Find a permutation P and Q to permute a square sparse matrix into upper block
 * triangular form.  A(P,Q) will contain a zero-free diagonal if A has
 * structural full-rank.  Otherwise, the number of nonzeros on the diagonal of
 * A(P,Q) will be maximized, and will equal the structural rank of A.
 *
 * Q[k] will be "flipped" if a zero-free diagonal was not found.  Q[k] will be
 * negative, and j = BTF_UNFLIP (Q [k]) gives the corresponding permutation.
 *
 * R defines the block boundaries of A(P,Q).  The kth block consists of rows
 * and columns R[k] to R[k+1]-1.
 *
 * If maxwork > 0 on input, then the work performed in btf_maxtrans is limited
 * to maxwork*nnz(A) (excluding the "cheap match" phase, which can take another
 * nnz(A) work).  On output, the work parameter gives the actual work performed,
 * or -1 if the limit was reached.  In the latter case, the diagonal of A(P,Q)
 * might not be zero-free, and the number of nonzeros on the diagonal of A(P,Q)
 * might not be equal to the structural rank.
 *
 * See btf.h for more details.
 */

#include "btf.h"
#include "btf_internal.h"

/* This function only operates on square matrices (either structurally full-
 * rank, or structurally rank deficient). */

Int BTF(order)      /* returns number of blocks found */
(
    /* input, not modified: */
    Int n,          /* A is n-by-n in compressed column form */
    Int Ap [ ],     /* size n+1 */
    Int Ai [ ],     /* size nz = Ap [n] */
    double maxwork, /* do at most maxwork*nnz(A) work in the maximum
                     * transversal; no limit if <= 0 */

    /* output, not defined on input */
    double *work,   /* work performed in maxtrans, or -1 if limit reached */
    Int P [ ],      /* size n, row permutation */
    Int Q [ ],      /* size n, column permutation */
    Int R [ ],      /* size n+1.  block b is in rows/cols R[b] ... R[b+1]-1 */
    Int *nmatch,    /* # nonzeros on diagonal of P*A*Q */

    /* workspace, not defined on input or output */
    Int Work [ ]    /* size 5n */
)
{
    Int *Flag ;
    Int nblocks, i, j, nbadcol ;

    /* ---------------------------------------------------------------------- */
    /* compute the maximum matching */
    /* ---------------------------------------------------------------------- */

    /* if maxwork > 0, then a maximum matching might not be found */

    *nmatch = BTF(maxtrans) (n, n, Ap, Ai, maxwork, work, Q, Work) ;

    /* ---------------------------------------------------------------------- */
    /* complete permutation if the matrix is structurally singular */
    /* ---------------------------------------------------------------------- */

    /* Since the matrix is square, ensure BTF_UNFLIP(Q[0..n-1]) is a
     * permutation of the columns of A so that A has as many nonzeros on the
     * diagonal as possible.
     */

    if (*nmatch < n)
    {
        /* get a size-n work array */
        Flag = Work + n ;
        for (j = 0 ; j < n ; j++)
        {
            Flag [j] = 0 ;
        }

        /* flag all matched columns */
        for (i = 0 ; i < n ; i++)
        {
            j = Q [i] ;
            if (j != EMPTY)
            {
                /* row i and column j are matched to each other */
                Flag [j] = 1 ;
            }
        }

        /* make a list of all unmatched columns, in Work [0..nbadcol-1]  */
        nbadcol = 0 ;
        for (j = n-1 ; j >= 0 ; j--)
        {
            if (!Flag [j])
            {
                /* j is matched to nobody */
                Work [nbadcol++] = j ;
            }
        }
        ASSERT (*nmatch + nbadcol == n) ;

        /* make an assignment for each unmatched row */
        for (i = 0 ; i < n ; i++)
        {
            if (Q [i] == EMPTY && nbadcol > 0)
            {
                /* get an unmatched column j */
                j = Work [--nbadcol] ;
                /* assign j to row i and flag the entry by "flipping" it */
                Q [i] = BTF_FLIP (j) ;
            }
        }
    }

    /* The permutation of a square matrix can be recovered as follows: Row i is
     * matched with column j, where j = BTF_UNFLIP (Q [i]) and where j
     * will always be in the valid range 0 to n-1.  The entry A(i,j) is zero
     * if BTF_ISFLIPPED (Q [i]) is true, and nonzero otherwise.  nmatch
     * is the number of entries in the Q array that are non-negative. */

    /* ---------------------------------------------------------------------- */
    /* find the strongly connected components */
    /* ---------------------------------------------------------------------- */

    nblocks = BTF(strongcomp) (n, Ap, Ai, Q, P, R, Work) ;
    return (nblocks) ;
}
