/* ========================================================================== */
/* === UMFPACK_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.  Factorizes A into its LU factors, given a symbolic
    pre-analysis computed by UMFPACK_symbolic.  See umfpack_numeric.h for a
    description.

    Dynamic memory usage: UMFPACK_numeric makes the heaviest usage of memory
    space of all UMFPACK routines.  Here is an outline of how it uses memory:

	1) calls UMF_malloc 13 times, to obtain temporary workspace of size f
	   doubles and 5*(n+1) + (2*n+1) + 3*(c+1) + 2*(r+1) + max(r,c) Int's,
	   where f is the size of the working array used for the frontal
	   matrices, r is the maximum number of rows in the working array, and
	   c is the maximum number of columns in the working array.  (f is
	   less than or equal to r*c.  It is typically but not always equal
	   r*c, since sometimes the maximums are not obtained simultaneously
	   during factorization).

	2) calls UMF_malloc 10 times, for a total space of sizeof (NumericType)
	   bytes, 8*(n+1) integers, and (n+1) doubles.  sizeof (NumericType) is
	   a small constant.  This space is part of the permanent Numeric object
	   (which holds the LU factors) and is not freed on return via
	   UMF_free unless an error occurs.

	3) calls UMF_malloc once, to allocate the variable-sized part of the
	   Numeric object, whose size is the larger of:
	   (Control [UMFPACK_ALLOC_INIT]) *  (the approximate upper bound
	   computed by UMFPACK_symbolic), and the minimum required to start the
	   numerical factorization.  The default value of
	   Control [UMFPACK_ALLOC_INIT] is 1.0.  This request is reduced if it
	   fails.  This object is not freed on return via UMF_free unless
	   an error occurs.

	4) During numerical factorization (inside UMF_kernel), the variable-size
	   block of memory is increased in size via a call to UMF_realloc if
	   it's found to be too small.  This is rare with the default control
	   setting of 1.0 (I've never observed it to happen, but it
	   theoretically could happen).  During factorization, this block holds
	   the pattern and values of L and U at the top end, and the elements
	   (contibution blocks) at the bottom end.

	   The peak memory usage at this point is roughly:

		f + n doubles (for the frontal matrix, f is usually r*c)
		15n + 3c + 2r + max(r,c) integers
		plus the size of the variable-sized part,

	   where r is the maximum number of rows in the working array used
	   to hold the current frontal matrix, and c is the maximum number of
	   columns in the working array.

	   The peak value of the variable-sized object is estimated in
	   UMFPACK_*symbolic (Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]).  Unless
	   there are very many dense columns, this is usually the peak memory
	   usage for all of UMFPACK (both symbolic and numeric factorization).

	5) After numerical factorization all of
	   the objects allocated in step (1) are freed via UMF_free.

	6) The variable-sized block is reduced to hold just L and U, via a call
	    to UMF_realloc, since the frontal matrices are no longer needed.

	7) This leaves a total of 11 objects allocated by UMF_malloc that form
	   the LU factorization.  These remain if UMFPACK_numeric was successful.
	   Otherwise, they are all freed via UMF_free.

    Dynamic memory usage of UMFPACK_free_numeric:

	1) It frees, via UMF_free, all 11 objects allocated for the
	   Numeric object, in a prior call to UMFPACK_numeric.

*/

/* ========================================================================== */

#include "umf_internal.h"
#include "umf_valid_symbolic.h"
#include "umf_set_stats.h"
#include "umf_kernel.h"
#include "umf_malloc.h"
#include "umf_free.h"
#include "umf_realloc.h"

#ifndef NDEBUG
PRIVATE Int init_count ;
#endif

PRIVATE Int work_alloc
(
    WorkType *Work
) ;

PRIVATE void free_work
(
    WorkType *Work
) ;

PRIVATE Int numeric_alloc
(
    NumericType **NumericHandle,
    SymbolicType *Symbolic,
    double alloc_init
) ;

PRIVATE void error
(
    NumericType **Numeric,
    WorkType *Work
) ;


/* ========================================================================== */

GLOBAL Int UMFPACK_numeric
(
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
    void *SymbolicHandle,
    void **NumericHandle,
    const double Control [UMFPACK_CONTROL],
    double User_Info [UMFPACK_INFO]
)
{

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

    NumericType *Numeric ;
    SymbolicType *Symbolic ;
    WorkType WorkSpace, *Work ;
    Int n, newsize, i, status, pivot_option ;
    Unit *new ;
    double Info2 [UMFPACK_INFO], *Info, alloc_init, relax, relpt, tstart, tend,
	relax2, relax3 ;

    /* ---------------------------------------------------------------------- */
    /* get the amount of time used by the process so far */
    /* ---------------------------------------------------------------------- */

    tstart = umfpack_timer ( ) ;

    /* ---------------------------------------------------------------------- */
    /* initialize and check inputs */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    init_count = UMF_malloc_count ;
#endif

    if (Control)
    {
	/* use the Control array passed to us by the caller. */
	relpt = Control [UMFPACK_PIVOT_TOLERANCE] ;
	relax = Control [UMFPACK_RELAXED_AMALGAMATION] ;
	relax2 = Control [UMFPACK_RELAXED2_AMALGAMATION] ;
	relax3 = Control [UMFPACK_RELAXED3_AMALGAMATION] ;
	alloc_init = Control [UMFPACK_ALLOC_INIT] ;
	pivot_option = (Int) Control [UMFPACK_PIVOT_OPTION] ;
    }
    else
    {
	/* no Control passed - use defaults instead */
	relpt = UMFPACK_DEFAULT_PIVOT_TOLERANCE ;
	relax = UMFPACK_DEFAULT_RELAXED_AMALGAMATION ;
	relax2 = UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ;
	relax3 = UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ;
	alloc_init = UMFPACK_DEFAULT_ALLOC_INIT ;
	pivot_option = UMFPACK_DEFAULT_PIVOT_OPTION ;
    }
    relpt = MAX (0.0, MIN (relpt, 1.0)) ;
    relax = MAX (0.0, relax) ;
    relax2 = MAX (0.0, relax2) ;
    relax3 = MAX (0.0, relax3) ;
    alloc_init = MAX (0.0, alloc_init) ;

    if (User_Info)
    {
	/* return Info in user's array */
	Info = User_Info ;
	for (i = UMFPACK_NUMERIC_SIZE ; i <= UMFPACK_NUMERIC_TIME ; i++)
	{
	    Info [i] = EMPTY ;
	}
    }
    else
    {
	/* no Info array passed - use local one instead */
	Info = Info2 ;
	for (i = 0 ; i < UMFPACK_INFO ; i++)
	{
	    Info [i] = EMPTY ;
	}
    }

    Symbolic = (SymbolicType *) SymbolicHandle ;
    *NumericHandle = (void *) NULL ;
    Numeric = (NumericType *) NULL ;
    if (!UMF_valid_symbolic (Symbolic))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Symbolic_object ;
	return (UMFPACK_ERROR_invalid_Symbolic_object) ;
    }

    n = Symbolic->n ;
    Info [UMFPACK_STATUS] = UMFPACK_OK ;
    Info [UMFPACK_N] = n ;
    Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ;
    Info [UMFPACK_NUMERIC_DEFRAG] = 0 ;
    Info [UMFPACK_NUMERIC_REALLOC] = 0 ;
    Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = 0 ;

    if (!Ap || !Ai || !Ax)
    {
	/* Ap, Ai, and/or Ax missing */
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

    Info [UMFPACK_NZ] = Ap [n] ;

    /* ---------------------------------------------------------------------- */
    /* allocate the Work object */
    /* ---------------------------------------------------------------------- */

    Work = &WorkSpace ;
    Work->n = n ;
    Work->maxfrsize = Symbolic->maxfrsize ;
    Work->maxnrows = Symbolic->maxnrows ;
    Work->maxncols = Symbolic->maxncols ;

    if (!work_alloc (Work))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Numeric, Work) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    ASSERT (UMF_malloc_count == init_count + 13) ;

    /* ---------------------------------------------------------------------- */
    /* allocate Numeric object */
    /* ---------------------------------------------------------------------- */

    if (!numeric_alloc (&Numeric, Symbolic, alloc_init))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Numeric, Work) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    ASSERT (UMF_malloc_count == init_count + 13 + 11) ;

    /* set control parameters */
    Numeric->relpt = relpt ;
    Numeric->relax = relax ;
    Numeric->relax2 = relax2 ;
    Numeric->relax3 = relax3 ;
    Numeric->alloc_init = alloc_init ;
    Numeric->pivot_option = pivot_option ;

    DEBUG0 (("umf control: relpt %g relax [%g %g %g] init %g inc %g red %g\n",
	relpt, relax, relax2, relax3, alloc_init,
	UMF_REALLOC_INCREASE, UMF_REALLOC_REDUCTION)) ;
    DEBUG0 (("umf pivot option: %d\n", pivot_option)) ;

    /* ---------------------------------------------------------------------- */
    /* factorize */
    /* ---------------------------------------------------------------------- */

    status = UMF_kernel (Ap, Ai, Ax, Numeric, Work, Symbolic) ;
    Info [UMFPACK_STATUS] = status ;
    Info [UMFPACK_VARIABLE_INIT] = Numeric->init_usage ;
    if (status != UMFPACK_OK)
    {
	/* out of memory, matrix is singular, or pattern has changed */
	error (&Numeric, Work) ;
	return (status) ;
    }

    /* ---------------------------------------------------------------------- */
    /* free Work object */
    /* ---------------------------------------------------------------------- */

    free_work (Work) ;
    ASSERT (UMF_malloc_count == init_count + 11) ;

    /* ---------------------------------------------------------------------- */
    /* reduce Numeric->Memory to hold just the LU factors at the head */
    /* ---------------------------------------------------------------------- */

    /* just changes the size of one object: */
    newsize = Numeric->ihead ;
    if (newsize < Numeric->size)
    {
	new = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ;
	if (new)
	{
	    /* realloc succeeded (how can it fail since the size is reduced?) */
	    Numeric->Memory = new ;
	    Numeric->size = newsize ;
	}
    }
    Numeric->ihead = Numeric->size ;
    Numeric->itail = Numeric->ihead ;
    Numeric->tail_usage = 0 ;
    Numeric->ibig = EMPTY ;
    /* UMF_mem_alloc_tail_block can no longer be called (no tail marker) */

    /* ---------------------------------------------------------------------- */
    /* report the results and return the Numeric object */
    /* ---------------------------------------------------------------------- */

    UMF_set_stats (
	Info,
	Symbolic,
	(double) Numeric->max_usage,	/* actual peak Numeric->Memory */
	(double) Numeric->size,		/* actual final Numeric->Memory */
	Numeric->flops,			/* actual "true flops" */
	(double) Numeric->lnz + n,	/* actual nz in L */
	(double) Numeric->unz + n,	/* actual nz in U */
	(double) Numeric->maxfrsize,	/* actual largest front size */
	ACTUAL) ;

    Info [UMFPACK_NUMERIC_DEFRAG] = Numeric->ngarbage ;
    Info [UMFPACK_NUMERIC_REALLOC] = Numeric->nrealloc ;
    Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = Numeric->ncostly ;
    Info [UMFPACK_COMPRESSED_PATTERN] = Numeric->isize ;
    Info [UMFPACK_LU_ENTRIES] = Numeric->nLentries + Numeric->nUentries + n ;

    Numeric->valid = NUMERIC_VALID ;
    *NumericHandle = (void *) Numeric ;

    /* Numeric has 11 objects */
    ASSERT (UMF_malloc_count == init_count + 11) ;

    /* ---------------------------------------------------------------------- */
    /* get the time used by UMFPACK_numeric */
    /* ---------------------------------------------------------------------- */

    tend = umfpack_timer ( ) ;
    Info [UMFPACK_NUMERIC_TIME] = MAX (0, tend - tstart) ;

    return (UMFPACK_OK) ;
}


/* ========================================================================== */
/* === numeric_alloc ======================================================== */
/* ========================================================================== */

/* Allocate the Numeric object */

PRIVATE Int numeric_alloc
(
    NumericType **NumericHandle,
    SymbolicType *Symbolic,
    double alloc_init
)
{
    Int n, min_usage, trying ;
    NumericType *Numeric ;
    double nsize, bsize ;

    ASSERT (Symbolic) ;
    n = Symbolic->n ;
    *NumericHandle = (NumericType *) NULL ;

    /* 1 allocation: */
    Numeric = (NumericType *) UMF_malloc (1, sizeof (NumericType)) ;

    if (!Numeric)
    {
	return (FALSE) ;	/* out of memory */
    }
    Numeric->valid = 0 ;
    *NumericHandle = Numeric ;

    /* 9 allocations: */
    Numeric->D = (double *) UMF_malloc (n+1, sizeof (double)) ;
    Numeric->Rperm = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Cperm = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Lpos = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Lilen = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Lip = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Upos = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Uilen = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Numeric->Uip = (Int *) UMF_malloc (n+1, sizeof (Int)) ;

    Numeric->Memory = (Unit *) NULL ;

    if (!Numeric->D || !Numeric->Rperm || !Numeric->Cperm || !Numeric->Upos ||
	!Numeric->Lpos || !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip ||
	!Numeric->Uip)
    {
	return (FALSE) ;	/* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* allocate initial Numeric->Memory for LU factors and elements */
    /* ---------------------------------------------------------------------- */

    nsize = (alloc_init * Symbolic->num_mem_usage_est) * (1.0 + MAX_EPSILON) ;
    nsize = ceil (nsize) ;
    min_usage = Symbolic->num_mem_init_usage ;

    /* Numeric->Memory must be large enough for UMF_kernel_init */
    nsize = MAX (ceil ((double) min_usage * (1.0 + MAX_EPSILON)), nsize) ;

    /* Numeric->Memory cannot be larger in size than Int_MAX / sizeof(Unit) */
    /* For ILP32 mode:  2GB (nsize cannot be bigger than 256 Mwords) */

    bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ;
    DEBUG0 (("bsize %g\n", bsize)) ;

    nsize = MIN (nsize, bsize) ;

    Numeric->size = (Int) nsize ;

    DEBUG0 (("Num init %g usage_est %g numsize "ID" minusage "ID"\n",
	alloc_init, Symbolic->num_mem_usage_est, Numeric->size, min_usage)) ;

    /* allocates 1 object: */
    /* keep trying until successful, or memory request is too small */
    trying = TRUE ;
    while (trying)
    {
	Numeric->Memory = (Unit *) UMF_malloc (Numeric->size, sizeof (Unit)) ;
	if (Numeric->Memory)
	{
	    DEBUG0 (("Successful Numeric->size: "ID"\n", Numeric->size)) ;
	    return (TRUE) ;
	}
	/* too much, reduce the request (but not below the minimum) */
	/* and try again */
	trying = Numeric->size > min_usage ;
	Numeric->size = (Int)
	    (UMF_REALLOC_REDUCTION * ((double) Numeric->size)) ;
	Numeric->size = MAX (min_usage, Numeric->size) ;
    }

    return (FALSE) ;	/* we failed to allocate Numeric->Memory */
}


/* ========================================================================== */
/* === work_alloc =========================================================== */
/* ========================================================================== */

/* Allocate the Work object.  Return TRUE if successful. */

PRIVATE Int work_alloc
(
    WorkType *Work
)
{
    Int n, maxfrsize, maxnrows, maxncols ;

    n = Work->n ;
    maxfrsize = Work->maxfrsize ;
    maxnrows = Work->maxnrows ;
    maxncols = Work->maxncols ;

    /* largest front will be maxnrows-by-maxncols, at most */
    DEBUG1 (("Allocating frontal matrix, size "ID" ("ID"-by-"ID") saved: "ID
	"\n", maxfrsize, maxnrows, maxncols,
	(maxnrows * maxncols) - maxfrsize)) ;

    /* 13 allocations, freed in free_work: */
    Work->Fx = (double *) UMF_malloc (maxfrsize, sizeof (double)) ;
    Work->Frpos = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Work->Fcpos = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Work->Lpattern = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Work->Upattern = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Work->Wp = (Int *) UMF_malloc (n+1, sizeof (Int)) ;
    Work->Frows = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ;
    Work->Fcols = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Wio = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Woi = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Woo = (Int *) UMF_malloc (MAX (maxnrows, maxncols) + 1, sizeof (Int));
    Work->Wm = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ;
    Work->E = (Int *) UMF_malloc (2*n+1, sizeof (Int)) ;

    return (Work->Fx && Work->Frpos
	&& Work->Fcpos && Work->Lpattern && Work->Upattern
	&& Work->E && Work->Frows && Work->Fcols && Work->Wio
	&& Work->Woi && Work->Woo && Work->Wm && Work->Wp) ;
}



/* ========================================================================== */
/* === free_work ============================================================ */
/* ========================================================================== */

PRIVATE void free_work
(
    WorkType *Work
)
{
    if (Work)
    {

	/* free 12 objects - all but E */
	Work->Fx = UMF_free ((void *) Work->Fx) ;
	Work->Frpos = (Int *) UMF_free ((void *) Work->Frpos) ;
	Work->Fcpos = (Int *) UMF_free ((void *) Work->Fcpos) ;
	Work->Lpattern = (Int *) UMF_free ((void *) Work->Lpattern) ;
	Work->Upattern = (Int *) UMF_free ((void *) Work->Upattern) ;
	Work->Wp = (Int *) UMF_free ((void *) Work->Wp) ;
	Work->Frows = (Int *) UMF_free ((void *) Work->Frows) ;
	Work->Fcols = (Int *) UMF_free ((void *) Work->Fcols) ;
	Work->Wio = (Int *) UMF_free ((void *) Work->Wio) ;
	Work->Woi = (Int *) UMF_free ((void *) Work->Woi) ;
	Work->Woo = (Int *) UMF_free ((void *) Work->Woo) ;
	Work->Wm = (Int *) UMF_free ((void *) Work->Wm) ;
	Work->E = (Int *) UMF_free ((void *) Work->E) ;

    }
}



/* ========================================================================== */
/* === error ================================================================ */
/* ========================================================================== */

/* Error return from UMFPACK_numeric.  Free all allocated memory. */

PRIVATE void error
(
    NumericType **Numeric,
    WorkType *Work
)
{
    free_work (Work) ;
    UMFPACK_free_numeric ((void **) Numeric) ;
    ASSERT (UMF_malloc_count == init_count) ;
}

