/* ========================================================================== */
/* === UMFPACK_solve ======================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 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.  Solves a linear system using the numerical factorization
    computed by UMFPACK_numeric.  See umfpack_solve.h for more details.
    Dynamically allocates several O(n) work arrays.

    Dynamic memory usage:  UMFPACK_solve calls UMF_malloc twice, for
    workspace of size n*sizeof(double) + n*sizeof(Int).  Next, if iterative
    refinement is request and Ax=b or A'x=b is being solved, three more calls
    to UMF_malloc are made to obtain 3*n*sizeof(double) more space.
    On return, all of this workspace is free'd via UMF_free.

*/

#include "umf_internal.h"
#include "umf_valid_numeric.h"
#include "umf_solve.h"
#include "umf_malloc.h"
#include "umf_free.h"

#ifndef NDEBUG
PRIVATE Int init_count ;
#endif

GLOBAL Int UMFPACK_solve
(
    const char sys [ ],
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
    double X [ ],
    const double B [ ],
    void *NumericHandle,
    const double Control [UMFPACK_CONTROL],
    double User_Info [UMFPACK_INFO]
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    NumericType *Numeric ;
    Int n, i, irstep, status ;
    double Info2 [UMFPACK_INFO], *Info, tstart, tend ;
    Int *Pattern ;
    double *W, *Y, *Z, *S ;

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

    tstart = umfpack_timer ( ) ;

#ifndef NDEBUG
    init_count = UMF_malloc_count ;
#endif

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

    if (Control)
    {
	/* use the Control array passed to us by the caller. */
	irstep = (Int) Control [UMFPACK_IRSTEP] ;
    }
    else
    {
	/* no Control passed - use defaults instead */
	irstep = UMFPACK_DEFAULT_IRSTEP ;
    }

    if (User_Info)
    {
	/* return Info in user's array */
	Info = User_Info ;
	for (i = UMFPACK_IR_TAKEN ; i <= UMFPACK_SOLVE_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 ;
	}
    }
    Info [UMFPACK_STATUS] = UMFPACK_OK ;
    Info [UMFPACK_SOLVE_FLOPS] = 0 ;

    Numeric = (NumericType *) NumericHandle ;
    if (!UMF_valid_numeric (Numeric))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Numeric_object ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    n = Numeric->n ;
    Info [UMFPACK_N] = n ;

    if (!X || !B || !sys)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

    /* ---------------------------------------------------------------------- */
    /* allocate the workspace */
    /* ---------------------------------------------------------------------- */

    Y = (double *) NULL ;
    Z = (double *) NULL ;
    S = (double *) NULL ;

    /* UMF_solve requires W and Pattern for solving any system */
    W = (double *) UMF_malloc (n, sizeof (double)) ;
    Pattern = (Int *) UMF_malloc (n, sizeof (Int)) ;
    if (!W || !Pattern)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	(void) UMF_free ((void *) W) ;
	(void) UMF_free ((void *) Pattern) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

    if (irstep > 0 && sys)
    {
	if (STRING_MATCH (sys, "Ax=b") || (STRING_MATCH (sys, "A'x=b")))
	{
	    /* UMF_solve needs 3*n double workspace for iter. refinement */
	    Y = (double *) UMF_malloc (n, sizeof (double)) ;
	    Z = (double *) UMF_malloc (n, sizeof (double)) ;
	    S = (double *) UMF_malloc (n, sizeof (double)) ;
	    if (!Y || !Z || !S)
	    {
		Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
		(void) UMF_free ((void *) W) ;
		(void) UMF_free ((void *) Pattern) ;
		(void) UMF_free ((void *) Y) ;
		(void) UMF_free ((void *) Z) ;
		(void) UMF_free ((void *) S) ;
		return (UMFPACK_ERROR_out_of_memory) ;
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* solve the system */
    /* ---------------------------------------------------------------------- */

    status = UMF_solve (sys, Ap, Ai, Ax, X, B, Numeric,
	irstep, Info, Pattern, W, Y, Z, S) ;

    /* ---------------------------------------------------------------------- */
    /* free the workspace */
    /* ---------------------------------------------------------------------- */

    /* note that free() is not called if the pointer is NULL */
    (void) UMF_free ((void *) W) ;
    (void) UMF_free ((void *) Pattern) ;
    (void) UMF_free ((void *) Y) ;
    (void) UMF_free ((void *) Z) ;
    (void) UMF_free ((void *) S) ;
    ASSERT (UMF_malloc_count == init_count) ;

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

    Info [UMFPACK_STATUS] = status ;
    if (status == UMFPACK_OK)
    {
	tend = umfpack_timer ( ) ;
	Info [UMFPACK_SOLVE_TIME] = MAX (0, tend - tstart) ;
    }

    return (status) ;
}

