/* ========================================================================== */
/* === umfpack mexFunction ================================================== */
/* ========================================================================== */

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

/*
    Matlab interface for umfpack.

    Factor or solve a sparse linear system, returning either the solution
    x to Ax=b or A'x'=b', or the factorization LU=PAQ.  A must be square
    (n-by-n), sparse, and non-singular, and b must be a dense n-by-1 vector.

    See umfpack.m and umfpack.h for details.

    Note that this mexFunction accesses only the user-callable UMFPACK routines.
    Thus, is also provides another example of how user C code can access
    UMFPACK.
*/


/* ========================================================================== */
/* === Include files ======================================================== */
/* ========================================================================== */

#include "umfpack.h"

#include "mex.h"
#include "matrix.h"
#include <string.h>

#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define STRING_MATCH(s1,s2) (strcmp ((s1), (s2)) == 0)
#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif

static void error
(
    char *s,
    int nlhs,
    mxArray *plhs [ ]
)
{
    int i ;
    for (i = 0 ; i < nlhs ; i++)
    {
	plhs [i] = mxCreateDoubleMatrix (0, 0, mxREAL) ;
    }
    mexErrMsgTxt (s) ;
}

static void warning
(
    char *s,
    int nlhs,
    mxArray *plhs [ ],
    double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO],
    int status,
    int do_info
)
{
    int i ;
    double *OutInfo ;
    umfpack_report_status (Control, status) ;
    umfpack_report_info (Control, Info) ;
    if (do_info > 0)
    {
	/* return Info */
	plhs [do_info] = mxCreateDoubleMatrix (1, UMFPACK_INFO, mxREAL) ;
	OutInfo = mxGetPr (plhs [do_info]) ;
	for (i = 0 ; i < UMFPACK_INFO ; i++)
	{
	    OutInfo [i] = Info [i] ;
	}
    }
    for (i = 0 ; i < nlhs ; i++)
    {
	if (i != do_info)
	{
	    plhs [i] = mxCreateDoubleMatrix (0, 0, mxREAL) ;
	}
    }
    if ((int) Control [UMFPACK_PRL] != 0)
    {
	mexWarnMsgTxt (s) ;
    }
}


/* ========================================================================== */
/* === umfpack ============================================================== */
/* ========================================================================== */

void mexFunction
(
    int nlhs,			/* number of left-hand sides */
    mxArray *plhs [ ],		/* left-hand side matrices */
    int nrhs,			/* number of right--hand sides */
    const mxArray *prhs [ ]	/* right-hand side matrices */
)
{

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

    void *Symbolic, *Numeric ;
    int *Lp, *Li, *Up, *Ui, *Ap, *Ai, *P, *Q, n, do_solve, i, lnz, unz, nn,
	transpose, size, do_info, do_numeric, *Qtree, *Front_npivots, op, k,
	*Front_parent, *Chain_start, *Chain_maxrows, *Chain_maxcols, nz, status,
	nfronts, nchains, nsparse_col, *Ltp, *Lti, *Qinit, print_level ;
    double *Lx, *Ux, *Ax, *B, *X, *p, *q, *User_Control,
	Info [UMFPACK_INFO], Control [UMFPACK_CONTROL], *OutInfo, *p1, *p2,
	*p3, *Ltx ;
    mxArray *Amatrix, *Bmatrix, *User_Control_matrix, *User_Qinit ;
    char *operator, *sys, *operation ;

    /* ---------------------------------------------------------------------- */
    /* get inputs A, b, and the operation to perform */
    /* ---------------------------------------------------------------------- */

    User_Control_matrix = (mxArray *) NULL ;
    User_Qinit = (mxArray *) NULL ;

    do_info = 0 ;
    do_solve = FALSE ;
    do_numeric = TRUE ;
    transpose = FALSE ;

    if (nrhs >= 2 && mxIsChar (prhs [1]))
    {
	op = 1 ;
    }
    else if (nrhs >= 3 && mxIsChar (prhs [2]))
    {
	op = 2 ;
    }
    else
    {
	/* no operator */
	op = 0 ;
    }

    if (op > 0)
    {
	operator = mxArrayToString (prhs [op]) ;

	if (STRING_MATCH (operator, "\\"))
	{

	    /* -------------------------------------------------------------- */
	    /* matrix left divide, x = A\b */
	    /* -------------------------------------------------------------- */

	    /*
		[x, Info] = umfpack (A, '\', b) ;
		[x, Info] = umfpack (A, '\', b, Control) ;
		[x, Info] = umfpack (A, Qinit, '\', b, Control) ;
		[x, Info] = umfpack (A, Qinit, '\', b) ;
	    */

	    operation = "x = A\\b" ;
	    do_solve = TRUE ;
	    Amatrix = (mxArray *) prhs [0] ;
	    Bmatrix = (mxArray *) prhs [op+1] ;

	    if (nlhs == 2)
	    {
		do_info = 1 ;
	    }
	    if (op == 2)
	    {
		User_Qinit = (mxArray *) prhs [1] ;
	    }
	    if ((op == 1 && nrhs == 4) || (op == 2 && nrhs == 5))
	    {
		User_Control_matrix = (mxArray *) prhs [nrhs-1] ;
	    }
	    if (nrhs < 3 || nrhs > 5 || nlhs > 2)
	    {
		error ("umfpack: wrong number of arguments", nlhs, plhs) ;
		return ;
	    }

	}
	else if (STRING_MATCH (operator, "/"))
	{

	    /* -------------------------------------------------------------- */
	    /* matrix right divide, x = b/A */
	    /* -------------------------------------------------------------- */

	    /*
		[x, Info] = umfpack (b, '/', A) ;
		[x, Info] = umfpack (b, '/', A, Control) ;
		[x, Info] = umfpack (b, '/', A, Qinit) ;
		[x, Info] = umfpack (b, '/', A, Qinit, Control) ;
	    */

	    operation = "x = b/A" ;
	    do_solve = TRUE ;
	    transpose = TRUE ;
	    Amatrix = (mxArray *) prhs [2] ;
	    Bmatrix = (mxArray *) prhs [0] ;

	    if (nlhs == 2)
	    {
		do_info = 1 ;
	    }
	    if (nrhs == 5)
	    {
		User_Qinit = (mxArray *) prhs [3] ;
		User_Control_matrix = (mxArray *) prhs [4] ;
	    }
	    else if (nrhs == 4)
	    {
		/* Control is k-by-1 where k > 1, Qinit is 1-by-n */
		if (mxGetM (prhs [3]) == 1)
		{
		    User_Qinit = (mxArray *) prhs [3] ;
		}
		else
		{
		    User_Control_matrix = (mxArray *) prhs [3] ;
		}
	    }
	    else if (nrhs < 3 || nrhs > 5 || nlhs > 2)
	    {
		error ("umfpack: wrong number of arguments", nlhs, plhs) ;
		return ;
	    }

	}
	else if (STRING_MATCH (operator, "symbolic"))
	{

	    /* -------------------------------------------------------------- */
	    /* symbolic factorization only */
	    /* -------------------------------------------------------------- */

	    /*
		[Qtree, Fr, Ch, Info] = umfpack (A, 'symbolic') ;
		[Qtree, Fr, Ch, Info] = umfpack (A, 'symbolic', Control) ;
		[Qtree, Fr, Ch, Info] = umfpack (A, Qinit, 'symbolic') ;
		[Qtree, Fr, Ch, Info] = umfpack (A, Qinit, 'symbolic', Control);
	    */

	    operation = "symbolic factorization" ;
	    do_numeric = FALSE ;
	    Amatrix = (mxArray *) prhs [0] ;

	    if (nlhs == 4)
	    {
		do_info = 3 ;
	    }
	    if (op == 2)
	    {
		User_Qinit = (mxArray *) prhs [1] ;
	    }
	    if ((op == 1 && nrhs == 3) || (op == 2 && nrhs == 4))
	    {
		User_Control_matrix = (mxArray *) prhs [nrhs-1] ;
	    }
	    if (nrhs < 2 || nrhs > 4 || nlhs > 4 || nlhs < 3)
	    {
		error ("umfpack: wrong number of arguments", nlhs, plhs) ;
		return ;
	    }

	}
	else
	{
	    error ("umfpack: operator must be '/', '\\', or 'symbolic'",
		nlhs, plhs) ;
	    return ;
	}
	mxFree (operator) ;

    }
    else if (nrhs > 0)
    {

	/* ------------------------------------------------------------------ */
	/* LU factorization */
	/* ------------------------------------------------------------------ */

	/*
	    [L, U, P, Q, Info] = umfpack (A) ;
	    [L, U, P, Q, Info] = umfpack (A, Control) ;
	    [L, U, P, Q, Info] = umfpack (A, Qinit) ;
	    [L, U, P, Q, Info] = umfpack (A, Qinit, Control) ;
	*/

	operation = "numeric factorization" ;
	Amatrix = (mxArray *) prhs [0] ;

	if (nlhs == 5)
	{
	    do_info = 4 ;
	}
	if (nrhs == 3)
	{
	    User_Qinit = (mxArray *) prhs [1] ;
	    User_Control_matrix = (mxArray *) prhs [2] ;
	}
	else if (nrhs == 2)
	{
	    /* Control is k-by-1 where k > 1, Qinit is 1-by-n */
	    if (mxGetM (prhs [1]) == 1)
	    {
		User_Qinit = (mxArray *) prhs [1] ;
	    }
	    else
	    {
		User_Control_matrix = (mxArray *) prhs [1] ;
	    }
	}
	else if (nrhs > 3 || nlhs > 5 || nlhs < 4)
	{
	    error ("umfpack: wrong number of arguments", nlhs, plhs) ;
	    return ;
	}

    }
    else
    {

	/* ------------------------------------------------------------------ */
	/* return default control settings */
	/* ------------------------------------------------------------------ */

	/*
	    Control = umfpack ;
	    umfpack ;
	*/

	if (nlhs > 1)
	{
	    error ("umfpack: wrong number of arguments", nlhs, plhs) ;
	    return ;
	}

	plhs [0] = mxCreateDoubleMatrix (UMFPACK_CONTROL, 1, mxREAL) ;
	User_Control = mxGetPr (plhs [0]) ;
	umfpack_defaults (User_Control) ;
	if (nlhs == 0)
	{
	    umfpack_report_control (User_Control) ;
	}
	return ;
    }

    /* ---------------------------------------------------------------------- */
    /* check inputs */
    /* ---------------------------------------------------------------------- */

    if (mxGetNumberOfDimensions (Amatrix) != 2)
    {
	error ("umfpack: input matrix A must be 2-dimensional", nlhs, plhs) ;
	return ;
    }
    n = mxGetN (Amatrix) ;
    if (n != mxGetM (Amatrix))
    {
	error ("umfpack: input matrix A must square", nlhs, plhs) ;
	return ;
    }
    if (mxIsComplex (Amatrix))
    {
	error ("umfpack: input matrix A must not be complex", nlhs, plhs) ;
	return ;
    }
    if (!mxIsSparse (Amatrix))
    {
	error ("umfpack: input matrix A must be sparse", nlhs, plhs) ;
	return ;
    }
    Ap = mxGetJc (Amatrix) ;
    Ai = mxGetIr (Amatrix) ;
    Ax = mxGetPr (Amatrix) ;

    if (do_solve)
    {
	if (transpose)
	{
	    if (mxGetM (Bmatrix) != 1 && mxGetN (Bmatrix) != n)
	    {
		error ("umfpack: b has the wrong dimensions", nlhs, plhs) ;
		return ;
	    }
	}
	else
	{
	    if (mxGetM (Bmatrix) != n && mxGetN (Bmatrix) != 1)
	    {
		error ("umfpack: b has the wrong dimensions", nlhs, plhs) ;
		return ;
	    }
	}
	if (mxGetNumberOfDimensions (Bmatrix) != 2)
	{
	    error ("umfpack: input matrix b must be 2-dimensional", nlhs, plhs);
	    return ;
	}
	if (mxIsComplex (Bmatrix))
	{
	    error ("umfpack: input matrix b must not be complex", nlhs, plhs) ;
	    return ;
	}
	if (mxGetClassID (Bmatrix) != mxDOUBLE_CLASS)
	{
	    error ("umfpack: input matrix b must be a double precision matrix",
		nlhs, plhs) ;
	    return ;
	}
	if (mxIsSparse (Bmatrix))
	{
	    error ("umfpack: input matrix b must be dense", nlhs, plhs) ;
	    return ;
	}
	B = mxGetPr (Bmatrix) ;
    }

    /* ---------------------------------------------------------------------- */
    /* set the Control parameters */
    /* ---------------------------------------------------------------------- */

    umfpack_defaults (Control) ;
    if (User_Control_matrix)
    {
	if (mxGetClassID (User_Control_matrix) != mxDOUBLE_CLASS ||
	    mxIsSparse (User_Control_matrix))
	{
	    error ("umfpack: Control must be a dense real matrix", nlhs, plhs) ;
	    return ;
	}
	size = UMFPACK_CONTROL ;
	size = MIN (size, mxGetNumberOfElements (User_Control_matrix)) ;
	User_Control = mxGetPr (User_Control_matrix) ;
	for (i = 0 ; i < size ; i++)
	{
	    Control [i] = User_Control [i] ;
	}
    }
    print_level = (int) Control [UMFPACK_PRL] ;

    /* ---------------------------------------------------------------------- */
    /* get Qinit, if present */
    /* ---------------------------------------------------------------------- */

    if (User_Qinit)
    {
	if (mxGetM (User_Qinit) != 1 || mxGetN (User_Qinit) != n)
	{
	    error ("umfpack: Qinit must be 1-by-n", nlhs, plhs) ;
	    return ;
	}
	if (mxGetNumberOfDimensions (User_Qinit) != 2)
	{
	    error ("umfpack: input Qinit must be 2-dimensional", nlhs, plhs) ;
	    return ;
	}
	if (mxIsComplex (User_Qinit))
	{
	    error ("umfpack: input Qinit must not be complex", nlhs, plhs) ;
	    return ;
	}
	if (mxGetClassID (User_Qinit) != mxDOUBLE_CLASS)
	{
	    error ("umfpack: input Qinit must be a double matrix", nlhs, plhs) ;
	    return ;
	}
	if (mxIsSparse (User_Qinit))
	{
	    error ("umfpack: input Qinit must be dense", nlhs, plhs) ;
	    return ;
	}
	Qinit = (int *) mxMalloc (n * sizeof (int)) ;
	p = mxGetPr (User_Qinit) ;
	for (k = 0 ; k < n ; k++)
	{
	    /* convert from 1-based to 0-based indexing */
	    Qinit [k] = ((int) (p [k])) - 1 ;
	}

    }
    else
    {
	/* umfpack_qsymbolic will call colamd to get Qinit. */
	/* This is the same as calling umfpack_symbolic with Qinit set to NULL*/
	Qinit = (int *) NULL ;
    }

    /* ---------------------------------------------------------------------- */
    /* report the inputs A and Qinit */
    /* ---------------------------------------------------------------------- */

    if (print_level >= 2)
    {
	/* print the operation */
	mexPrintf ("\numfpack: %s\n", operation) ;
    }
    umfpack_report_control (Control) ;
    (void) umfpack_report_matrix ("A", n, Ap, Ai, Ax, "column", Control) ;
    (void) umfpack_report_perm ("Qinit", n, Qinit, Control) ;

    /* ---------------------------------------------------------------------- */
    /* perform the symbolic factorization */
    /* ---------------------------------------------------------------------- */

    status = umfpack_qsymbolic (n, Ap, Ai, Qinit, &Symbolic, Control, Info) ;

    if (Qinit)
    {
	mxFree (Qinit) ;
    }

    if (status != UMFPACK_OK)
    {
	warning ("umfpack: symbolic factorization failed",
	    nlhs, plhs, Control, Info, status, do_info) ;
	return ;
    }

    /* ---------------------------------------------------------------------- */
    /* report the Symbolic object */
    /* ---------------------------------------------------------------------- */

    (void) umfpack_report_symbolic ("umfpack", Symbolic, Control) ;

    /* ---------------------------------------------------------------------- */
    /* perform numeric factorization, or just return symbolic factorization */
    /* ---------------------------------------------------------------------- */

    if (do_numeric)
    {

	/* ------------------------------------------------------------------ */
	/* perform the numeric factorization */
	/* ------------------------------------------------------------------ */

	status = umfpack_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control,
	    Info) ;

	/* ------------------------------------------------------------------ */
	/* free the symbolic factorization (only needed for umfpack_numeric) */
	/* ------------------------------------------------------------------ */

	umfpack_free_symbolic (&Symbolic) ;

	/* ------------------------------------------------------------------ */
	/* report the Numeric object */
	/* ------------------------------------------------------------------ */

	if (status != UMFPACK_OK)
	{
	    warning ("umfpack: numeric factorization failed",
		nlhs, plhs, Control, Info, status, do_info) ;
	    return ;
	}

	(void) umfpack_report_numeric ("umfpack", Numeric, Control) ;

	/* ------------------------------------------------------------------ */
	/* return the solution or the factorization */
	/* ------------------------------------------------------------------ */

	if (do_solve)
	{
	    /* -------------------------------------------------------------- */
	    /* solve Ax=b or A'x'=b', and return just the solution x */
	    /* -------------------------------------------------------------- */

	    if (transpose)
	    {
		plhs [0] = mxCreateDoubleMatrix (1, n, mxREAL) ;
		sys = "A'x=b" ;
	    }
	    else
	    {
		plhs [0] = mxCreateDoubleMatrix (n, 1, mxREAL) ;
		sys = "Ax=b" ;
	    }

	    /* -------------------------------------------------------------- */
	    /* print the right-hand-side, B */
	    /* -------------------------------------------------------------- */

	    (void) umfpack_report_vector ("right-hand side, b", n, B, Control) ;

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

	    X = mxGetPr (plhs [0]) ;

	    status = umfpack_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control,
		Info) ;

	    if (status != UMFPACK_OK)
	    {
		mxDestroyArray (plhs [0]) ;
		umfpack_free_numeric (&Numeric) ;
		warning ("umfpack: umfpack_solve failed",
		    nlhs, plhs, Control, Info, status, do_info) ;
		return ;
	    }

	    /* -------------------------------------------------------------- */
	    /* free the Numeric object */
	    /* -------------------------------------------------------------- */

	    umfpack_free_numeric (&Numeric) ;

	    /* -------------------------------------------------------------- */
	    /* print the solution, X */
	    /* -------------------------------------------------------------- */

	    (void) umfpack_report_vector ("solution, x", n, X, Control) ;

	}
	else
	{

	    /* -------------------------------------------------------------- */
	    /* get L, U, P, and Q */
	    /* -------------------------------------------------------------- */

	    status = umfpack_get_lunz (&lnz, &unz, &nn, Numeric) ;

	    if (status != UMFPACK_OK)
	    {
		umfpack_free_numeric (&Numeric) ;
		warning ("umfpack: umfpack_get_lunz failed",
		    nlhs, plhs, Control, Info, status, do_info) ;
		return ;
	    }
	    /* note: nn is set by umfpack_get_lunz, but is ignored since nn=n */

	    /* temporary space, for L' */
	    Ltp = (int *) mxMalloc ((n+1) * sizeof (int)) ;
	    Lti = (int *) mxMalloc (lnz * sizeof (int)) ;
	    Ltx = (double *) mxMalloc (lnz * sizeof (double)) ;

	    plhs [1] = mxCreateSparse (n, n, unz, mxREAL) ;
	    Up = mxGetJc (plhs [1]) ;
	    Ui = mxGetIr (plhs [1]) ;
	    Ux = mxGetPr (plhs [1]) ;

	    P = (int *) mxMalloc (n * sizeof (int)) ;
	    Q = (int *) mxMalloc (n * sizeof (int)) ;

	    status = umfpack_get_numeric (Ltp, Lti, Ltx, Up, Ui, Ux, P, Q,
		Numeric) ;

	    if (status != UMFPACK_OK)
	    {
		mxFree (Ltp) ;
		mxFree (Lti) ;
		mxFree (Ltx) ;
		mxFree (P) ;
		mxFree (Q) ;
		mxDestroyArray (plhs [1]) ;
		umfpack_free_numeric (&Numeric) ;
		warning ("umfpack: umfpack_get_numeric failed",
		    nlhs, plhs, Control, Info, status, do_info) ;
		return ;
	    }

	    umfpack_free_numeric (&Numeric) ;

	    plhs [2] = mxCreateDoubleMatrix (1, n, mxREAL) ;
	    plhs [3] = mxCreateDoubleMatrix (1, n, mxREAL) ;

	    p = mxGetPr (plhs [2]) ;
	    for (i = 0 ; i < n ; i++)
	    {
		/* UMFPACK is 0-based, but Matlab expects this to be 1-based */
		p [i] = (double) (P [i] + 1) ;
	    }

	    q = mxGetPr (plhs [3]) ;
	    for (i = 0 ; i < n ; i++)
	    {
		/* UMFPACK is 0-based, but Matlab expects this to be 1-based */
		q [i] = (double) (Q [i] + 1) ;
	    }

	    /* permanent copy of L */
	    plhs [0] = mxCreateSparse (n, n, MAX (n, lnz), mxREAL) ;
	    Lp = mxGetJc (plhs [0]) ;
	    Li = mxGetIr (plhs [0]) ;
	    Lx = mxGetPr (plhs [0]) ;

	    /* L = (Lt)' */
	    status = umfpack_transpose (n, Ltp, Lti, Ltx, (int *) NULL,
		(int *) NULL, Lp, Li, Lx) ;

	    if (status != UMFPACK_OK)
	    {
		mxFree (Ltp) ;
		mxFree (Lti) ;
		mxFree (Ltx) ;
		mxFree (P) ;
		mxFree (Q) ;
		mxDestroyArray (plhs [0]) ;
		mxDestroyArray (plhs [1]) ;
		mxDestroyArray (plhs [2]) ;
		mxDestroyArray (plhs [3]) ;
		warning ("umfpack: umfpack_tranpose failed",
		    nlhs, plhs, Control, Info, status, do_info) ;
		return ;
	    }

	    mxFree (Ltp) ;
	    mxFree (Lti) ;
	    mxFree (Ltx) ;

	    /* -------------------------------------------------------------- */
	    /* print L, U, P, and Q */
	    /* -------------------------------------------------------------- */

	    (void) umfpack_report_matrix ("L", n, Lp, Li, Lx, "column",Control);
	    (void) umfpack_report_matrix ("U", n, Up, Ui, Ux, "column",Control);
	    (void) umfpack_report_perm ("P", n, P, Control) ;
	    (void) umfpack_report_perm ("Q", n, Q, Control) ;

	    mxFree (P) ;
	    mxFree (Q) ;

	}

    }
    else
    {

	/* ------------------------------------------------------------------ */
	/* return the symbolic factorization */
	/* ------------------------------------------------------------------ */

	plhs [0] = mxCreateDoubleMatrix (1, n, mxREAL) ;
	Qtree = (int *) mxMalloc (n * sizeof (int)) ;
	Front_npivots = (int *) mxMalloc (n * sizeof (int)) ;
	Front_parent = (int *) mxMalloc (n * sizeof (int)) ;
	Chain_start = (int *) mxMalloc ((n+1) * sizeof (int)) ;
	Chain_maxrows = (int *) mxMalloc (n * sizeof (int)) ;
	Chain_maxcols = (int *) mxMalloc (n * sizeof (int)) ;

	status = umfpack_get_symbolic (&nn, &nz, &nfronts, &nchains,
	    &nsparse_col, Qtree, Front_npivots, Front_parent, Chain_start,
	    Chain_maxrows, Chain_maxcols, Symbolic) ;

	if (status != UMFPACK_OK)
	{
	    mxFree (Qtree) ;
	    mxFree (Front_npivots) ;
	    mxFree (Front_parent) ;
	    mxFree (Chain_start) ;
	    mxFree (Chain_maxrows) ;
	    mxFree (Chain_maxcols) ;
	    mxDestroyArray (plhs [0]) ;
	    umfpack_free_symbolic (&Symbolic) ;
	    warning ("umfpack: umfpack_get_symbolic failed",
		nlhs, plhs, Control, Info, status, do_info) ;
	    return ;
	}

	/* free the symbolic factorization */
	umfpack_free_symbolic (&Symbolic) ;

	plhs [1] = mxCreateDoubleMatrix (nfronts, 2, mxREAL) ;
	plhs [2] = mxCreateDoubleMatrix (nchains+1, 3, mxREAL) ;

	p = mxGetPr (plhs [0]) ;
	for (i = 0 ; i < n ; i++)
	{
	    /* UMFPACK is 0-based, but Matlab expects this to be 1-based */
	    p [i] = (double) (Qtree [i] + 1) ;
	}

	p1 = mxGetPr (plhs [1]) ;
	p2 = p1 + nfronts ;
	for (i = 0 ; i < nfronts ; i++)
	{
	    p1 [i] = (double) (Front_npivots [i]) ;
	    p2 [i] = (double) (Front_parent [i] + 1) ;	/* convert to 1-based */
	}

	p1 = mxGetPr (plhs [2]) ;
	p2 = p1 + nchains + 1 ;
	p3 = p2 + nchains + 1 ;
	for (i = 0 ; i < nchains ; i++)
	{
	    p1 [i] = (double) (Chain_start [i] + 1) ;	/* convert to 1-based */
	    p2 [i] = (double) (Chain_maxrows [i]) ;
	    p3 [i] = (double) (Chain_maxcols [i]) ;
	}
	p1 [nchains] = Chain_start [nchains] + 1 ;
	p2 [nchains] = 0 ;
	p3 [nchains] = 0 ;

	mxFree (Qtree) ;
	mxFree (Front_npivots) ;
	mxFree (Front_parent) ;
	mxFree (Chain_start) ;
	mxFree (Chain_maxrows) ;
	mxFree (Chain_maxcols) ;
    }

    /* ---------------------------------------------------------------------- */
    /* report Info */
    /* ---------------------------------------------------------------------- */

    umfpack_report_info (Control, Info) ;

    if (do_info > 0)
    {
	/* return Info */
	plhs [do_info] = mxCreateDoubleMatrix (1, UMFPACK_INFO, mxREAL) ;
	OutInfo = mxGetPr (plhs [do_info]) ;
	for (i = 0 ; i < UMFPACK_INFO ; i++)
	{
	    OutInfo [i] = Info [i] ;
	}
    }

}

