//------------------------------------------------------------------------------
// UMFPACK/Source/umf_dump.c: debug routines for UMFPACK
//------------------------------------------------------------------------------

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

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

/* These routines, and external variables, are used only when debugging. */
/* If debugging is disabled (for normal operation) then this entire file */
/* becomes empty */

#include "umf_internal.h"

#ifndef NDEBUG

/* These global debugging variables and arrays do not exist if debugging */
/* is disabled at compile time (which is the default). */
Int UMF_debug = -999 ;
Int UMF_allocfail = FALSE ;
double UMF_gprob = -1.0 ;

/* static debugging arrays used only in UMF_dump_rowcol */
PRIVATE Int UMF_DBflag = 0 ;
PRIVATE Int UMF_DBpacked [UMF_DBMAX+1] ;
PRIVATE Int UMF_DBscatter [UMF_DBMAX+1] ;

/* ========================================================================== */
/* === UMF_DBinit =========================================================== */
/* ========================================================================== */

/* clear the debugging arrays */

PRIVATE void UMF_DBinit
(
    void
)
{
    Int i ;

    /* Int_MAX is defined in umfpack.h */
    if (UMF_DBflag < 1 || UMF_DBflag == Int_MAX)
    {
	/* clear the debugging arrays */
	UMF_DBflag = 0 ;
	for (i = 0 ; i <= UMF_DBMAX ; i++)
	{
	    UMF_DBscatter [i] = 0 ;
	    UMF_DBpacked  [i] = 0 ;
	}
    }

    UMF_DBflag++ ;

    /* UMF_DBflag > UMF_DBscatter [0...UMF_DBmax] is now true */
}

/* ========================================================================== */
/* === UMF_dump_dense ======================================================= */
/* ========================================================================== */

void UMF_dump_dense
(
    Entry *C,
    Int dim,
    Int m,
    Int n
)
{

    /* dump C [1..m,1..n], with column dimenstion dim */
    Int i, j;

    if (UMF_debug < 7) return ;
    if (C == (Entry *) NULL)
    {
	DEBUG7 (("No dense matrix allocated\n")) ;
	return ;
    }
    DEBUG8 ((" dimension= "ID" rows= "ID" cols= "ID"\n", dim, m, n)) ;

    for (i = 0 ; i < m ; i++)
    {
	DEBUG9 ((ID": ", i)) ;
	for (j = 0 ; j < n ; j++)
	{
	    EDEBUG9 (C [i+j*dim]) ;
	    if (j % 6 == 5) DEBUG9 (("\n     ")) ;
	}
	DEBUG9 (("\n")) ;
    }

    for (i = 0 ; i < m ; i++)
    {
	for (j = 0 ; j < n ; j++)
	{
	    if (IS_ZERO (C [i+j*dim]))
	    {
		DEBUG8 ((".")) ;
	    }
	    else
	    {
		DEBUG8 (("X")) ;
	    }
	}
	DEBUG8 (("\n")) ;
    }
}

/* ========================================================================== */
/* === UMF_dump_element ===================================================== */
/* ========================================================================== */

void UMF_dump_element
(
    NumericType *Numeric,
    WorkType *Work,
    Int e,
    Int clean
)
{

    Int i, j, k, *Rows, *Cols, nrows, ncols, *E, row, col,
	*Row_degree, *Col_degree ;
    Entry *C ;
    Element *ep ;
    Unit *p ;

    if (UMF_debug < 7) return ;

    if (e == 0)
    {
	UMF_dump_current_front (Numeric, Work, FALSE) ;
	return ;
    }

    DEBUG7 (("\n====================ELEMENT: "ID" ", e)) ;
    if (!Numeric || !Work || !Numeric->Memory)
    {
	DEBUG7 ((" No Numeric, Work\n")) ;
	return ;
    }
    DEBUG7 ((" nel: "ID" of "ID, e, Work->nel)) ;
    E = Work->E ;
    if (!E)
    {
	DEBUG7 ((" No elements\n")) ;
	return ;
    }
    if (e < 0 || e > Work->nel)
    {
	DEBUG7 (("e out of range!\n")) ;
	return ;
    }
    if (!E [e])
    {
	DEBUG7 ((" deallocated\n")) ;
	return ;
    }
    DEBUG7 (("\n")) ;
    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;

    p = Numeric->Memory + E [e] ;
    DEBUG7 (("ep "ID"\n", (Int) (p-Numeric->Memory))) ;
    GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ;
    DEBUG7 (("nrows "ID" nrowsleft "ID"\n", nrows, ep->nrowsleft)) ;
    DEBUG7 (("ncols "ID" ncolsleft "ID"\n", ncols, ep->ncolsleft)) ;
    DEBUG7 (("cdeg-cdeg0 "ID" rdeg-rdeg0 "ID" next "ID"\n",
    ep->cdeg - Work->cdeg0, ep->rdeg - Work->rdeg0, ep->next)) ;

    DEBUG8 (("rows: ")) ;
    k = 0 ;
    for (i = 0 ; i < ep->nrows ; i++)
    {
	row = Rows [i] ;
	if (row >= 0)
	{
	    DEBUG8 ((" "ID, row)) ;
	    ASSERT (row < Work->n_row) ;
	    if ((k++ % 10) == 9) DEBUG8 (("\n")) ;
	    ASSERT (IMPLIES (clean, NON_PIVOTAL_ROW (row))) ;
	}
    }

    DEBUG8 (("\ncols: ")) ;
    k = 0 ;
    for (j = 0 ; j < ep->ncols ; j++)
    {
	col = Cols [j] ;
	if (col >= 0)
	{
	    DEBUG8 ((" "ID, col)) ;
	    ASSERT (col < Work->n_col) ;
	    if ((k++ % 10) == 9) DEBUG8 (("\n")) ;
	    ASSERT (IMPLIES (clean, NON_PIVOTAL_COL (col))) ;
	}
    }

    DEBUG8 (("\nvalues:\n")) ;
    if (UMF_debug >= 9)
    {
	for (i = 0 ; i < ep->nrows ; i++)
	{
	    row = Rows [i] ;
	    if (row >= 0)
	    {
		DEBUG9 ((ID": ", row)) ;
		k = 0 ;
		for (j = 0 ; j < ep->ncols ; j++)
		{
		    col = Cols [j] ;
		    if (col >= 0)
		    {
			EDEBUG9 (C [i+j*ep->nrows]) ;
			if (k++ % 6 == 5) DEBUG9 (("\n     ")) ;
		    }
		}
		DEBUG9 (("\n")) ;
	    }
	}
    }

    DEBUG7 (("====================\n")) ;
}


/* ========================================================================== */
/* === UMF_dump_rowcol ====================================================== */
/* ========================================================================== */

/* dump a row or a column, from one or more memory spaces */
/* return exact degree */

void UMF_dump_rowcol
(
    Int dumpwhich,		/* 0 for row, 1 for column */
    NumericType *Numeric,
    WorkType *Work,
    Int dumpindex,		/* row or column index to dump */
    Int check_degree	/* true if degree is to be checked */
)
{
    Entry value ;
    Entry *C ;
    Int f, nrows, j, jj, len, e, deg, rowcol, n_row, n_col, *Cols, *Rows, nn,
	dumpdeg, ncols, preve, *E, tpi, *Pattern, approx_deg, not_in_use ;
    Tuple *tp, *tend ;
    Element *ep ;
    Int *Row_tuples, *Row_degree, *Row_tlen ;
    Int *Col_tuples, *Col_degree, *Col_tlen ;
    Unit *p ;
    Int is_there ;

    /* clear the debugging arrays */
    UMF_DBinit () ;

    if (dumpwhich == 0)
    {
	DEBUG7 (("\n====================ROW: "ID, dumpindex)) ;
    }
    else
    {
	DEBUG7 (("\n====================COL: "ID, dumpindex)) ;
    }

    if (dumpindex == EMPTY)
    {
	DEBUG7 ((" (EMPTY)\n")) ;
	return ;
    }

    deg = 0 ;
    approx_deg = 0 ;

    if (!Numeric || !Work)
    {
	DEBUG7 ((" No Numeric, Work\n")) ;
	return ;
    }

    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nn = MAX (n_row, n_col) ;
    E = Work->E ;

    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;

    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;

	if (!E
	|| !Row_tuples || !Row_degree || !Row_tlen
	|| !Col_tuples || !Col_degree || !Col_tlen)
	{
	    DEBUG7 ((" No E, Rows, Cols\n")) ;
	    return ;
	}

	if (dumpwhich == 0)
	{
	    /* dump a row */
	    ASSERT (dumpindex >= 0 && dumpindex < n_row) ;
	    if (!NON_PIVOTAL_ROW (dumpindex))
	    {
		DEBUG7 ((" Pivotal\n")) ;
		return ;
	    }
	    len = Row_tlen [dumpindex] ;
	    dumpdeg = Row_degree [dumpindex] ;
	    tpi = Row_tuples [dumpindex] ;
	}
	else
	{
	    /* dump a column */
	    ASSERT (dumpindex >= 0 && dumpindex < n_col) ;
	    if (!NON_PIVOTAL_COL (dumpindex))
	    {
		DEBUG7 ((" Pivotal\n")) ;
		return ;
	    }
	    len = Col_tlen [dumpindex] ;
	    dumpdeg = Col_degree [dumpindex] ;
	    tpi = Col_tuples [dumpindex] ;
	}

	p = Numeric->Memory + tpi ;
	tp = (Tuple *) p ;
	if (!tpi)
	{
	    DEBUG7 ((" Nonpivotal, No tuple list tuples "ID" tlen "ID"\n",
		tpi, len)) ;
	    return ;
	}
	ASSERT (p >= Numeric->Memory + Numeric->itail) ;
	ASSERT (p <  Numeric->Memory + Numeric->size) ;

	DEBUG7 ((" degree: "ID" len: "ID"\n", dumpdeg, len)) ;
	not_in_use = (p-1)->header.size - UNITS (Tuple, len) ;
	DEBUG7 ((" Tuple list: p+1: "ID" size: "ID" units, "ID" not in use\n",
		(Int) (p-Numeric->Memory), (p-1)->header.size, not_in_use)) ;
	ASSERT (not_in_use >= 0) ;
	tend = tp + len ;
	preve = 0 ;
	for ( ; tp < tend ; tp++)
	{
	    /* row/col of element e, offset is f: */
	    /* DEBUG8 (("    (tp="ID")\n", tp)) ; */
	    e = tp->e ;
	    f = tp->f ;
	    DEBUG8 (("    (e="ID", f="ID")\n", e, f)) ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    /* dump the pattern and values */
	    if (E [e])
	    {
		p = Numeric->Memory + E [e] ;
		GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ;
		if (dumpwhich == 0)
		{
		    Pattern = Cols ;
		    jj = ep->ncols ;
		    is_there = Rows [f] >= 0 ;
		    if (is_there) approx_deg += ep->ncolsleft ;
		}
		else
		{
		    Pattern = Rows ;
		    jj = ep->nrows ;
		    is_there = Cols [f] >= 0 ;
		    if (is_there) approx_deg += ep->nrowsleft ;
		}
		if (!is_there)
		{
			DEBUG8 (("\t\tnot present\n")) ;
		}
		else
		{
		    for (j = 0 ; j < jj ; j++)
		    {
			rowcol = Pattern [j] ;
			value =
			    C [ (dumpwhich == 0) ? (f+nrows*j) : (j+nrows*f) ] ;
			if (rowcol >= 0)
			{
			    DEBUG8 (("\t\t"ID":", rowcol)) ;
			    EDEBUG8 (value) ;
			    DEBUG8 (("\n")) ;
			    if (dumpwhich == 0)
			    {
				/* col must be in the range 0..n_col-1 */
				ASSERT (rowcol < n_col) ;
			    }
			    else
			    {
				/* row must be in the range 0..n_row-1 */
				ASSERT (rowcol < n_row) ;
			    }

			    if (nn <= UMF_DBMAX)
			    {
				if (UMF_DBscatter [rowcol] != UMF_DBflag)
				{
				    UMF_DBpacked [deg++] = rowcol ;
				    UMF_DBscatter [rowcol] = UMF_DBflag ;
				}
			    }
			}
		    }
		}
		/* the (e,f) tuples should be in order of their creation */
		/* this means that garbage collection will not jumble them */
		ASSERT (preve < e) ;
		preve = e ;
	    }
	    else
	    {
		DEBUG8 (("\t\tdeallocated\n")) ;
	    }
	}

    if (nn <= UMF_DBMAX)
    {
	if (deg > 0)
	{
	    DEBUG7 ((" Assembled, actual deg: "ID" : ", deg)) ;
	    for (j = 0 ; j < deg ; j++)
	    {
		rowcol = UMF_DBpacked [j] ;
		DEBUG8 ((ID" ", rowcol)) ;
		if (j % 20 == 19) DEBUG8 (("\n ")) ;
		ASSERT (UMF_DBscatter [rowcol] == UMF_DBflag) ;
	    }
	    DEBUG7 (("\n")) ;
	}
    }

    /* Col_degree is not maintained when fixQ is true */
    if (check_degree)
    {
	DEBUG8 (("  approx_deg "ID"  dumpdeg "ID"\n", approx_deg, dumpdeg)) ;
	ASSERT (approx_deg == dumpdeg) ;
    }

    DEBUG7 (("====================\n")) ;

    /* deg is now the exact degree */
    /* if nn <= UMF_DBMAX, then UMF_DBscatter [i] == UMF_DBflag for every i */
    /* in the row or col, and != UMF_DBflag if not */

    return ;
}


/* ========================================================================== */
/* === UMF_dump_matrix ====================================================== */
/* ========================================================================== */

void UMF_dump_matrix
(
    NumericType *Numeric,
    WorkType *Work,
    Int check_degree
)
{

    Int e, row, col, intfrag, frag, n_row, n_col, *E, fullsize, actualsize ;
    Element *ep ;
    Unit *p ;

    DEBUG6 (("=================================================== MATRIX:\n")) ;
    if (!Numeric || !Work)
    {
	DEBUG6 (("No Numeric or Work allocated\n")) ;
	return ;
    }
    if (!Numeric->Memory)
    {
	DEBUG6 (("No Numeric->Memory\n")) ;
	return ;
    }

	n_row = Work->n_row ;
	n_col = Work->n_col ;
	DEBUG6 (("n_row "ID" n_col "ID" nz "ID"\n", n_row, n_col, Work->nz)) ;
	DEBUG6 (("============================ ELEMENTS: "ID" \n", Work->nel)) ;
	intfrag = 0 ;
	E = Work->E ;
	if (!E)
	{
	    DEBUG6 (("No elements allocated\n")) ;
	}
	else
	{
	    for (e = 0 ; e <= Work->nel ; e++)
	    {
		UMF_dump_element (Numeric, Work, e, FALSE) ;
		if (e > 0 && E [e])
		{
		    p = Numeric->Memory + E [e] ;
		    ep = (Element *) p ;
		    ASSERT (ep->nrowsleft > 0 || ep->ncolsleft > 0) ;
		    fullsize = GET_BLOCK_SIZE (p) ;
		    actualsize = GET_ELEMENT_SIZE (ep->nrowsleft,ep->ncolsleft);
		    frag =  fullsize - actualsize ;
		    intfrag += frag ;
		    DEBUG7 (("dump el: "ID", full "ID" actual "ID" frag: "ID
			" intfrag: "ID"\n", e, fullsize, actualsize, frag,
			intfrag)) ;
		}
	    }
	}

	DEBUG6 (("CURRENT INTERNAL FRAG in elements: "ID" \n", intfrag)) ;



    DEBUG6 (("======================================== ROWS: "ID"\n", n_row)) ;
    UMF_debug -= 2 ;
    for (row = 0 ; row < n_row ; row++)
    {
	UMF_dump_rowcol (0, Numeric, Work, row, check_degree) ;
    }
    UMF_debug += 2 ;
    DEBUG6 (("======================================== COLS: "ID"\n", n_col)) ;
    UMF_debug -= 2 ;
    for (col = 0 ; col < n_col ; col++)
    {
	UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
    }
    UMF_debug += 2 ;
    DEBUG6 (("============================================= END OF MATRIX:\n"));
}


/* ========================================================================== */
/* === UMF_dump_current_front =============================================== */
/* ========================================================================== */

void UMF_dump_current_front
(
    NumericType *Numeric,
    WorkType *Work,
    Int check
)
{

    Entry *Flublock, *Flblock, *Fublock, *Fcblock ;
    Int fnrows_max, fncols_max, fnrows, fncols, fnpiv, *Frows, *Fcols,
	i, j, *Fcpos, *Frpos, fnr_curr, fnc_curr, *E ;
    if (!Work) return ;
    DEBUG7 (("\n\n========CURRENT FRONTAL MATRIX:\n")) ;

    Flublock = Work->Flublock ;
    Flblock = Work->Flblock ;
    Fublock = Work->Fublock ;
    Fcblock = Work->Fcblock ;

    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;
    fnr_curr = Work->fnr_curr ;
    fnc_curr = Work->fnc_curr ;
    fnrows = Work->fnrows ;
    fncols = Work->fncols ;
    fnpiv = Work->fnpiv ;
    E = Work->E ;

    DEBUG6 (("=== fnpiv= "ID"\n", fnpiv)) ;
    DEBUG6 (("fnrows_max      fncols_max "ID" "ID"\n",fnrows_max, fncols_max)) ;
    DEBUG6 (("fnr_curr        fnc_curr   "ID" "ID"\n",fnr_curr,   fnc_curr)) ;
    DEBUG6 (("fnrows          fncols     "ID" "ID"\n",fnrows,     fncols)) ;
    ASSERT ((fnr_curr % 2 == 1) || fnr_curr == 0) ;
    DEBUG6 (("Pivot row pattern:\n")) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG7 ((ID" "ID" "ID" %d\n", j, Fcols [j], Fcpos [Fcols [j]],
	    j < fncols)) ;
	if (check)
	{
	    ASSERT (Fcols [j] >= 0 && Fcols [j] < Work->n_col) ;
	    ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ;
	}
    }
    DEBUG6 (("Pivot col pattern:\n")) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG7 ((ID" "ID" "ID" %d\n", i, Frows [i], Frpos [Frows [i]],
	    i < fnrows)) ;
	if (check)
	{
	    ASSERT (Frows [i] >= 0 && Frows [i] < Work->n_row) ;
	    ASSERT (Frpos [Frows [i]] == i) ;
	}
    }
    if (UMF_debug < 7) return ;

    if (!E [0])
    {
	DEBUG6 (("current front not allocated\n")) ;
	ASSERT (!Work->Flublock) ;
	return ;
    }

    ASSERT (Work->Flublock == (Entry *) (Numeric->Memory + E [0])) ;
    DEBUG7 (("C  block: ")) ;
    UMF_dump_dense (Fcblock,  fnr_curr, fnrows, fncols) ;
    DEBUG7 (("L  block: ")) ;
    UMF_dump_dense (Flblock,  fnr_curr, fnrows, fnpiv) ;
    DEBUG7 (("U' block: ")) ;
    UMF_dump_dense (Fublock,  fnc_curr, fncols, fnpiv) ;
    DEBUG7 (("LU block: ")) ;
    UMF_dump_dense (Flublock, Work->nb, fnpiv, fnpiv) ;
    if (fnpiv > 0)
    {
	DEBUG7 (("Pivot entry: ")) ;
	EDEBUG7 (Flublock [(fnpiv-1)+(fnpiv-1)*Work->nb]) ;
	DEBUG7 (("\n")) ;
    }
}

/* ========================================================================== */
/* === UMF_dump_lu ========================================================== */
/* ========================================================================== */

void UMF_dump_lu
(
    NumericType *Numeric
)
{
    Int i, n_row, n_col, *Cperm, *Rperm ;

    DEBUG6 (("=============================================== LU factors:\n")) ;
    if (!Numeric)
    {
	DEBUG6 (("No LU factors allocated\n")) ;
	return ;
    }
    n_row = Numeric->n_row ;
    n_col = Numeric->n_col ;
    DEBUG6 (("n_row: "ID" n_col: "ID"\n", n_row, n_col)) ;
    DEBUG6 (("nLentries: "ID" nUentries: "ID"\n",
	Numeric->nLentries, Numeric->nUentries)) ;

    if (Numeric->Cperm)
    {
	Cperm = Numeric->Cperm ;
	DEBUG7 (("Column permutations: (new: old)\n")) ;
	for (i = 0 ; i < n_col ; i++)
	{
	    if (Cperm [i] != EMPTY)
	    {
		DEBUG7 ((ID": "ID"\n", i, Cperm [i])) ;
	    }
	}
    }
    else
    {
	DEBUG7 (("No Numeric->Cperm allocatated\n")) ;
    }

    if (Numeric->Rperm)
    {
	Rperm = Numeric->Rperm ;
	DEBUG7 (("row permutations: (new: old)\n")) ;
	for (i = 0 ; i < n_row ; i++)
	{
	    if (Rperm [i] != EMPTY)
	    {
		DEBUG7 ((ID": "ID"\n", i, Rperm [i])) ;
	    }
	}
    }
    else
    {
	DEBUG7 (("No Numeric->Rperm allocatated\n")) ;
    }

    DEBUG6 (("========================================= END OF LU factors:\n"));
}


/* ========================================================================== */
/* === UMF_dump_memory ====================================================== */
/* ========================================================================== */

void UMF_dump_memory
(
    NumericType *Numeric
)
{

    Unit *p ;
    Int prevsize, s ;
    Int found ;

    if (!Numeric)
    {
	DEBUG6 (("No memory space S allocated\n")) ;
	return ;
    }

    DEBUG6 (("\n ============================================== MEMORY:\n")) ;
    if (!Numeric || !Numeric->Memory)
    {
	DEBUG6 (("No memory space Numeric allocated\n")) ;
	return ;
    }

    DEBUG6 (("S: "ID"\n", (Int) Numeric)) ;
    DEBUG6 (("S->ihead           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("S->itail           : "ID"\n", Numeric->itail)) ;
    DEBUG6 (("S->size            : "ID"\n", Numeric->size)) ;
    DEBUG6 (("S->ngarbage        : "ID"\n", Numeric->ngarbage)) ;
    DEBUG6 (("S->nrealloc        : "ID"\n", Numeric->nrealloc)) ;
    DEBUG6 (("   in use at head           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("   free space               : "ID"\n",
	Numeric->itail - Numeric->ihead)) ;
    DEBUG6 (("   blocks in use at tail    : "ID"\n",
	Numeric->size - Numeric->itail)) ;
    DEBUG6 (("   total in use             : "ID"\n",
	Numeric->size - (Numeric->itail - Numeric->ihead))) ;

    prevsize = 0 ;
    found = FALSE ;

    ASSERT (0 <= Numeric->ihead) ;
    ASSERT (Numeric->ihead <= Numeric->itail) ;
    ASSERT (Numeric->itail <= Numeric->size) ;

    p = Numeric->Memory + Numeric->itail ;

    while (p < Numeric->Memory + Numeric->size)
    {
	DEBUG8 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID,
	    (Int) (p-Numeric->Memory), (Int) (p+1-Numeric->Memory),
	    p->header.prevsize, p->header.size)) ;
	if (p->header.size < 0)
	{
	    DEBUG8 ((" free")) ;
	}

	if (p == Numeric->Memory + Numeric->itail)
	{
	    ASSERT (p->header.prevsize == 0) ;
	}
	else
	{
	    ASSERT (p->header.prevsize > 0) ;
	}

	ASSERT (p->header.size != 0) ;
	s = prevsize >= 0 ? prevsize : -prevsize ;
	ASSERT (p->header.prevsize == s) ;
	/* no adjacent free blocks */
	ASSERT (p->header.size > 0 || prevsize > 0) ;
	if (Numeric->ibig != EMPTY)
	{
	    if (p == Numeric->Memory + Numeric->ibig)
	    {
		ASSERT (p->header.size < 0) ;
		DEBUG8 ((" <===== Numeric->ibig")) ;
		found = TRUE ;
	    }
	}
	s = p->header.size ;
	prevsize = s ;
	s = s >= 0 ? s : -s ;
	p = p + 1 + s ;
	DEBUG8 (("\n")) ;
    }

    ASSERT (p == Numeric->Memory + Numeric->size) ;
    ASSERT (IMPLIES (Numeric->ibig != EMPTY, found)) ;
    DEBUG6 (("============================================= END OF MEMORY:\n"));

}


/* ========================================================================== */
/* === UMF_dump_packed_memory =============================================== */
/* ========================================================================== */

void UMF_dump_packed_memory
(
    NumericType *Numeric,
    WorkType *Work
)
{
    Unit *p, *p3 ;
    Int prevsize, col, row, *Rows, *Cols, ncols, nrows, k, esize,
	*Row_tuples, *Row_degree, *Col_tuples, *Col_degree ;
    Entry *C ;
    Element *ep ;

    Col_degree = Numeric->Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro */
    Row_tuples = Numeric->Uip ;
    Col_tuples = Numeric->Lip ;

    DEBUG6 (("============================================ PACKED MEMORY:\n")) ;
    if (!Numeric || !Numeric->Memory)
    {
	DEBUG6 (("No memory space S allocated\n")) ;
	return ;
    }
    DEBUG6 (("S: "ID"\n", (Int) Numeric)) ;
    DEBUG6 (("S->ihead           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("S->itail           : "ID"\n", Numeric->itail)) ;
    DEBUG6 (("S->size            : "ID"\n", Numeric->size)) ;
    DEBUG6 (("S->ngarbage        : "ID"\n", Numeric->ngarbage)) ;
    DEBUG6 (("S->nrealloc        : "ID"\n", Numeric->nrealloc)) ;
    DEBUG6 (("   in use at head           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("   free space               : "ID"\n",
	Numeric->itail - Numeric->ihead)) ;
    DEBUG6 (("   blocks in use at tail    : "ID"\n",
	Numeric->size - Numeric->itail)) ;
    DEBUG6 (("   total in use             : "ID"\n",
	Numeric->size - (Numeric->itail - Numeric->ihead))) ;

    ASSERT (0 <= Numeric->ihead) ;
    ASSERT (Numeric->ihead <= Numeric->itail) ;
    ASSERT (Numeric->itail <= Numeric->size) ;

    for (row = 0 ; row < Work->n_row ; row++)
    {
	ASSERT (IMPLIES (NON_PIVOTAL_ROW (row), !Row_tuples [row])) ;
    }
    for (col = 0 ; col < Work->n_col ; col++)
    {
	ASSERT (IMPLIES (NON_PIVOTAL_COL (col), !Col_tuples [col])) ;
    }

    prevsize = 0 ;
    p = Numeric->Memory + Numeric->itail ;
    while (p < Numeric->Memory + Numeric->size)
    {
	DEBUG9 (("====================\n")) ;
	DEBUG7 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID"\n",
	    (Int) (p-Numeric->Memory), (Int) (p+1-Numeric->Memory),
	    p->header.prevsize, p->header.size)) ;
	ASSERT (p->header.size > 0) ;

	if (p == Numeric->Memory + Numeric->itail)
	{
	    ASSERT (p->header.prevsize == 0) ;
	}
	else
	{
	    ASSERT (p->header.prevsize > 0) ;
	}

	ASSERT (p->header.prevsize == prevsize) ;
	prevsize = p->header.size ;

	if (p != Numeric->Memory + Numeric->size - 2)
	{

	    p3 = p + 1 ;
	    if (p3 == Numeric->Memory + Work->E [0])
	    {
		/* this is the current frontal matrix */
		UMF_dump_current_front (Numeric, Work, FALSE) ;
	    }
	    else
	    {

		/* this is a packed element */
		GET_ELEMENT (ep, p3, Cols, Rows, ncols, nrows, C) ;
		DEBUG9 (("ep "ID"\n nrows "ID" ncols "ID"\n",
		    (Int) ((p+1)-Numeric->Memory), ep->nrows, ep->ncols)) ;
		DEBUG9 (("rows:")) ;
		for (k = 0 ; k < ep->nrows; k++)
		{
		    row = Rows [k] ;
		    DEBUG9 ((" "ID, row)) ;
		    ASSERT (row >= 0 && row <= Work->n_row) ;
		    if ((k % 10) == 9) DEBUG9 (("\n")) ;
		}
		DEBUG9 (("\ncols:")) ;
		for (k = 0 ; k < ep->ncols; k++)
		{
		    col = Cols [k] ;
		    DEBUG9 ((" "ID, col)) ;
		    ASSERT (col >= 0 && col <= Work->n_col) ;
		    if ((k % 10) == 9) DEBUG9 (("\n")) ;
		}
		DEBUG9 (("\nvalues: ")) ;
		if (UMF_debug >= 9)
		{
		    UMF_dump_dense (C, ep->nrows, ep->nrows, ep->ncols) ;
		}
		esize = GET_ELEMENT_SIZE (ep->nrows, ep->ncols) ;
		DEBUG9 (("esize: "ID"\n", esize)) ;
		ASSERT (esize <= p->header.size) ;
	    }

	}
	else
	{
	    /* this is the final marker block */
	    ASSERT (p->header.size == 1) ;
	}
	p = p + 1 + p->header.size ;
    }

    ASSERT (Numeric->ibig == EMPTY) ;
    ASSERT (p == Numeric->Memory + Numeric->size) ;
    DEBUG6 (("======================================END OF PACKED MEMORY:\n")) ;

}

/* ========================================================================== */
/* === UMF_dump_col_matrix ================================================== */
/* ========================================================================== */

/* This code is the same for real or complex matrices. */

void UMF_dump_col_matrix
(
    const double Ax [ ],	/* Ax [0..nz-1]: real values, in column order */
#ifdef COMPLEX
    const double Az [ ],	/* Az [0..nz-1]: imag values, in column order */
#endif
    const Int Ai [ ],		/* Ai [0..nz-1]: row indices, in column order */
    const Int Ap [ ],		/* Ap [0..n_col]: column pointers */
    Int n_row,			/* number of rows of A */
    Int n_col,			/* number of columns of A */
    Int nz			/* number of entries */
)
{
    Int col, p, p1, p2, row ;
#ifdef COMPLEX
    Int split = SPLIT (Az) ;
#endif

    if (!Ai || !Ap) return ;
    DEBUG6 (("============================================ COLUMN FORM:\n")) ;

    ASSERT (n_col >= 0) ;
    nz = Ap [n_col] ;
    DEBUG2 (("UMF_dump_col:  nz "ID"\n", nz)) ;
    DEBUG2 (("n_row "ID"  \n", n_row)) ;
    DEBUG2 (("n_col "ID"  \n", n_col)) ;

    DEBUG6 ((" n_row = "ID", n_col ="ID" nz = "ID" Ap [0] "ID", Ap [n] "ID"\n",
	n_row, n_col, nz, Ap [0], Ap [n_col])) ;
    ASSERT (Ap [0] == 0) ;
    ASSERT (Ap [n_col] == nz) ;
    for (col = 0 ; col < n_col ; col++)
    {
	p1 = Ap [col] ;
	p2 = Ap [col+1] ;
	DEBUG6 (("col: "ID", length "ID"\n", col, p2 - p1)) ;
	ASSERT (p2 >= p1) ;
	for (p = p1 ; p < p2 ; p++)
	{
	    row = Ai [p] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    DEBUG6 (("\t"ID" ", row)) ;
	    if (Ax != (double *) NULL)
	    {
#ifdef COMPLEX
		if (split)
		{
		    DEBUG6 ((" (%e+%ei) ", Ax [p], Az [p])) ;
		}
		else
		{
		    DEBUG6 ((" (%e+%ei) ", Ax [2*p], Ax [2*p+1])) ;
		}
#else
		DEBUG6 ((" %e", Ax [p])) ;
#endif
	    }
	    DEBUG6 (("\n")) ;
	}
    }
    DEBUG6 (("========================================== COLUMN FORM done\n")) ;
}


/* ========================================================================== */
/* === UMF_dump_chain ======================================================= */
/* ========================================================================== */

void UMF_dump_chain
(
    Int frontid,
    Int Front_parent [ ],
    Int Front_npivcol [ ],
    Int Front_nrows [ ],
    Int Front_ncols [ ],
    Int nfr
)
{
    Int i, len = 0 ;

    /* print a list of contiguous parents */
    i = frontid ;
    ASSERT (Front_parent [i] == EMPTY ||
	(Front_parent [i] > i && Front_parent [i] < nfr)) ;

    len++ ;
    DEBUG3 (("Chain:\n	"ID" ["ID","ID"]("ID"-by-"ID")\n", i,
		Front_npivcol [i],
		MIN (Front_npivcol [i], Front_nrows [i]),
		Front_nrows [i],
		Front_ncols [i])) ;

    for (i = frontid ; i < nfr ; i++)
    {
	ASSERT (Front_parent [i] == EMPTY ||
	(Front_parent [i] > i && Front_parent [i] < nfr)) ;
	if (Front_parent [i] == i+1)
	{
	    len++ ;
	    DEBUG3 (("\t"ID" ["ID","ID"]("ID"-by-"ID")\n", i+1,
		Front_npivcol [i+1],
		MIN (Front_npivcol [i+1], Front_nrows [i+1]),
		Front_nrows [i+1],
		Front_ncols [i+1])) ;
	}
	else
	{
	    DEBUG2 (("Length of chain: "ID"\n", len)) ;
	    return ;
	}
    }
}


/* ========================================================================== */
/* === UMF_dump_start ======================================================= */
/* ========================================================================== */

void UMF_dump_start
(
    void
)
{
    FILE *ff ;

    AMD_debug_init ("from umfpack") ;

    /* get the debug print level from the "debug.umf" file, if it exists */
    UMF_debug = -999 ;
    ff = fopen ("debug.umf", "r") ;
    if (ff)
    {
	(void) fscanf (ff, ID, &UMF_debug) ;
	(void) fclose (ff) ;
    }

    DEBUG0 (("umfpack: debug version (SLOW!) ")) ;

    DEBUG0 ((" MATLAB: ")) ;
#ifdef MATLAB_MEX_FILE
    DEBUG0 (("mexFunction.\n")) ;
#else
#ifdef MATHWORKS
    DEBUG0 (("yes.\n")) ;
#else
    DEBUG0 (("no.\n")) ;
#endif
#endif

    UMF_gprob = -1.0 ;
    ff = fopen ("gprob.umf", "r") ;
    if (ff)
    {
	(void) fscanf (ff, "%lg", &UMF_gprob) ;
	(void) fclose (ff) ;
	srand (1) ;	/* restart the random number generator */
    }

    if (UMF_gprob > 1.0) UMF_gprob = 1.0 ;
    DEBUG1 (("factor: UMF_gprob: %e UMF_debug "ID"\n", UMF_gprob, UMF_debug)) ;

    DEBUG2 (("sizeof: (bytes / int / Units) \n")) ;
    DEBUG2 (("sizeof (Int)           %u %u %u\n",
    sizeof (Int), sizeof (Int) / sizeof (int), UNITS (Int, 1) )) ;
    DEBUG2 (("sizeof (int32_t)       %u %u %u\n",
    sizeof (int32_t), sizeof (int32_t) / sizeof (int), UNITS (int32_t, 1) )) ;
    DEBUG2 (("sizeof (size_t)        %u %u %u\n",
    sizeof (size_t), sizeof (size_t) / sizeof (int), UNITS (size_t, 1) )) ;
    DEBUG2 (("sizeof (int64_t)       %u %u %u\n",
    sizeof (int64_t),
    sizeof (int64_t) / sizeof (int),
    UNITS (int64_t, 1)));
    DEBUG2 (("sizeof (double)        %u %u %u\n",
    sizeof (double), sizeof (double) / sizeof (int), UNITS (double, 1) )) ;
    DEBUG2 (("sizeof (Unit)          %u %u %u\n",
    sizeof (Unit), sizeof (Unit) / sizeof (int), UNITS (Unit, 1) )) ;
    DEBUG2 (("sizeof (Entry)         %u %u %u\n",
    sizeof (Entry), sizeof (Entry) / sizeof (int), UNITS (Entry, 1) )) ;
    DEBUG2 (("sizeof (Tuple)         %u %u %u\n",
    sizeof (Tuple), sizeof (Tuple) / sizeof (int), UNITS (Tuple, 1) )) ;
    DEBUG2 (("sizeof (Tuple *)       %u %u %u\n",
    sizeof (Tuple *), sizeof (Tuple *) / sizeof (int), UNITS (Tuple *, 1) )) ;
    DEBUG2 (("sizeof (Element)       %u %u %u\n",
    sizeof (Element), sizeof (Element) / sizeof (int), UNITS (Element, 1) )) ;
    DEBUG2 (("sizeof (Element *)     %u %u %u\n",
    sizeof (Element *), sizeof (Element *) / sizeof (int),
    UNITS (Element *, 1) )) ;
    DEBUG2 (("sizeof (WorkType)      %u %u %u\n",
    sizeof (WorkType), sizeof (WorkType) / sizeof (int),
    UNITS (WorkType, 1) )) ;
    DEBUG2 (("sizeof (NumericType)   %u %u %u\n",
    sizeof (NumericType), sizeof (NumericType) / sizeof (int),
    UNITS (NumericType, 1) )) ;
    DEBUG2 (("sizeof (SymbolicType)  %u %u %u\n",
    sizeof (SymbolicType), sizeof (SymbolicType) / sizeof (int),
    UNITS (SymbolicType, 1) )) ;

}


/* ========================================================================== */
/* === UMF_dump_rowmerge ==================================================== */
/* ========================================================================== */

void UMF_dump_rowmerge
(
    NumericType *Numeric,
    SymbolicType *Symbolic,
    WorkType *Work
)
{
    Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, row2,
	fleftmost, nfr, n_row, *Row_degree, i, frontid, row ;

    nfr = Symbolic->nfr ;
    DEBUG3 (("\n================== Row merge sets: nfr "ID"\n", nfr)) ;
    Front_leftmostdesc = Symbolic->Front_leftmostdesc ;
    Front_1strow = Symbolic->Front_1strow ;
    Front_new1strow = Work->Front_new1strow ;
    n_row = Symbolic->n_row ;
    Row_degree = Numeric->Rperm ;
    frontid = Work->frontid ;

    for (i = frontid ; i <= nfr ; i++)
    {
	DEBUG3 (("----------------------\n")) ;
	if (i == nfr) DEBUG3 (("Dummy: ")) ;
	DEBUG3 (("Front "ID" 1strow "ID" new1strow "ID" leftmostdesc "ID,
	    i, Front_1strow [i], Front_new1strow [i], Front_leftmostdesc [i])) ;
	DEBUG3 ((" parent "ID" pivcol "ID"\n", Symbolic->Front_parent [i],
	    Symbolic->Front_npivcol [i])) ;

	if (i == nfr)
	{
	    fleftmost = -1 ;
	    row1 = Front_new1strow [i] ;
	    row2 = n_row-1 ;
	}
	else
	{
	    fleftmost = Front_leftmostdesc [i] ;
	    row1 = Front_new1strow [fleftmost] ;
	    row2 = Front_1strow [i+1] - 1 ;
	}
	DEBUG3 (("Leftmost: "ID"  Rows ["ID" to "ID"], search ["ID" to "ID"]\n",
	    fleftmost, Front_1strow [i], row2, row1, row2)) ;

	for (row = row1 ; row <= row2 ; row++)
	{
	    ASSERT (row >= 0 && row < n_row) ;
	    DEBUG3 (("   Row "ID" live: %d\n", row, NON_PIVOTAL_ROW (row))) ;
	}
    }
}

/* ========================================================================== */
/* === UMF_dump_diagonal_map ================================================ */
/* ========================================================================== */

void UMF_dump_diagonal_map
(
    Int Diagonal_map [ ],
    Int Diagonal_imap [ ],
    Int nn
)
{
    Int row, col ;
    if (Diagonal_map != (Int *) NULL)
    {
	DEBUG2 (("\nDump the Diagonal_map: nn "ID"\n", nn)) ;
	for (col = 0 ; col < nn ; col++)
	{
	    row = Diagonal_map [col] ;
	    DEBUG2 (("     Diagonal_map [col = "ID"] gives "ID": ", col, row)) ;
	    row = UNFLIP (row) ;
	    DEBUG2 ((" row "ID"\n", row)) ;
	    ASSERT (Diagonal_imap [row] == col) ;
	}
    }
}

#endif /* NDEBUG */
