//------------------------------------------------------------------------------
// UMFPACK/Source/umfpack_report_numeric: print a Numeric object
//------------------------------------------------------------------------------

// UMFPACK, Copyright (c) 2005-2023, Timothy A. Davis, All Rights Reserved.
// SPDX-License-Identifier: GPL-2.0+

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

/*
    User-callable.  Prints the Numeric object.
    See umfpack.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 report_L
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
) ;


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

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

int UMFPACK_report_numeric
(
    void *NumericHandle,
    const double Control [UMFPACK_CONTROL]
)
{
    Int prl, *W, nn, n_row, n_col, n_inner, num_fixed_size, numeric_size,
	npiv ;
    NumericType *Numeric ;

    prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ;

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

    PRINTF (("Numeric object:  ")) ;

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

    n_row = Numeric->n_row ;
    n_col = Numeric->n_col ;
    nn = MAX (n_row, n_col) ;
    n_inner = MIN (n_row, n_col) ;
    npiv = Numeric->npiv ;

    DEBUG1 (("n_row "ID" n_col "ID" nn "ID" n_inner "ID" npiv "ID"\n",
	n_row, n_col, nn, n_inner, npiv)) ;

    /* size of Numeric object, except Numeric->Memory and Numeric->Upattern */
    /* see also UMF_set_stats */
    num_fixed_size =
	UNITS (NumericType, 1)		/* Numeric structure */
	+ UNITS (Entry, n_inner+1)	/* D */
	+ UNITS (Int, n_row+1)		/* Rperm */
	+ UNITS (Int, n_col+1)		/* Cperm */
	+ 6 * UNITS (Int, npiv+1)	/* Lpos, Uilen, Uip, Upos, Lilen, Lip */
	+ ((Numeric->scale != UMFPACK_SCALE_NONE) ?
		UNITS (Entry, n_row) : 0) ; /* Rs */

    DEBUG1 (("num fixed size: "ID"\n", num_fixed_size)) ;
    DEBUG1 (("Numeric->size "ID"\n", Numeric->size)) ;
    DEBUG1 (("ulen units "ID"\n", UNITS (Int, Numeric->ulen))) ;

    /* size of Numeric->Memory is Numeric->size */
    /* size of Numeric->Upattern is Numeric->ulen */
    numeric_size = num_fixed_size + Numeric->size
	+ UNITS (Int, Numeric->ulen) ;

    DEBUG1 (("numeric total size "ID"\n", numeric_size)) ;

    if (prl >= 4)
    {
	PRINTF (("\n    n_row: "ID"  n_col: "ID"\n", n_row, n_col)) ;

	PRINTF (("    relative pivot tolerance used:              %g\n",
	    Numeric->relpt)) ;
	PRINTF (("    relative symmetric pivot tolerance used:    %g\n",
	    Numeric->relpt2)) ;

	PRINTF (("    matrix scaled: ")) ;
	if (Numeric->scale == UMFPACK_SCALE_NONE)
	{
	    PRINTF (("no")) ;
	}
	else if (Numeric->scale == UMFPACK_SCALE_SUM)
	{
	    PRINTF (("yes (divided each row by sum abs value in each row)\n")) ;
	    PRINTF (("    minimum sum (abs (rows of A)):              %.5e\n",
		Numeric->rsmin)) ;
	    PRINTF (("    maximum sum (abs (rows of A)):              %.5e",
		Numeric->rsmax)) ;
	}
	else if (Numeric->scale == UMFPACK_SCALE_MAX)
	{
	    PRINTF (("yes (divided each row by max abs value in each row)\n")) ;
	    PRINTF (("    minimum max (abs (rows of A)):              %.5e\n",
		Numeric->rsmin)) ;
	    PRINTF (("    maximum max (abs (rows of A)):              %.5e",
		Numeric->rsmax)) ;
	}
	PRINTF (("\n")) ;

	PRINTF (("    initial allocation parameter used:          %g\n",
	    Numeric->alloc_init)) ;
	PRINTF (("    frontal matrix allocation parameter used:   %g\n",
	    Numeric->front_alloc_init)) ;
	PRINTF (("    final total size of Numeric object (Units): "ID"\n",
	    numeric_size)) ;
	PRINTF (("    final total size of Numeric object (MBytes): %.1f\n",
	    MBYTES (numeric_size))) ;
	PRINTF (("    peak size of variable-size part (Units):    "ID"\n",
	    Numeric->max_usage)) ;
	PRINTF (("    peak size of variable-size part (MBytes):   %.1f\n",
	    MBYTES (Numeric->max_usage))) ;
	PRINTF (("    largest actual frontal matrix size:         "ID"\n",
	    Numeric->maxfrsize)) ;
	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)) ;
	PRINTF (("    number of nonzeros on diagonal of U:        "ID"\n",
	    Numeric->nnzpiv)) ;
	PRINTF (("    min abs. value on diagonal of U:            %.5e\n",
	    Numeric->min_udiag)) ;
	PRINTF (("    max abs. value on diagonal of U:            %.5e\n",
	    Numeric->max_udiag)) ;
	PRINTF (("    reciprocal condition number estimate:       %.2e\n",
	    Numeric->rcond)) ;
    }

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

    if (Numeric->Rs)
    {
#ifndef NRECIPROCAL
	if (Numeric->do_recip)
	{
	    PRINTF4 (("\nScale factors applied via multiplication\n")) ;
	}
	else
#endif
	{
	    PRINTF4 (("\nScale factors applied via division\n")) ;
	}
	PRINTF4 (("Scale factors, Rs: ")) ;
	(void) UMF_report_vector (n_row, Numeric->Rs, (double *) NULL,
	    prl, FALSE, TRUE) ;
    }
    else
    {
	PRINTF4 (("Scale factors, Rs: (not present)\n")) ;
    }

    PRINTF4 (("\nP: row ")) ;
    if (UMF_report_perm (n_row, Numeric->Rperm, W, prl, 0) != UMFPACK_OK)
    {
	(void) UMF_free ((void *) W) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    PRINTF4 (("\nQ: column ")) ;
    if (UMF_report_perm (n_col, Numeric->Cperm, W, prl, 0) != UMFPACK_OK)
    {
	(void) UMF_free ((void *) W) ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

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

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

    /* The diagonal of U is in "merged" (Entry) form, not "split" form. */
    PRINTF4 (("\ndiagonal of U: ")) ;
    (void) UMF_report_vector (n_inner, (double *) Numeric->D, (double *) NULL,
	prl, FALSE, FALSE) ;

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

    PRINTF4 (("    Numeric object:  ")) ;
    PRINTF (("OK\n\n")) ;
    return (UMFPACK_OK) ;
}


/* ========================================================================== */
/* === report_L ============================================================= */
/* ========================================================================== */

PRIVATE int report_L
(
    NumericType *Numeric,
    Int Pattern [ ],
    Int prl
)
{
    Int k, deg, *ip, j, row, n_row, *Lpos, *Lilen, valid, k1,
	*Lip, newLchain, llen, prl1, pos, lp, p, npiv, n1, *Li ;
    Entry *xp, *Lval ;

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

    ASSERT (prl >= 3) ;

    n_row = Numeric->n_row ;
    npiv = Numeric->npiv ;
    n1 = Numeric->n1 ;
    Lpos = Numeric->Lpos ;
    Lilen = Numeric->Lilen ;
    Lip = Numeric->Lip ;
    prl1 = prl ;
    deg = 0 ;

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

    ASSERT (Pattern != (Int *) NULL) ;

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

    k1 = 12 ;

    /* ---------------------------------------------------------------------- */
    /* print the singleton columns of L */
    /* ---------------------------------------------------------------------- */

    for (k = 0 ; k < n1 ; k++)
    {
	if (k1 > 0)
	{
	    prl = prl1 ;
	}
	lp = Lip [k] ;
	deg = Lilen [k] ;
	Li = (Int *) (Numeric->Memory + lp) ;
	lp += UNITS (Int, deg) ;
	Lval = (Entry *) (Numeric->Memory + lp) ;
	if (k1-- > 0)
	{
	    prl = prl1 ;
	}
	else if (prl == 4)
	{
	    PRINTF (("    ...\n")) ;
	    prl-- ;
	}
	PRINTF4 (("\n    column "ID":", INDEX (k))) ;
	PRINTF4 (("  length "ID".\n", deg)) ;
	for (j = 0 ; j < deg ; j++)
	{
	    row = Li [j] ;
	    PRINTF4 (("\trow "ID" : ", INDEX (row))) ;
	    if (prl >= 4) PRINT_ENTRY (Lval [j]) ;
	    if (row <= k || row >= n_row)
	    {
		return (FALSE) ;
	    }
	    PRINTF4 (("\n")) ;
	    /* truncate printout, but continue to check L */
	    if (prl == 4 && j == 9 && deg > 10)
	    {
		PRINTF (("\t...\n")) ;
		prl-- ;
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* print the regular columns of L */
    /* ---------------------------------------------------------------------- */

    for (k = n1 ; k < npiv ; 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 = (Entry *) (Numeric->Memory + p) ;
	if ((llen > 0 || deg > 0)
	    && (p + (Int) UNITS (Entry, 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" : ", INDEX (row))) ;
	    if (prl >= 4) PRINT_ENTRY (*xp) ;
	    if (row <= k || row >= n_row)
	    {
		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) ;
}


/* ========================================================================== */
/* === report_U ============================================================= */
/* ========================================================================== */

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

    Int k, deg, j, *ip, col, *Upos, *Uilen, k1, prl1, pos,
	*Uip, n_col, ulen, p, newUchain, up, npiv, n1, *Ui ;
    Entry *xp, *Uval ;

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

    ASSERT (prl >= 3) ;

    n_col = Numeric->n_col ;
    npiv = Numeric->npiv ;
    n1 = Numeric->n1 ;
    Upos = Numeric->Upos ;
    Uilen = Numeric->Uilen ;
    Uip = Numeric->Uip ;
    prl1 = prl ;

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

    ASSERT (Pattern != (Int *) NULL) ;

    k1 = 12 ;

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

    deg = Numeric->ulen ;
    if (deg > 0)
    {
	/* make last pivot row of U (singular matrices only) */
	for (j = 0 ; j < deg ; j++)
	{
	    Pattern [j] = Numeric->Upattern [j] ;
	}
    }

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

    for (k = npiv-1 ; k >= n1 ; 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 = (Entry *) (Numeric->Memory + p) ;
	if (deg > 0 && (p + (Int) UNITS (Entry, deg) > Numeric->size))
	{
	    return (FALSE) ;
	}
	for (j = 0 ; j < deg ; j++)
	{
	    col = Pattern [j] ;
	    PRINTF4 (("\tcol "ID" :", INDEX (col))) ;
	    if (prl >= 4) PRINT_ENTRY (*xp) ;
	    if (col <= k || col >= n_col)
	    {
		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)) ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* print the singleton rows of U */
    /* ---------------------------------------------------------------------- */

    for (k = n1 - 1 ; k >= 0 ; k--)
    {
	if (k1 > 0)
	{
	    prl = prl1 ;
	}
	up = Uip [k] ;
	deg = Uilen [k] ;
	Ui = (Int *) (Numeric->Memory + up) ;
	up += UNITS (Int, deg) ;
	Uval = (Entry *) (Numeric->Memory + up) ;
	if (k1-- > 0)
	{
	    prl = prl1 ;
	}
	else if (prl == 4)
	{
	    PRINTF (("    ...\n")) ;
	    prl-- ;
	}
	PRINTF4 (("\n    row "ID":", INDEX (k))) ;
	PRINTF4 (("  length "ID".\n", deg)) ;
	for (j = 0 ; j < deg ; j++)
	{
	    col = Ui [j] ;
	    PRINTF4 (("\tcol "ID" : ", INDEX (col))) ;
	    if (prl >= 4) PRINT_ENTRY (Uval [j]) ;
	    if (col <= k || col >= n_col)
	    {
		return (FALSE) ;
	    }
	    PRINTF4 (("\n")) ;
	    /* truncate printout, but continue to check U */
	    if (prl == 4 && j == 9 && deg > 10)
	    {
		PRINTF (("\t...\n")) ;
		prl-- ;
	    }
	}
    }

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