/* ========================================================================== */
/* === UMFPACK_report_numeric =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 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.    */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Prints the Numeric object.
    See umfpack_report_numeric.h for details.

    Dynamic memory usage:  Allocates a size n*sizeof(Int)
    workspace via a single call to UMF_malloc and then frees all of it via
    UMF_free on return.  The workspace is not allocated if an early error
    return occurs  before the workspace is needed.

*/

#include "umf_internal.h"
#include "umf_valid_numeric.h"
#include "umf_report_perm.h"
#include "umf_report_vector.h"
#include "umf_malloc.h"
#include "umf_free.h"


PRIVATE Int UMF_report_L
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
) ;


PRIVATE Int UMF_report_U
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
) ;


GLOBAL Int UMFPACK_report_numeric
(
    const char name [ ],
    void *NumericHandle,
    const double Control [UMFPACK_CONTROL]
)
{
    Int prl, *W, n ;
    NumericType *Numeric ;

    if (Control)
    {
	prl = (Int) Control [UMFPACK_PRL] ;
    }
    else
    {
	prl = UMFPACK_DEFAULT_PRL ;
    }

    if (prl <= 2)
    {
	return (UMFPACK_OK) ;
    }

    PRINTF (("\n")) ;
    if (name)
    {
	PRINTF (("%s: ", name)) ;
    }
    PRINTF (("Numeric object:  ")) ;

    Numeric = (NumericType *) NumericHandle ;
    if (!UMF_valid_numeric (Numeric))
    {
	PRINTF (("ERROR: invalid\n\n")) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    n = Numeric->n ;

    if (prl >= 4)
    {
	PRINTF (("\n    n: "ID"\n", n)) ;
	PRINTF (("    relative pivot tolerance used:             %g\n",
	    Numeric->relpt)) ;
	PRINTF (("    relaxed amalgamation parameter used:       %g\n",
	    Numeric->relax)) ;
	PRINTF (("    initial allocation parameter used:         %g\n",
	    Numeric->alloc_init)) ;
	PRINTF (("    peak size of variable-size part (Units):   "ID"\n",
	    Numeric->max_usage)) ;
	PRINTF (("    memory defragmentations:                   "ID"\n",
	    Numeric->ngarbage)) ;
	PRINTF (("    memory reallocations:                      "ID"\n",
	    Numeric->nrealloc)) ;
	PRINTF (("    costly memory reallocations:               "ID"\n",
	    Numeric->ncostly)) ;
	PRINTF (("    entries in compressed pattern (L and U):   "ID"\n",
	    Numeric->isize)) ;
	PRINTF (("    number of nonzeros in L (excl diag):       "ID"\n",
	    Numeric->lnz)) ;
	PRINTF (("    number of entries stored in L (excl diag): "ID"\n",
	    Numeric->nLentries)) ;
	PRINTF (("    number of nonzeros in U (excl diag):       "ID"\n",
	    Numeric->unz)) ;
	PRINTF (("    number of entries stored in U (excl diag): "ID"\n",
	    Numeric->nUentries)) ;
	PRINTF (("    factorization floating-point operations:   %g\n",
	    Numeric->flops)) ;
    }

    W = (Int *) UMF_malloc (n, sizeof (Int)) ;
    if (!W)
    {
	PRINTF ((" ERROR: out of memory to check Numeric object\n\n")) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

    if (UMF_report_perm ("Row permutation, P", n, Numeric->Rperm, W, prl, 0)
	!= UMFPACK_OK)
    {
	(void) UMF_free ((void *) W) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    if (UMF_report_perm ("Column permutation, Q", n, Numeric->Cperm, W, prl,0)
	!= UMFPACK_OK)
    {
	(void) UMF_free ((void *) W) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    if (!UMF_report_L (Numeric, W, prl))
    {
	(void) UMF_free ((void *) W) ;
	PRINTF ((" ERROR: invalid\n\n")) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    if (!UMF_report_U (Numeric, W, prl))
    {
	(void) UMF_free ((void *) W) ;
	PRINTF ((" ERROR: invalid\n\n")) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    (void) UMF_report_vector ("Diagonal of U", n, Numeric->D, prl, 0) ;

    (void) UMF_free ((void *) W) ;

    PRINTF4 (("    ")) ;
    if (name)
    {
	PRINTF (("%s: ", name)) ;
    }
    PRINTF4 (("Numeric object:  ")) ;
    PRINTF (("OK\n\n")) ;
    return (UMFPACK_OK) ;
}


/* ========================================================================== */
/* === UMF_report_L ========================================================= */
/* ========================================================================== */

PRIVATE Int UMF_report_L
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
)
{
    Int k, deg, *ip, j, row, n, *Lpos, *Lilen, valid, k1,
	*Lip, newLchain, llen, prl1, pos, lp, p ;
    double *xp ;

    /* ---------------------------------------------------------------------- */

    ASSERT (prl >= 3) ;

    n = Numeric->n ;
    Lpos = Numeric->Lpos ;
    Lilen = Numeric->Lilen ;
    Lip = Numeric->Lip ;
    prl1 = prl ;

    PRINTF4 ((
    "L in Numeric object, in column-oriented compressed-pattern form:\n"
    "    Diagonal entries are all equal to 1.0 (not stored)\n")) ;

    ASSERT (Pattern) ;

    /* ---------------------------------------------------------------------- */
    /* print L */
    /* ---------------------------------------------------------------------- */

    k1 = 12 ;

    for (k = 0 ; k < n ; k++)
    {
	/* if prl is 4, print the first 10 entries of the first 10 columns */
	if (k1 > 0)
	{
	    prl = prl1 ;
	}

	lp = Lip [k] ;
	newLchain = (lp < 0) ;
	if (newLchain)
	{
	    lp = -lp ;
	    deg = 0 ;
	}

	if (k1-- > 0)
	{
	    prl = prl1 ;
	}
	else if (prl == 4)
	{
	    PRINTF (("    ...\n")) ;
	    prl-- ;
	}

	PRINTF4 (("\n    column "ID":", INDEX (k))) ;

	/* ------------------------------------------------------------------ */
	/* make column of L in Pattern [0..deg-1] */
	/* ------------------------------------------------------------------ */

	/* remove pivot row */
	pos = Lpos [k] ;
	if (pos != EMPTY)
	{
	    PRINTF4 (("  remove row "ID" at position "ID".",
		INDEX (Pattern [pos]), INDEX (pos))) ;
	    valid = (!newLchain) && (deg > 0) && (pos < deg) && (pos >= 0)
		&& (Pattern [pos] == k) ;
	    if (!valid)
	    {
		return (FALSE) ;
	    }
	    Pattern [pos] = Pattern [--deg] ;
	}

	/* concatenate the pattern */
	llen = Lilen [k] ;
	if (llen < 0)
	{
	    return (FALSE) ;
	}
	p = lp + UNITS (Int, llen) ;
	xp = (double *) (Numeric->Memory + p) ;
	if ((llen > 0 || deg > 0) && (p + (Int) UNITS (double, deg) > Numeric->size))
	{
	    return (FALSE) ;
	}
	if (llen > 0)
	{
	    PRINTF4 (("  add "ID" entries.", llen)) ;
	    ip = (Int *) (Numeric->Memory + lp) ;
	    for (j = 0 ; j < llen ; j++)
	    {
		Pattern [deg++] = *ip++ ;
	    }
	}

	/* ------------------------------------------------------------------ */
	/* print column k of L */
	/* ------------------------------------------------------------------ */

	PRINTF4 (("  length "ID".", deg)) ;
	if (newLchain)
	{
	    PRINTF4 (("  Start of Lchain.")) ;
	}
	PRINTF4 (("\n")) ;

	for (j = 0 ; j < deg ; j++)
	{
	    row = Pattern [j] ;
	    PRINTF4 (("\trow "ID" : %g ", INDEX (row), *xp)) ;
	    if (row <= k || row >= n)
	    {
		return (FALSE) ;
	    }
	    PRINTF4 (("\n")) ;
	    xp++ ;
	    /* truncate printout, but continue to check L */
	    if (prl == 4 && j == 9 && deg > 10)
	    {
		PRINTF (("\t...\n")) ;
		prl-- ;
	    }
	}
    }

    PRINTF4 (("\n")) ;
    return (TRUE) ;
}


/* ========================================================================== */
/* === UMF_report_U ========================================================= */
/* ========================================================================== */

PRIVATE Int UMF_report_U
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
)
{
    /* ---------------------------------------------------------------------- */

    Int k, deg, j, *ip, col, *Upos, *Uilen, k1, prl1, pos,
	*Uip, n, ulen, p, newUchain, up ;
    double *xp ;

    /* ---------------------------------------------------------------------- */

    ASSERT (prl >= 3) ;

    n = Numeric->n ;
    Upos = Numeric->Upos ;
    Uilen = Numeric->Uilen ;
    Uip = Numeric->Uip ;
    prl1 = prl ;

    PRINTF4 ((
    "U in Numeric object, in row-oriented compressed-pattern form:\n"
    "    Diagonal is stored separately.\n")) ;

    ASSERT (Pattern) ;

    /* ---------------------------------------------------------------------- */
    /* print the sparse part of U */
    /* ---------------------------------------------------------------------- */

    k1 = 12 ;
    deg = 0 ;
    PRINTF4 (("\n    row "ID":  length 0.  End of Uchain.\n", INDEX (n-1))) ;

    for (k = n-1 ; k >= 0 ; k--)
    {

	/* ------------------------------------------------------------------ */
	/* print row k of U */
	/* ------------------------------------------------------------------ */

	/* if prl is 3, print the first 10 entries of the first 10 columns */
	if (k1 > 0)
	{
	    prl = prl1 ;
	}

	up = Uip [k] ;
	ulen = Uilen [k] ;
	if (ulen < 0)
	{
	    return (FALSE) ;
	}
	newUchain = (up < 0) ;
	if (newUchain)
	{
	    up = -up ;
	    p = up + UNITS (Int, ulen) ;
	}
	else
	{
	    p = up ;
	}
	xp = (double *) (Numeric->Memory + p) ;
	if (deg > 0 && (p + (Int) UNITS (double, deg) > Numeric->size))
	{
	    return (FALSE) ;
	}
	for (j = 0 ; j < deg ; j++)
	{
	    col = Pattern [j] ;
	    PRINTF4 (("\tcol "ID" : %g ", INDEX (col), *xp)) ;
	    if (col <= k || col >= n)
	    {
		return (FALSE) ;
	    }
	    PRINTF4 (("\n")) ;
	    xp++ ;
	    /* truncate printout, but continue to check U */
	    if (prl == 4 && j == 9 && deg > 10)
	    {
		PRINTF (("\t...\n")) ;
		prl-- ;
	    }
	}

	/* ------------------------------------------------------------------ */
	/* make row k-1 of U in Pattern [0..deg-1] */
	/* ------------------------------------------------------------------ */

	if (k1-- > 0)
	{
	    prl = prl1 ;
	}
	else if (prl == 4)
	{
	    PRINTF (("    ...\n")) ;
	    prl-- ;
	}

	if (k > 0)
	{
	    PRINTF4 (("\n    row "ID":  ", INDEX (k-1))) ;
	}

	if (newUchain)
	{
	    /* next row is a new Uchain */
	    if (k > 0)
	    {
		deg = ulen ;
		PRINTF4 (("length "ID".  End of Uchain.\n", deg)) ;
		if (up + (Int) UNITS (Int, ulen) > Numeric->size)
		{
		    return (FALSE) ;
		}
		ip = (Int *) (Numeric->Memory + up) ;
		for (j = 0 ; j < deg ; j++)
		{
		    Pattern [j] = *ip++ ;
		}
	    }
	}
	else
	{
	    if (ulen > 0)
	    {
		PRINTF4 (("remove "ID" entries.  ", ulen)) ;
	    }
	    deg -= ulen ;
	    if (deg < 0)
	    {
		return (FALSE) ;
	    }
	    pos = Upos [k] ;
	    if (pos != EMPTY)
	    {
		/* add the pivot column */
		PRINTF4 (("add column "ID" at position "ID".  ",
		    INDEX (k), INDEX (pos))) ;
		if (pos < 0 || pos > deg)
		{
		    return (FALSE) ;
		}
		Pattern [deg++] = Pattern [pos] ;
		Pattern [pos] = k ;
	    }
	    PRINTF4 (("length "ID".\n", deg)) ;
	}
    }

    prl = prl1 ;
    PRINTF4 (("\n")) ;
    return (TRUE) ;
}

