/* ========================================================================== */
/* === UMFPACK_global ======================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* Copyright (c) 2005-2012 by Timothy A. Davis, http://www.suitesparse.com.   */
/* All Rights Reserved.  See ../Doc/License for License.                      */
/* -------------------------------------------------------------------------- */

/*
    Global variables.  UMFPACK uses these function pointers for several
    user-redefinable functions.   The amd_* functions are defined in
    AMD/Source/amd_global.c.

    Function pointer	    default	    for mexFunction
					    (see MATLAB/umfpackmex.c)
    ----------------	    -------	    ---------------
    amd_malloc		    malloc	    mxMalloc
    amd_free		    free	    mxFree
    amd_realloc		    realloc	    mxRealloc
    amd_calloc		    calloc	    mxCalloc
    amd_printf		    printf	    mexPrintf

    umfpack_hypot	    umf_hypot	    umf_hypot
    umfpack_divcomplex	    umf_divcomplex  umf_divcomplex

    This routine is compiled just once for all four versions of UMFPACK
    (int/SuiteSparse_long, double/complex).
*/

#include "umf_internal.h"

double (*umfpack_hypot) (double, double) = umf_hypot ;
int (*umfpack_divcomplex) (double, double, double, double, double *, double *)
    = umf_divcomplex ;


/* ========================================================================== */
/* === umf_hypot ============================================================ */
/* ========================================================================== */

/* There is an equivalent routine called hypot in <math.h>, which conforms
 * to ANSI C99.  However, UMFPACK does not assume that ANSI C99 is available.
 * You can use the ANSI C99 hypot routine with:
 *
 *	#include <math.h>
 *	umfpack_hypot = hypot ;
 *
 * prior to calling any UMFPACK routine.
 *
 * s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately.
 *
 * The NaN case for the double relops x >= y and x+y == x is safely ignored.
 */

double umf_hypot (double x, double y)
{
    double s, r ;
    x = SCALAR_ABS (x) ;
    y = SCALAR_ABS (y) ;
    if (x >= y)
    {
	if (x + y == x)
	{
	    s = x ;
	}
	else
	{
	    r = y / x ;
	    s = x * sqrt (1.0 + r*r) ;
	}
    }
    else
    {
	if (y + x == y)
	{
	    s = y ;
	}
	else
	{
	    r = x / y ;
	    s = y * sqrt (1.0 + r*r) ;
	}
    } 
    return (s) ;
}


/* ========================================================================== */
/* === umf_divcomplex ======================================================= */
/* ========================================================================== */

/* c = a/b where c, a, and b are complex.  The real and imaginary parts are
 * passed as separate arguments to this routine.  The NaN case is ignored
 * for the double relop br >= bi.  Returns TRUE (1) if the denominator is
 * zero, FALSE (0) otherwise.
 *
 * This uses ACM Algo 116, by R. L. Smith, 1962, which tries to avoid
 * underflow and overflow.
 *
 * c can be the same variable as a or b.
 */

int umf_divcomplex
(
    double ar, double ai,	/* real and imaginary parts of a */
    double br, double bi,	/* real and imaginary parts of b */
    double *cr, double *ci	/* real and imaginary parts of c */
)
{
    double tr, ti, r, den ;
    if (SCALAR_ABS (br) >= SCALAR_ABS (bi))
    {
	r = bi / br ;
	den = br + r * bi ;
	tr = (ar + ai * r) / den ;
	ti = (ai - ar * r) / den ;
    }
    else
    {
	r = br / bi ;
	den = r * br + bi ;
	tr = (ar * r + ai) / den ;
	ti = (ai * r - ar) / den ;
    }
    *cr = tr ;
    *ci = ti ;
    return (SCALAR_IS_ZERO (den)) ;
}
