/* ========================================================================== */
/* === UMF_kernel_init ====================================================== */
/* ========================================================================== */

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

/*
    Initialize the kernel, build tuple lists.  Assumes elements are packed.
    Returns TRUE if successful, FALSE if out of memory.  UMFPACK_numeric
    allocates at least enough space for UMF_kernel_init to succeed; otherwise
    it does not call UMF_kernel_init.  So FALSE is an internal error.  It
    "cannot" occur.
*/

#include "umf_internal.h"
#include "umf_tuple_lengths.h"
#include "umf_build_tuples.h"
#include "umf_mem_init_memoryspace.h"
#include "umf_mem_alloc_element.h"

GLOBAL Int UMF_kernel_init
(
    const Int Ap [ ],		/* user's input matrix (not modified) */
    const Int Ai [ ],
    const double Ax [ ],
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int row, k, oldcol, size, e, p1, p2, p, *ip, nz, *Rows, *Cols, *E, i, *Upos,
	*Lpos, n, *Wp, *Cperm_init, *Frpos, *Fcpos, *Row_degree,
	*Row_tlen, *Col_tuples, *Col_degree, *Col_tlen, deg, oldrow, newrow,
	sym_pivoting ;
    double *xp, *C, unused = 0 ;
    Element *ep ;

#ifndef NDEBUG
    double gprob_save = UMF_gprob ;
    UMF_gprob = 0 ;
#endif

    /* ---------------------------------------------------------------------- */
    /* get parameters */
    /* ---------------------------------------------------------------------- */

    DEBUG0 (("KERNEL INIT\n")) ;

    n = Symbolic->n ;
    ASSERT (n > 0) ;
    nz = Ap [n] ;	/* should be the same as Symbolic->nz */
    ASSERT (nz > 0) ;
    Cperm_init = Symbolic->Cperm_init ;

    /* ---------------------------------------------------------------------- */
    /* initialize the Numeric->Memory space for LU, elements, and tuples */
    /* ---------------------------------------------------------------------- */

    UMF_mem_init_memoryspace (Numeric) ;

    /* ---------------------------------------------------------------------- */
    /* initialize the Work and Numeric objects */
    /* ---------------------------------------------------------------------- */

    /* current front is empty */
    Work->fnpiv = 0 ;
    Work->fncols = 0 ;
    Work->fnrows = 0 ;
    Work->fnzeros = 0 ;		/* fixed in Version 3.2 and following */

    Work->overlap = 0 ;
    Work->nz = nz ;
    Work->nel = n ;
    Work->nelorig = n ;
    Work->prior_element = EMPTY ;
    Work->ulen = 0 ;
    Work->llen = 0 ;
    Work->npiv = 0 ;

    Row_degree = Numeric->Rperm ;
    Col_degree = Numeric->Cperm ;
    /* Row_tuples = Numeric->Uip ; */
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;

    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;
    Wp = Work->Wp ;

    /* D = Numeric->D ; */
    Upos = Numeric->Upos ;
    Lpos = Numeric->Lpos ;

    for (i = 0 ; i <= n ; i++)
    {
	/* D [i] = 0. ; no need to initialize this */
	Upos [i] = EMPTY ;
	Lpos [i] = EMPTY ;
	/* Row_tuples [i] = 0 ; set in UMF_build_tuples */
	Row_degree [i] = 0 ;
	Row_tlen [i] = 0 ;
	/* Col_tuples [i] = 0 ; set in UMF_build_tuples */
	/* Col_degree [i] = 0 ; initialized below */
	Col_tlen [i] = 0 ;
	/* Frpos [i] = EMPTY ;  do this later */
	Fcpos [i] = EMPTY ;
	Wp [i] = EMPTY ;
    }
    Work->Wpflag = -2 ;

    /* When cleared, Wp [0..n] is < 0 and > wpflag. */
    /* In row search, Wp [col] is set to wpflag, which is negative. */
    /* In col search, Wp [row] is set to a position, which is >= 0. */
    /* Wp is cleared immediately after the row and col searches. */

    /* no need to initialize Wm, Wio, Woi, and Woo */
    /* Wm used here for inverse column permutation, to permute the rows. */

    /* clear the external degree counters */
    Work->cdeg0 = INITIAL_TAG_MARK ;
    Work->rdeg0 = INITIAL_TAG_MARK ;

    E = Work->E ;

    Numeric->n = n ;
    Numeric->isize = 0 ;
    Numeric->nLentries = 0 ;
    Numeric->nUentries = 0 ;
    Numeric->lnz = 0 ;
    Numeric->unz = 0 ;
    Numeric->maxfrsize = 0 ;
    Numeric->flops = 0. ;

    /* ---------------------------------------------------------------------- */
    /* allocate the elements, copy the columns of A, and initialize degrees */
    /* ---------------------------------------------------------------------- */

    /* also apply the column pre-ordering.  Ignore dense columns. */
    /* for symmetric pivoting, permute the rows the same as the columns. */

    DEBUG3 (("LOAD_MATRIX:\n")) ;

    /* construct the inverse row/col permutation for sym. pivoting */
    sym_pivoting = Numeric->pivot_option != UMFPACK_DEFAULT_PIVOT_OPTION ;
    if (sym_pivoting)
    {
	for (newrow = 0 ; newrow < n ; newrow++)
	{
	    oldrow = Cperm_init [newrow] ;
	    Frpos [oldrow] = newrow ;
	}
    }

    e = 0 ;
    E [e] = 0 ;
    for (k = 0 ; k < n ; k++)
    {
	oldcol = Cperm_init [k] ;
	p1 = Ap [oldcol] ;
	p2 = Ap [oldcol+1] ;
	deg = p2 - p1 ;
	Col_degree [k] = deg ;
	e++ ;
	E [e] = UMF_mem_alloc_element (Numeric, deg, 1, &Rows, &Cols, &C,
	    &size, &ep) ;
	if (E [e] <= 0)
	{
	    /* We ran out of memory, which can only mean that */
	    /* the pattern (Ap and or Ai) has changed (gotten larger). */
	    DEBUG0 (("Pattern has gotten larger - alloc elements failed\n")) ;
	    DEBUG0 (("column k = "ID" size "ID"\n", k, size)) ;
	    return (FALSE) ;	/* pattern changed */
	}
	Cols [0] = k ;
	ip = Rows ;
	xp = C ;
	if (sym_pivoting)
	{
	    for (p = p1 ; p < p2 ; p++)
	    {
		oldrow = Ai [p] ;
		newrow = Frpos [oldrow] ;
		*ip++ = newrow ;
		*xp++ = Ax [p] ;
		Row_degree [newrow]++ ;
	    }
	}
	else
	{
	    for (p = p1 ; p < p2 ; p++)
	    {
		row = Ai [p] ;
		*ip++ = row ;
		*xp++ = Ax [p] ;
		Row_degree [row]++ ;
	    }
	}
    }

    Col_degree [n] = 0 ;
    Col_tuples [n] = 0 ;

    for (e++ ; e <= 2*n ; e++)
    {
	E [e] = 0 ;
    }

    /* Frpos no longer needed */
    for (i = 0 ; i <= n ; i++)
    {
	Frpos [i] = EMPTY ;
    }

    /* ---------------------------------------------------------------------- */
    /* build the tuple lists */
    /* ---------------------------------------------------------------------- */

    /* if the memory usage changes, then the pattern has changed */

    (void) UMF_tuple_lengths (Numeric, Work, &unused) ;
    if (!UMF_build_tuples (Numeric, Work))
    {
	/* We ran out of memory, which can only mean that */
	/* the pattern (Ap and or Ai) has changed (gotten larger). */
	DEBUG0 (("Pattern has gotten larger - build tuples failed\n")) ;
	return (FALSE) ;	/* pattern changed */
    }

    Numeric->init_usage = Numeric->max_usage ;
    if (Symbolic->num_mem_init_usage != Numeric->init_usage)
    {
	/* the pattern (Ap and or Ai) has changed */
	DEBUG0 (("Pattern has changed in size\n")) ;
	return (FALSE) ;	/* pattern changed */
    }

    /* NOTE:  this test does not detect all changes to Ap and/or Ai since the */
    /* last call to UMFPACK_*symbolic.  The pattern can change with the memory*/
    /* usage remaining the same. */

#ifndef NDEBUG
    DEBUG6 (("Column form of original matrix:\n")) ;
    UMF_dump_col_matrix (Ax, Ai, Ap, n, nz) ;
    /* initialize debugging information, b = A'x */
    if (n <= UMF_DBMAX)
    {
	for (k = 0 ; k < n ; k++)
	{
	    oldcol = Cperm_init [k] ;
	    UMF_DBrhs [k] = 0. ;
	    for (k = Ap [oldcol] ; k < Ap [oldcol+1] ; k++)
	    {
		UMF_DBrhs [k] += Ax [k] * XTRUE (Ai [k], n) ;
	    }
	}
    }
    UMF_gprob = gprob_save ;
    UMF_dump_memory (Numeric) ;
    UMF_dump_matrix (Numeric, Work, FALSE) ;
    DEBUG0 (("kernel init done...\n")) ;
#endif

    return (TRUE) ;

}

