/* ========================================================================== */
/* === UMF_kernel =========================================================== */
/* ========================================================================== */

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

/*
    Primary factorization routine.   Called by UMFPACK_numeric.
    Returns:
	UMFPACK_OK if successful,
	UMFPACK_ERROR_out_of_memory if out of memory, or
	UMFPACK_ERROR_singular_matrix if the matrix is singular.
	UMFPACK_ERROR_different_pattern if pattern of matrix (Ap and/or Ai)
	   has changed since the call to UMFPACK_*symbolic.
*/

#include "umf_internal.h"
#include "umf_init_front.h"
#include "umf_assemble.h"
#include "umf_scale_column.h"
#include "umf_local_search.h"
#include "umf_create_element.h"
#include "umf_extend_front.h"
#include "umf_blas3_update.h"
#include "umf_kernel_init.h"
#include "umf_kernel_wrapup.h"

GLOBAL Int UMF_kernel
(
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
)
{

    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int frontid, j, k, frontid1, frontid2, chain, nchains, *Chain_start, status,
	*Chain_maxrows, *Chain_maxcols, *Front_npivots, jmax, nb ;

    /* ---------------------------------------------------------------------- */
    /* initialize memory space and load the matrix */
    /* ---------------------------------------------------------------------- */

    if (!UMF_kernel_init (Ap, Ai, Ax, Numeric, Work, Symbolic))
    {
	/* UMF_kernel_init is guaranteed to succeed, since UMFPACK_numeric */
	/* either allocates enough space or if not, UMF_kernel does not get */
	/* called.  So running out of memory here is a fatal error, and means */
	/* that the user changed Ap and/or Ai since the call to */
	/* UMFPACK_*symbolic. */
	return (UMFPACK_ERROR_different_pattern) ;
    }

    /* ---------------------------------------------------------------------- */
    /* get the symbolic factorization */
    /* ---------------------------------------------------------------------- */

    nchains = Symbolic->nchains ;
    Chain_start = Symbolic->Chain_start ;
    Chain_maxrows = Symbolic->Chain_maxrows ;
    Chain_maxcols = Symbolic->Chain_maxcols ;
    Front_npivots = Symbolic->Front_npivots ;
    DEBUG0 (("Starting Kernel, nchains "ID"\n", nchains)) ;
    nb = Symbolic->nb ;

    /* ---------------------------------------------------------------------- */
    /* factorize each chain of frontal matrices */
    /* ---------------------------------------------------------------------- */

    for (chain = 0 ; chain < nchains ; chain++)
    {
	ASSERT (Work->fnrows == 0 && Work->fncols == 0 && Work->fnpiv == 0) ;
	frontid1 = Chain_start [chain] ;
	frontid2 = Chain_start [chain+1] - 1 ;
	Work->fnrows_max = Chain_maxrows [chain] ;
	Work->fncols_max = Chain_maxcols [chain] ;
	DEBUG2 (("Starting chain "ID". start "ID" end "ID" maxrows "ID
	    " maxcols "ID"\n", chain, frontid1, frontid2, Work->fnrows_max,
	    Work->fncols_max)) ;
	ASSERT (Work->fnrows_max * Work->fncols_max <= Work->maxfrsize) ;

	for (frontid = frontid1 ; frontid <= frontid2 ; frontid++)
	{
	    DEBUG2 (("\n\n::: "ID" : Npiv: "ID"\n", frontid, Work->npiv)) ;

	    /* -------------------------------------------------------------- */
	    /* Initialize the pivot column candidate set */
	    /* -------------------------------------------------------------- */

	    /* next pivot (in range 0 to n-1) */
	    k = Work->npiv ;
	    Work->ncand = Front_npivots [frontid] ;
	    jmax = MIN (MAX_CANDIDATES, Work->ncand) ;
	    Work->nextcand = k + jmax ;
	    for (j = 0 ; j < jmax ; j++)
	    {
		Work->Candidates [j] = k + j ;
		DEBUG3 ((""ID" Candidate: "ID"\n", j, Work->Candidates [j])) ;
	    }

	    /* -------------------------------------------------------------- */
	    /* Assemble and factorize the current frontal matrix */
	    /* -------------------------------------------------------------- */

	    while (Work->ncand > 0)
	    {
		status = UMF_local_search (Numeric, Work) ;
		if (status != UMFPACK_OK)
		{
		    return (status) ; /* singular matrix or different pattern */
		}
		Work->ncand-- ;

		/* ---------------------------------------------------------- */
		/* extend the frontal matrix, or start a new one */
		/* ---------------------------------------------------------- */

		if (Work->do_extend)
		{
		    /* apply pending updates if front would grow too much */
		    if (Work->do_update)
		    {
			UMF_blas3_update (Work) ;
		    }
		    /* extend the current front */
		    UMF_extend_front (Work) ;
		}
		else
		{
		    /* finish the current front (if any) and start a new one */
		    if (!UMF_create_element (Numeric, Work))
		    {
			return (UMFPACK_ERROR_out_of_memory) ;
		    }
		    UMF_init_front (Work) ;
		}

		/* ---------------------------------------------------------- */
		/* Numerical & symbolic assembly into current frontal matrix */
		/* ---------------------------------------------------------- */

		UMF_assemble (Numeric, Work) ;

		/* ---------------------------------------------------------- */
		/* scale the pivot column, and save row and column of U and L */
		/* ---------------------------------------------------------- */

		if (!UMF_scale_column (Numeric, Work))
		{
		    return (UMFPACK_ERROR_out_of_memory) ;
		}

		/* ---------------------------------------------------------- */
		/* Numerical update if enough pivots accumulated */
		/* ---------------------------------------------------------- */

		if (Work->fnpiv >= nb)
		{
		    UMF_blas3_update (Work) ;
		}

		Work->pivrow_in_front = FALSE ;
		Work->pivcol_in_front = FALSE ;
	    }
	}

	/* ------------------------------------------------------------------ */
	/* Wrapup the current frontal matrix.  This is the last in a chain */
	/* in the column elimination tree.  The next frontal matrix */
	/* cannot overlap with the current one, which will be its sibling */
	/* in the column etree. */
	/* ------------------------------------------------------------------ */

	if (!UMF_create_element (Numeric, Work))
	{
	    return (UMFPACK_ERROR_out_of_memory) ;
	}

	/* ------------------------------------------------------------------ */
	/* current front is now empty */
	/* ------------------------------------------------------------------ */

	Work->fnrows = 0 ;
	Work->fncols = 0 ;
    }

    /* ---------------------------------------------------------------------- */
    /* end the last Lchain and Uchain and finalize the LU factors */
    /* ---------------------------------------------------------------------- */

    UMF_kernel_wrapup (Numeric, Symbolic, Work) ;
    return (UMFPACK_OK) ;
}

