/* ========================================================================== */
/* === UMF_analyze ========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 3.2 (Jan. 1, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis, University of Florida, davis@cise.ufl.edu.  All Rights Reserved.    */
/* See README, umfpack.h, or type "umfpack_details" in Matlab for License.    */
/* -------------------------------------------------------------------------- */

/*
    Symbolic LL' factorization of A'*A, to get upper bounds on the size of
    L and U for LU = PAQ, and to determine the frontal matrices and
    (supernodal) column elimination tree.  No fill-reducing column pre-ordering
    is used.

    Returns TRUE if successful, FALSE if out of memory.  UMF_analyze can only
    run out of memory if anzmax (which is Ap [n_row]) is too small.

    Uses workspace of size O(nonzeros in A).  On input, the matrix A is
    stored in row-form at the tail end of Ai.  It is destroyed on output.
    The rows of A must be sorted by increasing first column index.
    The matrix is assumed to be valid.

*/

#include "umf_internal.h"
#include "umf_order_front_tree.h"
#include "umf_apply_order.h"

/* ========================================================================== */

GLOBAL Int UMF_analyze
(
    Int n_row,		/* A is n_row-by-n_col */
    Int n_col,
    Int Ai [ ],		/* Ai [Ap [0]..Ap[n_row]-1]: column indices */
			/* destroyed on output.  Note that this is NOT the */
			/* user's Ai that was passed to UMFPACK_*symbolic */
			/* size of Ai, Ap [n_row] = anzmax >= anz + n_col */

    Int Ap [ ],		/* Ap [0..n_row]: row pointers */
			/* Row i is in Ai [Ap [i] ... Ap [i+1]-1] */

			/* input only, not modified, in packed form. */
			/* rows must have smallest col index first, or be */
			/* in sorted form.  Used as workspace, and destroyed. */

			/* Note that this is NOT the */
			/* user's Ap that was passed to UMFPACK_*symbolic */

    Int Up [ ],		/* workspace of size n_col, and output column perm. */

    /* temporary workspaces: */
    Int W [ ],		/* W [0..n_col-1] */
    Int Link [ ],	/* Link [0..n_col-1] */

    /* output: information about each frontal matrix: */
    Int Front_ncols [ ],	/* size n_col */
    Int Front_nrows [ ],	/* of size n_col */
    Int Front_npivots [ ],	/* of size n_col */
    Int Front_parent [ ],	/* of size n_col */
    Int *nfr_out,

    Int *p_ncompactions		/* number of compactions in UMF_analyze */
)
{
    /* ====================================================================== */
    /* ==== local variables ================================================= */
    /* ====================================================================== */

    Int j, j3, col, k, row, parent, j2, pdest, p, p2, thickness, npivots, nfr,
	nrows, ncols, frsize, i, *Winv, kk, npiv, jnext, krow, knext, pfirst,
	jlast, ncompactions, *Stack, *Front_maxfr, *Front_order, *Front_child,
	*Front_sibling, fprev, maxfrsize, bigf, fnext, bigfprev, f, Wflag ;

#ifndef NDEBUG
    Int nfr2, nchild ;
#endif

    nfr = 0 ;
    DEBUGk (-1,("UMF_analyze: anzmax "ID" anrow "ID"\n", Ap [n_row], n_row)) ;

    /* ====================================================================== */
    /* ==== initializations ================================================= */
    /* ====================================================================== */

    for (j = 0 ; j < n_col ; j++)
    {
	Link [j] = EMPTY ;
	W [j] = EMPTY ;
	Up [j] = EMPTY ;
	Front_npivots [j] = 0 ;
	Front_nrows [j] = 0 ;		/* no frontal matrices yet */
	Front_ncols [j] = 0 ;
	Front_parent [j] = EMPTY ;
    }

    /* the rows must be sorted by increasing min col */
    krow = 0 ;
    pfirst = Ap [0] ;
    jlast = EMPTY ;
    jnext = EMPTY ;
    Wflag = 0 ;

    ASSERT (pfirst >= n_col) ;	/* Ai must be large enough */

    /* pdest points to the first free space in Ai */
    pdest = 0 ;
    ncompactions = 0 ;

    /* ====================================================================== */
    /* === compute symbolic LL' factorization (unsorted) ==================== */
    /* ====================================================================== */

    for (j = 0 ; j < n_col ; j = jnext)
    {

	/* ================================================================== */
	/* === garbage collection =========================================== */
	/* ================================================================== */

	if (pdest + (n_col-j) > pfirst)
	{
	    /* we might run out ... compact the rows of U */

#ifndef NDEBUG
	    DEBUGk (-1,("umf_analyze COMPACTION, j="ID" pfirst="ID"\n",
		j, pfirst)) ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", INDEX(row))) ;
		    p = Up [row] ;
		    p2 = p + Front_ncols [row] ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, INDEX (Ai [p]))) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }
	    DEBUG1 (("\nStarting to compact:\n")) ;
#endif

	    pdest = 0 ;
	    ncompactions++ ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", INDEX(row))) ;
		    ASSERT (row < n_col) ;
		    p = Up [row] ;
		    p2 = p + Front_ncols [row] ;
		    Up [row] = pdest ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, INDEX (Ai [p]))) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
			Ai [pdest++] = Ai [p] ;
			ASSERT (pdest <= pfirst) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }

#ifndef NDEBUG
	    DEBUG1 (("\nAFTER COMPACTION, j="ID" pfirst="ID"\n", j, pfirst)) ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", INDEX(row))) ;
		    p = Up [row] ;
		    p2 = p + Front_ncols [row] ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, INDEX (Ai [p]))) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }
#endif

	}

	if (pdest + (n_col-j) > pfirst)
	{
	   /* Out of memory!  This is not supposed to happen ... */
	   /* it can't, if pfirst >= n_col */
	   return (FALSE) ;	/* internal error! */
	}

	/* ------------------------------------------------------------------ */
	/* is the last front a child of this one? */
	/* ------------------------------------------------------------------ */

	if (jlast != EMPTY && Link [j] == jlast)
	{
	    /* yes - create row j by appending to jlast */
	    DEBUG1 (("GOT:last front is child of this one: j "ID" jlast "ID"\n",
		INDEX (j), INDEX (jlast))) ;
	    ASSERT (jlast >= 0 && jlast < j) ;

	    Up [j] = Up [jlast] ;
	    Up [jlast] = EMPTY ;

	    /* find the parent, delete column j, and update W */
	    parent = n_col ;
	    for (p = Up [j] ; p < pdest ; )
	    {
		j3 = Ai [p] ;
		DEBUG1 (("Initial row of U: col "ID" ", INDEX (j3))) ;
		DEBUG1 (("W: "ID" \n", INDEX (W [j3]))) ;
		ASSERT (j3 >= 0 && j3 < n_col) ;
		ASSERT (W [j3] == Wflag) ;
		if (j == j3)
		{
		    DEBUG1 (("Found column j at p = "ID"\n", INDEX (p))) ;
		    Ai [p] = Ai [--pdest] ;
		}
		else
		{
		    if (j3 < parent)
		    {
			parent = j3 ;
		    }
		    p++ ;
	    	}
	    }

	    /* delete jlast from the link list of j */
	    Link [j] = Link [jlast] ;

	    thickness = Front_nrows [jlast] ;

	}
	else
	{
	    Up [j] = pdest ;
	    parent = n_col ;
	    /* thickness: number of (nonpivotal) rows in frontal matrix j */
	    thickness = 0 ;
	    Wflag = j ;
	}

	/* ================================================================== */
	/* === compute row j of A*A' ======================================== */
	/* ================================================================== */

	/* ------------------------------------------------------------------ */
	/* flag the diagonal entry in row U, but do not add to pattern */
	/* ------------------------------------------------------------------ */

	ASSERT (pdest <= pfirst) ;
	W [j] = Wflag ;

	DEBUG1 (("\nComputing row "ID" of A'*A\n", INDEX (j))) ;
	DEBUG2 (("	col: "ID" (diagonal)\n", INDEX(j))) ;

	/* ------------------------------------------------------------------ */
	/* find the rows the contribute to this column j */
	/* ------------------------------------------------------------------ */

	jnext = n_col ;
	for (knext = krow ; knext < n_row ; knext++)
	{
	    if (Ap [knext] < Ap [knext+1])
	    {
		ASSERT (Ap [knext] >= pfirst && Ap [knext] <= Ap [n_row]) ;
		jnext = Ai [Ap [knext]] ;
		ASSERT (jnext >= j) ;
		if (jnext != j)
		{
		    break ;
		}
	    }
	    else
	    {
		/* nothing left */
		knext = n_row ;
		jnext = n_col ;
		break ;
	    }
	}

	/* rows krow ... knext-1 all have first column index of j */
	/* (or are empty) */

	/* row knext has first column index of jnext */
	/* if knext = n_row, then jnext is n_col */
	if (knext == n_row)
	{
	    jnext = n_col ;
	}

	ASSERT (jnext > j) ;
	ASSERT (jnext <= n_col) ;

	/* ------------------------------------------------------------------ */
	/* for each nonzero A (k,j) in column j of A do: */
	/* ------------------------------------------------------------------ */

	for (k = krow ; k < knext ; k++)
	{
	    p = Ap [k] ;
	    p2 = Ap [k+1] ;
	    if (p < p2)
	    {
		/* merge row k of A into W */
		DEBUG2 (("	---- A row "ID" ", INDEX(k))) ;
		ASSERT (k >= 0 && k < n_row) ;
		ASSERT (Ai [p] == j) ;
		DEBUG2 (("  p "ID" p2 "ID"\n        cols:", p, p2)) ;
		ASSERT (p  >= pfirst && p  < Ap [n_row]) ;
		ASSERT (p2 >  pfirst && p2 <= Ap [n_row]) ;
		for ( ; p < p2 ; p++)
		{
		    /* add to pattern if seen for the first time */
		    col = Ai [p] ;
		    ASSERT (col >= j && col < n_col) ;
		    DEBUG3 ((" "ID, INDEX (col))) ;
		    if (W [col] != Wflag)
		    {
			Ai [pdest++] = col ;
			ASSERT (pdest <= pfirst) ;
			/* flag this column has having been seen for row j */
			W [col] = Wflag ;
			if (col < parent)
			{
			    parent = col ;
			}
		    }
		}
		DEBUG2 (("\n")) ;
		thickness++ ;
	    }
	}

#ifndef NDEBUG
	DEBUG3 (("\nRow "ID" of A'A:\n", INDEX (j))) ;
	for (p = Up [j] ; p < pdest ; p++)
	{
	    DEBUG3 ((" "ID, INDEX (Ai [p]))) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ------------------------------------------------------------------ */
	/* delete rows up to but not including knext */
	/* ------------------------------------------------------------------ */

	krow = knext ;
	pfirst = Ap [knext] ;

	/* we can now use Ai [0..pfirst-1] as workspace for rows of U */

	/* ================================================================== */
	/* === compute jth row of U ========================================= */
	/* ================================================================== */

	/* for each nonzero U (k,j) in column j of U (1:j-1,:) do */
	for (k = Link [j] ; k != EMPTY ; k = Link [k])
	{
	    /* merge row k of U into W */
	    DEBUG2 (("	---- U row "ID, INDEX(k))) ;
	    ASSERT (k >= 0 && k < n_col) ;
	    ASSERT (Up [k] != EMPTY) ;
	    p = Up [k] ;
	    p2 = p + Front_ncols [k] ;
	    DEBUG2 (("  p "ID" p2 "ID"\n        cols:", p, p2)) ;
	    ASSERT (p <= pfirst) ;
	    ASSERT (p2 <= pfirst) ;
	    for ( ; p < p2 ; p++)
	    {
		/* add to pattern if seen for the first time */
		col = Ai [p] ;
		ASSERT (col >= j && col < n_col) ;
		DEBUG3 ((ID, INDEX(col))) ;
		if (W [col] != Wflag)
		{
		    Ai [pdest++] = col ;
		    ASSERT (pdest <= pfirst) ;
		    /* flag this col has having been seen for row j */
		    W [col] = Wflag ;
		    if (col < parent)
		    {
			parent = col ;
		    }
		}
	    }
	    DEBUG2 (("\n")) ;

	    /* mark the row k as deleted */
	    Up [k] = EMPTY ;

	    thickness += Front_nrows [k] ;
	    ASSERT (Front_parent [k] == j) ;
	}

#ifndef NDEBUG
	DEBUG3 (("\nRow "ID" of U prior to supercolumn detection:\n",
	    INDEX (j)));
	for (p = Up [j] ; p < pdest ; p++)
	{
	    DEBUG3 ((" "ID, INDEX (Ai [p]))) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ================================================================== */
	/* === quicky mass elimination ====================================== */
	/* ================================================================== */

	/* this code detects some supernodes, but it might miss */
	/* some because the elimination tree (created on the fly) */
	/* is not yet post-ordered, and because the pattern of A'*A */
	/* is also computed on the fly. */

	/* Front_ncols [j] excludes the diagonal entry, which is not stored */

	for (j2 = j+1 ; j2 < jnext ; j2++)
	{
	    if (W [j2] != Wflag || Link [j2] != EMPTY)
	    {
		break ;
	    }
	}

	/* the loop above terminated with j2 at the first non-supernode */
	DEBUG1 (("jnext = "ID"\n", INDEX(jnext))) ;
	ASSERT (j2 <= jnext) ;
	jnext = j2 ;
	j2-- ;
	DEBUG1 (("j2 = "ID"\n", INDEX(j2))) ;
	ASSERT (j2 < n_col) ;

	npivots = j2-j+1 ;
	thickness -= npivots ;

	/* If the matrix is structurally singular, then thickness can be < 0. */
	/* This condition is checked in UMFPACK_symbolic before calling */
	/* this routine, however.   So if thickness < 0, we have a bug. */
	ASSERT (thickness >= 0) ;

	/* rows j:j2 have the same nonzero pattern, except for columns j:j2-1 */

	if (j2 > j)
	{
	    /* supernode detected, prune the pattern of new row j */
	    ASSERT (parent == j+1) ;
	    ASSERT (j2 < n_col) ;
	    DEBUG1 (("Supernode detected, j "ID" to j2 "ID"\n",
	    	INDEX (j), INDEX (j2))) ;

	    parent = n_col ;
	    p2 = pdest ;
	    pdest = Up [j] ;
	    for (p = Up [j] ; p < p2 ; p++)
	    {
		col = Ai [p] ;
		ASSERT (col >= 0 && col < n_col) ;
		ASSERT (W [col] == Wflag) ;
		if (col > j2)
		{
		    /* keep this col in the pattern of the new row j */
		    Ai [pdest++] = col ;
		    if (col < parent)
		    {
			parent = col ;
		    }
		}
	    }
	}

	DEBUG1 (("Front_ncols["ID"] = "ID"\n", INDEX(j), Front_ncols [j])) ;
	DEBUG1 (("Parent ["ID"] = "ID"\n", INDEX(j), INDEX(parent))) ;
	ASSERT (parent > j2) ;

	if (parent == n_col)
	{
	    /* this front has no parent - it is the root of a subtree */
	    parent = EMPTY ;
	}

#ifndef NDEBUG
	DEBUG3 (("\nFinal row "ID" of U after supercolumn detection:\n",
	    INDEX (j))) ;
	for (p = Up [j] ; p < pdest ; p++)
	{
	    DEBUG3 ((" "ID" ("ID")", INDEX (Ai [p]), INDEX (W [Ai [p]]))) ;
	    ASSERT (W [Ai [p]] == Wflag) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ================================================================== */
	/* === frontal matrix =============================================== */
	/* ================================================================== */

	/* front has Front_npivots [j] pivots */
	/* contribution block is Front_nrows [j] -by- Front_ncols [j] */
	/* j is first column in the front */

	Front_npivots [j] = npivots ;
	Front_nrows [j] = thickness ;
	Front_ncols [j] = pdest - Up [j] ;

	if (Front_nrows [j] == 0 || Front_ncols [j] == 0)
	{
	    /* front has no contribution block and thus needs no parent */
	    Up [j] = EMPTY ;
	    parent = EMPTY ;
	}

	Front_parent [j] = parent ;
	ASSERT (npivots > 0) ;

	/* Front_parent [j] is the first column of the parent frontal matrix */

	DEBUG1 (("\n\n==== Front "ID", pivot columns "ID":"ID" contrib: "ID
	    "-by-"ID"\n", INDEX(nfr), INDEX(j),INDEX(j+npivots-1),
	    Front_nrows [j], Front_ncols [j])) ;
	nfr++ ;

	/* ================================================================== */
	/* === prepare this row for its parent ============================== */
	/* ================================================================== */

	if (parent != EMPTY)
	{
	    Link [j] = Link [parent] ;
	    Link [parent] = j ;
	}

	ASSERT (jnext > j) ;

	jlast = j ;
    }

    /* ====================================================================== */
    /* === scan the fronts ================================================== */
    /* ====================================================================== */

    *nfr_out = nfr ;

    /* use Ap for Front_child and use Link for Front_sibling [ */
    Front_child = Ap ;
    Front_sibling = Link ;

    /* use W for Front_maxfr [ */
    Front_maxfr = W ;

    for (j = 0 ; j < n_col ; j++)
    {
	Front_child [j] = EMPTY ;
	Front_sibling [j] = EMPTY ;
	Front_maxfr [j] = EMPTY ;
    }

    DEBUG1 (("\n\n========================================FRONTS:\n")) ;

    /* ---------------------------------------------------------------------- */
    /* find max front size for tree rooted at node j, for each front j */
    /* ---------------------------------------------------------------------- */

    for (j = 0 ; j < n_col ; j++)
    {
	DEBUG1 ((""ID" : npiv "ID" nrows "ID" ncols "ID" parent "ID" ",
		INDEX (j), Front_npivots [j], Front_nrows [j], Front_ncols [j],
		Front_parent [j])) ;
	if (Front_npivots [j] > 0)
	{
	    /* this is a frontal matrix */
	    parent = Front_parent [j] ;
	    npivots = Front_npivots [j] ;
	    nrows = Front_nrows [j] ;
	    ncols = Front_ncols [j] ;
	    frsize = (npivots + nrows) * (npivots + ncols) ;

	    DEBUG1 ((" a front, frsize "ID", true parent: "ID"\n", frsize,
		INDEX (parent))) ;

	    Front_maxfr [j] = MAX (Front_maxfr [j], frsize) ;
	    DEBUG1 (("Front_maxfr [j = "ID"] = "ID"\n",
		INDEX (j), Front_maxfr [j])) ;

	    if (parent != EMPTY)
	    {
		ASSERT (Front_npivots [parent] > 0) ;
		ASSERT (parent > j) ;

		/* find the maximum frontsize of self and children */
		Front_maxfr [parent] = MAX (Front_maxfr [parent],
			Front_maxfr [j]) ;
		DEBUG1 (("Front_maxfr [parent = "ID"] = "ID"\n",
		    INDEX (parent), Front_maxfr [parent]));
	    }
	}
	DEBUG1 (("\n")) ;
    }

    /* ---------------------------------------------------------------------- */
    /* place the children in link lists - bigger fronts will tend to be last */
    /* ---------------------------------------------------------------------- */

    for (j = n_col-1 ; j >= 0 ; j--)
    {
	if (Front_npivots [j] > 0)
	{
	    /* this is a frontal matrix */
	    parent = Front_parent [j] ;
	    if (parent != EMPTY)
	    {
		/* place the front in link list of the children its parent */
		Front_sibling [j] = Front_child [parent] ;
		Front_child [parent] = j ;
	    }
	}
    }

#ifndef NDEBUG
    DEBUG1 (("\n\n========================================FRONTS (again):\n")) ;
    nfr2 = 0 ;
    for (j = 0 ; j < n_col ; j++)
    {
	if (Front_npivots [j] > 0)
	{
	    DEBUG1 (( ""ID" :  nfr "ID" npiv "ID" nrows "ID" ncols "ID
		" parent "ID" maxfr "ID"\n", INDEX (j), nfr2,
		Front_npivots [j], Front_nrows [j], Front_ncols [j],
		INDEX (Front_parent [j]), Front_maxfr [j])) ;

	    /* this is a frontal matrix */

	    /* dump the link list of children */
	    DEBUG1 (("    Children: ")) ;
	    for (f = Front_child [j] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 ((ID, INDEX (f))) ;
		ASSERT (Front_parent [f] == j) ;
	    }
	    DEBUG1 (("\n")) ;

	    parent = Front_parent [j] ;
	    if (parent != EMPTY)
	    {
		/* Assert that the parent front can absorb the child element */
		ASSERT (Front_npivots [parent] > 0) ;
		ASSERT (Front_nrows [j]
		<= Front_npivots [parent] + Front_nrows [parent]) ;
		ASSERT (Front_ncols [j]
		<= Front_npivots [parent] + Front_ncols [parent]) ;
	    }
	    nfr2++ ;
	}
    }
    ASSERT (nfr == nfr2) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* Order the front tree via depth-first-search */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < n_col ; i++)
    {
	if (Front_npivots [i] > 0 && Front_child [i] != EMPTY)
	{

#ifndef NDEBUG
	    DEBUG1 (("Before partial sort, front "ID"\n", i)) ;
	    nchild = 0 ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 (("        "ID"  "ID"\n", f, Front_maxfr [f])) ;
		nchild++ ;
	    }
#endif

	    /* find the biggest front in the child list */
	    fprev = EMPTY ;
	    maxfrsize = EMPTY ;
	    bigfprev = EMPTY ;
	    bigf = EMPTY ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		frsize = Front_maxfr [f] ;
		if (frsize >= maxfrsize)
		{
		    /* this is the biggest seen so far */
		    maxfrsize = frsize ;
		    bigfprev = fprev ;
		    bigf = f ;
		}
		fprev = f ;
	    }
	    ASSERT (bigf != EMPTY) ;

	    fnext = Front_sibling [bigf] ;

	    DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID" fprev "
		ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ;

	    if (fnext != EMPTY)
	    {
		/* if fnext is EMPTY, then bigf is already at the end of list */

		if (bigfprev == EMPTY)
		{
		    /* delete bigf from the front of the list */
		    Front_child [i] = fnext ;
		}
		else
		{
		    /* delete bigf from the middle of the list */
		    Front_sibling [bigfprev] = fnext ;
		}

		/* put bigf at the end of the list */
		Front_sibling [bigf] = EMPTY ;
		ASSERT (Front_child [i] != EMPTY) ;
		ASSERT (fprev != bigf) ;
		ASSERT (fprev != EMPTY) ;
		Front_sibling [fprev] = bigf ;
	    }

#ifndef NDEBUG
	    DEBUG1 (("After partial sort, front "ID"\n", i)) ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 (("        "ID"  "ID"\n", f, Front_maxfr [f])) ;
		ASSERT (Front_npivots [f] > 0) ;
		nchild-- ;
	    }
	    ASSERT (nchild == 0) ;
#endif

	}
    }

    /* Front_maxfr no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* postorder the supernodal column elimination tree */
    /* ---------------------------------------------------------------------- */

    /* use W for Front_order ( */
    Front_order = W ;

    /* use Ai as Stack for UMF_order_front_tree [ */
    Stack = Ai ;

    for (i = 0 ; i < n_col ; i++)
    {
	Front_order [i] = EMPTY ;
    }

#ifndef NDEBUG
    UMF_nbug = n_col ;	/* frontal id's are in the range 0..n_col-1 */
    UMF_fbug = nfr ;	/* total number of frontal matrices */
#endif

    k = 0 ;
    for (i = 0 ; i < n_col ; i++)
    {
	if (Front_parent [i] == EMPTY && Front_npivots [i] != 0)
	{
	    DEBUG1 (("Root of front tree "ID"\n", INDEX (i))) ;
	    k = UMF_order_front_tree (i, k, Front_child, Front_sibling,
		Front_order, Stack) ;
	}
    }

    /* Stack no longer needed ] */
    /* Front_child, Front_sibling no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* construct the column permutation (return in Up) */
    /* ---------------------------------------------------------------------- */

    /* Front_order [i] = k means that front i is kth front in the new order. */
    /* i is in the range 0 to n_col-1, and k is in the range 0 to nfr-1 */

    /* Use Ai as workspace for Winv [ */
    Winv = Ai ;
    for (k = 0 ; k < nfr ; k++)
    {
	Winv [k] = EMPTY ;
    }

    /* compute the inverse of Front_order, so that Winv [k] = i */
    /* if Front_order [i] = k */

    DEBUG1 (("\n\nComputing output column permutation:\n")) ;
    for (i = 0 ; i < n_col ; i++)
    {
	k = Front_order [i] ;
	if (k != EMPTY)
	{
	    DEBUG1 (("Front "ID" new order: "ID"\n", INDEX (i), INDEX (k))) ;
	    ASSERT (k >= 0 && k < nfr) ;
	    ASSERT (Winv [k] == EMPTY) ;
	    Winv [k] = i ;
	}
    }

    /* Use Up as output permutation */
    kk = 0 ;
    for (k = 0 ; k < nfr ; k++)
    {
	i = Winv [k] ;
	DEBUG1 (("Old Front "ID" New Front "ID" npivots "ID"\n",
	    i, k, Front_npivots [i])) ;
	ASSERT (i >= 0 && i < n_col) ;
	ASSERT (Front_npivots [i] > 0) ;
	for (npiv = 0 ; npiv < Front_npivots [i] ; npiv++)
	{
	    Up [kk] = i + npiv ;
	    DEBUG1 (("    Cperm ["ID"] = "ID"\n", INDEX (kk), INDEX(Up [kk]))) ;
	    kk++ ;
	}
    }
    ASSERT (kk == n_col) ;

    /* Winv no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* apply the postorder traversal to renumber the frontal matrices */
    /* ---------------------------------------------------------------------- */

    /* use Ai as workspace */

    UMF_apply_order (Front_npivots, Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_nrows,   Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_ncols,   Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_parent,  Front_order, Ai, n_col, nfr) ;

    /* fix the parent to refer to the new numbering */
    for (i = 0 ; i < nfr ; i++)
    {
	parent = Front_parent [i] ;
	if (parent != EMPTY)
	{
	    ASSERT (parent >= 0 && parent < n_col) ;
	    ASSERT (Front_order [parent] >= 0 && Front_order [parent] < nfr) ;
	    Front_parent [i] = Front_order [parent] ;
	}
    }

    /* Front_order longer needed ) */

#ifndef NDEBUG
    DEBUG1 (("\nFinal frontal matrices:\n")) ;
    for (i = 0 ; i < nfr ; i++)
    {
	DEBUG1 (("Final front "ID": npiv "ID" nrows "ID" ncols "ID" parent "
	    ID"\n", INDEX (i), Front_npivots [i], Front_nrows [i],
	    Front_ncols [i], INDEX (Front_parent [i]))) ;
    }
    DEBUGk (-1, ("\numf_analyze done\n")) ;
#endif

    *p_ncompactions = ncompactions ;
    return (TRUE) ;
}

