#line 1 "numpy/core/src/npymath/npy_math_complex.c.src"

/*
 *****************************************************************************
 **       This file was autogenerated from a template  DO NOT EDIT!!!!      **
 **       Changes should be made to the original source (.src) file         **
 *****************************************************************************
 */

#line 1
/*
 * vim: syntax=c
 *
 * Implement some C99-compatible complex math functions
 *
 * Most of the code is taken from the msun library in FreeBSD (HEAD @ 4th
 * October 2013), under the following license:
 *
 * Copyright (c) 2007, 2011 David Schultz <das@FreeBSD.ORG>
 * Copyright (c) 2012 Stephen Montgomery-Smith <stephen@FreeBSD.ORG>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "npy_math_common.h"
#include "npy_math_private.h"
#include <numpy/utils.h>

/*
 * Hack inherited from BSD, the intent is to set the FPU inexact
 * flag in an efficient way. The flag is IEEE specific. See
 * https://github.com/freebsd/freebsd/blob/4c6378299/lib/msun/src/catrig.c#L42
 */
#if !defined(HAVE_CACOSF) || !defined(HAVE_CACOSL) || !defined(HAVE_CASINHF) || !defined(HAVE_CASINHL)
#define raise_inexact() do {                        \
    volatile npy_float NPY_UNUSED(junk) = 1 + tiny; \
} while (0)


static const volatile npy_float tiny = 3.9443045e-31f;
#endif

#line 63

/*==========================================================
 * Constants
 *=========================================================*/
static const npy_cfloat c_1f = {1.0F, 0.0};

/*==========================================================
 * Helper functions
 *
 * These are necessary because we do not count on using a
 * C99 compiler.
 *=========================================================*/
static inline
npy_cfloat
cmulf(npy_cfloat a, npy_cfloat b)
{
    npy_float ar, ai, br, bi;
    ar = npy_crealf(a);
    ai = npy_cimagf(a);
    br = npy_crealf(b);
    bi = npy_cimagf(b);
    return npy_cpackf(ar*br - ai*bi, ar*bi + ai*br);
}

static inline
npy_cfloat
cdivf(npy_cfloat a, npy_cfloat b)
{
    npy_float ar, ai, br, bi, abs_br, abs_bi;
    ar = npy_crealf(a);
    ai = npy_cimagf(a);
    br = npy_crealf(b);
    bi = npy_cimagf(b);
    abs_br = npy_fabsf(br);
    abs_bi = npy_fabsf(bi);

    if (abs_br >= abs_bi) {
        if (abs_br == 0 && abs_bi == 0) {
            /* divide by zeros should yield a complex inf or nan */
            return npy_cpackf(ar/abs_br, ai/abs_bi);
        }
        else {
            npy_float rat = bi/br;
            npy_float scl = 1.0F/(br+bi*rat);
            return npy_cpackf((ar + ai*rat)*scl, (ai - ar*rat)*scl);
        }
    }
    else {
        npy_float rat = br/bi;
        npy_float scl = 1.0F/(bi + br*rat);
        return npy_cpackf((ar*rat + ai)*scl, (ai*rat - ar)*scl);
    }
}

/*==========================================================
 * Custom implementation of missing complex C99 functions
 *=========================================================*/

#ifndef HAVE_CABSF
npy_float
npy_cabsf(npy_cfloat z)
{
    return npy_hypotf(npy_crealf(z), npy_cimagf(z));
}
#endif

#ifndef HAVE_CARGF
npy_float
npy_cargf(npy_cfloat z)
{
    return npy_atan2f(npy_cimagf(z), npy_crealf(z));
}
#endif

/*
 * cexp and (ccos, csin)h functions need to calculate exp scaled by another
 * number.  This can be difficult if exp(x) overflows.  By doing this way, we
 * don't risk overflowing exp. This likely raises floating-point exceptions,
 * if we decide that we care.
 *
 * This is only useful over a limited range, (see below) an expects that the
 * input values are in this range.
 *
 * This is based on the technique used in FreeBSD's __frexp_exp and
 * __ldexp_(c)exp functions by David Schultz.
 *
 * SCALED_CEXP_LOWER = log(FLT_MAX)
 * SCALED_CEXP_UPPER = log(2) + log(FLT_MAX) - log(FLT_TRUE_MIN),
 * where FLT_TRUE_MIN is the smallest possible subnormal number.
 */

#define SCALED_CEXP_LOWERF 88.722839f
#define SCALED_CEXP_UPPERF 192.69492f
#define SCALED_CEXP_LOWER 710.47586007394386
#define SCALED_CEXP_UPPER 1454.9159319953251
#define SCALED_CEXP_LOWERL 11357.216553474703895L
#define SCALED_CEXP_UPPERL 22756.021937783004509L

#if !defined(HAVE_CSINHF) || \
    !defined(HAVE_CCOSHF) || \
    !defined(HAVE_CEXPF)

static
npy_cfloat
_npy_scaled_cexpf(npy_float x, npy_float y, npy_int expt)
{
#if 1 == 1
    const npy_int k = 235;
#endif
#if 1 == 2
    const npy_int k = 1799;
#endif
#if 1 == 3
    const npy_int k = 19547;
#endif
    const npy_float kln2 = k * NPY_LOGE2f;
    npy_float mant, mantcos, mantsin;
    npy_int ex, excos, exsin;

    mant = npy_frexpf(npy_expf(x - kln2), &ex);
    mantcos = npy_frexpf(npy_cosf(y), &excos);
    mantsin = npy_frexpf(npy_sinf(y), &exsin);

    expt += ex + k;
    return npy_cpackf( npy_ldexpf(mant * mantcos, expt + excos),
                         npy_ldexpf(mant * mantsin, expt + exsin));
}

#endif

#ifndef HAVE_CEXPF

npy_cfloat
npy_cexpf(npy_cfloat z)
{
    npy_float x, c, s;
    npy_float r, i;
    npy_cfloat ret;

    r = npy_crealf(z);
    i = npy_cimagf(z);

    if (npy_isfinite(r)) {
        if (r >= SCALED_CEXP_LOWERF && r <= SCALED_CEXP_UPPERF) {
            ret = _npy_scaled_cexpf(r, i, 0);
        }
        else {
            x = npy_expf(r);

            c = npy_cosf(i);
            s = npy_sinf(i);

            if (npy_isfinite(i)) {
                ret = npy_cpackf(x * c, x * s);
            }
            else {
                ret = npy_cpackf(NPY_NANF, npy_copysignf(NPY_NANF, i));
            }
        }

    }
    else  if (npy_isnan(r)) {
        /* r is nan */
        if (i == 0) {
            ret = z;
        }
        else {
            ret = npy_cpackf(r, npy_copysignf(NPY_NANF, i));
        }
    }
    else {
        /* r is +- inf */
        if (r > 0) {
            if (i == 0) {
                ret = npy_cpackf(r, i);
            }
            else if (npy_isfinite(i)) {
                c = npy_cosf(i);
                s = npy_sinf(i);

                ret = npy_cpackf(r * c, r * s);
            }
            else {
                /* x = +inf, y = +-inf | nan */
                npy_set_floatstatus_invalid();
                ret = npy_cpackf(r, NPY_NANF);
            }
        }
        else {
            if (npy_isfinite(i)) {
                x = npy_expf(r);
                c = npy_cosf(i);
                s = npy_sinf(i);

                ret = npy_cpackf(x * c, x * s);
            }
            else {
                /* x = -inf, y = nan | +i inf */
                ret = npy_cpackf(0, 0);
            }
        }
    }

    return ret;
}
#endif

#ifndef HAVE_CLOGF
/* algorithm from cpython, rev. d86f5686cef9
 *
 * The usual formula for the real part is log(hypot(z.real, z.imag)).
 * There are four situations where this formula is potentially
 * problematic:
 *
 * (1) the absolute value of z is subnormal.  Then hypot is subnormal,
 * so has fewer than the usual number of bits of accuracy, hence may
 * have large relative error.  This then gives a large absolute error
 * in the log.  This can be solved by rescaling z by a suitable power
 * of 2.
 *
 * (2) the absolute value of z is greater than DBL_MAX (e.g. when both
 * z.real and z.imag are within a factor of 1/sqrt(2) of DBL_MAX)
 * Again, rescaling solves this.
 *
 * (3) the absolute value of z is close to 1.  In this case it's
 * difficult to achieve good accuracy, at least in part because a
 * change of 1ulp in the real or imaginary part of z can result in a
 * change of billions of ulps in the correctly rounded answer.
 *
 * (4) z = 0.  The simplest thing to do here is to call the
 * floating-point log with an argument of 0, and let its behaviour
 * (returning -infinity, signaling a floating-point exception, setting
 * errno, or whatever) determine that of c_log.  So the usual formula
 * is fine here.
*/
npy_cfloat
npy_clogf(npy_cfloat z)
{
    npy_float ax = npy_fabsf(npy_crealf(z));
    npy_float ay = npy_fabsf(npy_cimagf(z));
    npy_float rr, ri;

    if (ax > FLT_MAX/4 || ay > FLT_MAX/4) {
        rr = npy_logf(npy_hypotf(ax/2, ay/2)) + NPY_LOGE2f;
    }
    else if (ax < FLT_MIN && ay < FLT_MIN) {
        if (ax > 0  || ay > 0) {
            /* catch cases where hypot(ax, ay) is subnormal */
            rr = npy_logf(npy_hypotf(npy_ldexpf(ax, FLT_MANT_DIG),
                 npy_ldexpf(ay, FLT_MANT_DIG))) - FLT_MANT_DIG*NPY_LOGE2f;
        }
        else {
            /* log(+/-0 +/- 0i) */
            /* raise divide-by-zero floating point exception */
            rr = -1.0f / npy_crealf(z);
            rr = npy_copysignf(rr, -1);
            ri = npy_cargf(z);
            return npy_cpackf(rr, ri);
        }
    }
    else {
        npy_float h = npy_hypotf(ax, ay);
        if (0.71 <= h && h <= 1.73) {
            npy_float am = ax > ay ? ax : ay; /* max(ax, ay) */
            npy_float an = ax > ay ? ay : ax; /* min(ax, ay) */
            rr = npy_log1pf((am-1)*(am+1)+an*an)/2;
        }
        else {
            rr = npy_logf(h);
        }
    }
    ri = npy_cargf(z);

    return npy_cpackf(rr, ri);
}
#endif

#ifndef HAVE_CSQRTF

/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */
#define THRESH  (FLT_MAX / (1 + NPY_SQRT2f))

npy_cfloat
npy_csqrtf(npy_cfloat z)
{
    npy_cfloat result;
    npy_float a, b;
    npy_float t;
    int scale;

    a = npy_crealf(z);
    b = npy_cimagf(z);

    /* Handle special cases. */
    if (a == 0 && b == 0) {
        return (npy_cpackf(0, b));
    }
    if (npy_isinf(b)) {
        return (npy_cpackf(NPY_INFINITYF, b));
    }
    if (npy_isnan(a)) {
        t = (b - b) / (b - b);  /* raise invalid if b is not a NaN */
        return (npy_cpackf(a, t));    /* return NaN + NaN i */
    }
    if (npy_isinf(a)) {
        /*
         * csqrt(inf + NaN i)  = inf +  NaN i
         * csqrt(inf + y i)    = inf +  0 i
         * csqrt(-inf + NaN i) = NaN +- inf i
         * csqrt(-inf + y i)   = 0   +  inf i
         */
        if (npy_signbit(a)) {
            return (npy_cpackf(npy_fabsf(b - b), npy_copysignf(a, b)));
        }
        else {
            return (npy_cpackf(a, npy_copysignf(b - b, b)));
        }
    }
    /*
     * The remaining special case (b is NaN) is handled just fine by
     * the normal code path below.
     */

    /* Scale to avoid overflow. */
    if (npy_fabsf(a) >= THRESH || npy_fabsf(b) >= THRESH) {
        a *= 0.25;
        b *= 0.25;
        scale = 1;
    }
    else {
        scale = 0;
    }

    /* Algorithm 312, CACM vol 10, Oct 1967. */
    if (a >= 0) {
        t = npy_sqrtf((a + npy_hypotf(a, b)) * 0.5f);
        result = npy_cpackf(t, b / (2 * t));
    }
    else {
        t = npy_sqrtf((-a + npy_hypotf(a, b)) * 0.5f);
        result = npy_cpackf(npy_fabsf(b) / (2 * t), npy_copysignf(t, b));
    }

    /* Rescale. */
    if (scale) {
        return (npy_cpackf(npy_crealf(result) * 2, npy_cimagf(result)));
    }
    else {
        return (result);
    }
}
#undef THRESH
#endif

/*
 * Always use this function because of the multiplication for small
 * integer powers, but in the body use cpow if it is available.
 */

/* private function for use in npy_pow{f, ,l} */
#ifdef HAVE_CPOWF
static npy_cfloat
sys_cpowf(npy_cfloat x, npy_cfloat y)
{
    __npy_cfloat_to_c99_cast xcast;
    __npy_cfloat_to_c99_cast ycast;
    __npy_cfloat_to_c99_cast ret;
    xcast.npy_z = x;
    ycast.npy_z = y;
    ret.c99_z = cpowf(xcast.c99_z, ycast.c99_z);
    return ret.npy_z;
}
#endif


npy_cfloat
npy_cpowf (npy_cfloat a, npy_cfloat b)
{
    npy_intp n;
    npy_float ar = npy_crealf(a);
    npy_float br = npy_crealf(b);
    npy_float ai = npy_cimagf(a);
    npy_float bi = npy_cimagf(b);
    npy_cfloat r;

    /*
     * Checking if in a^b, if b is zero.
     * If a is not zero then by definition of logarithm a^0 is 1.
     * If a is also zero then 0^0 is best defined as 1.
     */
    if (br == 0. && bi == 0.) {
        return npy_cpackf(1., 0.);
    }
    /* case 0^b
     * If a is a complex zero (ai=ar=0), then the result depends 
     * upon values of br and bi. The result is either:
     * 0 (in magnitude), undefined or 1.
     * The later case is for br=bi=0 and independent of ar and ai
     * but is handled above).
     */
    else if (ar == 0. && ai == 0.) {
        /* 
         * If the real part of b is positive (br>0) then this is
         * the zero complex with positive sign on both the
         * real and imaginary part.
         */
         if (br > 0) {
             return npy_cpackf(0., 0.);
         }
        /* else we are in the case where the
         * real part of b is negative (br<0).
         * Here we should return a complex nan
         * and raise FloatingPointError: invalid value...
         */
         
         /* Raise invalid value by calling inf - inf*/
          volatile npy_float tmp = NPY_INFINITYF;
          tmp -= NPY_INFINITYF;
          ar = tmp;
          
          r = npy_cpackf(NPY_NANF, NPY_NANF);
          return r;
    }
    if (bi == 0 && (n=(npy_intp)br) == br) {
        if (n == 1) {
            /* unroll: handle inf better */
            return npy_cpackf(ar, ai);
        }
        else if (n == 2) {
            /* unroll: handle inf better */
            return cmulf(a, a);
        }
        else if (n == 3) {
            /* unroll: handle inf better */
            return cmulf(a, cmulf(a, a));
        }
        else if (n > -100 && n < 100) {
            npy_cfloat p, aa;
            npy_intp mask = 1;
            if (n < 0) {
                n = -n;
            }
            aa = c_1f;
            p = npy_cpackf(ar, ai);
            while (1) {
                if (n & mask) {
                    aa = cmulf(aa,p);
                }
                mask <<= 1;
                if (n < mask || mask <= 0) {
                    break;
                }
                p = cmulf(p,p);
            }
            r = npy_cpackf(npy_crealf(aa), npy_cimagf(aa));
            if (br < 0) {
                r = cdivf(c_1f, r);
            }
            return r;
        }
    }

#ifdef HAVE_CPOWF
    return sys_cpowf(a, b);

#else
    {
        npy_cfloat loga = npy_clogf(a);

        ar = npy_crealf(loga);
        ai = npy_cimagf(loga);
        return npy_cexpf(npy_cpackf(ar*br - ai*bi, ar*bi + ai*br));
    }

#endif
}


#ifndef HAVE_CCOSF
npy_cfloat
npy_ccosf(npy_cfloat z)
{
    /* ccos(z) = ccosh(I * z) */
    return npy_ccoshf(npy_cpackf(-npy_cimagf(z), npy_crealf(z)));
}
#endif

#ifndef HAVE_CSINF
npy_cfloat
npy_csinf(npy_cfloat z)
{
    /* csin(z) = -I * csinh(I * z) */
    z = npy_csinhf(npy_cpackf(-npy_cimagf(z), npy_crealf(z)));
    return npy_cpackf(npy_cimagf(z), -npy_crealf(z));
}
#endif

#ifndef HAVE_CTANF
npy_cfloat
npy_ctanf(npy_cfloat z)
{
    /* ctan(z) = -I * ctanh(I * z) */
    z = npy_ctanhf(npy_cpackf(-npy_cimagf(z), npy_crealf(z)));
    return (npy_cpackf(npy_cimagf(z), -npy_crealf(z)));
}
#endif

#ifndef HAVE_CCOSHF
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic cosine of a complex argument z = x + i y.
 *
 * cosh(z) = cosh(x+iy)
 *         = cosh(x) cos(y) + i sinh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 *
 * CCOSH_BIG is chosen such that
 * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG)
 * although the exact value assigned to CCOSH_BIG is not so important
 */
npy_cfloat
npy_ccoshf(npy_cfloat z)
{
#if 1 == 1
    const npy_float CCOSH_BIG = 9.0f;
    const npy_float CCOSH_HUGE = 1.70141183e+38f;
#endif
#if 1 == 2
    const npy_double CCOSH_BIG = 22.0;
    const npy_double CCOSH_HUGE = 8.9884656743115795e+307;
#endif
#if 1 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CCOSH_BIG = 22.0L;
    const npy_longdouble CCOSH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CCOSH_BIG = 24.0L;
    const npy_longdouble CCOSH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_float  x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_crealf(z);
    y = npy_cimagf(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpackf(npy_coshf(x), x * y);
        }
        absx = npy_fabsf(x);
        if (absx < CCOSH_BIG) {
            /* small x: normal case */
            return npy_cpackf(npy_coshf(x) * npy_cosf(y),
                                npy_sinhf(x) * npy_sinf(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWERF) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_expf(absx) * 0.5f;
            return npy_cpackf(h * npy_cosf(y),
                                npy_copysignf(h, x) * npy_sinf(y));
        }
        else if (absx < SCALED_CEXP_UPPERF) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexpf(absx, y, -1);
            return npy_cpackf(npy_crealf(z),
                                npy_cimagf(z) * npy_copysignf(1, x));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CCOSH_HUGE * x;
            return npy_cpackf(h * h * npy_cosf(y), h * npy_sinf(y));
        }
    }

    /*
     * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpackf(y - y, npy_copysignf(0, x * (y - y)));
    }

    /*
     * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0.
     *
     * cosh(NaN +- I 0)   = d(NaN) + I sign(d(NaN, +-0))0.
     * The sign of 0 in the result is unspecified.
     */
    if (y == 0 && !xfinite) {
        return npy_cpackf(x * x, npy_copysignf(0, x) * y);
    }

    /*
     * cosh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * cosh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpackf(y - y, x * (y - y));
    }

    /*
     * cosh(+-Inf + I NaN)  = +Inf + I d(NaN).
     *
     * cosh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * cosh(+-Inf + I y)   = +Inf cos(y) +- I Inf sin(y)
     */
    if (npy_isinf(x)) {
        if (!yfinite) {
            return npy_cpackf(x * x, x * (y - y));
        }
        return npy_cpackf((x * x) * npy_cosf(y), x * npy_sinf(y));
    }

    /*
     * cosh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * cosh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * cosh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpackf((x * x) * (y - y), (x + x) * (y - y));
}
#undef CCOSH_BIG
#undef CCOSH_HUGE
#endif

#ifndef HAVE_CSINHF
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic sine of a complex argument z = x + i y.
 *
 * sinh(z) = sinh(x+iy)
 *         = sinh(x) cos(y) + i cosh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 */
npy_cfloat
npy_csinhf(npy_cfloat z)
{
#if 1 == 1
    const npy_float CSINH_BIG = 9.0f;
    const npy_float CSINH_HUGE = 1.70141183e+38f;
#endif
#if 1 == 2
    const npy_double CSINH_BIG = 22.0;
    const npy_double CSINH_HUGE = 8.9884656743115795e+307;
#endif
#if 1 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CSINH_BIG = 22.0L;
    const npy_longdouble CSINH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CSINH_BIG = 24.0L;
    const npy_longdouble CSINH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_float x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_crealf(z);
    y = npy_cimagf(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpackf(npy_sinhf(x), y);
        }
        absx = npy_fabsf(x);
        if (absx < CSINH_BIG) {
            /* small x: normal case */
            return npy_cpackf(npy_sinhf(x) * npy_cosf(y),
                                npy_coshf(x) * npy_sinf(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWERF) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_expf(npy_fabsf(x)) * 0.5f;
            return npy_cpackf(npy_copysignf(h, x) * npy_cosf(y),
                                h * npy_sinf(y));
        }
        else if (x < SCALED_CEXP_UPPERF) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexpf(absx, y, -1);
            return npy_cpackf(npy_crealf(z) * npy_copysignf(1, x),
                                npy_cimagf(z));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CSINH_HUGE * x;
            return npy_cpackf(h * npy_cosf(y), h * h * npy_sinf(y));
        }
    }

    /*
     * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN).
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpackf(npy_copysignf(0, x * (y - y)), y - y);
    }

    /*
     * sinh(+-Inf +- I 0) = +-Inf + I +-0.
     *
     * sinh(NaN +- I 0)   = d(NaN) + I +-0.
     */
    if (y == 0 && !xfinite) {
        if (npy_isnan(x)) {
            return z;
        }
        return npy_cpackf(x, npy_copysignf(0, y));
    }

    /*
     * sinh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * sinh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpackf(y - y, x * (y - y));
    }

    /*
     * sinh(+-Inf + I NaN)  = +-Inf + I d(NaN).
     * The sign of Inf in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     *
     * sinh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * sinh(+-Inf + I y)   = +-Inf cos(y) + I Inf sin(y)
     */
    if (!xfinite && !npy_isnan(x)) {
        if (!yfinite) {
            return npy_cpackf(x * x, x * (y - y));
        }
        return npy_cpackf(x * npy_cosf(y),
                            NPY_INFINITYF * npy_sinf(y));
    }

    /*
     * sinh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * sinh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * sinh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpackf((x * x) * (y - y), (x + x) * (y - y));
}
#undef CSINH_BIG
#undef CSINH_HUGE
#endif

#ifndef HAVE_CTANHF
/*
 * Taken from the msun library in FreeBSD, rev 226600.
 *
 * Hyperbolic tangent of a complex argument z = x + i y.
 *
 * The algorithm is from:
 *
 *   W. Kahan.  Branch Cuts for Complex Elementary Functions or Much
 *   Ado About Nothing's Sign Bit.  In The State of the Art in
 *   Numerical Analysis, pp. 165 ff.  Iserles and Powell, eds., 1987.
 *
 * Method:
 *
 *   Let t    = tan(x)
 *       beta = 1/cos^2(y)
 *       s    = sinh(x)
 *       rho  = cosh(x)
 *
 *   We have:
 *
 *   tanh(z) = sinh(z) / cosh(z)
 *
 *             sinh(x) cos(y) + i cosh(x) sin(y)
 *           = ---------------------------------
 *             cosh(x) cos(y) + i sinh(x) sin(y)
 *
 *             cosh(x) sinh(x) / cos^2(y) + i tan(y)
 *           = -------------------------------------
 *                    1 + sinh^2(x) / cos^2(y)
 *
 *             beta rho s + i t
 *           = ----------------
 *               1 + beta s^2
 *
 * Modifications:
 *
 *   I omitted the original algorithm's handling of overflow in tan(x) after
 *   verifying with nearpi.c that this can't happen in IEEE single or double
 *   precision.  I also handle large x differently.
 */

#define TANH_HUGE 22.0
#define TANHF_HUGE 11.0F
#define TANHL_HUGE 42.0L

npy_cfloat
npy_ctanhf(npy_cfloat z)
{
    npy_float x, y;
    npy_float t, beta, s, rho, denom;

    x = npy_crealf(z);
    y = npy_cimagf(z);

    /*
     * ctanh(NaN + i 0) = NaN + i 0
     *
     * ctanh(NaN + i y) = NaN + i NaN        for y != 0
     *
     * The imaginary part has the sign of x*sin(2*y), but there's no
     * special effort to get this right.
     *
     * ctanh(+-Inf +- i Inf) = +-1 +- 0
     *
     * ctanh(+-Inf + i y) = +-1 + 0 sin(2y)        for y finite
     *
     * The imaginary part of the sign is unspecified.  This special
     * case is only needed to avoid a spurious invalid exception when
     * y is infinite.
     */
        if (!npy_isfinite(x)) {
            if (npy_isnan(x)) {
                return npy_cpackf(x, (y == 0 ? y : x * y));
            }
            return npy_cpackf(npy_copysignf(1,x),
                                npy_copysignf(0,
                                npy_isinf(y) ?
                                    y : npy_sinf(y) * npy_cosf(y)));
        }

    /*
     * ctanh(x + i NAN) = NaN + i NaN
     * ctanh(x +- i Inf) = NaN + i NaN
     */
    if (!npy_isfinite(y)) {
        return (npy_cpackf(y - y, y - y));
    }

    /*
     * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the
     * approximation sinh^2(huge) ~= exp(2*huge) / 4.
     * We use a modified formula to avoid spurious overflow.
     */
    if (npy_fabsf(x) >= TANHF_HUGE) {
        npy_float exp_mx = npy_expf(-npy_fabsf(x));
        return npy_cpackf(npy_copysignf(1, x),
                            4 * npy_sinf(y) * npy_cosf(y) *
                                exp_mx * exp_mx);
    }

    /* Kahan's algorithm */
    t = npy_tanf(y);
    beta = 1 + t * t;    /* = 1 / cos^2(y) */
    s = npy_sinhf(x);
    rho = npy_sqrtf(1 + s * s);    /* = cosh(x) */
    denom = 1 + beta * s * s;
    return (npy_cpackf((beta * rho * s) / denom, t / denom));
}
#undef TANH_HUGE
#undef TANHF_HUGE
#undef TANHL_HUGE
#endif

#if !defined (HAVE_CACOSF) || !defined (HAVE_CASINHF)
/*
 * Complex inverse trig functions taken from the msum library in FreeBSD
 * revision 251404
 *
 * The algorithm is very close to that in "Implementing the complex arcsine
 * and arccosine functions using exception handling" by T. E. Hull, Thomas F.
 * Fairgrieve, and Ping Tak Peter Tang, published in ACM Transactions on
 * Mathematical Software, Volume 23 Issue 3, 1997, Pages 299-335,
 * http://dl.acm.org/citation.cfm?id=275324.
 *
 * Throughout we use the convention z = x + I*y.
 *
 * casinh(z) = sign(x)*log(A+sqrt(A*A-1)) + I*asin(B)
 * where
 * A = (|z+I| + |z-I|) / 2
 * B = (|z+I| - |z-I|) / 2 = y/A
 *
 * These formulas become numerically unstable:
 *   (a) for Re(casinh(z)) when z is close to the line segment [-I, I] (that
 *       is, Re(casinh(z)) is close to 0);
 *   (b) for Im(casinh(z)) when z is close to either of the intervals
 *       [I, I*infinity) or (-I*infinity, -I] (that is, |Im(casinh(z))| is
 *       close to PI/2).
 *
 * These numerical problems are overcome by defining
 * f(a, b) = (hypot(a, b) - b) / 2 = a*a / (hypot(a, b) + b) / 2
 * Then if A < A_crossover, we use
 *   log(A + sqrt(A*A-1)) = log1p((A-1) + sqrt((A-1)*(A+1)))
 *   A-1 = f(x, 1+y) + f(x, 1-y)
 * and if B > B_crossover, we use
 *   asin(B) = atan2(y, sqrt(A*A - y*y)) = atan2(y, sqrt((A+y)*(A-y)))
 *   A-y = f(x, y+1) + f(x, y-1)
 * where without loss of generality we have assumed that x and y are
 * non-negative.
 *
 * Much of the difficulty comes because the intermediate computations may
 * produce overflows or underflows.  This is dealt with in the paper by Hull
 * et al by using exception handling.  We do this by detecting when
 * computations risk underflow or overflow.  The hardest part is handling the
 * underflows when computing f(a, b).
 *
 * Note that the function f(a, b) does not appear explicitly in the paper by
 * Hull et al, but the idea may be found on pages 308 and 309.  Introducing the
 * function f(a, b) allows us to concentrate many of the clever tricks in this
 * paper into one function.
 */

/*
 * Function f(a, b, hypot_a_b) = (hypot(a, b) - b) / 2.
 * Pass hypot(a, b) as the third argument.
 */
static inline npy_float
_ff(npy_float a, npy_float b, npy_float hypot_a_b)
{
    if (b < 0) {
        return ((hypot_a_b - b) / 2);
    }
    if (b == 0) {
        return (a / 2);
    }
    return (a * a / (hypot_a_b + b) / 2);
}

/*
 * All the hard work is contained in this function.
 * x and y are assumed positive or zero, and less than RECIP_EPSILON.
 * Upon return:
 * rx = Re(casinh(z)) = -Im(cacos(y + I*x)).
 * B_is_usable is set to 1 if the value of B is usable.
 * If B_is_usable is set to 0, sqrt_A2my2 = sqrt(A*A - y*y), and new_y = y.
 * If returning sqrt_A2my2 has potential to result in an underflow, it is
 * rescaled, and new_y is similarly rescaled.
 */
static inline void
_do_hard_workf(npy_float x, npy_float y, npy_float *rx,
    npy_int *B_is_usable, npy_float *B, npy_float *sqrt_A2my2, npy_float *new_y)
{
#if 1 == 1
    const npy_float A_crossover = 10.0f;
    const npy_float B_crossover = 0.6417f;
    const npy_float FOUR_SQRT_MIN = 4.3368086899420177e-19f;
#endif
#if 1 == 2
    const npy_double A_crossover = 10.0;
    const npy_double B_crossover = 0.6417;
    const npy_double FOUR_SQRT_MIN = 5.9666725849601654e-154;
#endif
#if 1 == 3
    const npy_longdouble A_crossover = 10.0l;
    const npy_longdouble B_crossover = 0.6417l;
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble FOUR_SQRT_MIN = 5.9666725849601654e-154;
#else
    const npy_longdouble FOUR_SQRT_MIN = 7.3344154702193886625e-2466l;
#endif
#endif
    npy_float R, S, A; /* A, B, R, and S are as in Hull et al. */
    npy_float Am1, Amy; /* A-1, A-y. */

    R = npy_hypotf(x, y + 1);        /* |z+I| */
    S = npy_hypotf(x, y - 1);        /* |z-I| */

    /* A = (|z+I| + |z-I|) / 2 */
    A = (R + S) / 2;
    /*
     * Mathematically A >= 1.  There is a small chance that this will not
     * be so because of rounding errors.  So we will make certain it is
     * so.
     */
    if (A < 1) {
        A = 1;
    }

    if (A < A_crossover) {
        /*
         * Am1 = fp + fm, where fp = f(x, 1+y), and fm = f(x, 1-y).
         * rx = log1p(Am1 + sqrt(Am1*(A+1)))
         */
        if (y == 1 && x < FLT_EPSILON * FLT_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *rx = npy_sqrtf(x);
        }
        else if (x >= FLT_EPSILON * npy_fabsf(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN
             */
            Am1 = _ff(x, 1 + y, R) + _ff(x, 1 - y, S);
            *rx = npy_log1pf(Am1 + npy_sqrtf(Am1 * (A + 1)));
        }
        else if (y < 1) {
            /*
             * fp = x*x/(1+y)/4, fm = x*x/(1-y)/4, and
             * A = 1 (inexactly).
             */
            *rx = x / npy_sqrtf((1 - y) * (1 + y));
        }
        else {        /* if (y > 1) */
            /*
             * A-1 = y-1 (inexactly).
             */
            *rx = npy_log1pf((y - 1) + npy_sqrtf((y - 1) * (y + 1)));
        }
    }
    else {
        *rx = npy_logf(A + npy_sqrtf(A * A - 1));
    }

    *new_y = y;

    if (y < FOUR_SQRT_MIN) {
        /*
         * Avoid a possible underflow caused by y/A.  For casinh this
         * would be legitimate, but will be picked up by invoking atan2
         * later on.  For cacos this would not be legitimate.
         */
        *B_is_usable = 0;
        *sqrt_A2my2 = A * (2 / FLT_EPSILON);
        *new_y = y * (2 / FLT_EPSILON);
        return;
    }

    /* B = (|z+I| - |z-I|) / 2 = y/A */
    *B = y / A;
    *B_is_usable = 1;

    if (*B > B_crossover) {
        *B_is_usable = 0;
        /*
         * Amy = fp + fm, where fp = f(x, y+1), and fm = f(x, y-1).
         * sqrt_A2my2 = sqrt(Amy*(A+y))
         */
        if (y == 1 && x < FLT_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrtf(x) * npy_sqrtf((A + y) / 2);
        }
        else if (x >= FLT_EPSILON * npy_fabsf(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN
             * and
             * x >= DBL_EPSILON^2 >= FOUR_SQRT_MIN
             */
            Amy = _ff(x, y + 1, R) + _ff(x, y - 1, S);
            *sqrt_A2my2 = npy_sqrtf(Amy * (A + y));
        }
        else if (y > 1) {
            /*
             * fp = x*x/(y+1)/4, fm = x*x/(y-1)/4, and
             * A = y (inexactly).
             *
             * y < RECIP_EPSILON.  So the following
             * scaling should avoid any underflow problems.
             */
            *sqrt_A2my2 = x * (4 / FLT_EPSILON / FLT_EPSILON) * y /
                npy_sqrtf((y + 1) * (y - 1));
            *new_y = y * (4 / FLT_EPSILON / FLT_EPSILON);
        }
        else {        /* if (y < 1) */
            /*
             * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrtf((1 - y) * (1 + y));
        }
    }
}

/*
 * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON.
 */
static inline void
_clog_for_large_valuesf(npy_float x, npy_float y,
    npy_float *rr, npy_float *ri)
{
#if 1 == 1
    const npy_float QUARTER_SQRT_MAX = 4.611685743549481e+18f;
    const npy_float SQRT_MIN = 1.0842021724855044e-19f;
 #endif
#if 1 == 2
    const npy_double QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_double SQRT_MIN = 1.4916681462400413e-154;
 #endif
#if 1 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_longdouble SQRT_MIN = 1.4916681462400413e-154;
#else
    const npy_longdouble QUARTER_SQRT_MAX = 2.7268703390485398235e+2465l;
    const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    npy_float ax, ay, t;

    ax = npy_fabsf(x);
    ay = npy_fabsf(y);
    if (ax < ay) {
        t = ax;
        ax = ay;
        ay = t;
    }

    /*
     * Avoid overflow in hypot() when x and y are both very large.
     * Divide x and y by E, and then add 1 to the logarithm.  This depends
     * on E being larger than sqrt(2).
     * Dividing by E causes an insignificant loss of accuracy; however
     * this method is still poor since it is unnecessarily slow.
     */
    if (ax > FLT_MAX / 2) {
        *rr = npy_logf(npy_hypotf(x / NPY_Ef, y / NPY_Ef)) + 1;
    }
    /*
     * Avoid overflow when x or y is large.  Avoid underflow when x or
     * y is small.
     */
    else if (ax > QUARTER_SQRT_MAX || ay < SQRT_MIN) {
        *rr = npy_logf(npy_hypotf(x, y));
    }
    else {
        *rr = npy_logf(ax * ax + ay * ay) / 2;
    }
    *ri = npy_atan2f(y, x);
}
#endif

#ifndef HAVE_CACOSF
npy_cfloat
npy_cacosf(npy_cfloat z)
{
#if 1 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 1 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 1 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_float RECIP_EPSILON = 1.0f / FLT_EPSILON;
    const npy_float pio2_hi = NPY_PI_2f;
    npy_float x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2mx2, new_x;
    npy_int sx, sy;
    npy_int B_is_usable;

    x = npy_crealf(z);
    y = npy_cimagf(z);
    sx = npy_signbit(x);
    sy = npy_signbit(y);
    ax = npy_fabsf(x);
    ay = npy_fabsf(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */
        if (npy_isinf(x)) {
            return npy_cpackf(y + y, -NPY_INFINITYF);
        }
        /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */
        if (npy_isinf(y)) {
            return npy_cpackf(x + x, -y);
        }
        /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */
        if (x == 0) {
            return npy_cpackf(pio2_hi + pio2_lo, y + y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackf(NPY_NANF, NPY_NANF);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        _clog_for_large_valuesf(x, y, &wx, &wy);
        rx = npy_fabsf(wy);
        ry = wx + NPY_LOGE2f;
        if (sy == 0) {
            ry = -ry;
        }
        return npy_cpackf(rx, ry);
    }

    /* Avoid spuriously raising inexact for z = 1. */
    if (x == 1 && y == 0) {
        return npy_cpackf(0, -y);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return npy_cpackf(pio2_hi - (x - pio2_lo), -y);
    }

    _do_hard_workf(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x);
    if (B_is_usable) {
        if (sx == 0) {
            rx = npy_acosf(B);
        }
        else {
            rx = npy_acosf(-B);
        }
    }
    else {
        if (sx == 0) {
            rx = npy_atan2f(sqrt_A2mx2, new_x);
        }
        else {
            rx = npy_atan2f(sqrt_A2mx2, -new_x);
        }
    }
    if (sy == 0) {
        ry = -ry;
    }
    return npy_cpackf(rx, ry);
}
#endif

#ifndef HAVE_CASINF
npy_cfloat
npy_casinf(npy_cfloat z)
{
    /* casin(z) = I * conj( casinh(I * conj(z)) ) */
    z = npy_casinhf(npy_cpackf(npy_cimagf(z), npy_crealf(z)));
    return npy_cpackf(npy_cimagf(z), npy_crealf(z));
}
#endif

#ifndef HAVE_CATANF
npy_cfloat
npy_catanf(npy_cfloat z)
{
    /* catan(z) = I * conj( catanh(I * conj(z)) ) */
    z = npy_catanhf(npy_cpackf(npy_cimagf(z), npy_crealf(z)));
    return npy_cpackf(npy_cimagf(z), npy_crealf(z));
}
#endif

#ifndef HAVE_CACOSHF
npy_cfloat
npy_cacoshf(npy_cfloat z)
{
    /*
     * cacosh(z) = I*cacos(z) or -I*cacos(z)
     * where the sign is chosen so Re(cacosh(z)) >= 0.
     */
    npy_cfloat  w;
    npy_float rx, ry;

    w = npy_cacosf(z);
    rx = npy_crealf(w);
    ry = npy_cimagf(w);
    /* cacosh(NaN + I*NaN) = NaN + I*NaN */
    if (npy_isnan(rx) && npy_isnan(ry)) {
        return npy_cpackf(ry, rx);
    }
    /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */
    /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */
    if (npy_isnan(rx)) {
        return npy_cpackf(npy_fabsf(ry), rx);
    }
    /* cacosh(0 + I*NaN) = NaN + I*NaN */
    if (npy_isnan(ry)) {
        return npy_cpackf(ry, ry);
    }
    return npy_cpackf(npy_fabsf(ry), npy_copysignf(rx, npy_cimagf(z)));
}
#endif

#ifndef HAVE_CASINHF
/*
 * casinh(z) = z + O(z^3)   as z -> 0
 *
 * casinh(z) = sign(x)*clog(sign(x)*z) + O(1/z^2)   as z -> infinity
 * The above formula works for the imaginary part as well, because
 * Im(casinh(z)) = sign(x)*atan2(sign(x)*y, fabs(x)) + O(y/z^3)
 *    as z -> infinity, uniformly in y
 */
npy_cfloat
npy_casinhf(npy_cfloat z)
{
#if 1 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
#endif
#if 1 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
#endif
#if 1 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
#endif
    const npy_float RECIP_EPSILON = 1.0f / FLT_EPSILON;
    npy_float x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y;
    npy_int B_is_usable;

    x = npy_crealf(z);
    y = npy_cimagf(z);
    ax = npy_fabsf(x);
    ay = npy_fabsf(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpackf(x, y + y);
        }
        /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */
        if (npy_isinf(y)) {
            return npy_cpackf(y, x + x);
        }
        /* casinh(NaN + I*0) = NaN + I*0 */
        if (y == 0) {
            return npy_cpackf(x + x, y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackf(NPY_NANF, NPY_NANF);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        if (npy_signbit(x) == 0) {
            _clog_for_large_valuesf(x, y, &wx, &wy);
            wx += NPY_LOGE2f;
        }
        else {
            _clog_for_large_valuesf(-x, -y, &wx, &wy);
            wx += NPY_LOGE2f;
        }
        return npy_cpackf(npy_copysignf(wx, x), npy_copysignf(wy, y));
    }

    /* Avoid spuriously raising inexact for z = 0. */
    if (x == 0 && y == 0) {
        return (z);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return (z);
    }

    _do_hard_workf(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y);
    if (B_is_usable) {
        ry = npy_asinf(B);
    }
    else {
        ry = npy_atan2f(new_y, sqrt_A2my2);
    }
    return npy_cpackf(npy_copysignf(rx, x), npy_copysignf(ry, y));
}
#endif

#ifndef HAVE_CATANHF
/*
 * sum_squares(x,y) = x*x + y*y (or just x*x if y*y would underflow).
 * Assumes x*x and y*y will not overflow.
 * Assumes x and y are finite.
 * Assumes y is non-negative.
 * Assumes fabs(x) >= DBL_EPSILON.
 */
static inline npy_float
_sum_squaresf(npy_float x, npy_float y)
{
#if 1 == 1
const npy_float SQRT_MIN = 1.0842022e-19f;
#endif
#if 1 == 2
const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#endif
#if 1 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
const npy_longdouble SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#else
/* this is correct for 80 bit long doubles */
const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    /* Avoid underflow when y is small. */
    if (y < SQRT_MIN) {
        return (x * x);
    }

    return (x * x + y * y);
}

/*
 * real_part_reciprocal(x, y) = Re(1/(x+I*y)) = x/(x*x + y*y).
 * Assumes x and y are not NaN, and one of x and y is larger than
 * RECIP_EPSILON.  We avoid unwarranted underflow.  It is important to not use
 * the code creal(1/z), because the imaginary part may produce an unwanted
 * underflow.
 * This is only called in a context where inexact is always raised before
 * the call, so no effort is made to avoid or force inexact.
 */
#if 1 == 1
#define BIAS (FLT_MAX_EXP - 1)
#define CUTOFF (FLT_MANT_DIG / 2 + 1)
static inline npy_float
_real_part_reciprocalf(npy_float x, npy_float y)
{
    npy_float scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    GET_FLOAT_WORD(hx, x);
    ix = hx & 0x7f800000;
    GET_FLOAT_WORD(hy, y);
    iy = hy & 0x7f800000;
    if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) {
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 23) {
        return (x / y / y);
    }
    if (ix <= (BIAS + FLT_MAX_EXP / 2 - CUTOFF) << 23) {
        return (x / (x * x + y * y));
    }
    SET_FLOAT_WORD(scale, 0x7f800000 - ix);
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 1 == 2
#define BIAS (DBL_MAX_EXP - 1)
/*  more guard digits are useful iff there is extra precision. */
#define CUTOFF (DBL_MANT_DIG / 2 + 1)  /* just half or 1 guard digit */
static inline npy_double
_real_part_reciprocal(npy_double x, npy_double y)
{
    npy_double scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    /*
     * This code is inspired by the C99 document n1124.pdf, Section G.5.1,
     * example 2.
     */
    GET_HIGH_WORD(hx, x);
    ix = hx & 0x7ff00000;
    GET_HIGH_WORD(hy, y);
    iy = hy & 0x7ff00000;
    if (ix - iy >= CUTOFF << 20 || npy_isinf(x)) {
        /* +-Inf -> +-0 is special */
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 20) {
        /* should avoid double div, but hard */
        return (x / y / y);
    }
    if (ix <= (BIAS + DBL_MAX_EXP / 2 - CUTOFF) << 20) {
        return (x / (x * x + y * y));
    }
    scale = 1;
    SET_HIGH_WORD(scale, 0x7ff00000 - ix);  /* 2**(1-ilogb(x)) */
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 1 == 3
#if !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE) && \
    !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE)

#define BIAS (LDBL_MAX_EXP - 1)
#define CUTOFF (LDBL_MANT_DIG / 2 + 1)
static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    npy_longdouble scale;
    union IEEEl2bitsrep ux, uy, us;
    npy_int32 ix, iy;

    ux.e = x;
    ix = GET_LDOUBLE_EXP(ux);
    uy.e = y;
    iy = GET_LDOUBLE_EXP(uy);
    if (ix - iy >= CUTOFF || npy_isinf(x)) {
        return (1/x);
    }
    if (iy - ix >= CUTOFF) {
        return (x/y/y);
    }
    if (ix <= BIAS + LDBL_MAX_EXP / 2 - CUTOFF) {
        return (x/(x*x + y*y));
    }
    us.e = 1;
    SET_LDOUBLE_EXP(us, 0x7fff - ix);
    scale = us.e;
    x *= scale;
    y *= scale;
    return (x/(x*x + y*y) * scale);
}
#undef BIAS
#undef CUTOFF

#else

static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    return x/(x*x + y*y);
}

#endif
#endif

npy_cfloat
npy_catanhf(npy_cfloat z)
{
#if 1 == 1
    /* this is sqrt(3*EPS) */
    const npy_float SQRT_3_EPSILON = 5.9801995673e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 1 == 2
    const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 1 == 3
    const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_float RECIP_EPSILON = 1.0f / FLT_EPSILON;
    const npy_float pio2_hi = NPY_PI_2f;
    npy_float x, y, ax, ay, rx, ry;

    x = npy_crealf(z);
    y = npy_cimagf(z);
    ax = npy_fabsf(x);
    ay = npy_fabsf(y);

    /* This helps handle many cases. */
    if (y == 0 && ax <= 1) {
        return npy_cpackf(npy_atanhf(x), y);
    }

    /* To ensure the same accuracy as atan(), and to filter out z = 0. */
    if (x == 0) {
        return npy_cpackf(x, npy_atanf(y));
    }

    if (npy_isnan(x) || npy_isnan(y)) {
        /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpackf(npy_copysignf(0, x), y + y);
        }
        /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */
        if (npy_isinf(y)) {
            return npy_cpackf(npy_copysignf(0, x),
                npy_copysignf(pio2_hi + pio2_lo, y));
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackf(NPY_NANF, NPY_NANF);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        return npy_cpackf(_real_part_reciprocalf(x, y),
            npy_copysignf(pio2_hi + pio2_lo, y));
    }

    if (ax < SQRT_3_EPSILON / 2 && ay < SQRT_3_EPSILON / 2) {
        /*
         * z = 0 was filtered out above.  All other cases must raise
         * inexact, but this is the only one that needs to do it
         * explicitly.
         */
        raise_inexact();
        return (z);
    }

    if (ax == 1 && ay < FLT_EPSILON) {
        rx = (NPY_LOGE2f - npy_logf(ay)) / 2;
    }
    else {
        rx = npy_log1pf(4 * ax / _sum_squaresf(ax - 1, ay)) / 4;
    }

    if (ax == 1) {
        ry = npy_atan2f(2, -ay) / 2;
    }
    else if (ay < FLT_EPSILON) {
        ry = npy_atan2f(2 * ay, (1 - ax) * (1 + ax)) / 2;
    }
    else {
        ry = npy_atan2f(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2;
    }

    return npy_cpackf(npy_copysignf(rx, x), npy_copysignf(ry, y));
}
#endif

#line 63

/*==========================================================
 * Constants
 *=========================================================*/
static const npy_cdouble c_1 = {1.0, 0.0};

/*==========================================================
 * Helper functions
 *
 * These are necessary because we do not count on using a
 * C99 compiler.
 *=========================================================*/
static inline
npy_cdouble
cmul(npy_cdouble a, npy_cdouble b)
{
    npy_double ar, ai, br, bi;
    ar = npy_creal(a);
    ai = npy_cimag(a);
    br = npy_creal(b);
    bi = npy_cimag(b);
    return npy_cpack(ar*br - ai*bi, ar*bi + ai*br);
}

static inline
npy_cdouble
cdiv(npy_cdouble a, npy_cdouble b)
{
    npy_double ar, ai, br, bi, abs_br, abs_bi;
    ar = npy_creal(a);
    ai = npy_cimag(a);
    br = npy_creal(b);
    bi = npy_cimag(b);
    abs_br = npy_fabs(br);
    abs_bi = npy_fabs(bi);

    if (abs_br >= abs_bi) {
        if (abs_br == 0 && abs_bi == 0) {
            /* divide by zeros should yield a complex inf or nan */
            return npy_cpack(ar/abs_br, ai/abs_bi);
        }
        else {
            npy_double rat = bi/br;
            npy_double scl = 1.0/(br+bi*rat);
            return npy_cpack((ar + ai*rat)*scl, (ai - ar*rat)*scl);
        }
    }
    else {
        npy_double rat = br/bi;
        npy_double scl = 1.0/(bi + br*rat);
        return npy_cpack((ar*rat + ai)*scl, (ai*rat - ar)*scl);
    }
}

/*==========================================================
 * Custom implementation of missing complex C99 functions
 *=========================================================*/

#ifndef HAVE_CABS
npy_double
npy_cabs(npy_cdouble z)
{
    return npy_hypot(npy_creal(z), npy_cimag(z));
}
#endif

#ifndef HAVE_CARG
npy_double
npy_carg(npy_cdouble z)
{
    return npy_atan2(npy_cimag(z), npy_creal(z));
}
#endif

/*
 * cexp and (ccos, csin)h functions need to calculate exp scaled by another
 * number.  This can be difficult if exp(x) overflows.  By doing this way, we
 * don't risk overflowing exp. This likely raises floating-point exceptions,
 * if we decide that we care.
 *
 * This is only useful over a limited range, (see below) an expects that the
 * input values are in this range.
 *
 * This is based on the technique used in FreeBSD's __frexp_exp and
 * __ldexp_(c)exp functions by David Schultz.
 *
 * SCALED_CEXP_LOWER = log(FLT_MAX)
 * SCALED_CEXP_UPPER = log(2) + log(FLT_MAX) - log(FLT_TRUE_MIN),
 * where FLT_TRUE_MIN is the smallest possible subnormal number.
 */

#define SCALED_CEXP_LOWERF 88.722839f
#define SCALED_CEXP_UPPERF 192.69492f
#define SCALED_CEXP_LOWER 710.47586007394386
#define SCALED_CEXP_UPPER 1454.9159319953251
#define SCALED_CEXP_LOWERL 11357.216553474703895L
#define SCALED_CEXP_UPPERL 22756.021937783004509L

#if !defined(HAVE_CSINH) || \
    !defined(HAVE_CCOSH) || \
    !defined(HAVE_CEXP)

static
npy_cdouble
_npy_scaled_cexp(npy_double x, npy_double y, npy_int expt)
{
#if 2 == 1
    const npy_int k = 235;
#endif
#if 2 == 2
    const npy_int k = 1799;
#endif
#if 2 == 3
    const npy_int k = 19547;
#endif
    const npy_double kln2 = k * NPY_LOGE2;
    npy_double mant, mantcos, mantsin;
    npy_int ex, excos, exsin;

    mant = npy_frexp(npy_exp(x - kln2), &ex);
    mantcos = npy_frexp(npy_cos(y), &excos);
    mantsin = npy_frexp(npy_sin(y), &exsin);

    expt += ex + k;
    return npy_cpack( npy_ldexp(mant * mantcos, expt + excos),
                         npy_ldexp(mant * mantsin, expt + exsin));
}

#endif

#ifndef HAVE_CEXP

npy_cdouble
npy_cexp(npy_cdouble z)
{
    npy_double x, c, s;
    npy_double r, i;
    npy_cdouble ret;

    r = npy_creal(z);
    i = npy_cimag(z);

    if (npy_isfinite(r)) {
        if (r >= SCALED_CEXP_LOWER && r <= SCALED_CEXP_UPPER) {
            ret = _npy_scaled_cexp(r, i, 0);
        }
        else {
            x = npy_exp(r);

            c = npy_cos(i);
            s = npy_sin(i);

            if (npy_isfinite(i)) {
                ret = npy_cpack(x * c, x * s);
            }
            else {
                ret = npy_cpack(NPY_NAN, npy_copysign(NPY_NAN, i));
            }
        }

    }
    else  if (npy_isnan(r)) {
        /* r is nan */
        if (i == 0) {
            ret = z;
        }
        else {
            ret = npy_cpack(r, npy_copysign(NPY_NAN, i));
        }
    }
    else {
        /* r is +- inf */
        if (r > 0) {
            if (i == 0) {
                ret = npy_cpack(r, i);
            }
            else if (npy_isfinite(i)) {
                c = npy_cos(i);
                s = npy_sin(i);

                ret = npy_cpack(r * c, r * s);
            }
            else {
                /* x = +inf, y = +-inf | nan */
                npy_set_floatstatus_invalid();
                ret = npy_cpack(r, NPY_NAN);
            }
        }
        else {
            if (npy_isfinite(i)) {
                x = npy_exp(r);
                c = npy_cos(i);
                s = npy_sin(i);

                ret = npy_cpack(x * c, x * s);
            }
            else {
                /* x = -inf, y = nan | +i inf */
                ret = npy_cpack(0, 0);
            }
        }
    }

    return ret;
}
#endif

#ifndef HAVE_CLOG
/* algorithm from cpython, rev. d86f5686cef9
 *
 * The usual formula for the real part is log(hypot(z.real, z.imag)).
 * There are four situations where this formula is potentially
 * problematic:
 *
 * (1) the absolute value of z is subnormal.  Then hypot is subnormal,
 * so has fewer than the usual number of bits of accuracy, hence may
 * have large relative error.  This then gives a large absolute error
 * in the log.  This can be solved by rescaling z by a suitable power
 * of 2.
 *
 * (2) the absolute value of z is greater than DBL_MAX (e.g. when both
 * z.real and z.imag are within a factor of 1/sqrt(2) of DBL_MAX)
 * Again, rescaling solves this.
 *
 * (3) the absolute value of z is close to 1.  In this case it's
 * difficult to achieve good accuracy, at least in part because a
 * change of 1ulp in the real or imaginary part of z can result in a
 * change of billions of ulps in the correctly rounded answer.
 *
 * (4) z = 0.  The simplest thing to do here is to call the
 * floating-point log with an argument of 0, and let its behaviour
 * (returning -infinity, signaling a floating-point exception, setting
 * errno, or whatever) determine that of c_log.  So the usual formula
 * is fine here.
*/
npy_cdouble
npy_clog(npy_cdouble z)
{
    npy_double ax = npy_fabs(npy_creal(z));
    npy_double ay = npy_fabs(npy_cimag(z));
    npy_double rr, ri;

    if (ax > DBL_MAX/4 || ay > DBL_MAX/4) {
        rr = npy_log(npy_hypot(ax/2, ay/2)) + NPY_LOGE2;
    }
    else if (ax < DBL_MIN && ay < DBL_MIN) {
        if (ax > 0  || ay > 0) {
            /* catch cases where hypot(ax, ay) is subnormal */
            rr = npy_log(npy_hypot(npy_ldexp(ax, DBL_MANT_DIG),
                 npy_ldexp(ay, DBL_MANT_DIG))) - DBL_MANT_DIG*NPY_LOGE2;
        }
        else {
            /* log(+/-0 +/- 0i) */
            /* raise divide-by-zero floating point exception */
            rr = -1.0 / npy_creal(z);
            rr = npy_copysign(rr, -1);
            ri = npy_carg(z);
            return npy_cpack(rr, ri);
        }
    }
    else {
        npy_double h = npy_hypot(ax, ay);
        if (0.71 <= h && h <= 1.73) {
            npy_double am = ax > ay ? ax : ay; /* max(ax, ay) */
            npy_double an = ax > ay ? ay : ax; /* min(ax, ay) */
            rr = npy_log1p((am-1)*(am+1)+an*an)/2;
        }
        else {
            rr = npy_log(h);
        }
    }
    ri = npy_carg(z);

    return npy_cpack(rr, ri);
}
#endif

#ifndef HAVE_CSQRT

/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */
#define THRESH  (DBL_MAX / (1 + NPY_SQRT2))

npy_cdouble
npy_csqrt(npy_cdouble z)
{
    npy_cdouble result;
    npy_double a, b;
    npy_double t;
    int scale;

    a = npy_creal(z);
    b = npy_cimag(z);

    /* Handle special cases. */
    if (a == 0 && b == 0) {
        return (npy_cpack(0, b));
    }
    if (npy_isinf(b)) {
        return (npy_cpack(NPY_INFINITY, b));
    }
    if (npy_isnan(a)) {
        t = (b - b) / (b - b);  /* raise invalid if b is not a NaN */
        return (npy_cpack(a, t));    /* return NaN + NaN i */
    }
    if (npy_isinf(a)) {
        /*
         * csqrt(inf + NaN i)  = inf +  NaN i
         * csqrt(inf + y i)    = inf +  0 i
         * csqrt(-inf + NaN i) = NaN +- inf i
         * csqrt(-inf + y i)   = 0   +  inf i
         */
        if (npy_signbit(a)) {
            return (npy_cpack(npy_fabs(b - b), npy_copysign(a, b)));
        }
        else {
            return (npy_cpack(a, npy_copysign(b - b, b)));
        }
    }
    /*
     * The remaining special case (b is NaN) is handled just fine by
     * the normal code path below.
     */

    /* Scale to avoid overflow. */
    if (npy_fabs(a) >= THRESH || npy_fabs(b) >= THRESH) {
        a *= 0.25;
        b *= 0.25;
        scale = 1;
    }
    else {
        scale = 0;
    }

    /* Algorithm 312, CACM vol 10, Oct 1967. */
    if (a >= 0) {
        t = npy_sqrt((a + npy_hypot(a, b)) * 0.5);
        result = npy_cpack(t, b / (2 * t));
    }
    else {
        t = npy_sqrt((-a + npy_hypot(a, b)) * 0.5);
        result = npy_cpack(npy_fabs(b) / (2 * t), npy_copysign(t, b));
    }

    /* Rescale. */
    if (scale) {
        return (npy_cpack(npy_creal(result) * 2, npy_cimag(result)));
    }
    else {
        return (result);
    }
}
#undef THRESH
#endif

/*
 * Always use this function because of the multiplication for small
 * integer powers, but in the body use cpow if it is available.
 */

/* private function for use in npy_pow{f, ,l} */
#ifdef HAVE_CPOW
static npy_cdouble
sys_cpow(npy_cdouble x, npy_cdouble y)
{
    __npy_cdouble_to_c99_cast xcast;
    __npy_cdouble_to_c99_cast ycast;
    __npy_cdouble_to_c99_cast ret;
    xcast.npy_z = x;
    ycast.npy_z = y;
    ret.c99_z = cpow(xcast.c99_z, ycast.c99_z);
    return ret.npy_z;
}
#endif


npy_cdouble
npy_cpow (npy_cdouble a, npy_cdouble b)
{
    npy_intp n;
    npy_double ar = npy_creal(a);
    npy_double br = npy_creal(b);
    npy_double ai = npy_cimag(a);
    npy_double bi = npy_cimag(b);
    npy_cdouble r;

    /*
     * Checking if in a^b, if b is zero.
     * If a is not zero then by definition of logarithm a^0 is 1.
     * If a is also zero then 0^0 is best defined as 1.
     */
    if (br == 0. && bi == 0.) {
        return npy_cpack(1., 0.);
    }
    /* case 0^b
     * If a is a complex zero (ai=ar=0), then the result depends 
     * upon values of br and bi. The result is either:
     * 0 (in magnitude), undefined or 1.
     * The later case is for br=bi=0 and independent of ar and ai
     * but is handled above).
     */
    else if (ar == 0. && ai == 0.) {
        /* 
         * If the real part of b is positive (br>0) then this is
         * the zero complex with positive sign on both the
         * real and imaginary part.
         */
         if (br > 0) {
             return npy_cpack(0., 0.);
         }
        /* else we are in the case where the
         * real part of b is negative (br<0).
         * Here we should return a complex nan
         * and raise FloatingPointError: invalid value...
         */
         
         /* Raise invalid value by calling inf - inf*/
          volatile npy_double tmp = NPY_INFINITY;
          tmp -= NPY_INFINITY;
          ar = tmp;
          
          r = npy_cpack(NPY_NAN, NPY_NAN);
          return r;
    }
    if (bi == 0 && (n=(npy_intp)br) == br) {
        if (n == 1) {
            /* unroll: handle inf better */
            return npy_cpack(ar, ai);
        }
        else if (n == 2) {
            /* unroll: handle inf better */
            return cmul(a, a);
        }
        else if (n == 3) {
            /* unroll: handle inf better */
            return cmul(a, cmul(a, a));
        }
        else if (n > -100 && n < 100) {
            npy_cdouble p, aa;
            npy_intp mask = 1;
            if (n < 0) {
                n = -n;
            }
            aa = c_1;
            p = npy_cpack(ar, ai);
            while (1) {
                if (n & mask) {
                    aa = cmul(aa,p);
                }
                mask <<= 1;
                if (n < mask || mask <= 0) {
                    break;
                }
                p = cmul(p,p);
            }
            r = npy_cpack(npy_creal(aa), npy_cimag(aa));
            if (br < 0) {
                r = cdiv(c_1, r);
            }
            return r;
        }
    }

#ifdef HAVE_CPOW
    return sys_cpow(a, b);

#else
    {
        npy_cdouble loga = npy_clog(a);

        ar = npy_creal(loga);
        ai = npy_cimag(loga);
        return npy_cexp(npy_cpack(ar*br - ai*bi, ar*bi + ai*br));
    }

#endif
}


#ifndef HAVE_CCOS
npy_cdouble
npy_ccos(npy_cdouble z)
{
    /* ccos(z) = ccosh(I * z) */
    return npy_ccosh(npy_cpack(-npy_cimag(z), npy_creal(z)));
}
#endif

#ifndef HAVE_CSIN
npy_cdouble
npy_csin(npy_cdouble z)
{
    /* csin(z) = -I * csinh(I * z) */
    z = npy_csinh(npy_cpack(-npy_cimag(z), npy_creal(z)));
    return npy_cpack(npy_cimag(z), -npy_creal(z));
}
#endif

#ifndef HAVE_CTAN
npy_cdouble
npy_ctan(npy_cdouble z)
{
    /* ctan(z) = -I * ctanh(I * z) */
    z = npy_ctanh(npy_cpack(-npy_cimag(z), npy_creal(z)));
    return (npy_cpack(npy_cimag(z), -npy_creal(z)));
}
#endif

#ifndef HAVE_CCOSH
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic cosine of a complex argument z = x + i y.
 *
 * cosh(z) = cosh(x+iy)
 *         = cosh(x) cos(y) + i sinh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 *
 * CCOSH_BIG is chosen such that
 * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG)
 * although the exact value assigned to CCOSH_BIG is not so important
 */
npy_cdouble
npy_ccosh(npy_cdouble z)
{
#if 2 == 1
    const npy_float CCOSH_BIG = 9.0f;
    const npy_float CCOSH_HUGE = 1.70141183e+38f;
#endif
#if 2 == 2
    const npy_double CCOSH_BIG = 22.0;
    const npy_double CCOSH_HUGE = 8.9884656743115795e+307;
#endif
#if 2 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CCOSH_BIG = 22.0L;
    const npy_longdouble CCOSH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CCOSH_BIG = 24.0L;
    const npy_longdouble CCOSH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_double  x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_creal(z);
    y = npy_cimag(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpack(npy_cosh(x), x * y);
        }
        absx = npy_fabs(x);
        if (absx < CCOSH_BIG) {
            /* small x: normal case */
            return npy_cpack(npy_cosh(x) * npy_cos(y),
                                npy_sinh(x) * npy_sin(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWER) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_exp(absx) * 0.5;
            return npy_cpack(h * npy_cos(y),
                                npy_copysign(h, x) * npy_sin(y));
        }
        else if (absx < SCALED_CEXP_UPPER) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexp(absx, y, -1);
            return npy_cpack(npy_creal(z),
                                npy_cimag(z) * npy_copysign(1, x));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CCOSH_HUGE * x;
            return npy_cpack(h * h * npy_cos(y), h * npy_sin(y));
        }
    }

    /*
     * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpack(y - y, npy_copysign(0, x * (y - y)));
    }

    /*
     * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0.
     *
     * cosh(NaN +- I 0)   = d(NaN) + I sign(d(NaN, +-0))0.
     * The sign of 0 in the result is unspecified.
     */
    if (y == 0 && !xfinite) {
        return npy_cpack(x * x, npy_copysign(0, x) * y);
    }

    /*
     * cosh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * cosh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpack(y - y, x * (y - y));
    }

    /*
     * cosh(+-Inf + I NaN)  = +Inf + I d(NaN).
     *
     * cosh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * cosh(+-Inf + I y)   = +Inf cos(y) +- I Inf sin(y)
     */
    if (npy_isinf(x)) {
        if (!yfinite) {
            return npy_cpack(x * x, x * (y - y));
        }
        return npy_cpack((x * x) * npy_cos(y), x * npy_sin(y));
    }

    /*
     * cosh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * cosh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * cosh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpack((x * x) * (y - y), (x + x) * (y - y));
}
#undef CCOSH_BIG
#undef CCOSH_HUGE
#endif

#ifndef HAVE_CSINH
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic sine of a complex argument z = x + i y.
 *
 * sinh(z) = sinh(x+iy)
 *         = sinh(x) cos(y) + i cosh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 */
npy_cdouble
npy_csinh(npy_cdouble z)
{
#if 2 == 1
    const npy_float CSINH_BIG = 9.0f;
    const npy_float CSINH_HUGE = 1.70141183e+38f;
#endif
#if 2 == 2
    const npy_double CSINH_BIG = 22.0;
    const npy_double CSINH_HUGE = 8.9884656743115795e+307;
#endif
#if 2 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CSINH_BIG = 22.0L;
    const npy_longdouble CSINH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CSINH_BIG = 24.0L;
    const npy_longdouble CSINH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_double x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_creal(z);
    y = npy_cimag(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpack(npy_sinh(x), y);
        }
        absx = npy_fabs(x);
        if (absx < CSINH_BIG) {
            /* small x: normal case */
            return npy_cpack(npy_sinh(x) * npy_cos(y),
                                npy_cosh(x) * npy_sin(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWER) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_exp(npy_fabs(x)) * 0.5;
            return npy_cpack(npy_copysign(h, x) * npy_cos(y),
                                h * npy_sin(y));
        }
        else if (x < SCALED_CEXP_UPPER) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexp(absx, y, -1);
            return npy_cpack(npy_creal(z) * npy_copysign(1, x),
                                npy_cimag(z));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CSINH_HUGE * x;
            return npy_cpack(h * npy_cos(y), h * h * npy_sin(y));
        }
    }

    /*
     * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN).
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpack(npy_copysign(0, x * (y - y)), y - y);
    }

    /*
     * sinh(+-Inf +- I 0) = +-Inf + I +-0.
     *
     * sinh(NaN +- I 0)   = d(NaN) + I +-0.
     */
    if (y == 0 && !xfinite) {
        if (npy_isnan(x)) {
            return z;
        }
        return npy_cpack(x, npy_copysign(0, y));
    }

    /*
     * sinh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * sinh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpack(y - y, x * (y - y));
    }

    /*
     * sinh(+-Inf + I NaN)  = +-Inf + I d(NaN).
     * The sign of Inf in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     *
     * sinh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * sinh(+-Inf + I y)   = +-Inf cos(y) + I Inf sin(y)
     */
    if (!xfinite && !npy_isnan(x)) {
        if (!yfinite) {
            return npy_cpack(x * x, x * (y - y));
        }
        return npy_cpack(x * npy_cos(y),
                            NPY_INFINITY * npy_sin(y));
    }

    /*
     * sinh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * sinh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * sinh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpack((x * x) * (y - y), (x + x) * (y - y));
}
#undef CSINH_BIG
#undef CSINH_HUGE
#endif

#ifndef HAVE_CTANH
/*
 * Taken from the msun library in FreeBSD, rev 226600.
 *
 * Hyperbolic tangent of a complex argument z = x + i y.
 *
 * The algorithm is from:
 *
 *   W. Kahan.  Branch Cuts for Complex Elementary Functions or Much
 *   Ado About Nothing's Sign Bit.  In The State of the Art in
 *   Numerical Analysis, pp. 165 ff.  Iserles and Powell, eds., 1987.
 *
 * Method:
 *
 *   Let t    = tan(x)
 *       beta = 1/cos^2(y)
 *       s    = sinh(x)
 *       rho  = cosh(x)
 *
 *   We have:
 *
 *   tanh(z) = sinh(z) / cosh(z)
 *
 *             sinh(x) cos(y) + i cosh(x) sin(y)
 *           = ---------------------------------
 *             cosh(x) cos(y) + i sinh(x) sin(y)
 *
 *             cosh(x) sinh(x) / cos^2(y) + i tan(y)
 *           = -------------------------------------
 *                    1 + sinh^2(x) / cos^2(y)
 *
 *             beta rho s + i t
 *           = ----------------
 *               1 + beta s^2
 *
 * Modifications:
 *
 *   I omitted the original algorithm's handling of overflow in tan(x) after
 *   verifying with nearpi.c that this can't happen in IEEE single or double
 *   precision.  I also handle large x differently.
 */

#define TANH_HUGE 22.0
#define TANHF_HUGE 11.0F
#define TANHL_HUGE 42.0L

npy_cdouble
npy_ctanh(npy_cdouble z)
{
    npy_double x, y;
    npy_double t, beta, s, rho, denom;

    x = npy_creal(z);
    y = npy_cimag(z);

    /*
     * ctanh(NaN + i 0) = NaN + i 0
     *
     * ctanh(NaN + i y) = NaN + i NaN        for y != 0
     *
     * The imaginary part has the sign of x*sin(2*y), but there's no
     * special effort to get this right.
     *
     * ctanh(+-Inf +- i Inf) = +-1 +- 0
     *
     * ctanh(+-Inf + i y) = +-1 + 0 sin(2y)        for y finite
     *
     * The imaginary part of the sign is unspecified.  This special
     * case is only needed to avoid a spurious invalid exception when
     * y is infinite.
     */
        if (!npy_isfinite(x)) {
            if (npy_isnan(x)) {
                return npy_cpack(x, (y == 0 ? y : x * y));
            }
            return npy_cpack(npy_copysign(1,x),
                                npy_copysign(0,
                                npy_isinf(y) ?
                                    y : npy_sin(y) * npy_cos(y)));
        }

    /*
     * ctanh(x + i NAN) = NaN + i NaN
     * ctanh(x +- i Inf) = NaN + i NaN
     */
    if (!npy_isfinite(y)) {
        return (npy_cpack(y - y, y - y));
    }

    /*
     * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the
     * approximation sinh^2(huge) ~= exp(2*huge) / 4.
     * We use a modified formula to avoid spurious overflow.
     */
    if (npy_fabs(x) >= TANH_HUGE) {
        npy_double exp_mx = npy_exp(-npy_fabs(x));
        return npy_cpack(npy_copysign(1, x),
                            4 * npy_sin(y) * npy_cos(y) *
                                exp_mx * exp_mx);
    }

    /* Kahan's algorithm */
    t = npy_tan(y);
    beta = 1 + t * t;    /* = 1 / cos^2(y) */
    s = npy_sinh(x);
    rho = npy_sqrt(1 + s * s);    /* = cosh(x) */
    denom = 1 + beta * s * s;
    return (npy_cpack((beta * rho * s) / denom, t / denom));
}
#undef TANH_HUGE
#undef TANHF_HUGE
#undef TANHL_HUGE
#endif

#if !defined (HAVE_CACOS) || !defined (HAVE_CASINH)
/*
 * Complex inverse trig functions taken from the msum library in FreeBSD
 * revision 251404
 *
 * The algorithm is very close to that in "Implementing the complex arcsine
 * and arccosine functions using exception handling" by T. E. Hull, Thomas F.
 * Fairgrieve, and Ping Tak Peter Tang, published in ACM Transactions on
 * Mathematical Software, Volume 23 Issue 3, 1997, Pages 299-335,
 * http://dl.acm.org/citation.cfm?id=275324.
 *
 * Throughout we use the convention z = x + I*y.
 *
 * casinh(z) = sign(x)*log(A+sqrt(A*A-1)) + I*asin(B)
 * where
 * A = (|z+I| + |z-I|) / 2
 * B = (|z+I| - |z-I|) / 2 = y/A
 *
 * These formulas become numerically unstable:
 *   (a) for Re(casinh(z)) when z is close to the line segment [-I, I] (that
 *       is, Re(casinh(z)) is close to 0);
 *   (b) for Im(casinh(z)) when z is close to either of the intervals
 *       [I, I*infinity) or (-I*infinity, -I] (that is, |Im(casinh(z))| is
 *       close to PI/2).
 *
 * These numerical problems are overcome by defining
 * f(a, b) = (hypot(a, b) - b) / 2 = a*a / (hypot(a, b) + b) / 2
 * Then if A < A_crossover, we use
 *   log(A + sqrt(A*A-1)) = log1p((A-1) + sqrt((A-1)*(A+1)))
 *   A-1 = f(x, 1+y) + f(x, 1-y)
 * and if B > B_crossover, we use
 *   asin(B) = atan2(y, sqrt(A*A - y*y)) = atan2(y, sqrt((A+y)*(A-y)))
 *   A-y = f(x, y+1) + f(x, y-1)
 * where without loss of generality we have assumed that x and y are
 * non-negative.
 *
 * Much of the difficulty comes because the intermediate computations may
 * produce overflows or underflows.  This is dealt with in the paper by Hull
 * et al by using exception handling.  We do this by detecting when
 * computations risk underflow or overflow.  The hardest part is handling the
 * underflows when computing f(a, b).
 *
 * Note that the function f(a, b) does not appear explicitly in the paper by
 * Hull et al, but the idea may be found on pages 308 and 309.  Introducing the
 * function f(a, b) allows us to concentrate many of the clever tricks in this
 * paper into one function.
 */

/*
 * Function f(a, b, hypot_a_b) = (hypot(a, b) - b) / 2.
 * Pass hypot(a, b) as the third argument.
 */
static inline npy_double
_f(npy_double a, npy_double b, npy_double hypot_a_b)
{
    if (b < 0) {
        return ((hypot_a_b - b) / 2);
    }
    if (b == 0) {
        return (a / 2);
    }
    return (a * a / (hypot_a_b + b) / 2);
}

/*
 * All the hard work is contained in this function.
 * x and y are assumed positive or zero, and less than RECIP_EPSILON.
 * Upon return:
 * rx = Re(casinh(z)) = -Im(cacos(y + I*x)).
 * B_is_usable is set to 1 if the value of B is usable.
 * If B_is_usable is set to 0, sqrt_A2my2 = sqrt(A*A - y*y), and new_y = y.
 * If returning sqrt_A2my2 has potential to result in an underflow, it is
 * rescaled, and new_y is similarly rescaled.
 */
static inline void
_do_hard_work(npy_double x, npy_double y, npy_double *rx,
    npy_int *B_is_usable, npy_double *B, npy_double *sqrt_A2my2, npy_double *new_y)
{
#if 2 == 1
    const npy_float A_crossover = 10.0f;
    const npy_float B_crossover = 0.6417f;
    const npy_float FOUR_SQRT_MIN = 4.3368086899420177e-19f;
#endif
#if 2 == 2
    const npy_double A_crossover = 10.0;
    const npy_double B_crossover = 0.6417;
    const npy_double FOUR_SQRT_MIN = 5.9666725849601654e-154;
#endif
#if 2 == 3
    const npy_longdouble A_crossover = 10.0l;
    const npy_longdouble B_crossover = 0.6417l;
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble FOUR_SQRT_MIN = 5.9666725849601654e-154;
#else
    const npy_longdouble FOUR_SQRT_MIN = 7.3344154702193886625e-2466l;
#endif
#endif
    npy_double R, S, A; /* A, B, R, and S are as in Hull et al. */
    npy_double Am1, Amy; /* A-1, A-y. */

    R = npy_hypot(x, y + 1);        /* |z+I| */
    S = npy_hypot(x, y - 1);        /* |z-I| */

    /* A = (|z+I| + |z-I|) / 2 */
    A = (R + S) / 2;
    /*
     * Mathematically A >= 1.  There is a small chance that this will not
     * be so because of rounding errors.  So we will make certain it is
     * so.
     */
    if (A < 1) {
        A = 1;
    }

    if (A < A_crossover) {
        /*
         * Am1 = fp + fm, where fp = f(x, 1+y), and fm = f(x, 1-y).
         * rx = log1p(Am1 + sqrt(Am1*(A+1)))
         */
        if (y == 1 && x < DBL_EPSILON * DBL_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *rx = npy_sqrt(x);
        }
        else if (x >= DBL_EPSILON * npy_fabs(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN
             */
            Am1 = _f(x, 1 + y, R) + _f(x, 1 - y, S);
            *rx = npy_log1p(Am1 + npy_sqrt(Am1 * (A + 1)));
        }
        else if (y < 1) {
            /*
             * fp = x*x/(1+y)/4, fm = x*x/(1-y)/4, and
             * A = 1 (inexactly).
             */
            *rx = x / npy_sqrt((1 - y) * (1 + y));
        }
        else {        /* if (y > 1) */
            /*
             * A-1 = y-1 (inexactly).
             */
            *rx = npy_log1p((y - 1) + npy_sqrt((y - 1) * (y + 1)));
        }
    }
    else {
        *rx = npy_log(A + npy_sqrt(A * A - 1));
    }

    *new_y = y;

    if (y < FOUR_SQRT_MIN) {
        /*
         * Avoid a possible underflow caused by y/A.  For casinh this
         * would be legitimate, but will be picked up by invoking atan2
         * later on.  For cacos this would not be legitimate.
         */
        *B_is_usable = 0;
        *sqrt_A2my2 = A * (2 / DBL_EPSILON);
        *new_y = y * (2 / DBL_EPSILON);
        return;
    }

    /* B = (|z+I| - |z-I|) / 2 = y/A */
    *B = y / A;
    *B_is_usable = 1;

    if (*B > B_crossover) {
        *B_is_usable = 0;
        /*
         * Amy = fp + fm, where fp = f(x, y+1), and fm = f(x, y-1).
         * sqrt_A2my2 = sqrt(Amy*(A+y))
         */
        if (y == 1 && x < DBL_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrt(x) * npy_sqrt((A + y) / 2);
        }
        else if (x >= DBL_EPSILON * npy_fabs(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN
             * and
             * x >= DBL_EPSILON^2 >= FOUR_SQRT_MIN
             */
            Amy = _f(x, y + 1, R) + _f(x, y - 1, S);
            *sqrt_A2my2 = npy_sqrt(Amy * (A + y));
        }
        else if (y > 1) {
            /*
             * fp = x*x/(y+1)/4, fm = x*x/(y-1)/4, and
             * A = y (inexactly).
             *
             * y < RECIP_EPSILON.  So the following
             * scaling should avoid any underflow problems.
             */
            *sqrt_A2my2 = x * (4 / DBL_EPSILON / DBL_EPSILON) * y /
                npy_sqrt((y + 1) * (y - 1));
            *new_y = y * (4 / DBL_EPSILON / DBL_EPSILON);
        }
        else {        /* if (y < 1) */
            /*
             * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrt((1 - y) * (1 + y));
        }
    }
}

/*
 * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON.
 */
static inline void
_clog_for_large_values(npy_double x, npy_double y,
    npy_double *rr, npy_double *ri)
{
#if 2 == 1
    const npy_float QUARTER_SQRT_MAX = 4.611685743549481e+18f;
    const npy_float SQRT_MIN = 1.0842021724855044e-19f;
 #endif
#if 2 == 2
    const npy_double QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_double SQRT_MIN = 1.4916681462400413e-154;
 #endif
#if 2 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_longdouble SQRT_MIN = 1.4916681462400413e-154;
#else
    const npy_longdouble QUARTER_SQRT_MAX = 2.7268703390485398235e+2465l;
    const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    npy_double ax, ay, t;

    ax = npy_fabs(x);
    ay = npy_fabs(y);
    if (ax < ay) {
        t = ax;
        ax = ay;
        ay = t;
    }

    /*
     * Avoid overflow in hypot() when x and y are both very large.
     * Divide x and y by E, and then add 1 to the logarithm.  This depends
     * on E being larger than sqrt(2).
     * Dividing by E causes an insignificant loss of accuracy; however
     * this method is still poor since it is unnecessarily slow.
     */
    if (ax > DBL_MAX / 2) {
        *rr = npy_log(npy_hypot(x / NPY_E, y / NPY_E)) + 1;
    }
    /*
     * Avoid overflow when x or y is large.  Avoid underflow when x or
     * y is small.
     */
    else if (ax > QUARTER_SQRT_MAX || ay < SQRT_MIN) {
        *rr = npy_log(npy_hypot(x, y));
    }
    else {
        *rr = npy_log(ax * ax + ay * ay) / 2;
    }
    *ri = npy_atan2(y, x);
}
#endif

#ifndef HAVE_CACOS
npy_cdouble
npy_cacos(npy_cdouble z)
{
#if 2 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 2 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 2 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_double RECIP_EPSILON = 1.0 / DBL_EPSILON;
    const npy_double pio2_hi = NPY_PI_2;
    npy_double x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2mx2, new_x;
    npy_int sx, sy;
    npy_int B_is_usable;

    x = npy_creal(z);
    y = npy_cimag(z);
    sx = npy_signbit(x);
    sy = npy_signbit(y);
    ax = npy_fabs(x);
    ay = npy_fabs(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */
        if (npy_isinf(x)) {
            return npy_cpack(y + y, -NPY_INFINITY);
        }
        /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */
        if (npy_isinf(y)) {
            return npy_cpack(x + x, -y);
        }
        /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */
        if (x == 0) {
            return npy_cpack(pio2_hi + pio2_lo, y + y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpack(NPY_NAN, NPY_NAN);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        _clog_for_large_values(x, y, &wx, &wy);
        rx = npy_fabs(wy);
        ry = wx + NPY_LOGE2;
        if (sy == 0) {
            ry = -ry;
        }
        return npy_cpack(rx, ry);
    }

    /* Avoid spuriously raising inexact for z = 1. */
    if (x == 1 && y == 0) {
        return npy_cpack(0, -y);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return npy_cpack(pio2_hi - (x - pio2_lo), -y);
    }

    _do_hard_work(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x);
    if (B_is_usable) {
        if (sx == 0) {
            rx = npy_acos(B);
        }
        else {
            rx = npy_acos(-B);
        }
    }
    else {
        if (sx == 0) {
            rx = npy_atan2(sqrt_A2mx2, new_x);
        }
        else {
            rx = npy_atan2(sqrt_A2mx2, -new_x);
        }
    }
    if (sy == 0) {
        ry = -ry;
    }
    return npy_cpack(rx, ry);
}
#endif

#ifndef HAVE_CASIN
npy_cdouble
npy_casin(npy_cdouble z)
{
    /* casin(z) = I * conj( casinh(I * conj(z)) ) */
    z = npy_casinh(npy_cpack(npy_cimag(z), npy_creal(z)));
    return npy_cpack(npy_cimag(z), npy_creal(z));
}
#endif

#ifndef HAVE_CATAN
npy_cdouble
npy_catan(npy_cdouble z)
{
    /* catan(z) = I * conj( catanh(I * conj(z)) ) */
    z = npy_catanh(npy_cpack(npy_cimag(z), npy_creal(z)));
    return npy_cpack(npy_cimag(z), npy_creal(z));
}
#endif

#ifndef HAVE_CACOSH
npy_cdouble
npy_cacosh(npy_cdouble z)
{
    /*
     * cacosh(z) = I*cacos(z) or -I*cacos(z)
     * where the sign is chosen so Re(cacosh(z)) >= 0.
     */
    npy_cdouble  w;
    npy_double rx, ry;

    w = npy_cacos(z);
    rx = npy_creal(w);
    ry = npy_cimag(w);
    /* cacosh(NaN + I*NaN) = NaN + I*NaN */
    if (npy_isnan(rx) && npy_isnan(ry)) {
        return npy_cpack(ry, rx);
    }
    /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */
    /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */
    if (npy_isnan(rx)) {
        return npy_cpack(npy_fabs(ry), rx);
    }
    /* cacosh(0 + I*NaN) = NaN + I*NaN */
    if (npy_isnan(ry)) {
        return npy_cpack(ry, ry);
    }
    return npy_cpack(npy_fabs(ry), npy_copysign(rx, npy_cimag(z)));
}
#endif

#ifndef HAVE_CASINH
/*
 * casinh(z) = z + O(z^3)   as z -> 0
 *
 * casinh(z) = sign(x)*clog(sign(x)*z) + O(1/z^2)   as z -> infinity
 * The above formula works for the imaginary part as well, because
 * Im(casinh(z)) = sign(x)*atan2(sign(x)*y, fabs(x)) + O(y/z^3)
 *    as z -> infinity, uniformly in y
 */
npy_cdouble
npy_casinh(npy_cdouble z)
{
#if 2 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
#endif
#if 2 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
#endif
#if 2 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
#endif
    const npy_double RECIP_EPSILON = 1.0 / DBL_EPSILON;
    npy_double x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y;
    npy_int B_is_usable;

    x = npy_creal(z);
    y = npy_cimag(z);
    ax = npy_fabs(x);
    ay = npy_fabs(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpack(x, y + y);
        }
        /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */
        if (npy_isinf(y)) {
            return npy_cpack(y, x + x);
        }
        /* casinh(NaN + I*0) = NaN + I*0 */
        if (y == 0) {
            return npy_cpack(x + x, y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpack(NPY_NAN, NPY_NAN);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        if (npy_signbit(x) == 0) {
            _clog_for_large_values(x, y, &wx, &wy);
            wx += NPY_LOGE2;
        }
        else {
            _clog_for_large_values(-x, -y, &wx, &wy);
            wx += NPY_LOGE2;
        }
        return npy_cpack(npy_copysign(wx, x), npy_copysign(wy, y));
    }

    /* Avoid spuriously raising inexact for z = 0. */
    if (x == 0 && y == 0) {
        return (z);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return (z);
    }

    _do_hard_work(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y);
    if (B_is_usable) {
        ry = npy_asin(B);
    }
    else {
        ry = npy_atan2(new_y, sqrt_A2my2);
    }
    return npy_cpack(npy_copysign(rx, x), npy_copysign(ry, y));
}
#endif

#ifndef HAVE_CATANH
/*
 * sum_squares(x,y) = x*x + y*y (or just x*x if y*y would underflow).
 * Assumes x*x and y*y will not overflow.
 * Assumes x and y are finite.
 * Assumes y is non-negative.
 * Assumes fabs(x) >= DBL_EPSILON.
 */
static inline npy_double
_sum_squares(npy_double x, npy_double y)
{
#if 2 == 1
const npy_float SQRT_MIN = 1.0842022e-19f;
#endif
#if 2 == 2
const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#endif
#if 2 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
const npy_longdouble SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#else
/* this is correct for 80 bit long doubles */
const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    /* Avoid underflow when y is small. */
    if (y < SQRT_MIN) {
        return (x * x);
    }

    return (x * x + y * y);
}

/*
 * real_part_reciprocal(x, y) = Re(1/(x+I*y)) = x/(x*x + y*y).
 * Assumes x and y are not NaN, and one of x and y is larger than
 * RECIP_EPSILON.  We avoid unwarranted underflow.  It is important to not use
 * the code creal(1/z), because the imaginary part may produce an unwanted
 * underflow.
 * This is only called in a context where inexact is always raised before
 * the call, so no effort is made to avoid or force inexact.
 */
#if 2 == 1
#define BIAS (FLT_MAX_EXP - 1)
#define CUTOFF (FLT_MANT_DIG / 2 + 1)
static inline npy_float
_real_part_reciprocalf(npy_float x, npy_float y)
{
    npy_float scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    GET_FLOAT_WORD(hx, x);
    ix = hx & 0x7f800000;
    GET_FLOAT_WORD(hy, y);
    iy = hy & 0x7f800000;
    if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) {
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 23) {
        return (x / y / y);
    }
    if (ix <= (BIAS + FLT_MAX_EXP / 2 - CUTOFF) << 23) {
        return (x / (x * x + y * y));
    }
    SET_FLOAT_WORD(scale, 0x7f800000 - ix);
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 2 == 2
#define BIAS (DBL_MAX_EXP - 1)
/*  more guard digits are useful iff there is extra precision. */
#define CUTOFF (DBL_MANT_DIG / 2 + 1)  /* just half or 1 guard digit */
static inline npy_double
_real_part_reciprocal(npy_double x, npy_double y)
{
    npy_double scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    /*
     * This code is inspired by the C99 document n1124.pdf, Section G.5.1,
     * example 2.
     */
    GET_HIGH_WORD(hx, x);
    ix = hx & 0x7ff00000;
    GET_HIGH_WORD(hy, y);
    iy = hy & 0x7ff00000;
    if (ix - iy >= CUTOFF << 20 || npy_isinf(x)) {
        /* +-Inf -> +-0 is special */
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 20) {
        /* should avoid double div, but hard */
        return (x / y / y);
    }
    if (ix <= (BIAS + DBL_MAX_EXP / 2 - CUTOFF) << 20) {
        return (x / (x * x + y * y));
    }
    scale = 1;
    SET_HIGH_WORD(scale, 0x7ff00000 - ix);  /* 2**(1-ilogb(x)) */
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 2 == 3
#if !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE) && \
    !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE)

#define BIAS (LDBL_MAX_EXP - 1)
#define CUTOFF (LDBL_MANT_DIG / 2 + 1)
static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    npy_longdouble scale;
    union IEEEl2bitsrep ux, uy, us;
    npy_int32 ix, iy;

    ux.e = x;
    ix = GET_LDOUBLE_EXP(ux);
    uy.e = y;
    iy = GET_LDOUBLE_EXP(uy);
    if (ix - iy >= CUTOFF || npy_isinf(x)) {
        return (1/x);
    }
    if (iy - ix >= CUTOFF) {
        return (x/y/y);
    }
    if (ix <= BIAS + LDBL_MAX_EXP / 2 - CUTOFF) {
        return (x/(x*x + y*y));
    }
    us.e = 1;
    SET_LDOUBLE_EXP(us, 0x7fff - ix);
    scale = us.e;
    x *= scale;
    y *= scale;
    return (x/(x*x + y*y) * scale);
}
#undef BIAS
#undef CUTOFF

#else

static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    return x/(x*x + y*y);
}

#endif
#endif

npy_cdouble
npy_catanh(npy_cdouble z)
{
#if 2 == 1
    /* this is sqrt(3*EPS) */
    const npy_float SQRT_3_EPSILON = 5.9801995673e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 2 == 2
    const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 2 == 3
    const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_double RECIP_EPSILON = 1.0 / DBL_EPSILON;
    const npy_double pio2_hi = NPY_PI_2;
    npy_double x, y, ax, ay, rx, ry;

    x = npy_creal(z);
    y = npy_cimag(z);
    ax = npy_fabs(x);
    ay = npy_fabs(y);

    /* This helps handle many cases. */
    if (y == 0 && ax <= 1) {
        return npy_cpack(npy_atanh(x), y);
    }

    /* To ensure the same accuracy as atan(), and to filter out z = 0. */
    if (x == 0) {
        return npy_cpack(x, npy_atan(y));
    }

    if (npy_isnan(x) || npy_isnan(y)) {
        /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpack(npy_copysign(0, x), y + y);
        }
        /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */
        if (npy_isinf(y)) {
            return npy_cpack(npy_copysign(0, x),
                npy_copysign(pio2_hi + pio2_lo, y));
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpack(NPY_NAN, NPY_NAN);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        return npy_cpack(_real_part_reciprocal(x, y),
            npy_copysign(pio2_hi + pio2_lo, y));
    }

    if (ax < SQRT_3_EPSILON / 2 && ay < SQRT_3_EPSILON / 2) {
        /*
         * z = 0 was filtered out above.  All other cases must raise
         * inexact, but this is the only one that needs to do it
         * explicitly.
         */
        raise_inexact();
        return (z);
    }

    if (ax == 1 && ay < DBL_EPSILON) {
        rx = (NPY_LOGE2 - npy_log(ay)) / 2;
    }
    else {
        rx = npy_log1p(4 * ax / _sum_squares(ax - 1, ay)) / 4;
    }

    if (ax == 1) {
        ry = npy_atan2(2, -ay) / 2;
    }
    else if (ay < DBL_EPSILON) {
        ry = npy_atan2(2 * ay, (1 - ax) * (1 + ax)) / 2;
    }
    else {
        ry = npy_atan2(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2;
    }

    return npy_cpack(npy_copysign(rx, x), npy_copysign(ry, y));
}
#endif

#line 63

/*==========================================================
 * Constants
 *=========================================================*/
static const npy_clongdouble c_1l = {1.0L, 0.0};

/*==========================================================
 * Helper functions
 *
 * These are necessary because we do not count on using a
 * C99 compiler.
 *=========================================================*/
static inline
npy_clongdouble
cmull(npy_clongdouble a, npy_clongdouble b)
{
    npy_longdouble ar, ai, br, bi;
    ar = npy_creall(a);
    ai = npy_cimagl(a);
    br = npy_creall(b);
    bi = npy_cimagl(b);
    return npy_cpackl(ar*br - ai*bi, ar*bi + ai*br);
}

static inline
npy_clongdouble
cdivl(npy_clongdouble a, npy_clongdouble b)
{
    npy_longdouble ar, ai, br, bi, abs_br, abs_bi;
    ar = npy_creall(a);
    ai = npy_cimagl(a);
    br = npy_creall(b);
    bi = npy_cimagl(b);
    abs_br = npy_fabsl(br);
    abs_bi = npy_fabsl(bi);

    if (abs_br >= abs_bi) {
        if (abs_br == 0 && abs_bi == 0) {
            /* divide by zeros should yield a complex inf or nan */
            return npy_cpackl(ar/abs_br, ai/abs_bi);
        }
        else {
            npy_longdouble rat = bi/br;
            npy_longdouble scl = 1.0L/(br+bi*rat);
            return npy_cpackl((ar + ai*rat)*scl, (ai - ar*rat)*scl);
        }
    }
    else {
        npy_longdouble rat = br/bi;
        npy_longdouble scl = 1.0L/(bi + br*rat);
        return npy_cpackl((ar*rat + ai)*scl, (ai*rat - ar)*scl);
    }
}

/*==========================================================
 * Custom implementation of missing complex C99 functions
 *=========================================================*/

#ifndef HAVE_CABSL
npy_longdouble
npy_cabsl(npy_clongdouble z)
{
    return npy_hypotl(npy_creall(z), npy_cimagl(z));
}
#endif

#ifndef HAVE_CARGL
npy_longdouble
npy_cargl(npy_clongdouble z)
{
    return npy_atan2l(npy_cimagl(z), npy_creall(z));
}
#endif

/*
 * cexp and (ccos, csin)h functions need to calculate exp scaled by another
 * number.  This can be difficult if exp(x) overflows.  By doing this way, we
 * don't risk overflowing exp. This likely raises floating-point exceptions,
 * if we decide that we care.
 *
 * This is only useful over a limited range, (see below) an expects that the
 * input values are in this range.
 *
 * This is based on the technique used in FreeBSD's __frexp_exp and
 * __ldexp_(c)exp functions by David Schultz.
 *
 * SCALED_CEXP_LOWER = log(FLT_MAX)
 * SCALED_CEXP_UPPER = log(2) + log(FLT_MAX) - log(FLT_TRUE_MIN),
 * where FLT_TRUE_MIN is the smallest possible subnormal number.
 */

#define SCALED_CEXP_LOWERF 88.722839f
#define SCALED_CEXP_UPPERF 192.69492f
#define SCALED_CEXP_LOWER 710.47586007394386
#define SCALED_CEXP_UPPER 1454.9159319953251
#define SCALED_CEXP_LOWERL 11357.216553474703895L
#define SCALED_CEXP_UPPERL 22756.021937783004509L

#if !defined(HAVE_CSINHL) || \
    !defined(HAVE_CCOSHL) || \
    !defined(HAVE_CEXPL)

static
npy_clongdouble
_npy_scaled_cexpl(npy_longdouble x, npy_longdouble y, npy_int expt)
{
#if 3 == 1
    const npy_int k = 235;
#endif
#if 3 == 2
    const npy_int k = 1799;
#endif
#if 3 == 3
    const npy_int k = 19547;
#endif
    const npy_longdouble kln2 = k * NPY_LOGE2l;
    npy_longdouble mant, mantcos, mantsin;
    npy_int ex, excos, exsin;

    mant = npy_frexpl(npy_expl(x - kln2), &ex);
    mantcos = npy_frexpl(npy_cosl(y), &excos);
    mantsin = npy_frexpl(npy_sinl(y), &exsin);

    expt += ex + k;
    return npy_cpackl( npy_ldexpl(mant * mantcos, expt + excos),
                         npy_ldexpl(mant * mantsin, expt + exsin));
}

#endif

#ifndef HAVE_CEXPL

npy_clongdouble
npy_cexpl(npy_clongdouble z)
{
    npy_longdouble x, c, s;
    npy_longdouble r, i;
    npy_clongdouble ret;

    r = npy_creall(z);
    i = npy_cimagl(z);

    if (npy_isfinite(r)) {
        if (r >= SCALED_CEXP_LOWERL && r <= SCALED_CEXP_UPPERL) {
            ret = _npy_scaled_cexpl(r, i, 0);
        }
        else {
            x = npy_expl(r);

            c = npy_cosl(i);
            s = npy_sinl(i);

            if (npy_isfinite(i)) {
                ret = npy_cpackl(x * c, x * s);
            }
            else {
                ret = npy_cpackl(NPY_NANL, npy_copysignl(NPY_NANL, i));
            }
        }

    }
    else  if (npy_isnan(r)) {
        /* r is nan */
        if (i == 0) {
            ret = z;
        }
        else {
            ret = npy_cpackl(r, npy_copysignl(NPY_NANL, i));
        }
    }
    else {
        /* r is +- inf */
        if (r > 0) {
            if (i == 0) {
                ret = npy_cpackl(r, i);
            }
            else if (npy_isfinite(i)) {
                c = npy_cosl(i);
                s = npy_sinl(i);

                ret = npy_cpackl(r * c, r * s);
            }
            else {
                /* x = +inf, y = +-inf | nan */
                npy_set_floatstatus_invalid();
                ret = npy_cpackl(r, NPY_NANL);
            }
        }
        else {
            if (npy_isfinite(i)) {
                x = npy_expl(r);
                c = npy_cosl(i);
                s = npy_sinl(i);

                ret = npy_cpackl(x * c, x * s);
            }
            else {
                /* x = -inf, y = nan | +i inf */
                ret = npy_cpackl(0, 0);
            }
        }
    }

    return ret;
}
#endif

#ifndef HAVE_CLOGL
/* algorithm from cpython, rev. d86f5686cef9
 *
 * The usual formula for the real part is log(hypot(z.real, z.imag)).
 * There are four situations where this formula is potentially
 * problematic:
 *
 * (1) the absolute value of z is subnormal.  Then hypot is subnormal,
 * so has fewer than the usual number of bits of accuracy, hence may
 * have large relative error.  This then gives a large absolute error
 * in the log.  This can be solved by rescaling z by a suitable power
 * of 2.
 *
 * (2) the absolute value of z is greater than DBL_MAX (e.g. when both
 * z.real and z.imag are within a factor of 1/sqrt(2) of DBL_MAX)
 * Again, rescaling solves this.
 *
 * (3) the absolute value of z is close to 1.  In this case it's
 * difficult to achieve good accuracy, at least in part because a
 * change of 1ulp in the real or imaginary part of z can result in a
 * change of billions of ulps in the correctly rounded answer.
 *
 * (4) z = 0.  The simplest thing to do here is to call the
 * floating-point log with an argument of 0, and let its behaviour
 * (returning -infinity, signaling a floating-point exception, setting
 * errno, or whatever) determine that of c_log.  So the usual formula
 * is fine here.
*/
npy_clongdouble
npy_clogl(npy_clongdouble z)
{
    npy_longdouble ax = npy_fabsl(npy_creall(z));
    npy_longdouble ay = npy_fabsl(npy_cimagl(z));
    npy_longdouble rr, ri;

    if (ax > LDBL_MAX/4 || ay > LDBL_MAX/4) {
        rr = npy_logl(npy_hypotl(ax/2, ay/2)) + NPY_LOGE2l;
    }
    else if (ax < LDBL_MIN && ay < LDBL_MIN) {
        if (ax > 0  || ay > 0) {
            /* catch cases where hypot(ax, ay) is subnormal */
            rr = npy_logl(npy_hypotl(npy_ldexpl(ax, LDBL_MANT_DIG),
                 npy_ldexpl(ay, LDBL_MANT_DIG))) - LDBL_MANT_DIG*NPY_LOGE2l;
        }
        else {
            /* log(+/-0 +/- 0i) */
            /* raise divide-by-zero floating point exception */
            rr = -1.0l / npy_creall(z);
            rr = npy_copysignl(rr, -1);
            ri = npy_cargl(z);
            return npy_cpackl(rr, ri);
        }
    }
    else {
        npy_longdouble h = npy_hypotl(ax, ay);
        if (0.71 <= h && h <= 1.73) {
            npy_longdouble am = ax > ay ? ax : ay; /* max(ax, ay) */
            npy_longdouble an = ax > ay ? ay : ax; /* min(ax, ay) */
            rr = npy_log1pl((am-1)*(am+1)+an*an)/2;
        }
        else {
            rr = npy_logl(h);
        }
    }
    ri = npy_cargl(z);

    return npy_cpackl(rr, ri);
}
#endif

#ifndef HAVE_CSQRTL

/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */
#define THRESH  (LDBL_MAX / (1 + NPY_SQRT2l))

npy_clongdouble
npy_csqrtl(npy_clongdouble z)
{
    npy_clongdouble result;
    npy_longdouble a, b;
    npy_longdouble t;
    int scale;

    a = npy_creall(z);
    b = npy_cimagl(z);

    /* Handle special cases. */
    if (a == 0 && b == 0) {
        return (npy_cpackl(0, b));
    }
    if (npy_isinf(b)) {
        return (npy_cpackl(NPY_INFINITYL, b));
    }
    if (npy_isnan(a)) {
        t = (b - b) / (b - b);  /* raise invalid if b is not a NaN */
        return (npy_cpackl(a, t));    /* return NaN + NaN i */
    }
    if (npy_isinf(a)) {
        /*
         * csqrt(inf + NaN i)  = inf +  NaN i
         * csqrt(inf + y i)    = inf +  0 i
         * csqrt(-inf + NaN i) = NaN +- inf i
         * csqrt(-inf + y i)   = 0   +  inf i
         */
        if (npy_signbit(a)) {
            return (npy_cpackl(npy_fabsl(b - b), npy_copysignl(a, b)));
        }
        else {
            return (npy_cpackl(a, npy_copysignl(b - b, b)));
        }
    }
    /*
     * The remaining special case (b is NaN) is handled just fine by
     * the normal code path below.
     */

    /* Scale to avoid overflow. */
    if (npy_fabsl(a) >= THRESH || npy_fabsl(b) >= THRESH) {
        a *= 0.25;
        b *= 0.25;
        scale = 1;
    }
    else {
        scale = 0;
    }

    /* Algorithm 312, CACM vol 10, Oct 1967. */
    if (a >= 0) {
        t = npy_sqrtl((a + npy_hypotl(a, b)) * 0.5l);
        result = npy_cpackl(t, b / (2 * t));
    }
    else {
        t = npy_sqrtl((-a + npy_hypotl(a, b)) * 0.5l);
        result = npy_cpackl(npy_fabsl(b) / (2 * t), npy_copysignl(t, b));
    }

    /* Rescale. */
    if (scale) {
        return (npy_cpackl(npy_creall(result) * 2, npy_cimagl(result)));
    }
    else {
        return (result);
    }
}
#undef THRESH
#endif

/*
 * Always use this function because of the multiplication for small
 * integer powers, but in the body use cpow if it is available.
 */

/* private function for use in npy_pow{f, ,l} */
#ifdef HAVE_CPOWL
static npy_clongdouble
sys_cpowl(npy_clongdouble x, npy_clongdouble y)
{
    __npy_clongdouble_to_c99_cast xcast;
    __npy_clongdouble_to_c99_cast ycast;
    __npy_clongdouble_to_c99_cast ret;
    xcast.npy_z = x;
    ycast.npy_z = y;
    ret.c99_z = cpowl(xcast.c99_z, ycast.c99_z);
    return ret.npy_z;
}
#endif


npy_clongdouble
npy_cpowl (npy_clongdouble a, npy_clongdouble b)
{
    npy_intp n;
    npy_longdouble ar = npy_creall(a);
    npy_longdouble br = npy_creall(b);
    npy_longdouble ai = npy_cimagl(a);
    npy_longdouble bi = npy_cimagl(b);
    npy_clongdouble r;

    /*
     * Checking if in a^b, if b is zero.
     * If a is not zero then by definition of logarithm a^0 is 1.
     * If a is also zero then 0^0 is best defined as 1.
     */
    if (br == 0. && bi == 0.) {
        return npy_cpackl(1., 0.);
    }
    /* case 0^b
     * If a is a complex zero (ai=ar=0), then the result depends 
     * upon values of br and bi. The result is either:
     * 0 (in magnitude), undefined or 1.
     * The later case is for br=bi=0 and independent of ar and ai
     * but is handled above).
     */
    else if (ar == 0. && ai == 0.) {
        /* 
         * If the real part of b is positive (br>0) then this is
         * the zero complex with positive sign on both the
         * real and imaginary part.
         */
         if (br > 0) {
             return npy_cpackl(0., 0.);
         }
        /* else we are in the case where the
         * real part of b is negative (br<0).
         * Here we should return a complex nan
         * and raise FloatingPointError: invalid value...
         */
         
         /* Raise invalid value by calling inf - inf*/
          volatile npy_longdouble tmp = NPY_INFINITYL;
          tmp -= NPY_INFINITYL;
          ar = tmp;
          
          r = npy_cpackl(NPY_NANL, NPY_NANL);
          return r;
    }
    if (bi == 0 && (n=(npy_intp)br) == br) {
        if (n == 1) {
            /* unroll: handle inf better */
            return npy_cpackl(ar, ai);
        }
        else if (n == 2) {
            /* unroll: handle inf better */
            return cmull(a, a);
        }
        else if (n == 3) {
            /* unroll: handle inf better */
            return cmull(a, cmull(a, a));
        }
        else if (n > -100 && n < 100) {
            npy_clongdouble p, aa;
            npy_intp mask = 1;
            if (n < 0) {
                n = -n;
            }
            aa = c_1l;
            p = npy_cpackl(ar, ai);
            while (1) {
                if (n & mask) {
                    aa = cmull(aa,p);
                }
                mask <<= 1;
                if (n < mask || mask <= 0) {
                    break;
                }
                p = cmull(p,p);
            }
            r = npy_cpackl(npy_creall(aa), npy_cimagl(aa));
            if (br < 0) {
                r = cdivl(c_1l, r);
            }
            return r;
        }
    }

#ifdef HAVE_CPOWL
    return sys_cpowl(a, b);

#else
    {
        npy_clongdouble loga = npy_clogl(a);

        ar = npy_creall(loga);
        ai = npy_cimagl(loga);
        return npy_cexpl(npy_cpackl(ar*br - ai*bi, ar*bi + ai*br));
    }

#endif
}


#ifndef HAVE_CCOSL
npy_clongdouble
npy_ccosl(npy_clongdouble z)
{
    /* ccos(z) = ccosh(I * z) */
    return npy_ccoshl(npy_cpackl(-npy_cimagl(z), npy_creall(z)));
}
#endif

#ifndef HAVE_CSINL
npy_clongdouble
npy_csinl(npy_clongdouble z)
{
    /* csin(z) = -I * csinh(I * z) */
    z = npy_csinhl(npy_cpackl(-npy_cimagl(z), npy_creall(z)));
    return npy_cpackl(npy_cimagl(z), -npy_creall(z));
}
#endif

#ifndef HAVE_CTANL
npy_clongdouble
npy_ctanl(npy_clongdouble z)
{
    /* ctan(z) = -I * ctanh(I * z) */
    z = npy_ctanhl(npy_cpackl(-npy_cimagl(z), npy_creall(z)));
    return (npy_cpackl(npy_cimagl(z), -npy_creall(z)));
}
#endif

#ifndef HAVE_CCOSHL
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic cosine of a complex argument z = x + i y.
 *
 * cosh(z) = cosh(x+iy)
 *         = cosh(x) cos(y) + i sinh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 *
 * CCOSH_BIG is chosen such that
 * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG)
 * although the exact value assigned to CCOSH_BIG is not so important
 */
npy_clongdouble
npy_ccoshl(npy_clongdouble z)
{
#if 3 == 1
    const npy_float CCOSH_BIG = 9.0f;
    const npy_float CCOSH_HUGE = 1.70141183e+38f;
#endif
#if 3 == 2
    const npy_double CCOSH_BIG = 22.0;
    const npy_double CCOSH_HUGE = 8.9884656743115795e+307;
#endif
#if 3 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CCOSH_BIG = 22.0L;
    const npy_longdouble CCOSH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CCOSH_BIG = 24.0L;
    const npy_longdouble CCOSH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_longdouble  x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_creall(z);
    y = npy_cimagl(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpackl(npy_coshl(x), x * y);
        }
        absx = npy_fabsl(x);
        if (absx < CCOSH_BIG) {
            /* small x: normal case */
            return npy_cpackl(npy_coshl(x) * npy_cosl(y),
                                npy_sinhl(x) * npy_sinl(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWERL) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_expl(absx) * 0.5l;
            return npy_cpackl(h * npy_cosl(y),
                                npy_copysignl(h, x) * npy_sinl(y));
        }
        else if (absx < SCALED_CEXP_UPPERL) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexpl(absx, y, -1);
            return npy_cpackl(npy_creall(z),
                                npy_cimagl(z) * npy_copysignl(1, x));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CCOSH_HUGE * x;
            return npy_cpackl(h * h * npy_cosl(y), h * npy_sinl(y));
        }
    }

    /*
     * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpackl(y - y, npy_copysignl(0, x * (y - y)));
    }

    /*
     * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0.
     *
     * cosh(NaN +- I 0)   = d(NaN) + I sign(d(NaN, +-0))0.
     * The sign of 0 in the result is unspecified.
     */
    if (y == 0 && !xfinite) {
        return npy_cpackl(x * x, npy_copysignl(0, x) * y);
    }

    /*
     * cosh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * cosh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpackl(y - y, x * (y - y));
    }

    /*
     * cosh(+-Inf + I NaN)  = +Inf + I d(NaN).
     *
     * cosh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * cosh(+-Inf + I y)   = +Inf cos(y) +- I Inf sin(y)
     */
    if (npy_isinf(x)) {
        if (!yfinite) {
            return npy_cpackl(x * x, x * (y - y));
        }
        return npy_cpackl((x * x) * npy_cosl(y), x * npy_sinl(y));
    }

    /*
     * cosh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * cosh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * cosh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpackl((x * x) * (y - y), (x + x) * (y - y));
}
#undef CCOSH_BIG
#undef CCOSH_HUGE
#endif

#ifndef HAVE_CSINHL
/*
 * Taken from the msun library in FreeBSD, rev 226599.
 *
 * Hyperbolic sine of a complex argument z = x + i y.
 *
 * sinh(z) = sinh(x+iy)
 *         = sinh(x) cos(y) + i cosh(x) sin(y).
 *
 * Exceptional values are noted in the comments within the source code.
 * These values and the return value were taken from n1124.pdf.
 */
npy_clongdouble
npy_csinhl(npy_clongdouble z)
{
#if 3 == 1
    const npy_float CSINH_BIG = 9.0f;
    const npy_float CSINH_HUGE = 1.70141183e+38f;
#endif
#if 3 == 2
    const npy_double CSINH_BIG = 22.0;
    const npy_double CSINH_HUGE = 8.9884656743115795e+307;
#endif
#if 3 >= 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble CSINH_BIG = 22.0L;
    const npy_longdouble CSINH_HUGE = 8.9884656743115795e+307L;
#else
    const npy_longdouble CSINH_BIG = 24.0L;
    const npy_longdouble CSINH_HUGE = 5.94865747678615882543e+4931L;
#endif
#endif

    npy_longdouble x, y, h, absx;
    npy_int xfinite, yfinite;

    x = npy_creall(z);
    y = npy_cimagl(z);

    xfinite = npy_isfinite(x);
    yfinite = npy_isfinite(y);

    /* Handle the nearly-non-exceptional cases where x and y are finite. */
    if (xfinite && yfinite) {
        if (y == 0) {
            return npy_cpackl(npy_sinhl(x), y);
        }
        absx = npy_fabsl(x);
        if (absx < CSINH_BIG) {
            /* small x: normal case */
            return npy_cpackl(npy_sinhl(x) * npy_cosl(y),
                                npy_coshl(x) * npy_sinl(y));
        }

        /* |x| >= 22, so cosh(x) ~= exp(|x|) */
        if (absx < SCALED_CEXP_LOWERL) {
            /* x < 710: exp(|x|) won't overflow */
            h = npy_expl(npy_fabsl(x)) * 0.5l;
            return npy_cpackl(npy_copysignl(h, x) * npy_cosl(y),
                                h * npy_sinl(y));
        }
        else if (x < SCALED_CEXP_UPPERL) {
            /* x < 1455: scale to avoid overflow */
            z = _npy_scaled_cexpl(absx, y, -1);
            return npy_cpackl(npy_creall(z) * npy_copysignl(1, x),
                                npy_cimagl(z));
        }
        else {
            /* x >= 1455: the result always overflows */
            h = CSINH_HUGE * x;
            return npy_cpackl(h * npy_cosl(y), h * h * npy_sinl(y));
        }
    }

    /*
     * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN.
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as dNaN.  Raise the invalid floating-point exception.
     *
     * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN).
     * The sign of 0 in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     */
    if (x == 0 && !yfinite) {
        return npy_cpackl(npy_copysignl(0, x * (y - y)), y - y);
    }

    /*
     * sinh(+-Inf +- I 0) = +-Inf + I +-0.
     *
     * sinh(NaN +- I 0)   = d(NaN) + I +-0.
     */
    if (y == 0 && !xfinite) {
        if (npy_isnan(x)) {
            return z;
        }
        return npy_cpackl(x, npy_copysignl(0, y));
    }

    /*
     * sinh(x +- I Inf) = dNaN + I dNaN.
     * Raise the invalid floating-point exception for finite nonzero x.
     *
     * sinh(x + I NaN) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero x.  Choice = don't raise (except for signaling NaNs).
     */
    if (xfinite && !yfinite) {
        return npy_cpackl(y - y, x * (y - y));
    }

    /*
     * sinh(+-Inf + I NaN)  = +-Inf + I d(NaN).
     * The sign of Inf in the result is unspecified.  Choice = normally
     * the same as d(NaN).
     *
     * sinh(+-Inf +- I Inf) = +Inf + I dNaN.
     * The sign of Inf in the result is unspecified.  Choice = always +.
     * Raise the invalid floating-point exception.
     *
     * sinh(+-Inf + I y)   = +-Inf cos(y) + I Inf sin(y)
     */
    if (!xfinite && !npy_isnan(x)) {
        if (!yfinite) {
            return npy_cpackl(x * x, x * (y - y));
        }
        return npy_cpackl(x * npy_cosl(y),
                            NPY_INFINITYL * npy_sinl(y));
    }

    /*
     * sinh(NaN + I NaN)  = d(NaN) + I d(NaN).
     *
     * sinh(NaN +- I Inf) = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception.
     * Choice = raise.
     *
     * sinh(NaN + I y)    = d(NaN) + I d(NaN).
     * Optionally raises the invalid floating-point exception for finite
     * nonzero y.  Choice = don't raise (except for signaling NaNs).
     */
    return npy_cpackl((x * x) * (y - y), (x + x) * (y - y));
}
#undef CSINH_BIG
#undef CSINH_HUGE
#endif

#ifndef HAVE_CTANHL
/*
 * Taken from the msun library in FreeBSD, rev 226600.
 *
 * Hyperbolic tangent of a complex argument z = x + i y.
 *
 * The algorithm is from:
 *
 *   W. Kahan.  Branch Cuts for Complex Elementary Functions or Much
 *   Ado About Nothing's Sign Bit.  In The State of the Art in
 *   Numerical Analysis, pp. 165 ff.  Iserles and Powell, eds., 1987.
 *
 * Method:
 *
 *   Let t    = tan(x)
 *       beta = 1/cos^2(y)
 *       s    = sinh(x)
 *       rho  = cosh(x)
 *
 *   We have:
 *
 *   tanh(z) = sinh(z) / cosh(z)
 *
 *             sinh(x) cos(y) + i cosh(x) sin(y)
 *           = ---------------------------------
 *             cosh(x) cos(y) + i sinh(x) sin(y)
 *
 *             cosh(x) sinh(x) / cos^2(y) + i tan(y)
 *           = -------------------------------------
 *                    1 + sinh^2(x) / cos^2(y)
 *
 *             beta rho s + i t
 *           = ----------------
 *               1 + beta s^2
 *
 * Modifications:
 *
 *   I omitted the original algorithm's handling of overflow in tan(x) after
 *   verifying with nearpi.c that this can't happen in IEEE single or double
 *   precision.  I also handle large x differently.
 */

#define TANH_HUGE 22.0
#define TANHF_HUGE 11.0F
#define TANHL_HUGE 42.0L

npy_clongdouble
npy_ctanhl(npy_clongdouble z)
{
    npy_longdouble x, y;
    npy_longdouble t, beta, s, rho, denom;

    x = npy_creall(z);
    y = npy_cimagl(z);

    /*
     * ctanh(NaN + i 0) = NaN + i 0
     *
     * ctanh(NaN + i y) = NaN + i NaN        for y != 0
     *
     * The imaginary part has the sign of x*sin(2*y), but there's no
     * special effort to get this right.
     *
     * ctanh(+-Inf +- i Inf) = +-1 +- 0
     *
     * ctanh(+-Inf + i y) = +-1 + 0 sin(2y)        for y finite
     *
     * The imaginary part of the sign is unspecified.  This special
     * case is only needed to avoid a spurious invalid exception when
     * y is infinite.
     */
        if (!npy_isfinite(x)) {
            if (npy_isnan(x)) {
                return npy_cpackl(x, (y == 0 ? y : x * y));
            }
            return npy_cpackl(npy_copysignl(1,x),
                                npy_copysignl(0,
                                npy_isinf(y) ?
                                    y : npy_sinl(y) * npy_cosl(y)));
        }

    /*
     * ctanh(x + i NAN) = NaN + i NaN
     * ctanh(x +- i Inf) = NaN + i NaN
     */
    if (!npy_isfinite(y)) {
        return (npy_cpackl(y - y, y - y));
    }

    /*
     * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the
     * approximation sinh^2(huge) ~= exp(2*huge) / 4.
     * We use a modified formula to avoid spurious overflow.
     */
    if (npy_fabsl(x) >= TANHL_HUGE) {
        npy_longdouble exp_mx = npy_expl(-npy_fabsl(x));
        return npy_cpackl(npy_copysignl(1, x),
                            4 * npy_sinl(y) * npy_cosl(y) *
                                exp_mx * exp_mx);
    }

    /* Kahan's algorithm */
    t = npy_tanl(y);
    beta = 1 + t * t;    /* = 1 / cos^2(y) */
    s = npy_sinhl(x);
    rho = npy_sqrtl(1 + s * s);    /* = cosh(x) */
    denom = 1 + beta * s * s;
    return (npy_cpackl((beta * rho * s) / denom, t / denom));
}
#undef TANH_HUGE
#undef TANHF_HUGE
#undef TANHL_HUGE
#endif

#if !defined (HAVE_CACOSL) || !defined (HAVE_CASINHL)
/*
 * Complex inverse trig functions taken from the msum library in FreeBSD
 * revision 251404
 *
 * The algorithm is very close to that in "Implementing the complex arcsine
 * and arccosine functions using exception handling" by T. E. Hull, Thomas F.
 * Fairgrieve, and Ping Tak Peter Tang, published in ACM Transactions on
 * Mathematical Software, Volume 23 Issue 3, 1997, Pages 299-335,
 * http://dl.acm.org/citation.cfm?id=275324.
 *
 * Throughout we use the convention z = x + I*y.
 *
 * casinh(z) = sign(x)*log(A+sqrt(A*A-1)) + I*asin(B)
 * where
 * A = (|z+I| + |z-I|) / 2
 * B = (|z+I| - |z-I|) / 2 = y/A
 *
 * These formulas become numerically unstable:
 *   (a) for Re(casinh(z)) when z is close to the line segment [-I, I] (that
 *       is, Re(casinh(z)) is close to 0);
 *   (b) for Im(casinh(z)) when z is close to either of the intervals
 *       [I, I*infinity) or (-I*infinity, -I] (that is, |Im(casinh(z))| is
 *       close to PI/2).
 *
 * These numerical problems are overcome by defining
 * f(a, b) = (hypot(a, b) - b) / 2 = a*a / (hypot(a, b) + b) / 2
 * Then if A < A_crossover, we use
 *   log(A + sqrt(A*A-1)) = log1p((A-1) + sqrt((A-1)*(A+1)))
 *   A-1 = f(x, 1+y) + f(x, 1-y)
 * and if B > B_crossover, we use
 *   asin(B) = atan2(y, sqrt(A*A - y*y)) = atan2(y, sqrt((A+y)*(A-y)))
 *   A-y = f(x, y+1) + f(x, y-1)
 * where without loss of generality we have assumed that x and y are
 * non-negative.
 *
 * Much of the difficulty comes because the intermediate computations may
 * produce overflows or underflows.  This is dealt with in the paper by Hull
 * et al by using exception handling.  We do this by detecting when
 * computations risk underflow or overflow.  The hardest part is handling the
 * underflows when computing f(a, b).
 *
 * Note that the function f(a, b) does not appear explicitly in the paper by
 * Hull et al, but the idea may be found on pages 308 and 309.  Introducing the
 * function f(a, b) allows us to concentrate many of the clever tricks in this
 * paper into one function.
 */

/*
 * Function f(a, b, hypot_a_b) = (hypot(a, b) - b) / 2.
 * Pass hypot(a, b) as the third argument.
 */
static inline npy_longdouble
_fl(npy_longdouble a, npy_longdouble b, npy_longdouble hypot_a_b)
{
    if (b < 0) {
        return ((hypot_a_b - b) / 2);
    }
    if (b == 0) {
        return (a / 2);
    }
    return (a * a / (hypot_a_b + b) / 2);
}

/*
 * All the hard work is contained in this function.
 * x and y are assumed positive or zero, and less than RECIP_EPSILON.
 * Upon return:
 * rx = Re(casinh(z)) = -Im(cacos(y + I*x)).
 * B_is_usable is set to 1 if the value of B is usable.
 * If B_is_usable is set to 0, sqrt_A2my2 = sqrt(A*A - y*y), and new_y = y.
 * If returning sqrt_A2my2 has potential to result in an underflow, it is
 * rescaled, and new_y is similarly rescaled.
 */
static inline void
_do_hard_workl(npy_longdouble x, npy_longdouble y, npy_longdouble *rx,
    npy_int *B_is_usable, npy_longdouble *B, npy_longdouble *sqrt_A2my2, npy_longdouble *new_y)
{
#if 3 == 1
    const npy_float A_crossover = 10.0f;
    const npy_float B_crossover = 0.6417f;
    const npy_float FOUR_SQRT_MIN = 4.3368086899420177e-19f;
#endif
#if 3 == 2
    const npy_double A_crossover = 10.0;
    const npy_double B_crossover = 0.6417;
    const npy_double FOUR_SQRT_MIN = 5.9666725849601654e-154;
#endif
#if 3 == 3
    const npy_longdouble A_crossover = 10.0l;
    const npy_longdouble B_crossover = 0.6417l;
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble FOUR_SQRT_MIN = 5.9666725849601654e-154;
#else
    const npy_longdouble FOUR_SQRT_MIN = 7.3344154702193886625e-2466l;
#endif
#endif
    npy_longdouble R, S, A; /* A, B, R, and S are as in Hull et al. */
    npy_longdouble Am1, Amy; /* A-1, A-y. */

    R = npy_hypotl(x, y + 1);        /* |z+I| */
    S = npy_hypotl(x, y - 1);        /* |z-I| */

    /* A = (|z+I| + |z-I|) / 2 */
    A = (R + S) / 2;
    /*
     * Mathematically A >= 1.  There is a small chance that this will not
     * be so because of rounding errors.  So we will make certain it is
     * so.
     */
    if (A < 1) {
        A = 1;
    }

    if (A < A_crossover) {
        /*
         * Am1 = fp + fm, where fp = f(x, 1+y), and fm = f(x, 1-y).
         * rx = log1p(Am1 + sqrt(Am1*(A+1)))
         */
        if (y == 1 && x < LDBL_EPSILON * LDBL_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *rx = npy_sqrtl(x);
        }
        else if (x >= LDBL_EPSILON * npy_fabsl(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN
             */
            Am1 = _fl(x, 1 + y, R) + _fl(x, 1 - y, S);
            *rx = npy_log1pl(Am1 + npy_sqrtl(Am1 * (A + 1)));
        }
        else if (y < 1) {
            /*
             * fp = x*x/(1+y)/4, fm = x*x/(1-y)/4, and
             * A = 1 (inexactly).
             */
            *rx = x / npy_sqrtl((1 - y) * (1 + y));
        }
        else {        /* if (y > 1) */
            /*
             * A-1 = y-1 (inexactly).
             */
            *rx = npy_log1pl((y - 1) + npy_sqrtl((y - 1) * (y + 1)));
        }
    }
    else {
        *rx = npy_logl(A + npy_sqrtl(A * A - 1));
    }

    *new_y = y;

    if (y < FOUR_SQRT_MIN) {
        /*
         * Avoid a possible underflow caused by y/A.  For casinh this
         * would be legitimate, but will be picked up by invoking atan2
         * later on.  For cacos this would not be legitimate.
         */
        *B_is_usable = 0;
        *sqrt_A2my2 = A * (2 / LDBL_EPSILON);
        *new_y = y * (2 / LDBL_EPSILON);
        return;
    }

    /* B = (|z+I| - |z-I|) / 2 = y/A */
    *B = y / A;
    *B_is_usable = 1;

    if (*B > B_crossover) {
        *B_is_usable = 0;
        /*
         * Amy = fp + fm, where fp = f(x, y+1), and fm = f(x, y-1).
         * sqrt_A2my2 = sqrt(Amy*(A+y))
         */
        if (y == 1 && x < LDBL_EPSILON / 128) {
            /*
             * fp is of order x^2, and fm = x/2.
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrtl(x) * npy_sqrtl((A + y) / 2);
        }
        else if (x >= LDBL_EPSILON * npy_fabsl(y - 1)) {
            /*
             * Underflow will not occur because
             * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN
             * and
             * x >= DBL_EPSILON^2 >= FOUR_SQRT_MIN
             */
            Amy = _fl(x, y + 1, R) + _fl(x, y - 1, S);
            *sqrt_A2my2 = npy_sqrtl(Amy * (A + y));
        }
        else if (y > 1) {
            /*
             * fp = x*x/(y+1)/4, fm = x*x/(y-1)/4, and
             * A = y (inexactly).
             *
             * y < RECIP_EPSILON.  So the following
             * scaling should avoid any underflow problems.
             */
            *sqrt_A2my2 = x * (4 / LDBL_EPSILON / LDBL_EPSILON) * y /
                npy_sqrtl((y + 1) * (y - 1));
            *new_y = y * (4 / LDBL_EPSILON / LDBL_EPSILON);
        }
        else {        /* if (y < 1) */
            /*
             * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and
             * A = 1 (inexactly).
             */
            *sqrt_A2my2 = npy_sqrtl((1 - y) * (1 + y));
        }
    }
}

/*
 * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON.
 */
static inline void
_clog_for_large_valuesl(npy_longdouble x, npy_longdouble y,
    npy_longdouble *rr, npy_longdouble *ri)
{
#if 3 == 1
    const npy_float QUARTER_SQRT_MAX = 4.611685743549481e+18f;
    const npy_float SQRT_MIN = 1.0842021724855044e-19f;
 #endif
#if 3 == 2
    const npy_double QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_double SQRT_MIN = 1.4916681462400413e-154;
 #endif
#if 3 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
    const npy_longdouble QUARTER_SQRT_MAX = 3.3519519824856489e+153;
    const npy_longdouble SQRT_MIN = 1.4916681462400413e-154;
#else
    const npy_longdouble QUARTER_SQRT_MAX = 2.7268703390485398235e+2465l;
    const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    npy_longdouble ax, ay, t;

    ax = npy_fabsl(x);
    ay = npy_fabsl(y);
    if (ax < ay) {
        t = ax;
        ax = ay;
        ay = t;
    }

    /*
     * Avoid overflow in hypot() when x and y are both very large.
     * Divide x and y by E, and then add 1 to the logarithm.  This depends
     * on E being larger than sqrt(2).
     * Dividing by E causes an insignificant loss of accuracy; however
     * this method is still poor since it is unnecessarily slow.
     */
    if (ax > LDBL_MAX / 2) {
        *rr = npy_logl(npy_hypotl(x / NPY_El, y / NPY_El)) + 1;
    }
    /*
     * Avoid overflow when x or y is large.  Avoid underflow when x or
     * y is small.
     */
    else if (ax > QUARTER_SQRT_MAX || ay < SQRT_MIN) {
        *rr = npy_logl(npy_hypotl(x, y));
    }
    else {
        *rr = npy_logl(ax * ax + ay * ay) / 2;
    }
    *ri = npy_atan2l(y, x);
}
#endif

#ifndef HAVE_CACOSL
npy_clongdouble
npy_cacosl(npy_clongdouble z)
{
#if 3 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 3 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 3 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_longdouble RECIP_EPSILON = 1.0l / LDBL_EPSILON;
    const npy_longdouble pio2_hi = NPY_PI_2l;
    npy_longdouble x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2mx2, new_x;
    npy_int sx, sy;
    npy_int B_is_usable;

    x = npy_creall(z);
    y = npy_cimagl(z);
    sx = npy_signbit(x);
    sy = npy_signbit(y);
    ax = npy_fabsl(x);
    ay = npy_fabsl(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */
        if (npy_isinf(x)) {
            return npy_cpackl(y + y, -NPY_INFINITYL);
        }
        /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */
        if (npy_isinf(y)) {
            return npy_cpackl(x + x, -y);
        }
        /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */
        if (x == 0) {
            return npy_cpackl(pio2_hi + pio2_lo, y + y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackl(NPY_NANL, NPY_NANL);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        _clog_for_large_valuesl(x, y, &wx, &wy);
        rx = npy_fabsl(wy);
        ry = wx + NPY_LOGE2l;
        if (sy == 0) {
            ry = -ry;
        }
        return npy_cpackl(rx, ry);
    }

    /* Avoid spuriously raising inexact for z = 1. */
    if (x == 1 && y == 0) {
        return npy_cpackl(0, -y);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return npy_cpackl(pio2_hi - (x - pio2_lo), -y);
    }

    _do_hard_workl(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x);
    if (B_is_usable) {
        if (sx == 0) {
            rx = npy_acosl(B);
        }
        else {
            rx = npy_acosl(-B);
        }
    }
    else {
        if (sx == 0) {
            rx = npy_atan2l(sqrt_A2mx2, new_x);
        }
        else {
            rx = npy_atan2l(sqrt_A2mx2, -new_x);
        }
    }
    if (sy == 0) {
        ry = -ry;
    }
    return npy_cpackl(rx, ry);
}
#endif

#ifndef HAVE_CASINL
npy_clongdouble
npy_casinl(npy_clongdouble z)
{
    /* casin(z) = I * conj( casinh(I * conj(z)) ) */
    z = npy_casinhl(npy_cpackl(npy_cimagl(z), npy_creall(z)));
    return npy_cpackl(npy_cimagl(z), npy_creall(z));
}
#endif

#ifndef HAVE_CATANL
npy_clongdouble
npy_catanl(npy_clongdouble z)
{
    /* catan(z) = I * conj( catanh(I * conj(z)) ) */
    z = npy_catanhl(npy_cpackl(npy_cimagl(z), npy_creall(z)));
    return npy_cpackl(npy_cimagl(z), npy_creall(z));
}
#endif

#ifndef HAVE_CACOSHL
npy_clongdouble
npy_cacoshl(npy_clongdouble z)
{
    /*
     * cacosh(z) = I*cacos(z) or -I*cacos(z)
     * where the sign is chosen so Re(cacosh(z)) >= 0.
     */
    npy_clongdouble  w;
    npy_longdouble rx, ry;

    w = npy_cacosl(z);
    rx = npy_creall(w);
    ry = npy_cimagl(w);
    /* cacosh(NaN + I*NaN) = NaN + I*NaN */
    if (npy_isnan(rx) && npy_isnan(ry)) {
        return npy_cpackl(ry, rx);
    }
    /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */
    /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */
    if (npy_isnan(rx)) {
        return npy_cpackl(npy_fabsl(ry), rx);
    }
    /* cacosh(0 + I*NaN) = NaN + I*NaN */
    if (npy_isnan(ry)) {
        return npy_cpackl(ry, ry);
    }
    return npy_cpackl(npy_fabsl(ry), npy_copysignl(rx, npy_cimagl(z)));
}
#endif

#ifndef HAVE_CASINHL
/*
 * casinh(z) = z + O(z^3)   as z -> 0
 *
 * casinh(z) = sign(x)*clog(sign(x)*z) + O(1/z^2)   as z -> infinity
 * The above formula works for the imaginary part as well, because
 * Im(casinh(z)) = sign(x)*atan2(sign(x)*y, fabs(x)) + O(y/z^3)
 *    as z -> infinity, uniformly in y
 */
npy_clongdouble
npy_casinhl(npy_clongdouble z)
{
#if 3 == 1
    /* this is sqrt(6*EPS) */
    const npy_float SQRT_6_EPSILON = 8.4572793338e-4f;
#endif
#if 3 == 2
    const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08;
#endif
#if 3 == 3
    const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l;
#endif
    const npy_longdouble RECIP_EPSILON = 1.0l / LDBL_EPSILON;
    npy_longdouble x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y;
    npy_int B_is_usable;

    x = npy_creall(z);
    y = npy_cimagl(z);
    ax = npy_fabsl(x);
    ay = npy_fabsl(y);

    if (npy_isnan(x) || npy_isnan(y)) {
        /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpackl(x, y + y);
        }
        /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */
        if (npy_isinf(y)) {
            return npy_cpackl(y, x + x);
        }
        /* casinh(NaN + I*0) = NaN + I*0 */
        if (y == 0) {
            return npy_cpackl(x + x, y);
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackl(NPY_NANL, NPY_NANL);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        /* clog...() will raise inexact unless x or y is infinite. */
        if (npy_signbit(x) == 0) {
            _clog_for_large_valuesl(x, y, &wx, &wy);
            wx += NPY_LOGE2l;
        }
        else {
            _clog_for_large_valuesl(-x, -y, &wx, &wy);
            wx += NPY_LOGE2l;
        }
        return npy_cpackl(npy_copysignl(wx, x), npy_copysignl(wy, y));
    }

    /* Avoid spuriously raising inexact for z = 0. */
    if (x == 0 && y == 0) {
        return (z);
    }

    /* All remaining cases are inexact. */
    raise_inexact();

    if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) {
        return (z);
    }

    _do_hard_workl(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y);
    if (B_is_usable) {
        ry = npy_asinl(B);
    }
    else {
        ry = npy_atan2l(new_y, sqrt_A2my2);
    }
    return npy_cpackl(npy_copysignl(rx, x), npy_copysignl(ry, y));
}
#endif

#ifndef HAVE_CATANHL
/*
 * sum_squares(x,y) = x*x + y*y (or just x*x if y*y would underflow).
 * Assumes x*x and y*y will not overflow.
 * Assumes x and y are finite.
 * Assumes y is non-negative.
 * Assumes fabs(x) >= DBL_EPSILON.
 */
static inline npy_longdouble
_sum_squaresl(npy_longdouble x, npy_longdouble y)
{
#if 3 == 1
const npy_float SQRT_MIN = 1.0842022e-19f;
#endif
#if 3 == 2
const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#endif
#if 3 == 3
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
const npy_longdouble SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */
#else
/* this is correct for 80 bit long doubles */
const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l;
#endif
#endif
    /* Avoid underflow when y is small. */
    if (y < SQRT_MIN) {
        return (x * x);
    }

    return (x * x + y * y);
}

/*
 * real_part_reciprocal(x, y) = Re(1/(x+I*y)) = x/(x*x + y*y).
 * Assumes x and y are not NaN, and one of x and y is larger than
 * RECIP_EPSILON.  We avoid unwarranted underflow.  It is important to not use
 * the code creal(1/z), because the imaginary part may produce an unwanted
 * underflow.
 * This is only called in a context where inexact is always raised before
 * the call, so no effort is made to avoid or force inexact.
 */
#if 3 == 1
#define BIAS (FLT_MAX_EXP - 1)
#define CUTOFF (FLT_MANT_DIG / 2 + 1)
static inline npy_float
_real_part_reciprocalf(npy_float x, npy_float y)
{
    npy_float scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    GET_FLOAT_WORD(hx, x);
    ix = hx & 0x7f800000;
    GET_FLOAT_WORD(hy, y);
    iy = hy & 0x7f800000;
    if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) {
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 23) {
        return (x / y / y);
    }
    if (ix <= (BIAS + FLT_MAX_EXP / 2 - CUTOFF) << 23) {
        return (x / (x * x + y * y));
    }
    SET_FLOAT_WORD(scale, 0x7f800000 - ix);
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 3 == 2
#define BIAS (DBL_MAX_EXP - 1)
/*  more guard digits are useful iff there is extra precision. */
#define CUTOFF (DBL_MANT_DIG / 2 + 1)  /* just half or 1 guard digit */
static inline npy_double
_real_part_reciprocal(npy_double x, npy_double y)
{
    npy_double scale;
    npy_uint32 hx, hy;
    npy_int32 ix, iy;

    /*
     * This code is inspired by the C99 document n1124.pdf, Section G.5.1,
     * example 2.
     */
    GET_HIGH_WORD(hx, x);
    ix = hx & 0x7ff00000;
    GET_HIGH_WORD(hy, y);
    iy = hy & 0x7ff00000;
    if (ix - iy >= CUTOFF << 20 || npy_isinf(x)) {
        /* +-Inf -> +-0 is special */
        return (1 / x);
    }
    if (iy - ix >= CUTOFF << 20) {
        /* should avoid double div, but hard */
        return (x / y / y);
    }
    if (ix <= (BIAS + DBL_MAX_EXP / 2 - CUTOFF) << 20) {
        return (x / (x * x + y * y));
    }
    scale = 1;
    SET_HIGH_WORD(scale, 0x7ff00000 - ix);  /* 2**(1-ilogb(x)) */
    x *= scale;
    y *= scale;
    return (x / (x * x + y * y) * scale);
}
#undef BIAS
#undef CUTOFF
#endif

#if 3 == 3
#if !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE) && \
    !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE)

#define BIAS (LDBL_MAX_EXP - 1)
#define CUTOFF (LDBL_MANT_DIG / 2 + 1)
static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    npy_longdouble scale;
    union IEEEl2bitsrep ux, uy, us;
    npy_int32 ix, iy;

    ux.e = x;
    ix = GET_LDOUBLE_EXP(ux);
    uy.e = y;
    iy = GET_LDOUBLE_EXP(uy);
    if (ix - iy >= CUTOFF || npy_isinf(x)) {
        return (1/x);
    }
    if (iy - ix >= CUTOFF) {
        return (x/y/y);
    }
    if (ix <= BIAS + LDBL_MAX_EXP / 2 - CUTOFF) {
        return (x/(x*x + y*y));
    }
    us.e = 1;
    SET_LDOUBLE_EXP(us, 0x7fff - ix);
    scale = us.e;
    x *= scale;
    y *= scale;
    return (x/(x*x + y*y) * scale);
}
#undef BIAS
#undef CUTOFF

#else

static inline npy_longdouble
_real_part_reciprocall(npy_longdouble x,
    npy_longdouble y)
{
    return x/(x*x + y*y);
}

#endif
#endif

npy_clongdouble
npy_catanhl(npy_clongdouble z)
{
#if 3 == 1
    /* this is sqrt(3*EPS) */
    const npy_float SQRT_3_EPSILON = 5.9801995673e-4f;
    /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */
    const volatile npy_float pio2_lo = 7.5497899549e-9f;
#endif
#if 3 == 2
    const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8;
    const volatile npy_double pio2_lo = 6.1232339957367659e-17;
#endif
#if 3 == 3
    const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10l;
    const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l;
#endif
    const npy_longdouble RECIP_EPSILON = 1.0l / LDBL_EPSILON;
    const npy_longdouble pio2_hi = NPY_PI_2l;
    npy_longdouble x, y, ax, ay, rx, ry;

    x = npy_creall(z);
    y = npy_cimagl(z);
    ax = npy_fabsl(x);
    ay = npy_fabsl(y);

    /* This helps handle many cases. */
    if (y == 0 && ax <= 1) {
        return npy_cpackl(npy_atanhl(x), y);
    }

    /* To ensure the same accuracy as atan(), and to filter out z = 0. */
    if (x == 0) {
        return npy_cpackl(x, npy_atanl(y));
    }

    if (npy_isnan(x) || npy_isnan(y)) {
        /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */
        if (npy_isinf(x)) {
            return npy_cpackl(npy_copysignl(0, x), y + y);
        }
        /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */
        if (npy_isinf(y)) {
            return npy_cpackl(npy_copysignl(0, x),
                npy_copysignl(pio2_hi + pio2_lo, y));
        }
        /*
         * All other cases involving NaN return NaN + I*NaN.
         * C99 leaves it optional whether to raise invalid if one of
         * the arguments is not NaN, so we opt not to raise it.
         */
        return npy_cpackl(NPY_NANL, NPY_NANL);
    }

    if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) {
        return npy_cpackl(_real_part_reciprocall(x, y),
            npy_copysignl(pio2_hi + pio2_lo, y));
    }

    if (ax < SQRT_3_EPSILON / 2 && ay < SQRT_3_EPSILON / 2) {
        /*
         * z = 0 was filtered out above.  All other cases must raise
         * inexact, but this is the only one that needs to do it
         * explicitly.
         */
        raise_inexact();
        return (z);
    }

    if (ax == 1 && ay < LDBL_EPSILON) {
        rx = (NPY_LOGE2l - npy_logl(ay)) / 2;
    }
    else {
        rx = npy_log1pl(4 * ax / _sum_squaresl(ax - 1, ay)) / 4;
    }

    if (ax == 1) {
        ry = npy_atan2l(2, -ay) / 2;
    }
    else if (ay < LDBL_EPSILON) {
        ry = npy_atan2l(2 * ay, (1 - ax) * (1 + ax)) / 2;
    }
    else {
        ry = npy_atan2l(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2;
    }

    return npy_cpackl(npy_copysignl(rx, x), npy_copysignl(ry, y));
}
#endif


/*==========================================================
 * Decorate all the functions which are available natively
 *=========================================================*/

#line 1752

#line 1757
#ifdef HAVE_CABSF
npy_float
npy_cabsf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    z1.npy_z = z;
    return cabsf(z1.c99_z);
}
#endif

#line 1757
#ifdef HAVE_CARGF
npy_float
npy_cargf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    z1.npy_z = z;
    return cargf(z1.c99_z);
}
#endif


#line 1774
#ifdef HAVE_CEXPF
npy_cfloat
npy_cexpf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cexpf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CLOGF
npy_cfloat
npy_clogf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = clogf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSQRTF
npy_cfloat
npy_csqrtf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csqrtf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOSF
npy_cfloat
npy_ccosf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccosf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSINF
npy_cfloat
npy_csinf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csinf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTANF
npy_cfloat
npy_ctanf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctanf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOSHF
npy_cfloat
npy_ccoshf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccoshf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSINHF
npy_cfloat
npy_csinhf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csinhf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTANHF
npy_cfloat
npy_ctanhf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctanhf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOSF
npy_cfloat
npy_cacosf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacosf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASINF
npy_cfloat
npy_casinf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casinf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATANF
npy_cfloat
npy_catanf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catanf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOSHF
npy_cfloat
npy_cacoshf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacoshf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASINHF
npy_cfloat
npy_casinhf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casinhf(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATANHF
npy_cfloat
npy_catanhf(npy_cfloat z)
{
    __npy_cfloat_to_c99_cast z1;
    __npy_cfloat_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catanhf(z1.c99_z);
    return ret.npy_z;
}
#endif




#line 1752

#line 1757
#ifdef HAVE_CABS
npy_double
npy_cabs(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    z1.npy_z = z;
    return cabs(z1.c99_z);
}
#endif

#line 1757
#ifdef HAVE_CARG
npy_double
npy_carg(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    z1.npy_z = z;
    return carg(z1.c99_z);
}
#endif


#line 1774
#ifdef HAVE_CEXP
npy_cdouble
npy_cexp(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cexp(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CLOG
npy_cdouble
npy_clog(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = clog(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSQRT
npy_cdouble
npy_csqrt(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csqrt(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOS
npy_cdouble
npy_ccos(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccos(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSIN
npy_cdouble
npy_csin(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csin(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTAN
npy_cdouble
npy_ctan(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctan(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOSH
npy_cdouble
npy_ccosh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccosh(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSINH
npy_cdouble
npy_csinh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csinh(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTANH
npy_cdouble
npy_ctanh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctanh(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOS
npy_cdouble
npy_cacos(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacos(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASIN
npy_cdouble
npy_casin(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casin(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATAN
npy_cdouble
npy_catan(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catan(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOSH
npy_cdouble
npy_cacosh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacosh(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASINH
npy_cdouble
npy_casinh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casinh(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATANH
npy_cdouble
npy_catanh(npy_cdouble z)
{
    __npy_cdouble_to_c99_cast z1;
    __npy_cdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catanh(z1.c99_z);
    return ret.npy_z;
}
#endif




#line 1752

#line 1757
#ifdef HAVE_CABSL
npy_longdouble
npy_cabsl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    z1.npy_z = z;
    return cabsl(z1.c99_z);
}
#endif

#line 1757
#ifdef HAVE_CARGL
npy_longdouble
npy_cargl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    z1.npy_z = z;
    return cargl(z1.c99_z);
}
#endif


#line 1774
#ifdef HAVE_CEXPL
npy_clongdouble
npy_cexpl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cexpl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CLOGL
npy_clongdouble
npy_clogl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = clogl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSQRTL
npy_clongdouble
npy_csqrtl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csqrtl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOSL
npy_clongdouble
npy_ccosl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccosl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSINL
npy_clongdouble
npy_csinl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csinl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTANL
npy_clongdouble
npy_ctanl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctanl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CCOSHL
npy_clongdouble
npy_ccoshl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ccoshl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CSINHL
npy_clongdouble
npy_csinhl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = csinhl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CTANHL
npy_clongdouble
npy_ctanhl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = ctanhl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOSL
npy_clongdouble
npy_cacosl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacosl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASINL
npy_clongdouble
npy_casinl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casinl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATANL
npy_clongdouble
npy_catanl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catanl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CACOSHL
npy_clongdouble
npy_cacoshl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = cacoshl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CASINHL
npy_clongdouble
npy_casinhl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = casinhl(z1.c99_z);
    return ret.npy_z;
}
#endif

#line 1774
#ifdef HAVE_CATANHL
npy_clongdouble
npy_catanhl(npy_clongdouble z)
{
    __npy_clongdouble_to_c99_cast z1;
    __npy_clongdouble_to_c99_cast ret;
    z1.npy_z = z;
    ret.c99_z = catanhl(z1.c99_z);
    return ret.npy_z;
}
#endif





