From 5b58985aa73b36fa717a33247eb66d800aaa57cc Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Fri, 22 Feb 2013 17:30:52 -0500 Subject: [PATCH 1/2] BUG: Overflow in tan and tanh for large complex values np.tanh(1000+0j) gives nan+nan*j instead of 1.0+0j. np.tan(0+1000j) gives nan+nan*j instead of 1j. I've imported the implementation for ctanh from FreeBSD's math library which handles large arguments correctly and fixes this bug and the equivalent bug in np.tan. The problem here is that importing the function into npy_math_complex.c and adding a config check causes us to use the implementation from glibc on Linux, which also has this bug. Although it seems to have been fixed in glibc last April (http://sourceware.org/bugzilla/show_bug.cgi?id=11521). I see several things that could be done here, the easiest is probably to use our version of ctanh unconditionally. Although there are a multitude of ways to go about that, for instance should I remove the implementation from npy_math_complex.c and just place it directly in umath/funcs.inc, where the buggy version was? Or should I just remove the config check and add a note about it somewhere? Closes #2321. --- numpy/core/include/numpy/npy_math.h | 52 +- numpy/core/setup.py | 39 +- numpy/core/setup_common.py | 6 +- numpy/core/src/npymath/fpstatus.c | 262 +++ numpy/core/src/npymath/ieee754.c.src | 255 --- numpy/core/src/npymath/npy_math_complex.c.src | 1405 +++++++++++- numpy/core/src/npymath/npymath_tests.c | 135 ++ numpy/core/src/npymath/test_c99complex.c | 1880 +++++++++++++++++ numpy/core/src/umath/funcs.inc.src | 358 +--- numpy/core/src/umath/umathmodule.c | 6 + numpy/core/tests/test_umath.py | 102 +- numpy/core/tests/test_umath_complex.py | 163 ++ 12 files changed, 3981 insertions(+), 682 deletions(-) create mode 100644 numpy/core/src/npymath/fpstatus.c create mode 100644 numpy/core/src/npymath/npymath_tests.c create mode 100644 numpy/core/src/npymath/test_c99complex.c diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index 0137e05566af..7c696d830541 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -151,6 +151,9 @@ double npy_copysign(double x, double y); double npy_nextafter(double x, double y); double npy_spacing(double x); +double npy_ldexp(double x, int exp); +double npy_frexp(double x, int* exp); + /* * IEEE 754 fpu handling. Those are guaranteed to be macros */ @@ -258,8 +261,11 @@ float npy_copysignf(float x, float y); float npy_nextafterf(float x, float y); float npy_spacingf(float x); +float npy_ldexpf(float x, int exp); +float npy_frexpf(float x, int* exp); + /* - * float C99 math functions + * long double C99 math functions */ npy_longdouble npy_sinl(npy_longdouble x); @@ -302,6 +308,9 @@ npy_longdouble npy_copysignl(npy_longdouble x, npy_longdouble y); npy_longdouble npy_nextafterl(npy_longdouble x, npy_longdouble y); npy_longdouble npy_spacingl(npy_longdouble x); +npy_longdouble npy_ldexpl(npy_longdouble x, int exp); +npy_longdouble npy_frexpl(npy_longdouble x, int* exp); + /* * Non standard functions */ @@ -425,6 +434,19 @@ npy_cdouble npy_csqrt(npy_cdouble z); npy_cdouble npy_ccos(npy_cdouble z); npy_cdouble npy_csin(npy_cdouble z); +npy_cdouble npy_ctan(npy_cdouble z); + +npy_cdouble npy_ccosh(npy_cdouble z); +npy_cdouble npy_csinh(npy_cdouble z); +npy_cdouble npy_ctanh(npy_cdouble z); + +npy_cdouble npy_cacos(npy_cdouble z); +npy_cdouble npy_casin(npy_cdouble z); +npy_cdouble npy_catan(npy_cdouble z); + +npy_cdouble npy_cacosh(npy_cdouble z); +npy_cdouble npy_casinh(npy_cdouble z); +npy_cdouble npy_catanh(npy_cdouble z); /* * Single precision complex functions @@ -440,6 +462,20 @@ npy_cfloat npy_csqrtf(npy_cfloat z); npy_cfloat npy_ccosf(npy_cfloat z); npy_cfloat npy_csinf(npy_cfloat z); +npy_cfloat npy_ctanf(npy_cfloat z); + +npy_cfloat npy_ccoshf(npy_cfloat z); +npy_cfloat npy_csinhf(npy_cfloat z); +npy_cfloat npy_ctanhf(npy_cfloat z); + +npy_cfloat npy_cacosf(npy_cfloat z); +npy_cfloat npy_casinf(npy_cfloat z); +npy_cfloat npy_catanf(npy_cfloat z); + +npy_cfloat npy_cacoshf(npy_cfloat z); +npy_cfloat npy_casinhf(npy_cfloat z); +npy_cfloat npy_catanhf(npy_cfloat z); + /* * Extended precision complex functions @@ -455,6 +491,20 @@ npy_clongdouble npy_csqrtl(npy_clongdouble z); npy_clongdouble npy_ccosl(npy_clongdouble z); npy_clongdouble npy_csinl(npy_clongdouble z); +npy_clongdouble npy_ctanl(npy_clongdouble z); + +npy_clongdouble npy_ccoshl(npy_clongdouble z); +npy_clongdouble npy_csinhl(npy_clongdouble z); +npy_clongdouble npy_ctanhl(npy_clongdouble z); + +npy_clongdouble npy_cacosl(npy_clongdouble z); +npy_clongdouble npy_casinl(npy_clongdouble z); +npy_clongdouble npy_catanl(npy_clongdouble z); + +npy_clongdouble npy_cacoshl(npy_clongdouble z); +npy_clongdouble npy_casinhl(npy_clongdouble z); +npy_clongdouble npy_catanhl(npy_clongdouble z); + /* * Functions that set the floating point error diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 75d64d81b9a6..79d78545784b 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -209,6 +209,32 @@ def check_prec(prec): else: priv.extend([(fname2def(f), 1) for f in flist]) + flist = [f + prec for f in C99_COMPLEX_FUNCS_CHECKED] + decl = dict([(f, True) for f in flist]) + exists = [] + if not config.check_funcs_once(flist, call=decl, decl=decl, + libraries=mathlibs): + for f in C99_COMPLEX_FUNCS_CHECKED: + if config.check_func(f + prec, call=True, decl=True, + libraries=mathlibs): + exists.append(f) + else: + exists.extend(C99_COMPLEX_FUNCS_CHECKED) + + if len(exists) > 0: + fp = open(join('.', 'numpy', 'core', 'src', 'npymath', + 'test_c99complex.c'), 'r') + obody = fp.read() + fp.close() + precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec] + for f in exists: + body = obody.replace('PYTESTPRECISION', precname) \ + .replace('PYTESTFUNC', f.upper()) + inc_dir = join('.', 'numpy', 'core', 'src', 'npymath') + if config.try_run(body, libraries=mathlibs, + include_dirs=[inc_dir]): + priv.append((fname2def(f + prec), 1)) + check_prec('') check_prec('f') check_prec('l') @@ -683,7 +709,8 @@ def get_mathlib_info(*args): npymath_sources = [join('src', 'npymath', 'npy_math.c.src'), join('src', 'npymath', 'ieee754.c.src'), join('src', 'npymath', 'npy_math_complex.c.src'), - join('src', 'npymath', 'halffloat.c')] + join('src', 'npymath', 'halffloat.c'), + join('src', 'npymath', 'fpstatus.c')] config.add_installed_library('npymath', sources=npymath_sources + [get_mathlib_info], install_dir='lib') @@ -973,6 +1000,16 @@ def generate_umath_c(ext, build_dir): config.add_extension('operand_flag_tests', sources = [join('src', 'umath', 'operand_flag_tests.c.src')]) + ####################################################################### + # npymath_tests module # + ####################################################################### + + config.add_extension('npymath_tests', + sources = [join('src', 'npymath', 'npymath_tests.c')], + depends = ['test_c99complex.c'], + libraries = ['npymath'] + ) + config.add_data_dir('tests') config.add_data_dir('tests/data') diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index 0b18bc6c62fa..b37b413eb46e 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -153,8 +153,10 @@ def check_api_version(apiversion, codegen_dir): C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double'] -C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'csqrt', 'clog', - 'ccos', 'csin', 'cpow'] +C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg'] +C99_COMPLEX_FUNCS_CHECKED = ['cacos', 'casin', 'catan', 'cacosh', 'casinh', + 'catanh', 'ccos', 'csin', 'ctan', 'ccosh', 'csinh', + 'ctanh', 'cexp', 'clog', 'cpow', 'csqrt'] def fname2def(name): return "HAVE_%s" % name.upper() diff --git a/numpy/core/src/npymath/fpstatus.c b/numpy/core/src/npymath/fpstatus.c new file mode 100644 index 000000000000..da669ec1b61f --- /dev/null +++ b/numpy/core/src/npymath/fpstatus.c @@ -0,0 +1,262 @@ +/* + * Functions to set the floating point status word. + * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h + */ + +/* This include is wrapped so that these functions can also be called when + * doing config tests. + */ +#ifndef CONFIG_TESTS +#include "npy_math_common.h" +#endif + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif + +/* Solaris --------------------------------------------------------*/ +/* --------ignoring SunOS ieee_flags approach, someone else can +** deal with that! */ +#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \ + (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \ + defined(__NetBSD__) +#include + +int npy_get_floatstatus(void) +{ + int fpstatus = fpgetsticky(); + return ((FP_X_DZ & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FP_X_OFL & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FP_X_UFL & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FP_X_INV & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + fpsetsticky(0); + + return fpstatus; +} + +void npy_set_floatstatus_divbyzero(void) +{ + fpsetsticky(FP_X_DZ); +} + +void npy_set_floatstatus_overflow(void) +{ + fpsetsticky(FP_X_OFL); +} + +void npy_set_floatstatus_underflow(void) +{ + fpsetsticky(FP_X_UFL); +} + +void npy_set_floatstatus_invalid(void) +{ + fpsetsticky(FP_X_INV); +} + + +#elif defined(__GLIBC__) || defined(__APPLE__) || \ + defined(__CYGWIN__) || defined(__MINGW32__) || \ + (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) + +# if defined(__GLIBC__) || defined(__APPLE__) || \ + defined(__MINGW32__) || defined(__FreeBSD__) +# include +# elif defined(__CYGWIN__) +# include "numpy/fenv/fenv.h" +# endif + +int npy_get_floatstatus(void) +{ + int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + + return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + /* testing float status is 50-100 times faster than clearing on x86 */ + int fpstatus = npy_get_floatstatus(); + if (fpstatus != 0) { + feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + } + + return fpstatus; +} + + +void npy_set_floatstatus_divbyzero(void) +{ + feraiseexcept(FE_DIVBYZERO); +} + +void npy_set_floatstatus_overflow(void) +{ + feraiseexcept(FE_OVERFLOW); +} + +void npy_set_floatstatus_underflow(void) +{ + feraiseexcept(FE_UNDERFLOW); +} + +void npy_set_floatstatus_invalid(void) +{ + feraiseexcept(FE_INVALID); +} + +#elif defined(_AIX) +#include +#include + +int npy_get_floatstatus(void) +{ + int fpstatus = fp_read_flag(); + return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + fp_swap_flag(0); + + return fpstatus; +} + +void npy_set_floatstatus_divbyzero(void) +{ + fp_raise_xcp(FP_DIV_BY_ZERO); +} + +void npy_set_floatstatus_overflow(void) +{ + fp_raise_xcp(FP_OVERFLOW); +} + +void npy_set_floatstatus_underflow(void) +{ + fp_raise_xcp(FP_UNDERFLOW); +} + +void npy_set_floatstatus_invalid(void) +{ + fp_raise_xcp(FP_INVALID); +} + +#else + +/* MS Windows -----------------------------------------------------*/ +#if defined(_MSC_VER) + +#include + + +int npy_get_floatstatus(void) +{ +#if defined(_WIN64) + int fpstatus = _statusfp(); +#else + /* windows enables sse on 32 bit, so check both flags */ + int fpstatus, fpstatus2; + _statusfp2(&fpstatus, &fpstatus2); + fpstatus |= fpstatus2; +#endif + return ((SW_ZERODIVIDE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((SW_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((SW_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((SW_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + _clearfp(); + + return fpstatus; +} + +/* OSF/Alpha (Tru64) ---------------------------------------------*/ +#elif defined(__osf__) && defined(__alpha) + +#include + +int npy_get_floatstatus(void) +{ + unsigned long fpstatus = ieee_get_fp_control(); + return ((IEEE_STATUS_DZE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((IEEE_STATUS_OVF & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((IEEE_STATUS_UNF & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((IEEE_STATUS_INV & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + long fpstatus = npy_get_floatstatus(); + /* clear status bits as well as disable exception mode if on */ + ieee_set_fp_control(0); + + return fpstatus; +} + +#else + +int npy_get_floatstatus(void) +{ + return 0; +} + +int npy_clear_floatstatus(void) +{ + return 0; +} + +#endif + +/* + * By using a volatile floating point value, + * the compiler is forced to actually do the requested + * operations because of potential concurrency. + * + * We shouldn't write multiple values to a single + * global here, because that would cause + * a race condition. + */ +static volatile double _npy_floatstatus_x, + _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, + _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; + +void npy_set_floatstatus_divbyzero(void) +{ + _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; +} + +void npy_set_floatstatus_overflow(void) +{ + _npy_floatstatus_x = _npy_floatstatus_big * 1e300; +} + +void npy_set_floatstatus_underflow(void) +{ + _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; +} + +void npy_set_floatstatus_invalid(void) +{ + _npy_floatstatus_inf = NPY_INFINITY; + _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; +} + +#endif diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src index b8f6889e10b5..d734d393bc08 100644 --- a/numpy/core/src/npymath/ieee754.c.src +++ b/numpy/core/src/npymath/ieee754.c.src @@ -557,258 +557,3 @@ npy_longdouble npy_nextafterl(npy_longdouble x, npy_longdouble y) } #endif -/* - * Functions to set the floating point status word. - * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h - */ - -#if (defined(__unix__) || defined(unix)) && !defined(USG) -#include -#endif - -/* Solaris --------------------------------------------------------*/ -/* --------ignoring SunOS ieee_flags approach, someone else can -** deal with that! */ -#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \ - defined(__NetBSD__) -#include - -int npy_get_floatstatus(void) -{ - int fpstatus = fpgetsticky(); - return ((FP_X_DZ & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FP_X_OFL & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FP_X_UFL & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FP_X_INV & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - fpsetsticky(0); - - return fpstatus; -} - -void npy_set_floatstatus_divbyzero(void) -{ - fpsetsticky(FP_X_DZ); -} - -void npy_set_floatstatus_overflow(void) -{ - fpsetsticky(FP_X_OFL); -} - -void npy_set_floatstatus_underflow(void) -{ - fpsetsticky(FP_X_UFL); -} - -void npy_set_floatstatus_invalid(void) -{ - fpsetsticky(FP_X_INV); -} - - -#elif defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__CYGWIN__) || defined(__MINGW32__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) - -# if defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__MINGW32__) || defined(__FreeBSD__) -# include -# elif defined(__CYGWIN__) -# include "numpy/fenv/fenv.h" -# endif - -int npy_get_floatstatus(void) -{ - int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); - - return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - /* testing float status is 50-100 times faster than clearing on x86 */ - int fpstatus = npy_get_floatstatus(); - if (fpstatus != 0) { - feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); - } - - return fpstatus; -} - - -void npy_set_floatstatus_divbyzero(void) -{ - feraiseexcept(FE_DIVBYZERO); -} - -void npy_set_floatstatus_overflow(void) -{ - feraiseexcept(FE_OVERFLOW); -} - -void npy_set_floatstatus_underflow(void) -{ - feraiseexcept(FE_UNDERFLOW); -} - -void npy_set_floatstatus_invalid(void) -{ - feraiseexcept(FE_INVALID); -} - -#elif defined(_AIX) -#include -#include - -int npy_get_floatstatus(void) -{ - int fpstatus = fp_read_flag(); - return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - fp_swap_flag(0); - - return fpstatus; -} - -void npy_set_floatstatus_divbyzero(void) -{ - fp_raise_xcp(FP_DIV_BY_ZERO); -} - -void npy_set_floatstatus_overflow(void) -{ - fp_raise_xcp(FP_OVERFLOW); -} - -void npy_set_floatstatus_underflow(void) -{ - fp_raise_xcp(FP_UNDERFLOW); -} - -void npy_set_floatstatus_invalid(void) -{ - fp_raise_xcp(FP_INVALID); -} - -#else - -/* MS Windows -----------------------------------------------------*/ -#if defined(_MSC_VER) - -#include - - -int npy_get_floatstatus(void) -{ -#if defined(_WIN64) - int fpstatus = _statusfp(); -#else - /* windows enables sse on 32 bit, so check both flags */ - int fpstatus, fpstatus2; - _statusfp2(&fpstatus, &fpstatus2); - fpstatus |= fpstatus2; -#endif - return ((SW_ZERODIVIDE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((SW_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((SW_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((SW_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - _clearfp(); - - return fpstatus; -} - -/* OSF/Alpha (Tru64) ---------------------------------------------*/ -#elif defined(__osf__) && defined(__alpha) - -#include - -int npy_get_floatstatus(void) -{ - unsigned long fpstatus = ieee_get_fp_control(); - return ((IEEE_STATUS_DZE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((IEEE_STATUS_OVF & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((IEEE_STATUS_UNF & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((IEEE_STATUS_INV & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - long fpstatus = npy_get_floatstatus(); - /* clear status bits as well as disable exception mode if on */ - ieee_set_fp_control(0); - - return fpstatus; -} - -#else - -int npy_get_floatstatus(void) -{ - return 0; -} - -int npy_clear_floatstatus(void) -{ - return 0; -} - -#endif - -/* - * By using a volatile floating point value, - * the compiler is forced to actually do the requested - * operations because of potential concurrency. - * - * We shouldn't write multiple values to a single - * global here, because that would cause - * a race condition. - */ -static volatile double _npy_floatstatus_x, - _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, - _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; - -void npy_set_floatstatus_divbyzero(void) -{ - _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; -} - -void npy_set_floatstatus_overflow(void) -{ - _npy_floatstatus_x = _npy_floatstatus_big * 1e300; -} - -void npy_set_floatstatus_underflow(void) -{ - _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; -} - -void npy_set_floatstatus_invalid(void) -{ - _npy_floatstatus_inf = NPY_INFINITY; - _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; -} - -#endif diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 9cbfd32aeeb4..97c9d578c7e0 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -1,10 +1,13 @@ /* + * vim: syntax=c + * * Implement some C99-compatible complex math functions * - * Most of the code is taken from the msun library in FreeBSD (HEAD @ 30th June - * 2009), under the following license: + * Most of the code is taken from the msun library in FreeBSD (HEAD @ 4th + * October 2013), under the following license: * - * Copyright (c) 2007 David Schultz + * Copyright (c) 2007, 2011 David Schultz + * Copyright (c) 2012 Stephen Montgomery-Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,9 +34,8 @@ #include "npy_math_common.h" #include "npy_math_private.h" -/*========================================================== - * Custom implementation of missing complex C99 functions - *=========================================================*/ +static npy_float tiny = 3.9443045e-31f; +#define raise_inexact() do { volatile npy_float junk = 1 + tiny; } while(0) /**begin repeat * #type = npy_float, npy_double, npy_longdouble# @@ -41,7 +43,90 @@ * #c = f, , l# * #C = F, , L# * #TMAX = FLT_MAX, DBL_MAX, LDBL_MAX# + * #TMIN = FLT_MIN, DBL_MIN, LDBL_MIN# + * #TMANT_DIG = FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG# + * #TEPS = FLT_EPSILON, DBL_EPSILON, LDBL_EPSILON# + * #precision = 1, 2, 3# */ + +/*========================================================== + * Constants + *=========================================================*/ +static const @ctype@ c_1@c@ = {1.0@C@, 0.0}; +static const @ctype@ c_half@c@ = {0.5@C@, 0.0}; +static const @ctype@ c_i@c@ = {0.0, 1.0@C@}; +static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; + +/*========================================================== + * Helper functions + * + * These are necessary because we do not count on using a + * C99 compiler. + *=========================================================*/ +static NPY_INLINE @ctype@ cadd@c@(@ctype@ a, @ctype@ b) +{ + return npy_cpack@c@(npy_creal@c@(a) + npy_creal@c@(b), + npy_cimag@c@(a) + npy_cimag@c@(b)); +} + +static NPY_INLINE @ctype@ csub@c@(@ctype@ a, @ctype@ b) +{ + return npy_cpack@c@(npy_creal@c@(a) - npy_creal@c@(b), + npy_cimag@c@(a) - npy_cimag@c@(b)); +} + +static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) +{ + @type@ ar, ai, br, bi; + ar = npy_creal@c@(a); + ai = npy_cimag@c@(a); + br = npy_creal@c@(b); + bi = npy_cimag@c@(b); + return npy_cpack@c@(ar*br - ai*bi, ar*bi + ai*br); +} + +static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) +{ + @type@ ar, ai, br, bi, abs_br, abs_bi; + ar = npy_creal@c@(a); + ai = npy_cimag@c@(a); + br = npy_creal@c@(b); + bi = npy_cimag@c@(b); + abs_br = npy_fabs@c@(br); + abs_bi = npy_fabs@c@(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@c@(ar/abs_br, ai/abs_bi); + } + else { + @type@ rat = bi/br; + @type@ scl = 1.0@C@/(br+bi*rat); + return npy_cpack@c@((ar + ai*rat)*scl, (ai - ar*rat)*scl); + } + } + else { + @type@ rat = br/bi; + @type@ scl = 1.0@C@/(bi + br*rat); + return npy_cpack@c@((ar*rat + ai)*scl, (ai*rat - ar)*scl); + } +} + +static NPY_INLINE @ctype@ cneg@c@(@ctype@ a) +{ + return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a)); +} + +static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) +{ + return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a)); +} + +/*========================================================== + * Custom implementation of missing complex C99 functions + *=========================================================*/ + #ifndef HAVE_CABS@C@ @type@ npy_cabs@c@(@ctype@ z) { @@ -56,6 +141,53 @@ } #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 + +static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) +{ +#if @precision@ == 1 + const npy_int k = 235; +#endif +#if @precision@ == 2 + const npy_int k = 1799; +#endif +#if @precision@ == 3 + const npy_int k = 19547; +#endif + const @type@ kln2 = k * NPY_LOGE2@c@; + @type@ mant, mantcos, mantsin; + npy_int ex, excos, exsin; + + mant = npy_frexp@c@(npy_exp@c@(x - kln2), &ex); + mantcos = npy_frexp@c@(npy_cos@c@(y), &excos); + mantsin = npy_frexp@c@(npy_sin@c@(y), &exsin); + + expt += ex + k; + return npy_cpack@c@( npy_ldexp@c@(mant * mantcos, expt + excos), + npy_ldexp@c@(mant * mantsin, expt + exsin)); +} + #ifndef HAVE_CEXP@C@ @ctype@ npy_cexp@c@(@ctype@ z) { @@ -67,23 +199,28 @@ i = npy_cimag@c@(z); if (npy_isfinite(r)) { - x = npy_exp@c@(r); + if (r >= SCALED_CEXP_LOWER@C@ && r <= SCALED_CEXP_UPPER@C@) { + ret = _npy_scaled_cexp@c@(r, i, 0); + } + else { + x = npy_exp@c@(r); - c = npy_cos@c@(i); - s = npy_sin@c@(i); + c = npy_cos@c@(i); + s = npy_sin@c@(i); - if (npy_isfinite(i)) { - ret = npy_cpack@c@(x * c, x * s); - } else { - ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i)); + if (npy_isfinite(i)) { + ret = npy_cpack@c@(x * c, x * s); + } else { + ret = npy_cpack@c@(NPY_NAN@C@, npy_copysign@c@(NPY_NAN@C@, i)); + } } } else if (npy_isnan(r)) { /* r is nan */ if (i == 0) { - ret = npy_cpack@c@(r, 0); + ret = z; } else { - ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN, i)); + ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN@C@, i)); } } else { /* r is +- inf */ @@ -97,7 +234,8 @@ ret = npy_cpack@c@(r * c, r * s); } else { /* x = +inf, y = +-inf | nan */ - ret = npy_cpack@c@(r, NPY_NAN); + npy_set_floatstatus_invalid(); + ret = npy_cpack@c@(r, NPY_NAN@C@); } } else { if (npy_isfinite(i)) { @@ -118,9 +256,70 @@ #endif #ifndef HAVE_CLOG@C@ +/* 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. +*/ @ctype@ npy_clog@c@(@ctype@ z) { - return npy_cpack@c@(npy_log@c@ (npy_cabs@c@ (z)), npy_carg@c@ (z)); + @type@ ax = npy_fabs@c@(npy_creal@c@(z)); + @type@ ay = npy_fabs@c@(npy_cimag@c@(z)); + @type@ rr, ri; + + if (ax > @TMAX@/4 || ay > @TMAX@/4) { + rr = npy_log@c@(npy_hypot@c@(ax/2, ay/2)) + NPY_LOGE2@c@; + } + else if (ax < @TMIN@ && ay < @TMIN@) { + if (ax > 0 || ay > 0) { + /* catch cases where hypot(ax, ay) is subnormal */ + rr = npy_log@c@(npy_hypot@c@(npy_ldexp@c@(ax, @TMANT_DIG@), + npy_ldexp@c@(ay, @TMANT_DIG@))) - @TMANT_DIG@*NPY_LOGE2@c@; + } + else { + /* log(+/-0 +/- 0i) */ + /* raise divide-by-zero floating point exception */ + rr = -1.0@c@ / npy_creal@c@(z); + rr = npy_copysign@c@(rr, -1); + ri = npy_carg@c@(z); + return npy_cpack@c@(rr, ri); + } + } + else { + @type@ h = npy_hypot@c@(ax, ay); + if (0.71 <= h && h <= 1.73) { + @type@ am = ax > ay ? ax : ay; /* max(ax, ay) */ + @type@ an = ax > ay ? ay : ax; /* min(ax, ay) */ + rr = npy_log1p@c@((am-1)*(am+1)+an*an)/2; + } + else { + rr = npy_log@c@(h); + } + } + ri = npy_carg@c@(z); + return npy_cpack@c@(rr, ri); } #endif @@ -143,7 +342,7 @@ if (a == 0 && b == 0) return (npy_cpack@c@(0, b)); if (npy_isinf(b)) - return (npy_cpack@c@(NPY_INFINITY, b)); + return (npy_cpack@c@(NPY_INFINITY@C@, b)); if (npy_isnan(a)) { t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ return (npy_cpack@c@(a, t)); /* return NaN + NaN i */ @@ -176,10 +375,10 @@ /* Algorithm 312, CACM vol 10, Oct 1967. */ if (a >= 0) { - t = npy_sqrt@c@((a + npy_hypot@c@(a, b)) * 0.5); + t = npy_sqrt@c@((a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(t, b / (2 * t)); } else { - t = npy_sqrt@c@((-a + npy_hypot@c@(a, b)) * 0.5); + t = npy_sqrt@c@((-a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(npy_fabs@c@(b) / (2 * t), npy_copysign@c@(t, b)); } @@ -193,38 +392,1172 @@ #endif #ifndef HAVE_CPOW@C@ -@ctype@ npy_cpow@c@ (@ctype@ x, @ctype@ y) +@ctype@ npy_cpow@c@ (@ctype@ a, @ctype@ b) { - @ctype@ b; - @type@ br, bi, yr, yi; + npy_intp n; + @type@ ar = npy_creal@c@(a); + @type@ br = npy_creal@c@(b); + @type@ ai = npy_cimag@c@(a); + @type@ bi = npy_cimag@c@(b); + @ctype@ loga, r; - yr = npy_creal@c@(y); - yi = npy_cimag@c@(y); - b = npy_clog@c@(x); - br = npy_creal@c@(b); - bi = npy_cimag@c@(b); + if (br == 0. && bi == 0.) { + return npy_cpack@c@(1., 0.); + } + if (ar == 0. && ai == 0.) { + if (br > 0 && bi == 0) { + return npy_cpack@c@(0., 0.); + } + else { + volatile @type@ tmp = NPY_INFINITY@C@; + /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike + * for reals, c0**p, with `p` negative is in general + * ill-defined. + * + * c0**z with z complex is also ill-defined. + */ + r = npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); + + /* Raise invalid */ + tmp -= NPY_INFINITY@C@; + ar = tmp; + return r; + } + } + if (bi == 0 && (n=(npy_intp)br) == br) { + if (n == 1) { + /* unroll: handle inf better */ + return npy_cpack@c@(ar, ai); + } + else if (n == 2) { + /* unroll: handle inf better */ + return cmul@c@(a, a); + } + else if (n == 3) { + /* unroll: handle inf better */ + return cmul@c@(a, cmul@c@(a, a)); + } + else if (n > -100 && n < 100) { + @ctype@ p, aa; + npy_intp mask = 1; + if (n < 0) n = -n; + aa = c_1@c@; + p = npy_cpack@c@(ar, ai); + while (1) { + if (n & mask) + aa = cmul@c@(aa,p); + mask <<= 1; + if (n < mask || mask <= 0) break; + p = cmul@c@(p,p); + } + r = npy_cpack@c@(npy_creal@c@(aa), npy_cimag@c@(aa)); + if (br < 0) r = cdiv@c@(c_1@c@, r); + return r; + } + } + + loga = npy_clog@c@(a); + ar = npy_creal@c@(loga); + ai = npy_cimag@c@(loga); - return npy_cexp@c@(npy_cpack@c@(br * yr - bi * yi, br * yi + bi * yr)); + return npy_cexp@c@(npy_cpack@c@(ar * br - ai * bi, ar * bi + ai * br)); } #endif #ifndef HAVE_CCOS@C@ @ctype@ npy_ccos@c@(@ctype@ z) { - @type@ x, y; - x = npy_creal@c@(z); - y = npy_cimag@c@(z); - return npy_cpack@c@(npy_cos@c@(x) * npy_cosh@c@(y), -(npy_sin@c@(x) * npy_sinh@c@(y))); + /* ccos(z) = ccosh(I * z) */ + return npy_ccosh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); } #endif #ifndef HAVE_CSIN@C@ @ctype@ npy_csin@c@(@ctype@ z) +{ + /* csin(z) = -I * csinh(I * z) */ + z = npy_csinh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z)); +} +#endif + +#ifndef HAVE_CTAN@C@ +@ctype@ npy_ctan@c@(@ctype@ z) +{ + /* ctan(z) = -I * ctanh(I * z) */ + z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); + return (npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z))); +} +#endif + +#ifndef HAVE_CCOSH@C@ +/* + * 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 + */ +@ctype@ npy_ccosh@c@(@ctype@ z) +{ +#if @precision@ == 1 + const npy_float CCOSH_BIG = 9.0f; + const npy_float CCOSH_HUGE = 1.70141183e+38f; +#endif +#if @precision@ == 2 + const npy_double CCOSH_BIG = 22.0; + const npy_double CCOSH_HUGE = 8.9884656743115795e+307; +#endif +#if @precision@ >= 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 + + @type@ x, y, h, absx; + npy_int xfinite, yfinite; + + x = npy_creal@c@(z); + y = npy_cimag@c@(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@c@(npy_cosh@c@(x), x * y); + absx = npy_fabs@c@(x); + if (absx < CCOSH_BIG) /* small x: normal case */ + return npy_cpack@c@(npy_cosh@c@(x) * npy_cos@c@(y), + npy_sinh@c@(x) * npy_sin@c@(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (absx < SCALED_CEXP_LOWER@C@) { + /* x < 710: exp(|x|) won't overflow */ + h = npy_exp@c@(absx) * 0.5@c@; + return npy_cpack@c@(h * npy_cos@c@(y), + npy_copysign@c@(h, x) * npy_sin@c@(y)); + } else if (absx < SCALED_CEXP_UPPER@C@) { + /* x < 1455: scale to avoid overflow */ + z = _npy_scaled_cexp@c@(absx, y, -1); + return npy_cpack@c@(npy_creal@c@(z), + npy_cimag@c@(z) * npy_copysign@c@(1, x)); + } else { + /* x >= 1455: the result always overflows */ + h = CCOSH_HUGE * x; + return npy_cpack@c@(h * h * npy_cos@c@(y), h * npy_sin@c@(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@c@(y - y, npy_copysign@c@(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@c@(x * x, npy_copysign@c@(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@c@(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@c@(x * x, x * (y - y)); + return npy_cpack@c@((x * x) * npy_cos@c@(y), x * npy_sin@c@(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@c@((x * x) * (y - y), (x + x) * (y - y)); +} +#undef CCOSH_BIG +#undef CCOSH_HUGE +#endif + +#ifndef HAVE_CSINH@C@ +/* + * 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. + */ +@ctype@ npy_csinh@c@(@ctype@ z) +{ +#if @precision@ == 1 + const npy_float CSINH_BIG = 9.0f; + const npy_float CSINH_HUGE = 1.70141183e+38f; +#endif +#if @precision@ == 2 + const npy_double CSINH_BIG = 22.0; + const npy_double CSINH_HUGE = 8.9884656743115795e+307; +#endif +#if @precision@ >= 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 + + @type@ x, y, h, absx; + npy_int xfinite, yfinite; + + x = npy_creal@c@(z); + y = npy_cimag@c@(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@c@(npy_sinh@c@(x), y); + absx = npy_fabs@c@(x); + if (absx < CSINH_BIG) /* small x: normal case */ + return npy_cpack@c@(npy_sinh@c@(x) * npy_cos@c@(y), + npy_cosh@c@(x) * npy_sin@c@(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (absx < SCALED_CEXP_LOWER@C@) { + /* x < 710: exp(|x|) won't overflow */ + h = npy_exp@c@(npy_fabs@c@(x)) * 0.5@c@; + return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), + h * npy_sin@c@(y)); + } else if (x < SCALED_CEXP_UPPER@C@) { + /* x < 1455: scale to avoid overflow */ + z = _npy_scaled_cexp@c@(absx, y, -1); + return npy_cpack@c@(npy_creal@c@(z) * npy_copysign@c@(1, x), + npy_cimag@c@(z)); + } else { + /* x >= 1455: the result always overflows */ + h = CSINH_HUGE * x; + return npy_cpack@c@(h * npy_cos@c@(y), h * h * npy_sin@c@(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@c@(npy_copysign@c@(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@c@(x, npy_copysign@c@(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@c@(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@c@(x * x, x * (y - y)); + return npy_cpack@c@(x * npy_cos@c@(y), NPY_INFINITY@C@ * npy_sin@c@(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@c@((x * x) * (y - y), (x + x) * (y - y)); +} +#undef CSINH_BIG +#undef CSINH_HUGE +#endif + +#ifndef HAVE_CTANH@C@ +/* + * 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 + +@ctype@ npy_ctanh@c@(@ctype@ z) { @type@ x, y; + @type@ t, beta, s, rho, denom; + + x = npy_creal@c@(z); + y = npy_cimag@c@(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@c@(x, (y == 0 ? y : x * y)); + return npy_cpack@c@(npy_copysign@c@(1,x), + npy_copysign@c@(0, + npy_isinf(y) ? y : npy_sin@c@(y) * npy_cos@c@(y))); + } + + /* + * ctanh(x + i NAN) = NaN + i NaN + * ctanh(x +- i Inf) = NaN + i NaN + */ + if (!npy_isfinite(y)) + return (npy_cpack@c@(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@c@(x) >= TANH@C@_HUGE) { + @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); + return (npy_cpack@c@(npy_copysign@c@(1, x), + 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); + } + + /* Kahan's algorithm */ + t = npy_tan@c@(y); + beta = 1 + t * t; /* = 1 / cos^2(y) */ + s = npy_sinh@c@(x); + rho = npy_sqrt@c@(1 + s * s); /* = cosh(x) */ + denom = 1 + beta * s * s; + return (npy_cpack@c@((beta * rho * s) / denom, t / denom)); +} +#undef TANH_HUGE +#undef TANHF_HUGE +#undef TANHL_HUGE +#endif + +#if !defined (HAVE_CACOS@C@) || !defined (HAVE_CASINH@C@) +/* + * 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 @type@ _f@c@(@type@ a, @type@ b, @type@ 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@c@(@type@ x, @type@ y, @type@ *rx, + npy_int *B_is_usable, @type@ *B, @type@ *sqrt_A2my2, @type@ *new_y) +{ +#if @precision@ == 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 @precision@ == 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 @precision@ == 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 + @type@ R, S, A; /* A, B, R, and S are as in Hull et al. */ + @type@ Am1, Amy; /* A-1, A-y. */ + + R = npy_hypot@c@(x, y + 1); /* |z+I| */ + S = npy_hypot@c@(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 < @TEPS@ * @TEPS@ / 128) { + /* + * fp is of order x^2, and fm = x/2. + * A = 1 (inexactly). + */ + *rx = npy_sqrt@c@(x); + } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + /* + * Underflow will not occur because + * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN + */ + Am1 = _f@c@(x, 1 + y, R) + _f@c@(x, 1 - y, S); + *rx = npy_log1p@c@(Am1 + npy_sqrt@c@(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@c@((1 - y) * (1 + y)); + } else { /* if (y > 1) */ + /* + * A-1 = y-1 (inexactly). + */ + *rx = npy_log1p@c@((y - 1) + npy_sqrt@c@((y - 1) * (y + 1))); + } + } else { + *rx = npy_log@c@(A + npy_sqrt@c@(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 / @TEPS@); + *new_y = y * (2 / @TEPS@); + 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 < @TEPS@ / 128) { + /* + * fp is of order x^2, and fm = x/2. + * A = 1 (inexactly). + */ + *sqrt_A2my2 = npy_sqrt@c@(x) * npy_sqrt@c@((A + y) / 2); + } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + /* + * Underflow will not occur because + * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN + * and + * x >= DBL_EPSILON^2 >= FOUR_SQRT_MIN + */ + Amy = _f@c@(x, y + 1, R) + _f@c@(x, y - 1, S); + *sqrt_A2my2 = npy_sqrt@c@(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 / @TEPS@ / @TEPS@) * y / + npy_sqrt@c@((y + 1) * (y - 1)); + *new_y = y * (4 / @TEPS@ / @TEPS@); + } else { /* if (y < 1) */ + /* + * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and + * A = 1 (inexactly). + */ + *sqrt_A2my2 = npy_sqrt@c@((1 - y) * (1 + y)); + } + } +} + +/* + * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON. + */ +static inline void _clog_for_large_values@c@(@type@ x, @type@ y, + @type@ *rr, @type@ *ri) +{ +#if @precision@ == 1 + const npy_float QUARTER_SQRT_MAX = 4.611685743549481e+18f; + const npy_float SQRT_MIN = 1.0842021724855044e-19f; + #endif +#if @precision@ == 2 + const npy_double QUARTER_SQRT_MAX = 3.3519519824856489e+153; + const npy_double SQRT_MIN = 1.4916681462400413e-154; + #endif +#if @precision@ == 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 + @type@ ax, ay, t; + + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(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 uneccessarily slow. + */ + if (ax > @TMAX@ / 2) { + *rr = npy_log@c@(npy_hypot@c@(x / NPY_E@c@, y / NPY_E@c@)) + 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@c@(npy_hypot@c@(x, y)); + } + else { + *rr = npy_log@c@(ax * ax + ay * ay) / 2; + } + *ri = npy_atan2@c@(y, x); +} +#endif + +#ifndef HAVE_CACOS@C@ +@ctype@ npy_cacos@c@(@ctype@ z) +{ +#if @precision@ == 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 @precision@ == 2 + const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + @type@ 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@c@(z); + y = npy_cimag@c@(z); + sx = npy_signbit(x); + sy = npy_signbit(y); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + + if (npy_isnan(x) || npy_isnan(y)) { + /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */ + if (npy_isinf(x)) + return npy_cpack@c@(y + y, -NPY_INFINITY@C@); + /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */ + if (npy_isinf(y)) + return npy_cpack@c@(x + x, -y); + /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */ + if (x == 0) + return npy_cpack@c@(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@c@(NPY_NAN@C@, NPY_NAN@C@); + } + + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { + /* clog...() will raise inexact unless x or y is infinite. */ + _clog_for_large_values@c@(x, y, &wx, &wy); + rx = npy_fabs@c@(wy); + ry = wx + NPY_LOGE2@c@; + if (sy == 0) + ry = -ry; + return npy_cpack@c@(rx, ry); + } + + /* Avoid spuriously raising inexact for z = 1. */ + if (x == 1 && y == 0) + return npy_cpack@c@(0, -y); + + /* All remaining cases are inexact. */ + raise_inexact(); + + if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) + return npy_cpack@c@(pio2_hi - (x - pio2_lo), -y); + + _do_hard_work@c@(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x); + if (B_is_usable) { + if (sx == 0) + rx = npy_acos@c@(B); + else + rx = npy_acos@c@(-B); + } else { + if (sx == 0) + rx = npy_atan2@c@(sqrt_A2mx2, new_x); + else + rx = npy_atan2@c@(sqrt_A2mx2, -new_x); + } + if (sy == 0) + ry = -ry; + return npy_cpack@c@(rx, ry); +} +#endif + +#ifndef HAVE_CASIN@C@ +@ctype@ npy_casin@c@(@ctype@ z) +{ + /* casin(z) = I * conj( casinh(I * conj(z)) ) */ + z = npy_casinh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z)); +} +#endif + +#ifndef HAVE_CATAN@C@ +@ctype@ npy_catan@c@(@ctype@ z) +{ + /* catan(z) = I * conj( catanh(I * conj(z)) ) */ + z = npy_catanh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z)); +} +#endif + +#ifndef HAVE_CACOSH@C@ +@ctype@ npy_cacosh@c@(@ctype@ z) +{ + /* + * cacosh(z) = I*cacos(z) or -I*cacos(z) + * where the sign is chosen so Re(cacosh(z)) >= 0. + */ + @ctype@ w; + @type@ rx, ry; + + w = npy_cacos@c@(z); + rx = npy_creal@c@(w); + ry = npy_cimag@c@(w); + /* cacosh(NaN + I*NaN) = NaN + I*NaN */ + if (npy_isnan(rx) && npy_isnan(ry)) + return npy_cpack@c@(ry, rx); + /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */ + /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */ + if (npy_isnan(rx)) + return npy_cpack@c@(npy_fabs@c@(ry), rx); + /* cacosh(0 + I*NaN) = NaN + I*NaN */ + if (npy_isnan(ry)) + return npy_cpack@c@(ry, ry); + return npy_cpack@c@(npy_fabs@c@(ry), npy_copysign@c@(rx, npy_cimag@c@(z))); +} +#endif + +#ifndef HAVE_CASINH@C@ +/* + * 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 + */ +@ctype@ npy_casinh@c@(@ctype@ z) +{ +#if @precision@ == 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 @precision@ == 2 + const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + @type@ x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y; + npy_int B_is_usable; + x = npy_creal@c@(z); y = npy_cimag@c@(z); - return npy_cpack@c@(npy_sin@c@(x) * npy_cosh@c@(y), npy_cos@c@(x) * npy_sinh@c@(y)); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + + if (npy_isnan(x) || npy_isnan(y)) { + /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */ + if (npy_isinf(x)) + return npy_cpack@c@(x, y + y); + /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */ + if (npy_isinf(y)) + return npy_cpack@c@(y, x + x); + /* casinh(NaN + I*0) = NaN + I*0 */ + if (y == 0) + return npy_cpack@c@(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@c@(NPY_NAN@C@, NPY_NAN@C@); + } + + 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@c@(x, y, &wx, &wy); + wx += NPY_LOGE2@c@; + } + else { + _clog_for_large_values@c@(-x, -y, &wx, &wy); + wx += NPY_LOGE2@c@; + } + return npy_cpack@c@(npy_copysign@c@(wx, x), npy_copysign@c@(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@c@(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y); + if (B_is_usable) + ry = npy_asin@c@(B); + else + ry = npy_atan2@c@(new_y, sqrt_A2my2); + return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); +} +#endif + +#ifndef HAVE_CATANH@C@ +/* + * 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 @type@ _sum_squares@c@(@type@ x, @type@ y) +{ +#if @precision@ == 1 +const npy_float SQRT_MIN = 1.0842022e-19f; +#endif +#if @precision@ == 2 +const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */ +#endif +#if @precision@ == 3 +/* this is correct for 80 bit long doubles */ +const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; +#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 @precision@ == 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 @precision@ == 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)) + return (1 / x); /* +-Inf -> +-0 is special */ + if (iy - ix >= CUTOFF << 20) + return (x / y / y); /* should avoid double div, but hard */ + 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 @precision@ == 3 +#ifndef HAVE_LDOUBLE_DOUBLE_DOUBLE_BE +#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 + +@ctype@ npy_catanh@c@(@ctype@ z) +{ +#if @precision@ == 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 @precision@ == 2 + const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10l; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + @type@ x, y, ax, ay, rx, ry; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + + /* This helps handle many cases. */ + if (y == 0 && ax <= 1) + return npy_cpack@c@(npy_atanh@c@(x), y); + + /* To ensure the same accuracy as atan(), and to filter out z = 0. */ + if (x == 0) + return npy_cpack@c@(x, npy_atan@c@(y)); + + if (npy_isnan(x) || npy_isnan(y)) { + /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */ + if (npy_isinf(x)) + return npy_cpack@c@(npy_copysign@c@(0, x), y + y); + /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */ + if (npy_isinf(y)) + return npy_cpack@c@(npy_copysign@c@(0, x), + npy_copysign@c@(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@c@(NPY_NAN@C@, NPY_NAN@C@); + } + + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) + return npy_cpack@c@(_real_part_reciprocal@c@(x, y), + npy_copysign@c@(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 only that needs to do it + * explicitly. + */ + raise_inexact(); + return (z); + } + + if (ax == 1 && ay < @TEPS@) + rx = (NPY_LOGE2@c@ - npy_log@c@(ay)) / 2; + else + rx = npy_log1p@c@(4 * ax / _sum_squares@c@(ax - 1, ay)) / 4; + + if (ax == 1) + ry = npy_atan2@c@(2, -ay) / 2; + else if (ay < @TEPS@) + ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax)) / 2; + else + ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2; + + return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); } #endif /**end repeat**/ @@ -255,8 +1588,8 @@ /**end repeat1**/ /**begin repeat1 - * #kind = cexp,clog,csqrt,ccos,csin# - * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN# + * #kind = cexp,clog,csqrt,ccos,csin,ctan,ccosh,csinh,ctanh,cacos,casin,catan,cacosh,casinh,catanh# + * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CCOSH,CSINH,CTANH,CACOS,CASIN,CATAN,CACOSH,CASINH,CATANH# */ #ifdef HAVE_@KIND@@C@ @ctype@ npy_@kind@@c@(@ctype@ z) diff --git a/numpy/core/src/npymath/npymath_tests.c b/numpy/core/src/npymath/npymath_tests.c new file mode 100644 index 000000000000..6c4be49b7102 --- /dev/null +++ b/numpy/core/src/npymath/npymath_tests.c @@ -0,0 +1,135 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "Python.h" + +#include "npy_pycompat.h" +#include "npy_config.h" +#include "numpy/npy_math.h" + +#define CACOS 1 +#define CASIN 1 +#define CATAN 1 +#define CACOSH 1 +#define CASINH 1 +#define CATANH 1 +#define CCOS 1 +#define CSIN 1 +#define CTAN 1 +#define CCOSH 1 +#define CSINH 1 +#define CTANH 1 +#define CEXP 1 +#define CLOG 1 +#define CPOW 1 +#define CSQRT 1 +#define HAVE_NUMPY 1 + +#define FLOAT 1 +#include "test_c99complex.c" +#undef FLOAT + +#define DOUBLE 1 +#include "test_c99complex.c" +#undef DOUBLE + +#define LONGDOUBLE 1 +#include "test_c99complex.c" +#undef LONGDOUBLE + +#define TESTFUNC_INT(func, suffix) \ + static PyObject * CONCAT3(_test_, func, suffix)(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) \ + { \ + PyObject *errs; \ + errs = CONCAT3(test_, func, suffix)(); \ + if (errs == NULL) { \ + return errs; \ + } \ + if (PySequence_Size(errs) == 0) { \ + Py_DECREF(errs); \ + Py_INCREF(Py_None); \ + return Py_None; \ + } \ + else { \ + PyErr_SetObject(PyExc_AssertionError, errs); \ + return NULL; \ + } \ + } + +#define TESTFUNC(func) \ + TESTFUNC_INT(func, f) \ + TESTFUNC_INT(func, ) \ + TESTFUNC_INT(func, l) + +#define TESTMETHODDEF_INT(func, suffix) \ + {STRINGIZE(CONCAT3(test_, func, suffix)), CONCAT3(_test_, func, suffix), METH_VARARGS, ""} + +#define TESTMETHODDEF(func) \ + TESTMETHODDEF_INT(func, f), \ + TESTMETHODDEF_INT(func, ), \ + TESTMETHODDEF_INT(func, l) + +TESTFUNC(cacos) +TESTFUNC(casin) +TESTFUNC(catan) +TESTFUNC(cacosh) +TESTFUNC(casinh) +TESTFUNC(catanh) +TESTFUNC(ccos) +TESTFUNC(csin) +TESTFUNC(ctan) +TESTFUNC(ccosh) +TESTFUNC(csinh) +TESTFUNC(ctanh) +TESTFUNC(cexp) +TESTFUNC(clog) +TESTFUNC(cpow) +TESTFUNC(csqrt) + +static PyMethodDef methods[] = { + TESTMETHODDEF(cacos), + TESTMETHODDEF(casin), + TESTMETHODDEF(catan), + TESTMETHODDEF(cacosh), + TESTMETHODDEF(casinh), + TESTMETHODDEF(catanh), + TESTMETHODDEF(ccos), + TESTMETHODDEF(csin), + TESTMETHODDEF(ctan), + TESTMETHODDEF(ccosh), + TESTMETHODDEF(csinh), + TESTMETHODDEF(ctanh), + TESTMETHODDEF(cexp), + TESTMETHODDEF(clog), + TESTMETHODDEF(cpow), + TESTMETHODDEF(csqrt), + {NULL, NULL, 0, NULL} +}; + +#if defined(NPY_PY3K) +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "npymath_tests", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#if defined(NPY_PY3K) +PyMODINIT_FUNC PyInit_npymath_tests(void) +#else +PyMODINIT_FUNC +initnpymath_tests(void) +#endif +{ +#if defined(NPY_PY3K) + return PyModule_Create(&moduledef); +#else + Py_InitModule("npymath_tests", methods); +#endif +} + diff --git a/numpy/core/src/npymath/test_c99complex.c b/numpy/core/src/npymath/test_c99complex.c new file mode 100644 index 000000000000..64abe107775a --- /dev/null +++ b/numpy/core/src/npymath/test_c99complex.c @@ -0,0 +1,1880 @@ +#ifdef HAVE_NUMPY +#include +#include +#else +#include +#include +#ifdef __SUNPRO_CC +#include +#endif +#include +#endif + +#include + +#define PYTESTPRECISION 1 +#define PYTESTFUNC 1 + +#ifdef FLOAT +#define TYPE float +#define CTYPE float complex +#define SUFFIX f +#define EPS FLT_EPSILON +#define CLOSE_ATOL 0.0f +#define CLOSE_RTOL 1e-5f +#define BRANCH_SCALE 1e2f +#define BRANCH_ATOL 1e-2f +#define FMT "%.8e" +#else +#ifdef DOUBLE +#define TYPE double +#define CTYPE double complex +#define SUFFIX +#define EPS DBL_EPSILON +#define CLOSE_ATOL 0.0 +#define CLOSE_RTOL 1e-12 +#define BRANCH_SCALE 1e3 +#define BRANCH_ATOL 1e-4 +#define FMT "%.16e" +#else +#ifdef LONGDOUBLE +#define TYPE long double +#define CTYPE long double complex +#define SUFFIX l +#define EPS LDBL_EPSILON +#define CLOSE_ATOL 0.0l +#define CLOSE_RTOL 1e-12l +#define BRANCH_SCALE 1e3l +#define BRANCH_ATOL 1e-4l +#define FMT "%.18Le" +#else +#error "Define FLOAT or DOUBLE or LONGDOUBLE" +#endif +#endif +#endif + +#define STRINGIZE_INT(A) #A +#define STRINGIZE(A) STRINGIZE_INT(A) + +#define CONCAT(A, B) A ## B +#define ADDSUFFIX_INT(A, B) CONCAT(A, B) +#define ADDSUFFIX(A) ADDSUFFIX_INT(A, SUFFIX) +#define ADDPREFIX(A) ADDSUFFIX_INT(PREFIX, A) + +#define CONCAT3(A, B, C) A ## B ## C +#define FUNC_INT(A, B, C) CONCAT3(A, B, C) +#define FUNC(A) FUNC_INT(PREFIX, A, SUFFIX) + +#ifdef HAVE_NUMPY +#include "Python.h" + +/* Use our versions no matter what. */ +#ifdef NAN +#undef NAN +#endif +#define NAN NPY_NAN + +#ifdef INFINITY +#undef INFINITY +#endif +#define INFINITY NPY_INFINITY + +#ifdef NZERO +#undef NZERO +#endif +#define NZERO NPY_NZERO + +/* Use the numpy types since we need them to call npy_math functions. */ +#undef TYPE +#undef CTYPE + +#ifdef FLOAT +#define TYPE npy_float +#define CTYPE npy_cfloat +#else +#ifdef DOUBLE +#define TYPE npy_double +#define CTYPE npy_cdouble +#else +#ifdef LONGDOUBLE +#define TYPE npy_longdouble +#define CTYPE npy_clongdouble +#endif +#endif +#endif + +#define PREFIX npy_ + +#define RETTYPE PyObject* + +/* MSVC #defines copysign as _copysign. This conflicts with our prefixing */ +#ifdef _MSC_VER +#undef copysign +#endif + +#define INIT_FUNC() \ + PyObject *ret = PyList_New(0); \ + PyObject *entry; \ + const size_t bsize = 4096; \ + char buf[4096]; \ + int used_size + +#define TEST_FAILED_INT(printexpr) \ + do { \ + used_size = printexpr; \ + if (used_size >= 0) { \ + entry = PyUString_FromStringAndSize(buf, used_size); \ + if (entry == NULL) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + if (PyList_Append(ret, entry) == -1) { \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + return NULL; \ + } \ + }\ + else { \ + Py_DECREF(ret); \ + PyErr_SetString(PyExc_IOError, "PyOS_snprintf failed.");\ + } \ + } \ + while(0) + +#define TEST_FAILED(func, xr, xi, er, ei, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j: received: " FMT \ + " + " FMT "j\n", __LINE__, xr, xi, er, ei, rr, ri)) + +#define TEST_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j\n", __LINE__, xr, xi, er1, ei1, \ + er2, ei2, rr, ri)) + +#define TEST_FAILED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j or " FMT " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " \ + FMT "j\n", __LINE__, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, \ + rr, ri)) + +#define TEST_RAISES_FAILED(func, xr, xi, er, ei, rr, ri, efpe, rfpe) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j: received: " FMT \ + " + " FMT "j, required FPE: %d, recieved: %d\n", __LINE__, xr, xi, \ + er, ei, rr, ri, efpe, rfpe)) + +#define TEST_RAISES_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j, required FPE: %d, recieved: %d\n", \ + __LINE__, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe)) + +#define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) "(" FMT \ + " + " FMT "j, " FMT " + " FMT "j): expected: " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j\n", __LINE__, dxr, dxi, dyr, dyi, \ + der, dei, rr, ri)) + +#define TEST_BRANCH_CUT_FAILED(func, vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, STRINGIZE(func) \ + ": branch cut failure: x = " FMT " + " FMT "j, dx = " FMT " + " FMT \ + "j, rsign = %d, isign = %d, check_sign_zero = %d\n", vxr, vxi, \ + vdxr, vdxi, vrsign, visign, vcksignzero)) + +#define TEST_NEAR_CROSSOVER_FAILED(fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat)\ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%s: Loss of precision: j = %d, "\ + "k = %d\nzp = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\nzm = (" FMT \ + " + " FMT "j) -> (" FMT " + " FMT "j)\ndiff = " FMT \ + ", exact match = %d\n", fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, \ + czmr, czmi, diff, exmat)) + +#define TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%s: Loss of precision vs real:"\ + "\nx = " FMT "\nratio = " FMT "\n", fname, x, ratio)) + +#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ + do { \ + PyObject *temp; \ + entry = ADDSUFFIX(check_loss_of_precision)(FUNC(cfunc), FUNC(rfunc), real, \ + STRINGIZE(FUNC(cfunc))); \ + if (!PySequence_Check(entry)) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + temp = PySequence_Concat(ret, entry); \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + if (temp == NULL) { \ + return temp; \ + } else { \ + ret = temp; \ + temp = NULL; \ + } \ + entry = ADDSUFFIX(check_near_crossover)(FUNC(cfunc), STRINGIZE(FUNC(cfunc))); \ + if (!PySequence_Check(entry)) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + temp = PySequence_Concat(ret, entry); \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + if (temp == NULL) { \ + return temp; \ + } else { \ + ret = temp; \ + temp = NULL; \ + } \ + } \ + while(0) + +#else +/* We assume that we have INFINITY and NAN */ + +#ifdef NZERO +#undef NZERO +#endif +#define NZERO (-1.0 * 0.0) + +/* copied from npy_math.h for when we don't have it. */ +#define NPY_E 2.718281828459045235360287471352662498 /* e */ +#define NPY_LOGE2 0.693147180559945309417232121458176568 /* log_e 2 */ +#define NPY_PI 3.141592653589793238462643383279502884 /* pi */ +#define NPY_PI_2 1.570796326794896619231321691639751442 /* pi/2 */ +#define NPY_SQRT2 1.414213562373095048801688724209698079 /* sqrt(2) */ + +#define NPY_Ef 2.718281828459045235360287471352662498F /* e */ +#define NPY_LOGE2f 0.693147180559945309417232121458176568F /* log_e 2 */ +#define NPY_PIf 3.141592653589793238462643383279502884F /* pi */ +#define NPY_PI_2f 1.570796326794896619231321691639751442F /* pi/2 */ +#define NPY_SQRT2f 1.414213562373095048801688724209698079F /* sqrt(2) */ + +#define NPY_El 2.718281828459045235360287471352662498L /* e */ +#define NPY_LOGE2l 0.693147180559945309417232121458176568L /* log_e 2 */ +#define NPY_PIl 3.141592653589793238462643383279502884L /* pi */ +#define NPY_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */ +#define NPY_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */ + +#define NPY_FPE_DIVIDEBYZERO 1 +#define NPY_FPE_OVERFLOW 2 +#define NPY_FPE_UNDERFLOW 4 +#define NPY_FPE_INVALID 8 + +/* Including this directly is the easiest way to get npy_clear_floatstatus and + * npy_get_float_status during config tests. Defining CONFIG_TESTS makes this + * independent of numpy. + */ +#define CONFIG_TESTS +#include + +#define PREFIX + +#define RETTYPE int + +#define INIT_FUNC() int ret = 1 + +#define TEST_FAILED(func, xr, xi, er, ei, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j: received: " FMT " + " FMT "j\n", __LINE__, xr, xi, \ + er, ei, rr, ri); \ + } \ + while(0) + +#define TEST_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j\n", __LINE__, xr, xi, er1, ei1, er2, ei2, rr, ri); \ + } \ + while(0) + +#define TEST_FAILED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j or " FMT " + " FMT "j or " \ + FMT " + " FMT "j: received: " FMT " + " FMT "j\n", __LINE__, \ + xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri); \ + } \ + while(0) + +#define TEST_RAISES_FAILED(func, xr, xi, er, ei, rr, ri, efpe, rfpe) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j: received: " FMT " + " FMT "j, required FPE: %d, " \ + "recieved: %d\n", __LINE__, xr, xi, er, ei, rr, ri, efpe, rfpe); \ + } \ + while(0) + +#define TEST_RAISES_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j, required FPE: %d, recieved: %d\n", __LINE__, xr, xi, er1, \ + ei1, er2, ei2, rr, ri, efpe, rfpe); \ + } \ + while(0) + +#define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j, " FMT " + " FMT \ + "j): expected: " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j\n", __LINE__, dxr, dxi, dyr, dyi, der, dei, rr, ri); \ + } \ + while(0) + +#define TEST_BRANCH_CUT_FAILED(func, vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero) \ + do { \ + ret = 0; \ + printf(STRINGIZE(func) ": branch cut failure: x = " FMT " + " FMT \ + "j, dx = " FMT " + " FMT "j, rsign = %d, isign = %d, " \ + "check_sign_zero = %d\n", vxr, vxi, vdxr, vdxi, vrsign, visign, \ + vcksignzero); \ + } \ + while(0) + +#define TEST_NEAR_CROSSOVER_FAILED(fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat)\ + do { \ + ret = 0; \ + printf("%s: Loss of precision: j = %d, k = %d\nzp = (" FMT " + " FMT \ + "j) -> (" FMT " + " FMT "j)\nzm = (" FMT " + " FMT "j) -> (" FMT \ + " + " FMT "j)\ndiff = " FMT ", exact match = %d\n", fname, j, k, \ + zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat); \ + } \ + while(0) + +#define TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio) \ + do { \ + ret = 0; \ + printf("%s: Loss of precision vs real:\nx = " FMT "\nratio = " FMT "\n", \ + fname, x, ratio); \ + } \ + while(0) + +#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ + do { \ + if (!ADDSUFFIX(check_loss_of_precision)(FUNC(cfunc), FUNC(rfunc), real, \ + STRINGIZE(FUNC(cfunc)))) { \ + ret = 0; \ + } \ + if (!ADDSUFFIX(check_near_crossover)(FUNC(cfunc), STRINGIZE(FUNC(cfunc)))) { \ + ret = 0; \ + } \ + } \ + while(0) + +#endif + +#define TEST_INT(func, xr, xi, er, ei, rtest, itest) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der = er; \ + TYPE dei = ei; \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!(ADDSUFFIX(rtest)(rr, der) && ADDSUFFIX(itest)(ri, dei))) { \ + TEST_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_EE(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isequal, isequal) + +#define TEST_EC(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isequal, isclose) + +#define TEST_CE(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isclose, isequal) + +#define TEST_CC(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isclose, isclose) + +#define TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, rtest, itest) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!((ADDSUFFIX(rtest)(rr, der1) && ADDSUFFIX(itest)(ri, dei1)) || \ + (ADDSUFFIX(rtest)(rr, der2) && ADDSUFFIX(itest)(ri, dei2)))) { \ + TEST_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri);\ + } \ + } \ + while(0) + +#define TEST_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isequal, isequal) \ + +#define TEST_UNSPECIFIED2_CE(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isclose, isequal) \ + +#define TEST_UNSPECIFIED2_EC(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isequal, isclose) \ + +#define TEST_UNSPECIFIED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4)\ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + TYPE der3 = er3; \ + TYPE dei3 = ei3; \ + TYPE der4 = er4; \ + TYPE dei4 = ei4; \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!((ADDSUFFIX(isequal)(rr, der1) && ADDSUFFIX(isequal)(ri, dei1)) ||\ + (ADDSUFFIX(isequal)(rr, der2) && ADDSUFFIX(isequal)(ri, dei2)) ||\ + (ADDSUFFIX(isequal)(rr, der3) && ADDSUFFIX(isequal)(ri, dei3)) ||\ + (ADDSUFFIX(isequal)(rr, der4) && ADDSUFFIX(isequal)(ri, dei4)))){\ + TEST_FAILED4(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, der3, dei3, der4, dei4, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_CPOW_INT(xr, xi, yr, yi, er, ei, test) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE dyr = yr; \ + TYPE dyi = yi; \ + TYPE der = er; \ + TYPE dei = ei; \ + CTYPE x = FUNC(cpack)(xr, xi); \ + CTYPE y = FUNC(cpack)(yr, yi); \ + CTYPE r = FUNC(cpow)(x, y); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!(ADDSUFFIX(test)(rr, der) && ADDSUFFIX(test)(ri, dei))) { \ + TEST_CPOW_FAILED(FUNC(cpow), dxr, dxi, dyr, dyi, der, dei, rr, ri);\ + } \ + } \ + while(0) + +#define TEST_CPOW_EE(xr, xi, yr, yi, er, ei) \ + TEST_CPOW_INT(xr, xi, yr, yi, er, ei, isequal) + +#define TEST_CPOW_CC(xr, xi, yr, yi, er, ei) \ + TEST_CPOW_INT(xr, xi, yr, yi, er, ei, isclose) + +#define TEST_RAISES(func, xr, xi, er, ei, fpe) \ + do { \ + int except; \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der = er; \ + TYPE dei = ei; \ + CTYPE r; \ + CTYPE x = FUNC(cpack)(xr, xi); \ + TYPE rr, ri; \ + npy_clear_floatstatus(); \ + r = FUNC(func)(x); \ + except = npy_get_floatstatus(); \ + rr = FUNC(creal)(r); \ + ri = FUNC(cimag)(r); \ + if (!(except & fpe && ADDSUFFIX(isequal)(rr, der) \ + && ADDSUFFIX(isequal)(ri, dei))) { \ + TEST_RAISES_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri, fpe, except); \ + } \ + } \ + while(0) + +#define TEST_RAISES_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2, fpe) \ + do { \ + int except; \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + CTYPE r; \ + CTYPE x = FUNC(cpack)(xr, xi); \ + TYPE rr, ri; \ + npy_clear_floatstatus(); \ + r = FUNC(func)(x); \ + except = npy_get_floatstatus(); \ + rr = FUNC(creal)(r); \ + ri = FUNC(cimag)(r); \ + if (!(except & fpe && \ + ((ADDSUFFIX(isequal)(rr, der1) && ADDSUFFIX(isequal)(ri, dei1)) || \ + (ADDSUFFIX(isequal)(rr, der2) && ADDSUFFIX(isequal)(ri, dei2))))) {\ + TEST_RAISES_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri, fpe, except);\ + } \ + } \ + while(0) + +#define TEST_BRANCH_CUT(func, xr, xi, dxr, dxi, rsign, isign, cksignzero) \ + do { \ + TYPE vxr = xr; \ + TYPE vxi = xi; \ + TYPE vdxr = dxr; \ + TYPE vdxi = dxi; \ + int vrsign = rsign; \ + int visign = isign; \ + int vcksignzero = cksignzero; \ + int q = ADDSUFFIX(check_branch_cut)(FUNC(func), vxr, vxi, vdxr, vdxi, \ + vrsign, visign, vcksignzero); \ + if (!q) { \ + TEST_BRANCH_CUT_FAILED(FUNC(func), vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero); \ + } \ + } \ + while(0) + +CTYPE ADDSUFFIX(cpack)(TYPE r, TYPE i) +{ + union { + CTYPE z; + TYPE a[2]; + } z1; + z1.a[0] = r; + z1.a[1] = i; + return z1.z; +} + +static int ADDSUFFIX(isclose)(TYPE a, TYPE b) +{ + const TYPE atol = CLOSE_ATOL; + const TYPE rtol = CLOSE_RTOL; + + if (ADDPREFIX(isfinite)(a) && ADDPREFIX(isfinite)(b)) { + return (FUNC(fabs)(a - b) <= (atol + rtol*FUNC(fabs)(b))); + } + return 0; +} + +static int ADDSUFFIX(isequal)(TYPE a, TYPE b) +{ + if (ADDPREFIX(isfinite)(a) && ADDPREFIX(isfinite)(b)) { + if (a == 0 && b == 0) { + TYPE signa = FUNC(copysign)(1.0, a); + TYPE signb = FUNC(copysign)(1.0, b); + return signa == signb; + } + else { + return a == b; + } + } + else if (ADDPREFIX(isnan)(a) && ADDPREFIX(isnan)(b)) { + return 1; + } + else {/* infs */ + return a == b; + } +} + +#if defined(CACOS) || defined(CASIN) || defined(CATAN) || defined(CACOSH) || defined(CASINH) || defined(CATANH) || defined(CLOG) || defined(CSQRT) +typedef CTYPE (*ADDSUFFIX(complexfunc))(CTYPE); +typedef TYPE (*ADDSUFFIX(realfunc))(TYPE); + +static int ADDSUFFIX(check_branch_cut)(ADDSUFFIX(complexfunc) cfunc, TYPE x0r, TYPE x0i, TYPE dxr, TYPE dxi, + int re_sign, int im_sign, int sig_zero_ok) +{ + const TYPE scale = EPS * BRANCH_SCALE; + const TYPE atol = BRANCH_ATOL; + + TYPE scale2 = FUNC(cabs)(FUNC(cpack)(x0r, x0i)) / FUNC(cabs)(FUNC(cpack)(dxr, dxi)); + + TYPE shiftr = dxr*scale*scale2; + TYPE shifti = dxi*scale*scale2; + CTYPE y0 = cfunc(FUNC(cpack)(x0r, x0i)); + CTYPE yp = cfunc(FUNC(cpack)(x0r + shiftr, x0i + shifti)); + CTYPE ym = cfunc(FUNC(cpack)(x0r - shiftr, x0i - shifti)); + CTYPE x0; + + TYPE y0r, y0i, ypr, ypi, ymr, ymi; + + y0r = FUNC(creal)(y0); + y0i = FUNC(cimag)(y0); + ypr = FUNC(creal)(yp); + ypi = FUNC(cimag)(yp); + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); + + if (FUNC(fabs)(y0r - ypr) >= atol) + return 0; + if (FUNC(fabs)(y0i - ypi) >= atol) + return 0; + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + + if (sig_zero_ok) { + if (x0r == 0 && dxr != 0) { + x0 = FUNC(cpack)(NZERO, x0i); + ym = cfunc(x0); + + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + } + else if (x0i == 0 && dxi != 0) { + x0 = FUNC(cpack)(x0r, NZERO); + ym = cfunc(x0); + + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + } + } + return 1; +} +#endif + +#if defined(CASIN) || defined(CATAN) || defined(CASINH) || defined(CATANH) +static RETTYPE ADDSUFFIX(check_near_crossover)(ADDSUFFIX(complexfunc) cfunc, const char* fname) +{ + const TYPE x = 1e-3; + const int rpnt[] = {-1, -1, -1, 0, 0, 1, 1, 1}; + const int ipnt[] = {-1, 0, 1, -1, 1, -1, 0, -1}; + const int npnt = sizeof(rpnt) / sizeof(int); + const int dr[] = {1, 0, 1}; + const int di[] = {0, 1, 1}; + const int ndr = sizeof(dr) / sizeof(int); + int k, j; + int equal; + TYPE drj, dij, diff; + CTYPE zp, zm, czp, czm; + INIT_FUNC(); + + for (j = 0; j < ndr; j++) { + drj = 2 * x * dr[j] * EPS; + dij = 2 * x * di[j] * EPS; + for (k = 0; k < npnt; k++) { + zp = FUNC(cpack)(x*rpnt[k] + drj, x*ipnt[k] + dij); + zm = FUNC(cpack)(x*rpnt[k] - drj, x*ipnt[k] - dij); + + czp = cfunc(zp); + czm = cfunc(zm); + + diff = FUNC(cabs)(FUNC(cpack)(FUNC(creal)(czp) - FUNC(creal)(czm),\ + FUNC(cimag)(czp) - FUNC(cimag)(czm))); + equal = (FUNC(creal)(czp) == FUNC(creal)(czm)) && (FUNC(cimag)(czp) == FUNC(cimag)(czm)); + if ( diff > 2*EPS || equal) { + TEST_NEAR_CROSSOVER_FAILED(fname, j, k, FUNC(creal)(zp), \ + FUNC(cimag)(zp), FUNC(creal)(czp), FUNC(cimag)(czp), \ + FUNC(creal)(zm), FUNC(cimag)(zm), FUNC(creal)(czm), \ + FUNC(cimag)(czm), diff, equal); + } + } + } + return ret; +} + +static int ADDSUFFIX(clp_internal)(ADDSUFFIX(complexfunc) cfunc, ADDSUFFIX(realfunc) rfunc, int real, TYPE x) +{ + TYPE num = rfunc(x); + TYPE den; + CTYPE z; + + if (real == 1) { + z = FUNC(cpack)(x, 0); + z = cfunc(z); + den = FUNC(creal)(z); + } + else { + z = FUNC(cpack)(0, x); + z = cfunc(z); + den = FUNC(cimag)(z); + } + return FUNC(fabs)(num/den - 1); +} + +static RETTYPE ADDSUFFIX(check_loss_of_precision)(ADDSUFFIX(complexfunc) cfunc, ADDSUFFIX(realfunc) rfunc, int real, + const char* fname) +{ + const int n_series = 200; + const int n_basic = 10; + const TYPE rtol = 2*EPS; + + const TYPE xsb = -20; + const TYPE xse = -3.001; + const TYPE dxs = (xse - xsb) / n_series; + + const TYPE xbb = -2.999; + const TYPE xbe = 0; + const TYPE dxb = (xbe - xbb) / n_basic; + + TYPE x, ratio; + int k; + INIT_FUNC(); + + for(k = 0; k < n_series; k++) { + x = FUNC(pow)(10.0, xsb + k*dxs); + ratio = ADDSUFFIX(clp_internal)(cfunc, rfunc, real, x); + if (ratio > rtol) { + TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio); + } + } + + for(k = 0; k < n_basic; k++) { + x = FUNC(pow)(10.0, xbb + k*dxb); + ratio = ADDSUFFIX(clp_internal)(cfunc, rfunc, real, x); + if (ratio > rtol) { + TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio); + } + } + return ret; +} +#endif + +#ifdef CACOS +RETTYPE ADDSUFFIX(test_cacos)(void) +{ + INIT_FUNC(); + /* cacos(conj(z)) = conj(cacos(z)) */ + TEST_CE(cacos, 0, 0, ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(cacos, 0, NZERO, ADDSUFFIX(NPY_PI_2), 0); + + TEST_CE(cacos, NZERO, 0, ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(cacos, NZERO, NZERO, ADDSUFFIX(NPY_PI_2), 0); + + TEST_CE(cacos, 0, NAN, ADDSUFFIX(NPY_PI_2), NAN); + TEST_CE(cacos, NZERO, NAN, ADDSUFFIX(NPY_PI_2), NAN); + + TEST_CE(cacos, 2.0, INFINITY, ADDSUFFIX(NPY_PI_2), -INFINITY); + TEST_CE(cacos, 2.0, -INFINITY, ADDSUFFIX(NPY_PI_2), INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(cacos, 2.0, NAN, NAN, NAN); + + TEST_CE(cacos, -INFINITY, 2.0, ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, -INFINITY, -2.0, ADDSUFFIX(NPY_PI), INFINITY); + + TEST_EE(cacos, INFINITY, 2.0, 0, -INFINITY); + TEST_EE(cacos, INFINITY, -2.0, 0, INFINITY); + + TEST_CE(cacos, -INFINITY, INFINITY, 0.75 * ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, -INFINITY, -INFINITY, 0.75 * ADDSUFFIX(NPY_PI), INFINITY); + + TEST_CE(cacos, INFINITY, INFINITY, 0.25 * ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * ADDSUFFIX(NPY_PI), INFINITY); + + /* sign of imaginary part is unspecified. */ + TEST_UNSPECIFIED2(cacos, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(cacos, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(cacos, NAN, 2.0, NAN, NAN); + TEST_EE(cacos, NAN, -2.0, NAN, NAN); + + TEST_EE(cacos, NAN, INFINITY, NAN, -INFINITY); + TEST_EE(cacos, NAN, -INFINITY, NAN, INFINITY); + + TEST_EE(cacos, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(cacos, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacos, 2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacos, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacos, 0, 2, 1, 0, 1, 1, 1); + + TEST_CC(cacos, 0.5, 0.0, ADDSUFFIX(acos)(0.5), 0.0); + + return ret; +} +#endif + +#ifdef CASIN +RETTYPE ADDSUFFIX(test_casin)(void) +{ + INIT_FUNC(); + + /* casin(conj(z)) = conj(casin(z)) and casin is odd */ + TEST_EE(casin, 0, 0, 0, 0); + TEST_EE(casin, 0, NZERO, 0, NZERO); + TEST_EE(casin, NZERO, 0, NZERO, 0); + TEST_EE(casin, NZERO, NZERO, NZERO, NZERO); + + TEST_CE(casin, -INFINITY, 2.0, -ADDSUFFIX(NPY_PI_2), INFINITY); + TEST_CE(casin, INFINITY, 2.0, ADDSUFFIX(NPY_PI_2), INFINITY); + TEST_CE(casin, -INFINITY, -2.0, -ADDSUFFIX(NPY_PI_2), -INFINITY); + TEST_CE(casin, INFINITY, -2.0, ADDSUFFIX(NPY_PI_2), -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(casin, NAN, -2.0, NAN, NAN); + TEST_EE(casin, NAN, 2.0, NAN, NAN); + + TEST_EE(casin, -2.0, INFINITY, NZERO, INFINITY); + TEST_EE(casin, 2.0, INFINITY, 0, INFINITY); + TEST_EE(casin, -2.0, -INFINITY, NZERO, -INFINITY); + TEST_EE(casin, 2.0, -INFINITY, 0, -INFINITY); + + TEST_CE(casin, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI), INFINITY); + TEST_CE(casin, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI), INFINITY); + TEST_CE(casin, -INFINITY, -INFINITY, -0.25*ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(casin, INFINITY, -INFINITY, 0.25*ADDSUFFIX(NPY_PI), -INFINITY); + + TEST_EE(casin, NAN, INFINITY, NAN, INFINITY); + TEST_EE(casin, NAN, -INFINITY, NAN, -INFINITY); + + TEST_EE(casin, 0, NAN, 0, NAN); + TEST_EE(casin, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(casin, -2.0, NAN, NAN, NAN); + TEST_EE(casin, 2.0, NAN, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(casin, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(casin, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(casin, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(casin, asin, 0); + + TEST_CC(casin, 1e-5, 1e-5, 9.999999999666666667e-6, 1.0000000000333333333e-5); + + TEST_BRANCH_CUT(casin, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(casin, 2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(casin, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(casin, 0, 2, 1, 0, 1, 1, 1); + + TEST_CC(casin, 0.5, 0, FUNC(asin)(0.5), 0); + + return ret; +} +#endif + +#ifdef CATAN +RETTYPE ADDSUFFIX(test_catan)(void) +{ + INIT_FUNC(); + /* catan(conj(z)) = conj(catan(z)) and catan is odd */ + TEST_EE(catan, 0, 0, 0, 0); + TEST_EE(catan, 0, NZERO, 0, NZERO); + TEST_EE(catan, NZERO, 0, NZERO, 0); + TEST_EE(catan, NZERO, NZERO, NZERO, NZERO); + + TEST_EE(catan, NAN, 0, NAN, 0); + TEST_EE(catan, NAN, NZERO, NAN, NZERO); + + TEST_RAISES(catan, NZERO, 1, NZERO, INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, 0, 1, 0, INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, NZERO, -1, NZERO, -INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, 0, -1, 0, -INFINITY, NPY_FPE_DIVIDEBYZERO); + + TEST_CE(catan, -INFINITY, 2.0, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, INFINITY, 2.0, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -INFINITY, -2.0, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, INFINITY, -2.0, ADDSUFFIX(NPY_PI_2), NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(catan, NAN, -2.0, NAN, NAN); + TEST_EE(catan, NAN, 2.0, NAN, NAN); + + TEST_CE(catan, -2.0, INFINITY, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, 2.0, INFINITY, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -2.0, -INFINITY, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, 2.0, -INFINITY, ADDSUFFIX(NPY_PI_2), NZERO); + + TEST_CE(catan, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -INFINITY, -INFINITY, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, INFINITY, -INFINITY, ADDSUFFIX(NPY_PI_2), NZERO); + + TEST_EE(catan, NAN, INFINITY, NAN, 0); + TEST_EE(catan, NAN, -INFINITY, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(catan, -2.0, NAN, NAN, NAN); + TEST_EE(catan, 2.0, NAN, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2_CE(catan, -INFINITY, NAN, -ADDSUFFIX(NPY_PI_2), 0, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_UNSPECIFIED2_CE(catan, INFINITY, NAN, ADDSUFFIX(NPY_PI_2), 0, ADDSUFFIX(NPY_PI_2), NZERO); + + TEST_EE(catan, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(catan, atan, 0); + + TEST_CC(catan, 1e-5, 1e-5, 1.000000000066666666e-5, 9.999999999333333333e-6); + + TEST_BRANCH_CUT(catan, 0, -2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(catan, 0, 2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(catan, -2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(catan, 2, 0, 0, 1, 1, 1, 1); + + TEST_CC(catan, 0.5, 0, FUNC(atan)(0.5), 0); + + return ret; +} +#endif + +#ifdef CACOSH +RETTYPE ADDSUFFIX(test_cacosh)(void) +{ + INIT_FUNC(); + /* cacosh(conj(z)) = conj(cacosh(z)) */ + TEST_EC(cacosh, 0, 0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, 0, NZERO, 0, -ADDSUFFIX(NPY_PI_2)); + + TEST_EC(cacosh, NZERO, 0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, NZERO, NZERO, 0, -ADDSUFFIX(NPY_PI_2)); + + TEST_EC(cacosh, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); + + /* can raise FE_INVALID or not */ + TEST_EE(cacosh, 2.0, NAN, NAN, NAN); + + TEST_EC(cacosh, -INFINITY, 2.0, INFINITY, ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, -INFINITY, -2.0, INFINITY, -ADDSUFFIX(NPY_PI)); + + TEST_EE(cacosh, INFINITY, 2.0, INFINITY, 0); + TEST_EE(cacosh, INFINITY, -2.0, INFINITY, NZERO); + + TEST_EC(cacosh, -INFINITY, INFINITY, INFINITY, 0.75*ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, -INFINITY, -INFINITY, INFINITY, -0.75*ADDSUFFIX(NPY_PI)); + + TEST_EC(cacosh, INFINITY, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, INFINITY, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI)); + + TEST_EE(cacosh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(cacosh, -INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(cacosh, NAN, 2.0, NAN, NAN); + TEST_EE(cacosh, NAN, -2.0, NAN, NAN); + + TEST_EE(cacosh, NAN, INFINITY, INFINITY, NAN); + TEST_EE(cacosh, NAN, -INFINITY, INFINITY, NAN); + + TEST_EE(cacosh, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(cacosh, -1, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacosh, 0.5, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacosh, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacosh, 0, 2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacosh, 2, 0, 0, 1, 1, 1, 1); + + TEST_CC(cacosh, 1.5, 0, FUNC(acosh)(1.5), 0); + return ret; +} +#endif + +#ifdef CASINH +RETTYPE ADDSUFFIX(test_casinh)(void) +{ + INIT_FUNC(); + /* casinh(conj(z)) = conj(casinh(z)) and casinh is odd */ + TEST_EE(casinh, 0, 0, 0, 0); + TEST_EE(casinh, 0, NZERO, 0, NZERO); + TEST_EE(casinh, NZERO, 0, NZERO, 0); + TEST_EE(casinh, NZERO, NZERO, NZERO, NZERO); + + TEST_EC(casinh, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, -2.0, INFINITY, -INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, -2.0, -INFINITY, -INFINITY, -ADDSUFFIX(NPY_PI_2)); + + /* can raise FE_INVALID or not */ + TEST_EE(casinh, 2.0, NAN, NAN, NAN); + TEST_EE(casinh, -2.0, NAN, NAN, NAN); + + TEST_EE(casinh, INFINITY, 2.0, INFINITY, 0); + TEST_EE(casinh, INFINITY, -2.0, INFINITY, NZERO); + TEST_EE(casinh, -INFINITY, 2.0, -INFINITY, 0); + TEST_EE(casinh, -INFINITY, -2.0, -INFINITY, NZERO); + + TEST_EC(casinh, INFINITY, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, INFINITY, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, -INFINITY, INFINITY, -INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, -INFINITY, -INFINITY, -INFINITY, -0.25*ADDSUFFIX(NPY_PI)); + + TEST_EE(casinh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(casinh, -INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(casinh, NAN, 0, NAN, 0); + TEST_EE(casinh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(casinh, NAN, 2.0, NAN, NAN); + TEST_EE(casinh, NAN, -2.0, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(casinh, NAN, INFINITY, INFINITY, NAN, -INFINITY, NAN); + TEST_UNSPECIFIED2(casinh, NAN, -INFINITY, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(casinh, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(casinh, asinh, 1); + + TEST_CC(casinh, 1e-5, 1e-5, 1.0000000000333333333e-5, 9.999999999666666667e-6); + + TEST_BRANCH_CUT(casinh, 0, -2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(casinh, 0, 2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(casinh, -2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(casinh, 2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(casinh, 0, 0, 1, 0, 1, 1, 1); + + TEST_CC(casinh, 0.5, 0, FUNC(asinh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CATANH +RETTYPE ADDSUFFIX(test_catanh)(void) +{ + INIT_FUNC(); + /* catanh(conj(z)) = conj(catanh(z)) and catanh is odd */ + TEST_EE(catanh, 0, 0, 0, 0); + TEST_EE(catanh, 0, NZERO, 0, NZERO); + TEST_EE(catanh, NZERO, 0, NZERO, 0); + TEST_EE(catanh, NZERO, NZERO, NZERO, NZERO); + + TEST_EE(catanh, 0, NAN, 0, NAN); + TEST_EE(catanh, NZERO, NAN, NZERO, NAN); + + TEST_RAISES(catanh, 1, 0, INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, 1, NZERO, INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, -1, 0, -INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, -1, NZERO, -INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); + + TEST_EC(catanh, 2.0, INFINITY, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, 2.0, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -2.0, INFINITY, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -2.0, -INFINITY, NZERO, -ADDSUFFIX(NPY_PI_2)); + + /* can raise FE_INVALID or not */ + TEST_EE(catanh, 2.0, NAN, NAN, NAN); + TEST_EE(catanh, -2.0, NAN, NAN, NAN); + + TEST_EC(catanh, INFINITY, 2.0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, INFINITY, -2.0, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, 2.0, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, -2.0, NZERO, -ADDSUFFIX(NPY_PI_2)); + + TEST_EC(catanh, INFINITY, INFINITY, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, INFINITY, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, INFINITY, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, -INFINITY, NZERO, -ADDSUFFIX(NPY_PI_2)); + + TEST_EE(catanh, INFINITY, NAN, 0, NAN); + TEST_EE(catanh, -INFINITY, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(catanh, NAN, 2.0, NAN, NAN); + TEST_EE(catanh, NAN, -2.0, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2_EC(catanh, NAN, INFINITY, 0, ADDSUFFIX(NPY_PI_2), NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_UNSPECIFIED2_EC(catanh, NAN, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2), NZERO, -ADDSUFFIX(NPY_PI_2)); + + TEST_EE(catanh, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(catanh, atanh, 1); + + TEST_CC(catanh, 1e-5, 1e-5, 9.999999999333333333e-6, 1.000000000066666666e-5); + + TEST_BRANCH_CUT(catanh, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(catanh, 2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(catanh, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(catanh, 0, 2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(catanh, 0, 0, 0, 1, 1, 1, 1); + + TEST_CC(catanh, 0.5, 0, FUNC(atanh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CCOS +RETTYPE ADDSUFFIX(test_ccos)(void) +{ + INIT_FUNC(); + /* ccos(conj(z)) = conj(ccos(z)) and ccos is even */ + TEST_EE(ccos, NZERO, 0, 1, 0); + TEST_EE(ccos, 0, 0, 1, NZERO); + TEST_EE(ccos, NZERO, NZERO, 1, NZERO); + TEST_EE(ccos, 0, NZERO, 1, 0); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, 0, NAN, 0, \ + NAN, NZERO, NPY_FPE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccos, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccos, NAN, NZERO, NAN, 0, NAN, NZERO); + + TEST_RAISES(ccos, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ccos, NAN, 2.0, NAN, NAN); + TEST_EE(ccos, NAN, -2.0, NAN, NAN); + + TEST_EE(ccos, NZERO, INFINITY, INFINITY, 0); + TEST_EE(ccos, 0, INFINITY, INFINITY, NZERO); + TEST_EE(ccos, NZERO, -INFINITY, INFINITY, NZERO); + TEST_EE(ccos, 0, -INFINITY, INFINITY, 0); + + TEST_EE(ccos, -1.0, INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, 1.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, -1.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, 1.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, -2.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, 2.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, -2.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, 2.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, -4.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, 4.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, -4.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, 4.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, -5.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, 5.0, INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, -5.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, 5.0, -INFINITY, INFINITY, -INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, NPY_FPE_INVALID); + + TEST_EE(ccos, NAN, INFINITY, INFINITY, NAN); + TEST_EE(ccos, NAN, -INFINITY, INFINITY, NAN); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccos, 0, NAN, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccos, NZERO, NAN, NAN, 0, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ccos, -2.0, NAN, NAN, NAN); + TEST_EE(ccos, 2.0, NAN, NAN, NAN); + + TEST_EE(ccos, NAN, NAN, NAN, NAN); + + TEST_CC(ccos, 0.5, 0, FUNC(cos)(0.5), 0); + + return ret; +} +#endif + +#ifdef CSIN +RETTYPE ADDSUFFIX(test_csin)(void) +{ + INIT_FUNC(); + /* csin(conj(z)) = conj(csin(z)) and csin is odd */ + TEST_EE(csin, 0, 0, 0, 0); + TEST_EE(csin, 0, NZERO, 0, NZERO); + TEST_EE(csin, NZERO, 0, NZERO, 0); + TEST_EE(csin, NZERO, NZERO, NZERO, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, 0, NAN, 0, \ + NAN, NZERO, NPY_FPE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csin, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(csin, NAN, NZERO, NAN, 0, NAN, NZERO); + + TEST_RAISES(csin, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(csin, NAN, 2.0, NAN, NAN); + TEST_EE(csin, NAN, -2.0, NAN, NAN); + + TEST_EE(csin, NZERO, INFINITY, NZERO, INFINITY); + TEST_EE(csin, 0, INFINITY, 0, INFINITY); + TEST_EE(csin, NZERO, -INFINITY, NZERO, -INFINITY); + TEST_EE(csin, 0, -INFINITY, 0, -INFINITY); + + TEST_EE(csin, -1.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, 1.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csin, -1.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, 1.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, -2.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, 2.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, -2.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, 2.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(csin, -4.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, 4.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, -4.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(csin, 4.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, -5.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csin, 5.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, -5.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, 5.0, -INFINITY, -INFINITY, -INFINITY); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, INFINITY, NAN, INFINITY, \ + NAN, -INFINITY, NPY_FPE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csin, NAN, INFINITY, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(csin, NAN, -INFINITY, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(csin, 0, NAN, 0, NAN); + TEST_EE(csin, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(csin, -2.0, NAN, NAN, NAN); + TEST_EE(csin, 2.0, NAN, NAN, NAN); + + TEST_EE(csin, NAN, NAN, NAN, NAN); + + TEST_CC(csin, 0.5, 0, FUNC(sin)(0.5), 0); + + return ret; +} +#endif + +#ifdef CTAN +RETTYPE ADDSUFFIX(test_ctan)(void) +{ + INIT_FUNC(); + /* ctan(conj(z)) = conj(ctan(z)) and ctan is odd */ + TEST_EE(ctan, 0, 0, 0, 0); + TEST_EE(ctan, 0, NZERO, 0, NZERO); + TEST_EE(ctan, NZERO, 0, NZERO, 0); + TEST_EE(ctan, NZERO, NZERO, NZERO, NZERO); + + TEST_RAISES(ctan, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ctan, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ctan, NAN, 2.0, NAN, NAN); + TEST_EE(ctan, NAN, -2.0, NAN, NAN); + + TEST_EE(ctan, -1.0, INFINITY, NZERO, 1.0); + TEST_EE(ctan, 1.0, INFINITY, 0, 1.0); + TEST_EE(ctan, -1.0, -INFINITY, NZERO, -1.0); + TEST_EE(ctan, 1.0, -INFINITY, 0, -1.0); + TEST_EE(ctan, -2.0, INFINITY, 0, 1); + TEST_EE(ctan, 2.0, INFINITY, NZERO, 1); + TEST_EE(ctan, -2.0, -INFINITY, 0, -1); + TEST_EE(ctan, 2.0, -INFINITY, NZERO, -1); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(ctan, INFINITY, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, -INFINITY, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, INFINITY, -INFINITY, 0, -1, NZERO, -1); + TEST_UNSPECIFIED2(ctan, -INFINITY, -INFINITY, 0, -1, NZERO, -1); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(ctan, NAN, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, NAN, -INFINITY, 0, -1, NZERO, -1); + + TEST_EE(ctan, 0, NAN, 0, NAN); + TEST_EE(ctan, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(ctan, 2.0, NAN, NAN, NAN); + TEST_EE(ctan, -2.0, NAN, NAN, NAN); + + TEST_EE(ctan, NAN, NAN, NAN, NAN); + + TEST_CC(ctan, 0.5, 0, FUNC(tan)(0.5), 0); + + TEST_CC(ctan, 0, 1000, 0, 1); + TEST_CC(ctan, 0, -1000, 0, -1); + + return ret; +} +#endif + +#ifdef CCOSH +RETTYPE ADDSUFFIX(test_ccosh)(void) +{ + INIT_FUNC(); + /* ccosh(conj(z)) = conj(ccosh(z)) and ccosh is even */ + TEST_EE(ccosh, 0, 0, 1, 0); + TEST_EE(ccosh, 0, NZERO, 1, NZERO); + TEST_EE(ccosh, NZERO, 0, 1, NZERO); + TEST_EE(ccosh, NZERO, NZERO, 1, 0); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccosh, 0, INFINITY, NAN, 0, \ + NAN, NZERO, NPY_FPE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccosh, 0, NAN, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccosh, NZERO, NAN, NAN, 0, NAN, NZERO); + + TEST_RAISES(ccosh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, -2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ccosh, 2.0, NAN, NAN, NAN); + TEST_EE(ccosh, -2.0, NAN, NAN, NAN); + + TEST_EE(ccosh, INFINITY, 0, INFINITY, 0); + TEST_EE(ccosh, INFINITY, NZERO, INFINITY, NZERO); + TEST_EE(ccosh, -INFINITY, 0, INFINITY, NZERO); + TEST_EE(ccosh, -INFINITY, NZERO, INFINITY, 0); + + TEST_EE(ccosh, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, 1.0, INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, -1.0, INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, 2.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, -2.0, -INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, 4.0, -INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, -4.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, -5.0, INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, 5.0, INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, -5.0, INFINITY, -INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccosh, INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, NPY_FPE_INVALID); + + TEST_EE(ccosh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(ccosh, -INFINITY, NAN, INFINITY, NAN); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccosh, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccosh, NAN, NZERO, NAN, 0, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ccosh, NAN, 2.0, NAN, NAN); + TEST_EE(ccosh, NAN, -2.0, NAN, NAN); + + TEST_EE(ccosh, NAN, NAN, NAN, NAN); + + TEST_CC(ccosh, 0.5, 0, FUNC(cosh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CSINH +RETTYPE ADDSUFFIX(test_csinh)(void) +{ + INIT_FUNC(); + /* csinh(conj(z)) = conj(csinh(z)) and csinh is odd */ + TEST_EE(csinh, 0, 0, 0, 0); + TEST_EE(csinh, 0, NZERO, 0, NZERO); + TEST_EE(csinh, NZERO, 0, NZERO, 0); + TEST_EE(csinh, NZERO, NZERO, NZERO, NZERO); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csinh, 0, INFINITY, 0, NAN, \ + NZERO, NAN, NPY_FPE_INVALID); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(csinh, 0, NAN, 0, NAN, NZERO, NAN); + TEST_UNSPECIFIED2(csinh, NZERO, NAN, 0, NAN, NZERO, NAN); + + TEST_RAISES(csinh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, -2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(csinh, 2.0, NAN, NAN, NAN); + TEST_EE(csinh, -2.0, NAN, NAN, NAN); + + TEST_EE(csinh, INFINITY, 0, INFINITY, 0); + TEST_EE(csinh, INFINITY, NZERO, INFINITY, NZERO); + TEST_EE(csinh, -INFINITY, 0, -INFINITY, 0); + TEST_EE(csinh, -INFINITY, NZERO, -INFINITY, NZERO); + + TEST_EE(csinh, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, 1.0, -INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, -1.0, -INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, 2.0, INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, -2.0, INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, 4.0, INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, -4.0, INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, -5.0, INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, 5.0, -INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, -5.0, -INFINITY, INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csinh, INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, NPY_FPE_INVALID); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(csinh, INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + TEST_UNSPECIFIED2(csinh, -INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(csinh, NAN, 0, NAN, 0); + TEST_EE(csinh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(csinh, NAN, 2.0, NAN, NAN); + TEST_EE(csinh, NAN, -2.0, NAN, NAN); + + TEST_EE(csinh, NAN, NAN, NAN, NAN); + + TEST_CC(csinh, 0.5, 0, FUNC(sinh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CTANH +RETTYPE ADDSUFFIX(test_ctanh)(void) +{ + INIT_FUNC(); + /* ctanh(conj(z)) = conj(ctanh(z)) and ctanh is odd */ + TEST_EE(ctanh, 0, 0, 0, 0); + TEST_EE(ctanh, 0, NZERO, 0, NZERO); + TEST_EE(ctanh, NZERO, 0, NZERO, 0); + TEST_EE(ctanh, NZERO, NZERO, NZERO, NZERO); + + TEST_RAISES(ctanh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ctanh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ctanh, 2.0, NAN, NAN, NAN); + TEST_EE(ctanh, -2.0, NAN, NAN, NAN); + + TEST_EE(ctanh, INFINITY, 1.0, 1.0, 0); + TEST_EE(ctanh, INFINITY, -1.0, 1.0, NZERO); + TEST_EE(ctanh, -INFINITY, 1.0, -1.0, 0); + TEST_EE(ctanh, -INFINITY, -1.0, -1.0, NZERO); + TEST_EE(ctanh, INFINITY, 2.0, 1.0, NZERO); + TEST_EE(ctanh, INFINITY, -2.0, 1.0, 0); + TEST_EE(ctanh, -INFINITY, 2.0, -1.0, NZERO); + TEST_EE(ctanh, -INFINITY, -2.0, -1.0, 0); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ctanh, INFINITY, INFINITY, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, INFINITY, -INFINITY, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, INFINITY, -1, 0, -1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, -INFINITY, -1, 0, -1, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ctanh, INFINITY, NAN, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, NAN, -1, 0, -1, NZERO); + + TEST_EE(ctanh, NAN, 0, NAN, 0); + TEST_EE(ctanh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ctanh, NAN, 2.0, NAN, NAN); + TEST_EE(ctanh, NAN, -2.0, NAN, NAN); + + TEST_EE(ctanh, NAN, NAN, NAN, NAN); + + TEST_CC(ctanh, 0.5, 0, FUNC(tanh)(0.5), 0); + + TEST_CC(ctanh, 1000, 0, 1, 0); + TEST_CC(ctanh, -1000, 0, -1, 0); + + return ret; +} +#endif + +#ifdef CEXP +RETTYPE ADDSUFFIX(test_cexp)(void) +{ + INIT_FUNC(); + /* cexp(conj(z)) = conj(cexp(z)) */ + TEST_EE(cexp, 0, 0, 1, 0); + TEST_EE(cexp, 0, NZERO, 1, NZERO); + + TEST_EE(cexp, NZERO, 0, 1, 0); + TEST_EE(cexp, NZERO, NZERO, 1, NZERO); + + TEST_RAISES(cexp, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(cexp, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(cexp, 42.0, NAN, NAN, NAN); + + TEST_EE(cexp, INFINITY, 0, INFINITY, 0); + TEST_EE(cexp, INFINITY, NZERO, INFINITY, NZERO); + + TEST_EE(cexp, -INFINITY, 1.0, 0, 0); + TEST_EE(cexp, -INFINITY, -1.0, 0, NZERO); + TEST_EE(cexp, -INFINITY, 2.0, NZERO, 0); + TEST_EE(cexp, -INFINITY, -2.0, NZERO, NZERO); + TEST_EE(cexp, -INFINITY, 4.0, NZERO, NZERO); + TEST_EE(cexp, -INFINITY, -4.0, NZERO, 0); + TEST_EE(cexp, -INFINITY, 5.0, 0, NZERO); + TEST_EE(cexp, -INFINITY, -5.0, 0, 0); + + TEST_EE(cexp, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, -5.0, INFINITY, INFINITY); + + /* signs of both parts are unspecified */ + TEST_UNSPECIFIED4(cexp, -INFINITY, INFINITY, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + TEST_UNSPECIFIED4(cexp, -INFINITY, -INFINITY, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + + /* sign of real part is unspecifed */ + TEST_RAISES_UNSPECIFIED2(cexp, INFINITY, INFINITY, INFINITY, \ + NAN, -INFINITY, NAN, NPY_FPE_INVALID); + + /* signs of both parts are unspecified */ + TEST_UNSPECIFIED4(cexp, -INFINITY, NAN, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(cexp, INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(cexp, NAN, 0, NAN, 0); + TEST_EE(cexp, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(cexp, NAN, 2.0, NAN, NAN); + TEST_EE(cexp, NAN, -2.0, NAN, NAN); + + TEST_EE(cexp, NAN, NAN, NAN, NAN); + + TEST_CC(cexp, 0.5, 0, ADDSUFFIX(exp)(0.5), 0); + + TEST_CC(cexp, 1, 0, ADDSUFFIX(NPY_E), 0); + TEST_CC(cexp, 0, 1, FUNC(cos)(1), FUNC(sin)(1)); + TEST_CC(cexp, 1, 1, ADDSUFFIX(NPY_E)*FUNC(cos)(1), ADDSUFFIX(NPY_E)*FUNC(sin)(1)); + + return ret; +} +#endif + +#ifdef CLOG +RETTYPE ADDSUFFIX(test_clog)(void) +{ + INIT_FUNC(); + /* clog(conj(z)) = conj(clog(z)) */ + TEST_RAISES(clog, NZERO, 0, -INFINITY, ADDSUFFIX(NPY_PI), NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -ADDSUFFIX(NPY_PI), NPY_FPE_DIVIDEBYZERO); + + TEST_RAISES(clog, 0, 0, -INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(clog, 0, NZERO, -INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); + + TEST_EC(clog, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(clog, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); + + /* can raise FE_INVALID or not */ + TEST_EE(clog, 2.0, NAN, NAN, NAN); + + TEST_EC(clog, -INFINITY, 2.0, INFINITY, ADDSUFFIX(NPY_PI)); + TEST_EC(clog, -INFINITY, -2.0, INFINITY, -ADDSUFFIX(NPY_PI)); + + TEST_EE(clog, INFINITY, 2.0, INFINITY, 0); + TEST_EE(clog, INFINITY, -2.0, INFINITY, NZERO); + + TEST_EC(clog, -INFINITY, INFINITY, INFINITY, 0.75 * ADDSUFFIX(NPY_PI)); + TEST_EC(clog, -INFINITY, -INFINITY, INFINITY, -0.75 * ADDSUFFIX(NPY_PI)); + + TEST_EC(clog, INFINITY, INFINITY, INFINITY, 0.25 * ADDSUFFIX(NPY_PI)); + TEST_EC(clog, INFINITY, -INFINITY, INFINITY, -0.25 * ADDSUFFIX(NPY_PI)); + + TEST_EE(clog, INFINITY, NAN, INFINITY, NAN); + TEST_EE(clog, -INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(clog, NAN, 2.0, NAN, NAN); + TEST_EE(clog, NAN, -2.0, NAN, NAN); + + TEST_EE(clog, NAN, INFINITY, INFINITY, NAN); + TEST_EE(clog, NAN, -INFINITY, INFINITY, NAN); + + TEST_EE(clog, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(clog, -0.5, 0, 0, 1, 1, -1, 1); + + TEST_CC(clog, 0.5, 0, FUNC(log)(0.5), 0); + + TEST_CC(clog, 1, 0, 0, 0); + TEST_CC(clog, 1, 2, 0.80471895621705014, 1.1071487177940904); + + return ret; +} +#endif + +#ifdef CPOW +RETTYPE ADDSUFFIX(test_cpow)(void) +{ + INIT_FUNC(); + + /* there are _no_ annex G values for cpow. */ + /* We can check for branch cuts in here */ + + /* tests from test_umath.py: TestPower: test_power_complex */ + TEST_CPOW_CC(1, 2, 0, 0, 1, 0); + TEST_CPOW_CC(2, 3, 0, 0, 1, 0); + TEST_CPOW_CC(3, 4, 0, 0, 1, 0); + + TEST_CPOW_CC(1, 2, 1, 0, 1, 2); + TEST_CPOW_CC(2, 3, 1, 0, 2, 3); + TEST_CPOW_CC(3, 4, 1, 0, 3, 4); + + TEST_CPOW_CC(1, 2, 2, 0, -3, 4); + TEST_CPOW_CC(2, 3, 2, 0, -5, 12); + TEST_CPOW_CC(3, 4, 2, 0, -7, 24); + + TEST_CPOW_CC(1, 2, 3, 0, -11, -2); + TEST_CPOW_CC(2, 3, 3, 0, -46, 9); + TEST_CPOW_CC(3, 4, 3, 0, -117, 44); + + TEST_CPOW_CC(1, 2, 4, 0, -7, -24); + TEST_CPOW_CC(2, 3, 4, 0, -119, -120); + TEST_CPOW_CC(3, 4, 4, 0, -527, -336); + + TEST_CPOW_CC(1, 2, -1, 0, 1.0/5.0, -2.0/5.0); + TEST_CPOW_CC(2, 3, -1, 0, 2.0/13.0, -3.0/13.0); + TEST_CPOW_CC(3, 4, -1, 0, 3.0/25.0, -4.0/25.0); + + TEST_CPOW_CC(1, 2, -2, 0, -3.0/25.0, -4.0/25.0); + TEST_CPOW_CC(2, 3, -2, 0, -5.0/169.0, -12.0/169.0); + TEST_CPOW_CC(3, 4, -2, 0, -7.0/625.0, -24.0/625.0); + + TEST_CPOW_CC(1, 2, -3, 0, -11.0/125.0, 2.0/125.0); + TEST_CPOW_CC(2, 3, -3, 0, -46.0/2197.0, -9.0/2197.0); + TEST_CPOW_CC(3, 4, -3, 0, -117.0/15625.0, -44.0/15625.0); + + TEST_CPOW_CC(1, 2, 0.5, 0, 1.272019649514069, 0.7861513777574233); + TEST_CPOW_CC(2, 3, 0.5, 0, 1.6741492280355401, 0.895977476129838); + TEST_CPOW_CC(3, 4, 0.5, 0, 2, 1); + + TEST_CPOW_CC(1, 2, 14, 0, -76443, 16124); + TEST_CPOW_CC(2, 3, 14, 0, 23161315, 58317492); + TEST_CPOW_CC(3, 4, 14, 0, 5583548873, 2465133864); + + TEST_CPOW_EE(0, INFINITY, 1, 0, 0, INFINITY); + TEST_CPOW_EE(0, INFINITY, 2, 0, -INFINITY, NAN); + TEST_CPOW_EE(0, INFINITY, 3, 0, NAN, NAN); + + TEST_CPOW_EE(1, INFINITY, 1, 0, 1, INFINITY); + TEST_CPOW_EE(1, INFINITY, 2, 0, -INFINITY, INFINITY); + TEST_CPOW_EE(1, INFINITY, 3, 0, -INFINITY, NAN); + + /* tests from test_umath.py: TestPower: test_power_zero */ + TEST_CPOW_CC(0, 0, 0.33, 0, 0, 0); + TEST_CPOW_CC(0, 0, 0.5, 0, 0, 0); + TEST_CPOW_CC(0, 0, 1.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 1.5, 0, 0, 0); + TEST_CPOW_CC(0, 0, 2.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 3.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 4.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 5.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 6.6, 0, 0, 0); + + TEST_CPOW_EE(0, 0, 0, 0, 1, 0); + TEST_CPOW_EE(0, 0, 0, 1, NAN, NAN); + + TEST_CPOW_EE(0, 0, -0.33, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -0.5, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1.5, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -2.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -3.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -4.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -5.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -6.6, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1, 0.2, NAN, NAN); + + /* tests from test_umath_complex.py: TestCpow: test_simple + * --- skip, duplicating existing tests --- + */ + + /* tests from test_umath_complex.py: TestCpow: test_scalar, test_array + * these tests are equilvent for this level. + */ + TEST_CPOW_CC(1, 0, 1, 0, 1, 0); + TEST_CPOW_CC(1, 0, 0, 1, 1, 0); + TEST_CPOW_CC(1, 0, -0.5, 1.5, 1, 0); + TEST_CPOW_CC(1, 0, 2, 0, 1, 0); + TEST_CPOW_CC(1, 0, 3, 0, 1, 0); + + TEST_CPOW_CC(0, 1, 1, 0, 0, 1); + TEST_CPOW_CC(0, 1, 0, 1, FUNC(exp)(-ADDSUFFIX(NPY_PI_2)), 0); + TEST_CPOW_CC(0, 1, -0.5, 1.5, 0.067019739708273365, -0.067019739708273365); + TEST_CPOW_CC(0, 1, 2, 0, -1, 0); + TEST_CPOW_CC(0, 1, 3, 0, 0, -1); + + TEST_CPOW_CC(2, 0, 1, 0, 2, 0); + TEST_CPOW_CC(2, 0, 0, 1, FUNC(cos)(NPY_LOGE2), FUNC(sin)(NPY_LOGE2)); + TEST_CPOW_CC(2, 0, 2, 0, 4, 0); + TEST_CPOW_CC(2, 0, 3, 0, 8, 0); + + TEST_CPOW_CC(2.5, 0.375, 1, 0, 2.5, 0.375); + TEST_CPOW_CC(2.5, 0.375, 0, 1, 0.51691507509598866, 0.68939360813851125); + TEST_CPOW_CC(2.5, 0.375, -0.5, 1.5, 0.12646517347496394, 0.48690593271654437); + TEST_CPOW_CC(2.5, 0.375, 2, 0, 391.0/64.0, 15.0/8.0); + TEST_CPOW_CC(2.5, 0.375, 3, 0, 1865.0/128.0, 3573.0/512.0); + + TEST_CPOW_EE(INFINITY, 0, 1, 0, INFINITY, 0); + TEST_CPOW_EE(INFINITY, 0, 0, 1, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, -0.5, 1.5, 0, 0); + TEST_CPOW_EE(INFINITY, 0, 2, 0, INFINITY, NAN); + TEST_CPOW_EE(INFINITY, 0, 3, 0, NAN, NAN); + + TEST_CPOW_EE(NAN, 0, 1, 0, NAN, 0); + TEST_CPOW_EE(NAN, 0, 0, 1, NAN, NAN); + TEST_CPOW_EE(NAN, 0, -0.5, 1.5, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 2, 0, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 3, 0, NAN, NAN); + + return ret; +} +#endif + +#ifdef CSQRT +RETTYPE ADDSUFFIX(test_csqrt)(void) +{ + INIT_FUNC(); + /* csqrt(conj(z)) = conj(csqrt(z)) */ + TEST_EE(csqrt, 0, 0, 0, 0); + TEST_EE(csqrt, 0, NZERO, 0, NZERO); + + TEST_EE(csqrt, NZERO, 0, 0, 0); + TEST_EE(csqrt, NZERO, NZERO, 0, NZERO); + + TEST_EE(csqrt, 2.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, 2.0, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, NAN, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, NAN, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, INFINITY, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, INFINITY, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, -INFINITY, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, -INFINITY, -INFINITY, INFINITY, -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(csqrt, 2.0, NAN, NAN, NAN); + + TEST_EE(csqrt, -INFINITY, 2.0, 0, INFINITY); + TEST_EE(csqrt, -INFINITY, -2.0, 0, -INFINITY); + + TEST_EE(csqrt, INFINITY, 2.0, INFINITY, 0); + TEST_EE(csqrt, INFINITY, -2.0, INFINITY, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csqrt, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(csqrt, INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(csqrt, NAN, 2.0, NAN, NAN); + TEST_EE(csqrt, NAN, -2.0, NAN, NAN); + + TEST_EE(csqrt, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(csqrt, -0.5, 0, 0, 1, 1, -1, 1); + + TEST_CC(csqrt, 0.5, 0, FUNC(sqrt)(0.5), 0); + + TEST_CC(csqrt, 1, 0, 1, 0); + TEST_CC(csqrt, 0, 1, ADDSUFFIX(NPY_SQRT2)/2.0, ADDSUFFIX(NPY_SQRT2)/2.0); + TEST_CC(csqrt, -1, 0, 0, 1); + TEST_CC(csqrt, 1, 1, 1.0986841134678100, 0.4550898605622273); + TEST_CC(csqrt, 1, -1, 1.0986841134678100, -0.4550898605622273); + + return ret; +} +#endif + +#ifndef HAVE_NUMPY +int main(int argc, char** argv) +{ +#ifdef CACOS + return !ADDSUFFIX(test_cacos)(); +#endif +#ifdef CASIN + return !ADDSUFFIX(test_casin)(); +#endif +#ifdef CATAN + return !ADDSUFFIX(test_catan)(); +#endif +#ifdef CACOSH + return !ADDSUFFIX(test_cacosh)(); +#endif +#ifdef CASINH + return !ADDSUFFIX(test_casinh)(); +#endif +#ifdef CATANH + return !ADDSUFFIX(test_catanh)(); +#endif +#ifdef CCOS + return !ADDSUFFIX(test_ccos)(); +#endif +#ifdef CSIN + return !ADDSUFFIX(test_csin)(); +#endif +#ifdef CTAN + return !ADDSUFFIX(test_ctan)(); +#endif +#ifdef CCOSH + return !ADDSUFFIX(test_ccosh)(); +#endif +#ifdef CSINH + return !ADDSUFFIX(test_csinh)(); +#endif +#ifdef CTANH + return !ADDSUFFIX(test_ctanh)(); +#endif +#ifdef CEXP + return !ADDSUFFIX(test_cexp)(); +#endif +#ifdef CLOG + return !ADDSUFFIX(test_clog)(); +#endif +#ifdef CPOW + return !ADDSUFFIX(test_cpow)(); +#endif +#ifdef CSQRT + return !ADDSUFFIX(test_csqrt)(); +#endif +} +#endif + +#undef TYPE +#undef CTYPE +#undef SUFFIX +#undef EPS +#undef CLOSE_ATOL +#undef CLOSE_RTOL +#undef BRANCH_SCALE +#undef BRANCH_ATOL +#undef FMT + diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index 3aad44c9feec..f0fa3e9dd81c 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -177,49 +177,8 @@ npy_ObjectLogicalNot(PyObject *i1) * #ctype = npy_cfloat, npy_cdouble, npy_clongdouble# * #ftype = npy_float, npy_double, npy_longdouble# * #c = f, ,l# - * #C = F, ,L# - * #precision = 1,2,4# */ -/* - * Perform the operation result := 1 + coef * x * result, - * with real coefficient `coef`. - */ -#define SERIES_HORNER_TERM@c@(result, x, coef) \ - do { \ - nc_prod@c@((result), (x), (result)); \ - (result)->real *= (coef); \ - (result)->imag *= (coef); \ - nc_sum@c@((result), &nc_1@c@, (result)); \ - } while(0) - -/* constants */ -static @ctype@ nc_1@c@ = {1., 0.}; -static @ctype@ nc_half@c@ = {0.5, 0.}; -static @ctype@ nc_i@c@ = {0., 1.}; -static @ctype@ nc_i2@c@ = {0., 0.5}; -/* - * static @ctype@ nc_mi@c@ = {0.0@c@, -1.0@c@}; - * static @ctype@ nc_pi2@c@ = {NPY_PI_2@c@., 0.0@c@}; - */ - - -static void -nc_sum@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - r->real = a->real + b->real; - r->imag = a->imag + b->imag; - return; -} - -static void -nc_diff@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - r->real = a->real - b->real; - r->imag = a->imag - b->imag; - return; -} - static void nc_neg@c@(@ctype@ *a, @ctype@ *r) { @@ -228,26 +187,6 @@ nc_neg@c@(@ctype@ *a, @ctype@ *r) return; } -static void -nc_prod@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - @ftype@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; - r->real = ar*br - ai*bi; - r->imag = ar*bi + ai*br; - return; -} - -static void -nc_quot@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - - @ftype@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; - @ftype@ d = br*br + bi*bi; - r->real = (ar*br + ai*bi)/d; - r->imag = (ai*br - ar*bi)/d; - return; -} - static void nc_sqrt@c@(@ctype@ *x, @ctype@ *r) { @@ -307,164 +246,28 @@ nc_expm1@c@(@ctype@ *x, @ctype@ *r) static void nc_pow@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) { - npy_intp n; - @ftype@ ar = npy_creal@c@(*a); - @ftype@ br = npy_creal@c@(*b); - @ftype@ ai = npy_cimag@c@(*a); - @ftype@ bi = npy_cimag@c@(*b); - - if (br == 0. && bi == 0.) { - *r = npy_cpack@c@(1., 0.); - return; - } - if (ar == 0. && ai == 0.) { - if (br > 0 && bi == 0) { - *r = npy_cpack@c@(0., 0.); - } - else { - volatile @ftype@ tmp = NPY_INFINITY; - /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike - * for reals, c0**p, with `p` negative is in general - * ill-defined. - * - * c0**z with z complex is also ill-defined. - */ - *r = npy_cpack@c@(NPY_NAN, NPY_NAN); - - /* Raise invalid */ - tmp -= NPY_INFINITY; - ar = tmp; - } - return; - } - if (bi == 0 && (n=(npy_intp)br) == br) { - if (n == 1) { - /* unroll: handle inf better */ - *r = npy_cpack@c@(ar, ai); - return; - } - else if (n == 2) { - /* unroll: handle inf better */ - nc_prod@c@(a, a, r); - return; - } - else if (n == 3) { - /* unroll: handle inf better */ - nc_prod@c@(a, a, r); - nc_prod@c@(a, r, r); - return; - } - else if (n > -100 && n < 100) { - @ctype@ p, aa; - npy_intp mask = 1; - if (n < 0) n = -n; - aa = nc_1@c@; - p = npy_cpack@c@(ar, ai); - while (1) { - if (n & mask) - nc_prod@c@(&aa,&p,&aa); - mask <<= 1; - if (n < mask || mask <= 0) break; - nc_prod@c@(&p,&p,&p); - } - *r = npy_cpack@c@(npy_creal@c@(aa), npy_cimag@c@(aa)); - if (br < 0) nc_quot@c@(&nc_1@c@, r, r); - return; - } - } - - *r = npy_cpow@c@(*a, *b); + *r = npy_cpow@c@(*a, *b); return; } - -static void -nc_prodi@c@(@ctype@ *x, @ctype@ *r) -{ - @ftype@ xr = x->real; - r->real = -x->imag; - r->imag = xr; - return; -} - - static void nc_acos@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_neg(nc_prodi(nc_log(nc_sum(x,nc_prod(nc_i, - * nc_sqrt(nc_diff(nc_1,nc_prod(x,x)))))))); - */ - nc_prod@c@(x,x,r); - nc_diff@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_prodi@c@(r, r); - nc_sum@c@(x, r, r); - nc_log@c@(r, r); - nc_prodi@c@(r, r); - nc_neg@c@(r, r); + *r = npy_cacos@c@(*x); return; } static void nc_acosh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_log(nc_sum(x, - * nc_prod(nc_sqrt(nc_sum(x,nc_1)), nc_sqrt(nc_diff(x,nc_1))))); - */ - @ctype@ t; - - nc_sum@c@(x, &nc_1@c@, &t); - nc_sqrt@c@(&t, &t); - nc_diff@c@(x, &nc_1@c@, r); - nc_sqrt@c@(r, r); - nc_prod@c@(&t, r, r); - nc_sum@c@(x, r, r); - nc_log@c@(r, r); + *r = npy_cacosh@c@(*x); return; } static void nc_asin@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_neg(nc_prodi(nc_log(nc_sum(nc_prod(nc_i,x), - * nc_sqrt(nc_diff(nc_1,nc_prod(x,x))))))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_prod@c@(x, x, r); - nc_diff@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_prodi@c@(x, pa); - nc_sum@c@(pa, r, r); - nc_log@c@(r, r); - nc_prodi@c@(r, r); - nc_neg@c@(r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * asin(x) = x [1 + (1/6) x^2 [1 + (9/20) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, 81.0@C@/110); - SERIES_HORNER_TERM@c@(r, &x2, 49.0@C@/72); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, 25.0@C@/42); -#endif - SERIES_HORNER_TERM@c@(r, &x2, 9.0@C@/20); - SERIES_HORNER_TERM@c@(r, &x2, 1.0@C@/6); - nc_prod@c@(r, x, r); - } + *r = npy_casin@c@(*x); return; } @@ -472,134 +275,35 @@ nc_asin@c@(@ctype@ *x, @ctype@ *r) static void nc_asinh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_log(nc_sum(nc_sqrt(nc_sum(nc_1,nc_prod(x,x))),x)); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - nc_prod@c@(x, x, r); - nc_sum@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_sum@c@(r, x, r); - nc_log@c@(r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * asinh(x) = x [1 - (1/6) x^2 [1 - (9/20) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, -81.0@C@/110); - SERIES_HORNER_TERM@c@(r, &x2, -49.0@C@/72); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, -25.0@C@/42); -#endif - SERIES_HORNER_TERM@c@(r, &x2, -9.0@C@/20); - SERIES_HORNER_TERM@c@(r, &x2, -1.0@C@/6); - nc_prod@c@(r, x, r); - } + *r = npy_casinh@c@(*x); return; } static void nc_atan@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_prod(nc_i2,nc_log(nc_quot(nc_sum(nc_i,x),nc_diff(nc_i,x)))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_diff@c@(&nc_i@c@, x, pa); - nc_sum@c@(&nc_i@c@, x, r); - nc_quot@c@(r, pa, r); - nc_log@c@(r,r); - nc_prod@c@(&nc_i2@c@, r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 - (1/3) x^2 [1 - (3/5) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, -9.0@C@/11); - SERIES_HORNER_TERM@c@(r, &x2, -7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, -5.0@C@/7); -#endif - SERIES_HORNER_TERM@c@(r, &x2, -3.0@C@/5); - SERIES_HORNER_TERM@c@(r, &x2, -1.0@C@/3); - nc_prod@c@(r, x, r); - } + *r = npy_catan@c@(*x); return; } static void nc_atanh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_prod(nc_half,nc_log(nc_quot(nc_sum(nc_1,x),nc_diff(nc_1,x)))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_diff@c@(&nc_1@c@, x, r); - nc_sum@c@(&nc_1@c@, x, pa); - nc_quot@c@(pa, r, r); - nc_log@c@(r, r); - nc_prod@c@(&nc_half@c@, r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 + (1/3) x^2 [1 + (3/5) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, 9.0@C@/11); - SERIES_HORNER_TERM@c@(r, &x2, 7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, 5.0@C@/7); -#endif - SERIES_HORNER_TERM@c@(r, &x2, 3.0@C@/5); - SERIES_HORNER_TERM@c@(r, &x2, 1.0@C@/3); - nc_prod@c@(r, x, r); - } + *r = npy_catanh@c@(*x); return; } static void nc_cos@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xr)*npy_cosh@c@(xi); - r->imag = -npy_sin@c@(xr)*npy_sinh@c@(xi); + *r = npy_ccos@c@(*x); return; } static void nc_cosh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xi)*npy_cosh@c@(xr); - r->imag = npy_sin@c@(xi)*npy_sinh@c@(xr); + *r = npy_ccosh@c@(*x); return; } @@ -624,63 +328,29 @@ nc_log2@c@(@ctype@ *x, @ctype@ *r) static void nc_sin@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_sin@c@(xr)*npy_cosh@c@(xi); - r->imag = npy_cos@c@(xr)*npy_sinh@c@(xi); + *r = npy_csin@c@(*x); return; } static void nc_sinh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xi)*npy_sinh@c@(xr); - r->imag = npy_sin@c@(xi)*npy_cosh@c@(xr); + *r = npy_csinh@c@(*x); return; } static void nc_tan@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ sr,cr,shi,chi; - @ftype@ rs,is,rc,ic; - @ftype@ d; - @ftype@ xr=x->real, xi=x->imag; - sr = npy_sin@c@(xr); - cr = npy_cos@c@(xr); - shi = npy_sinh@c@(xi); - chi = npy_cosh@c@(xi); - rs = sr*chi; - is = cr*shi; - rc = cr*chi; - ic = -sr*shi; - d = rc*rc + ic*ic; - r->real = (rs*rc+is*ic)/d; - r->imag = (is*rc-rs*ic)/d; - return; + *r = npy_ctan@c@(*x); + return; } static void nc_tanh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ si,ci,shr,chr; - @ftype@ rs,is,rc,ic; - @ftype@ d; - @ftype@ xr=x->real, xi=x->imag; - si = npy_sin@c@(xi); - ci = npy_cos@c@(xi); - shr = npy_sinh@c@(xr); - chr = npy_cosh@c@(xr); - rs = ci*shr; - is = si*chr; - rc = ci*chr; - ic = si*shr; - d = rc*rc + ic*ic; - r->real = (rs*rc+is*ic)/d; - r->imag = (is*rc-rs*ic)/d; + *r = npy_ctanh@c@(*x); return; } -#undef SERIES_HORNER_TERM@c@ - /**end repeat**/ diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c index d792e8b24723..9931f9da1090 100644 --- a/numpy/core/src/umath/umathmodule.c +++ b/numpy/core/src/umath/umathmodule.c @@ -246,6 +246,7 @@ add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args) ***************************************************************************** */ + NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_out = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_subok = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_array_prepare = NULL; @@ -370,6 +371,11 @@ PyMODINIT_FUNC initumath(void) PyDict_SetItemString(d, "euler_gamma", s = PyFloat_FromDouble(NPY_EULER)); Py_DECREF(s); +#if defined(NPY_PY3K) + s = PyDict_GetItemString(d, "true_divide"); + PyDict_SetItemString(d, "divide", s); +#endif + #define ADDCONST(str) PyModule_AddIntConstant(m, #str, UFUNC_##str) #define ADDSCONST(str) PyModule_AddStringConstant(m, "UFUNC_" #str, UFUNC_##str) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index c71b7b6585a6..2953a9ea1c73 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1363,45 +1363,53 @@ def test_precisions_consistent(self) : def test_branch_cuts(self): # check branch cuts and continuity on them - yield _check_branch_cut, np.log, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1 - yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1 + yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True + yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1 - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1 - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, -1 ], -1, 1 + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1 - yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1 - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, -1j], 1, -1 + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True + yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True # check against bogus branch cuts: assert continuity between quadrants - yield _check_branch_cut, np.arcsin, [-2j, 2j], [ 1, 1], 1, 1 - yield _check_branch_cut, np.arccos, [-2j, 2j], [ 1, 1], 1, 1 + yield _check_branch_cut, np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1 + yield _check_branch_cut, np.arccos, [0-2j, 2j], [ 1, 1], 1, 1 yield _check_branch_cut, np.arctan, [ -2, 2], [1j, 1j], 1, 1 yield _check_branch_cut, np.arcsinh, [ -2, 2, 0], [1j, 1j, 1 ], 1, 1 - yield _check_branch_cut, np.arccosh, [-2j, 2j, 2], [1, 1, 1j], 1, 1 - yield _check_branch_cut, np.arctanh, [-2j, 2j, 0], [1, 1, 1j], 1, 1 + yield _check_branch_cut, np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1 + yield _check_branch_cut, np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1 - @dec.knownfailureif(True, "These branch cuts are known to fail") - def test_branch_cuts_failing(self): - # XXX: signed zero not OK with ICC on 64-bit platform for log, see - # http://permalink.gmane.org/gmane.comp.python.numeric.general/25335 - yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True - # XXX: signed zeros are not OK for sqrt or for the arc* functions - yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1, True - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1, True - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, -1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1, True - yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, -1j], 1, -1, True + def test_branch_cuts_complex64(self): + # check branch cuts and continuity on them + yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True, np.complex64 + + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True, np.complex64 + + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True, np.complex64 + yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + + # check against bogus branch cuts: assert continuity between quadrants + yield _check_branch_cut, np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arccos, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arctan, [ -2, 2], [1j, 1j], 1, 1, False, np.complex64 + + yield _check_branch_cut, np.arcsinh, [ -2, 2, 0], [1j, 1j, 1 ], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1, False, np.complex64 def test_against_cmath(self): import cmath, sys @@ -1565,8 +1573,12 @@ def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False, x0 = np.atleast_1d(x0).astype(dtype) dx = np.atleast_1d(dx).astype(dtype) - scale = np.finfo(dtype).eps * 1e3 - atol = 1e-4 + if np.dtype(dtype).char == 'F': + scale = np.finfo(dtype).eps * 1e2 + atol = np.float32(1e-2) + else: + scale = np.finfo(dtype).eps * 1e3 + atol = 1e-4 y0 = f(x0) yp = f(x0 + dx*scale*np.absolute(x0)/np.absolute(dx)) @@ -1581,16 +1593,20 @@ def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False, # check that signed zeros also work as a displacement jr = (x0.real == 0) & (dx.real != 0) ji = (x0.imag == 0) & (dx.imag != 0) - - x = -x0 - x.real[jr] = 0.*dx.real - x.imag[ji] = 0.*dx.imag - x = -x - ym = f(x) - ym = ym[jr | ji] - y0 = y0[jr | ji] - assert_(np.all(np.absolute(y0.real - ym.real*re_sign) < atol), (y0, ym)) - assert_(np.all(np.absolute(y0.imag - ym.imag*im_sign) < atol), (y0, ym)) + if np.any(jr): + x = x0[jr] + x.real = np.NZERO + ym = f(x) + assert_(np.all(np.absolute(y0[jr].real - ym.real*re_sign) < atol), (y0[jr], ym)) + assert_(np.all(np.absolute(y0[jr].imag - ym.imag*im_sign) < atol), (y0[jr], ym)) + + + if np.any(ji): + x = x0[ji] + x.imag = np.NZERO + ym = f(x) + assert_(np.all(np.absolute(y0[ji].real - ym.real*re_sign) < atol), (y0[ji], ym)) + assert_(np.all(np.absolute(y0[ji].imag - ym.imag*im_sign) < atol), (y0[ji], ym)) def test_copysign(): assert_(np.copysign(1, -1) == -1) diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 4f3da4397acf..a83ea902c7e0 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -1,4 +1,5 @@ from __future__ import division, absolute_import, print_function +from numpy.core.npymath_tests import * import sys import platform @@ -7,6 +8,8 @@ import numpy.core.umath as ncu import numpy as np +import numpy.core.npymath_tests as ncn + # TODO: branch cuts (use Pauli code) # TODO: conj 'symmetry' # TODO: FPU exceptions @@ -123,6 +126,11 @@ def _check_inf_nan(dummy): # cexp(nan + nani) is nan + nani yield check, f, np.nan, np.nan, np.nan, np.nan + # cexp(nan +-0) is nan +-0 + yield check, f, np.nan, np.PZERO, np.nan, np.PZERO, True + yield check, f, np.nan, np.NZERO, np.nan, np.NZERO, True + + @dec.knownfailureif(True, "cexp(nan + 0I) is wrong on most implementations") def test_special_values2(self): # XXX: most implementations get it wrong here (including glibc <= 2.10) @@ -517,6 +525,161 @@ def test_special_values(self): yield check_real_value, ncu._arg, np.nan, np.inf, np.nan, False yield check_real_value, ncu._arg, np.inf, np.nan, np.nan, False +class TestCtan(object): + def test_simple(self): + check_complex_value(ncu.tan, 0, 1000, 0, 1, True) + check_complex_value(ncu.tan, 0, -1000, 0, -1, True) + +class TestCtanh(object): + def test_simple(self): + check_complex_value(ncu.tanh, 1000, 0, 1, 0, True) + check_complex_value(ncu.tanh, -1000, 0, -1, 0, True) + +class TestNPyMath(object): + def test_cacosf(self): + ncn.test_cacosf() + + def test_cacos(self): + ncn.test_cacos() + + def test_cacosl(self): + ncn.test_cacosl() + + def test_casinf(self): + ncn.test_casinf() + + def test_casin(self): + ncn.test_casin() + + def test_casinl(self): + ncn.test_casinl() + + def test_catanf(self): + ncn.test_catanf() + + def test_catan(self): + ncn.test_catan() + + def test_catanl(self): + ncn.test_catanl() + + def test_cacoshf(self): + ncn.test_cacoshf() + + def test_cacosh(self): + ncn.test_cacosh() + + def test_cacoshl(self): + ncn.test_cacoshl() + + def test_casinhf(self): + ncn.test_casinhf() + + def test_casinh(self): + ncn.test_casinh() + + def test_casinhl(self): + ncn.test_casinhl() + + def test_catanhf(self): + ncn.test_catanhf() + + def test_catanh(self): + ncn.test_catanh() + + def test_catanhl(self): + ncn.test_catanhl() + + def test_ccosf(self): + ncn.test_ccosf() + + def test_ccos(self): + ncn.test_ccos() + + def test_ccosl(self): + ncn.test_ccosl() + + def test_csinf(self): + ncn.test_csinf() + + def test_csin(self): + ncn.test_csin() + + def test_csinl(self): + ncn.test_csinl() + + def test_ctanf(self): + ncn.test_ctanf() + + def test_ctan(self): + ncn.test_ctan() + + def test_ctanl(self): + ncn.test_ctanl() + + def test_ccoshf(self): + ncn.test_ccoshf() + + def test_ccosh(self): + ncn.test_ccosh() + + def test_ccoshl(self): + ncn.test_ccoshl() + + def test_csinhf(self): + ncn.test_csinhf() + + def test_csinh(self): + ncn.test_csinh() + + def test_csinhl(self): + ncn.test_csinhl() + + def test_ctanhf(self): + ncn.test_ctanhf() + + def test_catanh(self): + ncn.test_ctanh() + + def test_ctanhl(self): + ncn.test_ctanhl() + + def test_cexpf(self): + ncn.test_cexpf() + + def test_cexp(self): + ncn.test_cexp() + + def test_cexpl(self): + ncn.test_cexpl() + + def test_clogf(self): + ncn.test_clogf() + + def test_clog(self): + ncn.test_clog() + + def test_clogl(self): + ncn.test_clogl() + + def test_cpowf(self): + ncn.test_cpowf() + + def test_cpow(self): + ncn.test_cpow() + + def test_cpowl(self): + ncn.test_cpowl() + + def test_csqrtf(self): + ncn.test_csqrtf() + + def test_csqrt(self): + ncn.test_csqrt() + + def test_csqrtl(self): + ncn.test_csqrtl() + def check_real_value(f, x1, y1, x, exact=True): z1 = np.array([complex(x1, y1)]) if exact: From ff5a1c2a949516277ff373c134e999d97cf0d68e Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Tue, 27 Jan 2015 15:03:56 -0700 Subject: [PATCH 2/2] STY: C style cleanup. Omitted numpy/core/src/npymath/test_c99complex.c, which is a jungle of macro magic. --- numpy/core/src/npymath/fpstatus.c | 92 ++-- numpy/core/src/npymath/npy_math_complex.c.src | 443 ++++++++++++------ numpy/core/src/npymath/npymath_tests.c | 9 +- 3 files changed, 364 insertions(+), 180 deletions(-) diff --git a/numpy/core/src/npymath/fpstatus.c b/numpy/core/src/npymath/fpstatus.c index da669ec1b61f..82d163a93829 100644 --- a/numpy/core/src/npymath/fpstatus.c +++ b/numpy/core/src/npymath/fpstatus.c @@ -22,7 +22,8 @@ defined(__NetBSD__) #include -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { int fpstatus = fpgetsticky(); return ((FP_X_DZ & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | @@ -31,7 +32,8 @@ int npy_get_floatstatus(void) ((FP_X_INV & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { int fpstatus = npy_get_floatstatus(); fpsetsticky(0); @@ -39,22 +41,26 @@ int npy_clear_floatstatus(void) return fpstatus; } -void npy_set_floatstatus_divbyzero(void) +void +npy_set_floatstatus_divbyzero(void) { fpsetsticky(FP_X_DZ); } -void npy_set_floatstatus_overflow(void) +void +npy_set_floatstatus_overflow(void) { fpsetsticky(FP_X_OFL); } -void npy_set_floatstatus_underflow(void) +void +npy_set_floatstatus_underflow(void) { fpsetsticky(FP_X_UFL); } -void npy_set_floatstatus_invalid(void) +void +npy_set_floatstatus_invalid(void) { fpsetsticky(FP_X_INV); } @@ -71,7 +77,8 @@ void npy_set_floatstatus_invalid(void) # include "numpy/fenv/fenv.h" # endif -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INVALID); @@ -82,7 +89,8 @@ int npy_get_floatstatus(void) ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { /* testing float status is 50-100 times faster than clearing on x86 */ int fpstatus = npy_get_floatstatus(); @@ -95,22 +103,26 @@ int npy_clear_floatstatus(void) } -void npy_set_floatstatus_divbyzero(void) +void +npy_set_floatstatus_divbyzero(void) { feraiseexcept(FE_DIVBYZERO); } -void npy_set_floatstatus_overflow(void) +void +npy_set_floatstatus_overflow(void) { feraiseexcept(FE_OVERFLOW); } -void npy_set_floatstatus_underflow(void) +void +npy_set_floatstatus_underflow(void) { feraiseexcept(FE_UNDERFLOW); } -void npy_set_floatstatus_invalid(void) +void +npy_set_floatstatus_invalid(void) { feraiseexcept(FE_INVALID); } @@ -119,7 +131,8 @@ void npy_set_floatstatus_invalid(void) #include #include -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { int fpstatus = fp_read_flag(); return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | @@ -128,7 +141,8 @@ int npy_get_floatstatus(void) ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { int fpstatus = npy_get_floatstatus(); fp_swap_flag(0); @@ -136,17 +150,20 @@ int npy_clear_floatstatus(void) return fpstatus; } -void npy_set_floatstatus_divbyzero(void) +void +npy_set_floatstatus_divbyzero(void) { fp_raise_xcp(FP_DIV_BY_ZERO); } -void npy_set_floatstatus_overflow(void) +void +npy_set_floatstatus_overflow(void) { fp_raise_xcp(FP_OVERFLOW); } -void npy_set_floatstatus_underflow(void) +void +npy_set_floatstatus_underflow(void) { fp_raise_xcp(FP_UNDERFLOW); } @@ -164,7 +181,8 @@ void npy_set_floatstatus_invalid(void) #include -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { #if defined(_WIN64) int fpstatus = _statusfp(); @@ -180,7 +198,8 @@ int npy_get_floatstatus(void) ((SW_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { int fpstatus = npy_get_floatstatus(); _clearfp(); @@ -193,16 +212,19 @@ int npy_clear_floatstatus(void) #include -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { unsigned long fpstatus = ieee_get_fp_control(); + return ((IEEE_STATUS_DZE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | ((IEEE_STATUS_OVF & fpstatus) ? NPY_FPE_OVERFLOW : 0) | ((IEEE_STATUS_UNF & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | ((IEEE_STATUS_INV & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { long fpstatus = npy_get_floatstatus(); /* clear status bits as well as disable exception mode if on */ @@ -213,12 +235,14 @@ int npy_clear_floatstatus(void) #else -int npy_get_floatstatus(void) +int +npy_get_floatstatus(void) { return 0; } -int npy_clear_floatstatus(void) +int +npy_clear_floatstatus(void) { return 0; } @@ -234,26 +258,32 @@ int npy_clear_floatstatus(void) * global here, because that would cause * a race condition. */ -static volatile double _npy_floatstatus_x, - _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, - _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; - -void npy_set_floatstatus_divbyzero(void) +static volatile double _npy_floatstatus_x; +static volatile double _npy_floatstatus_zero = 0.0; +static volatile double _npy_floatstatus_big = 1e300; +static volatile double _npy_floatstatus_small = 1e-300; +static volatile double _npy_floatstatus_inf; + +void +npy_set_floatstatus_divbyzero(void) { _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; } -void npy_set_floatstatus_overflow(void) +void +npy_set_floatstatus_overflow(void) { _npy_floatstatus_x = _npy_floatstatus_big * 1e300; } -void npy_set_floatstatus_underflow(void) +void +npy_set_floatstatus_underflow(void) { _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; } -void npy_set_floatstatus_invalid(void) +void +npy_set_floatstatus_invalid(void) { _npy_floatstatus_inf = NPY_INFINITY; _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 97c9d578c7e0..e4791f698224 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -34,9 +34,12 @@ #include "npy_math_common.h" #include "npy_math_private.h" -static npy_float tiny = 3.9443045e-31f; #define raise_inexact() do { volatile npy_float junk = 1 + tiny; } while(0) + +static npy_float tiny = 3.9443045e-31f; + + /**begin repeat * #type = npy_float, npy_double, npy_longdouble# * #ctype = npy_cfloat,npy_cdouble,npy_clongdouble# @@ -60,22 +63,28 @@ static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; /*========================================================== * Helper functions * - * These are necessary because we do not count on using a + * These are necessary because we do not count on using a * C99 compiler. *=========================================================*/ -static NPY_INLINE @ctype@ cadd@c@(@ctype@ a, @ctype@ b) +static NPY_INLINE +@ctype@ +cadd@c@(@ctype@ a, @ctype@ b) { return npy_cpack@c@(npy_creal@c@(a) + npy_creal@c@(b), npy_cimag@c@(a) + npy_cimag@c@(b)); } -static NPY_INLINE @ctype@ csub@c@(@ctype@ a, @ctype@ b) +static NPY_INLINE +@ctype@ +csub@c@(@ctype@ a, @ctype@ b) { return npy_cpack@c@(npy_creal@c@(a) - npy_creal@c@(b), npy_cimag@c@(a) - npy_cimag@c@(b)); } -static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) +static NPY_INLINE +@ctype@ +cmul@c@(@ctype@ a, @ctype@ b) { @type@ ar, ai, br, bi; ar = npy_creal@c@(a); @@ -85,7 +94,9 @@ static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) return npy_cpack@c@(ar*br - ai*bi, ar*bi + ai*br); } -static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) +static NPY_INLINE +@ctype@ +cdiv@c@(@ctype@ a, @ctype@ b) { @type@ ar, ai, br, bi, abs_br, abs_bi; ar = npy_creal@c@(a); @@ -113,12 +124,16 @@ static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) } } -static NPY_INLINE @ctype@ cneg@c@(@ctype@ a) +static NPY_INLINE +@ctype@ +cneg@c@(@ctype@ a) { return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a)); } -static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) +static NPY_INLINE +@ctype@ +cmuli@c@(@ctype@ a) { return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a)); } @@ -128,20 +143,23 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) *=========================================================*/ #ifndef HAVE_CABS@C@ -@type@ npy_cabs@c@(@ctype@ z) +@type@ +npy_cabs@c@(@ctype@ z) { return npy_hypot@c@(npy_creal@c@(z), npy_cimag@c@(z)); } #endif #ifndef HAVE_CARG@C@ -@type@ npy_carg@c@(@ctype@ z) +@type@ +npy_carg@c@(@ctype@ z) { return npy_atan2@c@(npy_cimag@c@(z), npy_creal@c@(z)); } #endif -/* cexp and (ccos, csin)h functions need to calculate exp scaled by another +/* + * 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. @@ -164,7 +182,9 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) #define SCALED_CEXP_LOWERL 11357.216553474703895L #define SCALED_CEXP_UPPERL 22756.021937783004509L -static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) +static +@ctype@ +_npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) { #if @precision@ == 1 const npy_int k = 235; @@ -189,7 +209,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) } #ifndef HAVE_CEXP@C@ -@ctype@ npy_cexp@c@(@ctype@ z) +@ctype@ +npy_cexp@c@(@ctype@ z) { @type@ x, c, s; @type@ r, i; @@ -210,41 +231,49 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) if (npy_isfinite(i)) { ret = npy_cpack@c@(x * c, x * s); - } else { + } + else { ret = npy_cpack@c@(NPY_NAN@C@, npy_copysign@c@(NPY_NAN@C@, i)); } } - } else if (npy_isnan(r)) { + } + else if (npy_isnan(r)) { /* r is nan */ if (i == 0) { ret = z; - } else { + } + else { ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN@C@, i)); } - } else { + } + else { /* r is +- inf */ if (r > 0) { if (i == 0) { ret = npy_cpack@c@(r, i); - } else if (npy_isfinite(i)) { + } + else if (npy_isfinite(i)) { c = npy_cos@c@(i); s = npy_sin@c@(i); ret = npy_cpack@c@(r * c, r * s); - } else { + } + else { /* x = +inf, y = +-inf | nan */ npy_set_floatstatus_invalid(); ret = npy_cpack@c@(r, NPY_NAN@C@); } - } else { + } + else { if (npy_isfinite(i)) { x = npy_exp@c@(r); c = npy_cos@c@(i); s = npy_sin@c@(i); ret = npy_cpack@c@(x * c, x * s); - } else { + } + else { /* x = -inf, y = nan | +i inf */ ret = npy_cpack@c@(0, 0); } @@ -261,29 +290,30 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * 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. */ -@ctype@ npy_clog@c@(@ctype@ z) +@ctype@ +npy_clog@c@(@ctype@ z) { @type@ ax = npy_fabs@c@(npy_creal@c@(z)); @type@ ay = npy_fabs@c@(npy_cimag@c@(z)); @@ -319,6 +349,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) } } ri = npy_carg@c@(z); + return npy_cpack@c@(rr, ri); } #endif @@ -328,7 +359,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ #define THRESH (@TMAX@ / (1 + NPY_SQRT2@c@)) -@ctype@ npy_csqrt@c@(@ctype@ z) +@ctype@ +npy_csqrt@c@(@ctype@ z) { @ctype@ result; @type@ a, b; @@ -339,10 +371,12 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) b = npy_cimag@c@(z); /* Handle special cases. */ - if (a == 0 && b == 0) + if (a == 0 && b == 0) { return (npy_cpack@c@(0, b)); - if (npy_isinf(b)) + } + if (npy_isinf(b)) { return (npy_cpack@c@(NPY_INFINITY@C@, b)); + } if (npy_isnan(a)) { t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ return (npy_cpack@c@(a, t)); /* return NaN + NaN i */ @@ -354,10 +388,12 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * csqrt(-inf + NaN i) = NaN +- inf i * csqrt(-inf + y i) = 0 + inf i */ - if (npy_signbit(a)) + if (npy_signbit(a)) { return (npy_cpack@c@(npy_fabs@c@(b - b), npy_copysign@c@(a, b))); - else + } + else { return (npy_cpack@c@(a, npy_copysign@c@(b - b, b))); + } } /* * The remaining special case (b is NaN) is handled just fine by @@ -369,7 +405,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) a *= 0.25; b *= 0.25; scale = 1; - } else { + } + else { scale = 0; } @@ -377,22 +414,26 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) if (a >= 0) { t = npy_sqrt@c@((a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(t, b / (2 * t)); - } else { + } + else { t = npy_sqrt@c@((-a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(npy_fabs@c@(b) / (2 * t), npy_copysign@c@(t, b)); } /* Rescale. */ - if (scale) + if (scale) { return (npy_cpack@c@(npy_creal@c@(result) * 2, npy_cimag@c@(result))); - else + } + else { return (result); + } } #undef THRESH #endif #ifndef HAVE_CPOW@C@ -@ctype@ npy_cpow@c@ (@ctype@ a, @ctype@ b) +@ctype@ +npy_cpow@c@ (@ctype@ a, @ctype@ b) { npy_intp n; @type@ ar = npy_creal@c@(a); @@ -410,9 +451,10 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) } else { volatile @type@ tmp = NPY_INFINITY@C@; - /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike - * for reals, c0**p, with `p` negative is in general - * ill-defined. + /* + * NB: there are four complex zeros; c0 = (+-0, +-0), so that + * unlike for reals, c0**p, with `p` negative is in general + * ill-defined. * * c0**z with z complex is also ill-defined. */ @@ -440,18 +482,25 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) else if (n > -100 && n < 100) { @ctype@ p, aa; npy_intp mask = 1; - if (n < 0) n = -n; + if (n < 0) { + n = -n; + } aa = c_1@c@; p = npy_cpack@c@(ar, ai); while (1) { - if (n & mask) + if (n & mask) { aa = cmul@c@(aa,p); + } mask <<= 1; - if (n < mask || mask <= 0) break; + if (n < mask || mask <= 0) { + break; + } p = cmul@c@(p,p); } r = npy_cpack@c@(npy_creal@c@(aa), npy_cimag@c@(aa)); - if (br < 0) r = cdiv@c@(c_1@c@, r); + if (br < 0) { + r = cdiv@c@(c_1@c@, r); + } return r; } } @@ -465,7 +514,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #endif #ifndef HAVE_CCOS@C@ -@ctype@ npy_ccos@c@(@ctype@ z) +@ctype@ +npy_ccos@c@(@ctype@ z) { /* ccos(z) = ccosh(I * z) */ return npy_ccosh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); @@ -473,7 +523,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #endif #ifndef HAVE_CSIN@C@ -@ctype@ npy_csin@c@(@ctype@ z) +@ctype@ +npy_csin@c@(@ctype@ z) { /* csin(z) = -I * csinh(I * z) */ z = npy_csinh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); @@ -482,7 +533,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #endif #ifndef HAVE_CTAN@C@ -@ctype@ npy_ctan@c@(@ctype@ z) +@ctype@ +npy_ctan@c@(@ctype@ z) { /* ctan(z) = -I * ctanh(I * z) */ z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); @@ -506,7 +558,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG) * although the exact value assigned to CCOSH_BIG is not so important */ -@ctype@ npy_ccosh@c@(@ctype@ z) +@ctype@ +npy_ccosh@c@(@ctype@ z) { #if @precision@ == 1 const npy_float CCOSH_BIG = 9.0f; @@ -537,12 +590,15 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* Handle the nearly-non-exceptional cases where x and y are finite. */ if (xfinite && yfinite) { - if (y == 0) + if (y == 0) { return npy_cpack@c@(npy_cosh@c@(x), x * y); + } absx = npy_fabs@c@(x); - if (absx < CCOSH_BIG) /* small x: normal case */ + if (absx < CCOSH_BIG) { + /* small x: normal case */ return npy_cpack@c@(npy_cosh@c@(x) * npy_cos@c@(y), npy_sinh@c@(x) * npy_sin@c@(y)); + } /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { @@ -550,12 +606,14 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) h = npy_exp@c@(absx) * 0.5@c@; return npy_cpack@c@(h * npy_cos@c@(y), npy_copysign@c@(h, x) * npy_sin@c@(y)); - } else if (absx < SCALED_CEXP_UPPER@C@) { + } + else if (absx < SCALED_CEXP_UPPER@C@) { /* x < 1455: scale to avoid overflow */ z = _npy_scaled_cexp@c@(absx, y, -1); return npy_cpack@c@(npy_creal@c@(z), npy_cimag@c@(z) * npy_copysign@c@(1, x)); - } else { + } + else { /* x >= 1455: the result always overflows */ h = CCOSH_HUGE * x; return npy_cpack@c@(h * h * npy_cos@c@(y), h * npy_sin@c@(y)); @@ -571,8 +629,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * The sign of 0 in the result is unspecified. Choice = normally * the same as d(NaN). */ - if (x == 0 && !yfinite) + if (x == 0 && !yfinite) { return npy_cpack@c@(y - y, npy_copysign@c@(0, x * (y - y))); + } /* * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0. @@ -580,8 +639,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * 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) + if (y == 0 && !xfinite) { return npy_cpack@c@(x * x, npy_copysign@c@(0, x) * y); + } /* * cosh(x +- I Inf) = dNaN + I dNaN. @@ -591,8 +651,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * Optionally raises the invalid floating-point exception for finite * nonzero x. Choice = don't raise (except for signaling NaNs). */ - if (xfinite && !yfinite) + if (xfinite && !yfinite) { return npy_cpack@c@(y - y, x * (y - y)); + } /* * cosh(+-Inf + I NaN) = +Inf + I d(NaN). @@ -604,8 +665,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * cosh(+-Inf + I y) = +Inf cos(y) +- I Inf sin(y) */ if (npy_isinf(x)) { - if (!yfinite) + if (!yfinite) { return npy_cpack@c@(x * x, x * (y - y)); + } return npy_cpack@c@((x * x) * npy_cos@c@(y), x * npy_sin@c@(y)); } @@ -638,7 +700,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * Exceptional values are noted in the comments within the source code. * These values and the return value were taken from n1124.pdf. */ -@ctype@ npy_csinh@c@(@ctype@ z) +@ctype@ +npy_csinh@c@(@ctype@ z) { #if @precision@ == 1 const npy_float CSINH_BIG = 9.0f; @@ -669,12 +732,15 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* Handle the nearly-non-exceptional cases where x and y are finite. */ if (xfinite && yfinite) { - if (y == 0) + if (y == 0) { return npy_cpack@c@(npy_sinh@c@(x), y); + } absx = npy_fabs@c@(x); - if (absx < CSINH_BIG) /* small x: normal case */ + if (absx < CSINH_BIG) { + /* small x: normal case */ return npy_cpack@c@(npy_sinh@c@(x) * npy_cos@c@(y), npy_cosh@c@(x) * npy_sin@c@(y)); + } /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { @@ -682,12 +748,14 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) h = npy_exp@c@(npy_fabs@c@(x)) * 0.5@c@; return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), h * npy_sin@c@(y)); - } else if (x < SCALED_CEXP_UPPER@C@) { + } + else if (x < SCALED_CEXP_UPPER@C@) { /* x < 1455: scale to avoid overflow */ z = _npy_scaled_cexp@c@(absx, y, -1); return npy_cpack@c@(npy_creal@c@(z) * npy_copysign@c@(1, x), npy_cimag@c@(z)); - } else { + } + else { /* x >= 1455: the result always overflows */ h = CSINH_HUGE * x; return npy_cpack@c@(h * npy_cos@c@(y), h * h * npy_sin@c@(y)); @@ -703,8 +771,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * The sign of 0 in the result is unspecified. Choice = normally * the same as d(NaN). */ - if (x == 0 && !yfinite) + if (x == 0 && !yfinite) { return npy_cpack@c@(npy_copysign@c@(0, x * (y - y)), y - y); + } /* * sinh(+-Inf +- I 0) = +-Inf + I +-0. @@ -712,8 +781,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * sinh(NaN +- I 0) = d(NaN) + I +-0. */ if (y == 0 && !xfinite) { - if (npy_isnan(x)) + if (npy_isnan(x)) { return z; + } return npy_cpack@c@(x, npy_copysign@c@(0, y)); } @@ -725,8 +795,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * Optionally raises the invalid floating-point exception for finite * nonzero x. Choice = don't raise (except for signaling NaNs). */ - if (xfinite && !yfinite) + if (xfinite && !yfinite) { return npy_cpack@c@(y - y, x * (y - y)); + } /* * sinh(+-Inf + I NaN) = +-Inf + I d(NaN). @@ -740,9 +811,11 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * sinh(+-Inf + I y) = +-Inf cos(y) + I Inf sin(y) */ if (!xfinite && !npy_isnan(x)) { - if (!yfinite) + if (!yfinite) { return npy_cpack@c@(x * x, x * (y - y)); - return npy_cpack@c@(x * npy_cos@c@(y), NPY_INFINITY@C@ * npy_sin@c@(y)); + } + return npy_cpack@c@(x * npy_cos@c@(y), + NPY_INFINITY@C@ * npy_sin@c@(y)); } /* @@ -808,7 +881,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #define TANHF_HUGE 11.0F #define TANHL_HUGE 42.0L -@ctype@ npy_ctanh@c@(@ctype@ z) +@ctype@ +npy_ctanh@c@(@ctype@ z) { @type@ x, y; @type@ t, beta, s, rho, denom; @@ -833,19 +907,22 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * y is infinite. */ if (!npy_isfinite(x)) { - if (npy_isnan(x)) + if (npy_isnan(x)) { return npy_cpack@c@(x, (y == 0 ? y : x * y)); + } return npy_cpack@c@(npy_copysign@c@(1,x), - npy_copysign@c@(0, - npy_isinf(y) ? y : npy_sin@c@(y) * npy_cos@c@(y))); + npy_copysign@c@(0, + npy_isinf(y) ? + y : npy_sin@c@(y) * npy_cos@c@(y))); } /* * ctanh(x + i NAN) = NaN + i NaN * ctanh(x +- i Inf) = NaN + i NaN */ - if (!npy_isfinite(y)) + if (!npy_isfinite(y)) { return (npy_cpack@c@(y - y, y - y)); + } /* * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the @@ -854,8 +931,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) */ if (npy_fabs@c@(x) >= TANH@C@_HUGE) { @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); - return (npy_cpack@c@(npy_copysign@c@(1, x), - 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); + return npy_cpack@c@(npy_copysign@c@(1, x), + 4 * npy_sin@c@(y) * npy_cos@c@(y) * + exp_mx * exp_mx); } /* Kahan's algorithm */ @@ -923,12 +1001,15 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * Function f(a, b, hypot_a_b) = (hypot(a, b) - b) / 2. * Pass hypot(a, b) as the third argument. */ -static inline @type@ _f@c@(@type@ a, @type@ b, @type@ hypot_a_b) +static NPY_INLINE @type@ +_f@c@(@type@ a, @type@ b, @type@ hypot_a_b) { - if (b < 0) + if (b < 0) { return ((hypot_a_b - b) / 2); - if (b == 0) + } + if (b == 0) { return (a / 2); + } return (a * a / (hypot_a_b + b) / 2); } @@ -942,7 +1023,8 @@ static inline @type@ _f@c@(@type@ a, @type@ b, @type@ hypot_a_b) * 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@c@(@type@ x, @type@ y, @type@ *rx, +static NPY_INLINE void +_do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, npy_int *B_is_usable, @type@ *B, @type@ *sqrt_A2my2, @type@ *new_y) { #if @precision@ == 1 @@ -977,8 +1059,9 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, * be so because of rounding errors. So we will make certain it is * so. */ - if (A < 1) + if (A < 1) { A = 1; + } if (A < A_crossover) { /* @@ -991,26 +1074,30 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, * A = 1 (inexactly). */ *rx = npy_sqrt@c@(x); - } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + } + else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { /* * Underflow will not occur because * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN */ Am1 = _f@c@(x, 1 + y, R) + _f@c@(x, 1 - y, S); *rx = npy_log1p@c@(Am1 + npy_sqrt@c@(Am1 * (A + 1))); - } else if (y < 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@c@((1 - y) * (1 + y)); - } else { /* if (y > 1) */ + } + else { /* if (y > 1) */ /* * A-1 = y-1 (inexactly). */ *rx = npy_log1p@c@((y - 1) + npy_sqrt@c@((y - 1) * (y + 1))); } - } else { + } + else { *rx = npy_log@c@(A + npy_sqrt@c@(A * A - 1)); } @@ -1044,7 +1131,8 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, * A = 1 (inexactly). */ *sqrt_A2my2 = npy_sqrt@c@(x) * npy_sqrt@c@((A + y) / 2); - } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + } + else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { /* * Underflow will not occur because * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN @@ -1053,7 +1141,8 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, */ Amy = _f@c@(x, y + 1, R) + _f@c@(x, y - 1, S); *sqrt_A2my2 = npy_sqrt@c@(Amy * (A + y)); - } else if (y > 1) { + } + else if (y > 1) { /* * fp = x*x/(y+1)/4, fm = x*x/(y-1)/4, and * A = y (inexactly). @@ -1064,7 +1153,8 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, *sqrt_A2my2 = x * (4 / @TEPS@ / @TEPS@) * y / npy_sqrt@c@((y + 1) * (y - 1)); *new_y = y * (4 / @TEPS@ / @TEPS@); - } else { /* if (y < 1) */ + } + else { /* if (y < 1) */ /* * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and * A = 1 (inexactly). @@ -1077,7 +1167,8 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, /* * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON. */ -static inline void _clog_for_large_values@c@(@type@ x, @type@ y, +static NPY_INLINE void +_clog_for_large_values@c@(@type@ x, @type@ y, @type@ *rr, @type@ *ri) { #if @precision@ == 1 @@ -1114,7 +1205,7 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, * Dividing by E causes an insignificant loss of accuracy; however * this method is still poor since it is uneccessarily slow. */ - if (ax > @TMAX@ / 2) { + if (ax > @TMAX@ / 2) { *rr = npy_log@c@(npy_hypot@c@(x / NPY_E@c@, y / NPY_E@c@)) + 1; } /* @@ -1132,7 +1223,8 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, #endif #ifndef HAVE_CACOS@C@ -@ctype@ npy_cacos@c@(@ctype@ z) +@ctype@ +npy_cacos@c@(@ctype@ z) { #if @precision@ == 1 /* this is sqrt(6*EPS) */ @@ -1163,14 +1255,17 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, if (npy_isnan(x) || npy_isnan(y)) { /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */ - if (npy_isinf(x)) + if (npy_isinf(x)) { return npy_cpack@c@(y + y, -NPY_INFINITY@C@); + } /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */ - if (npy_isinf(y)) + if (npy_isinf(y)) { return npy_cpack@c@(x + x, -y); + } /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */ - if (x == 0) + if (x == 0) { return npy_cpack@c@(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 @@ -1184,41 +1279,51 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, _clog_for_large_values@c@(x, y, &wx, &wy); rx = npy_fabs@c@(wy); ry = wx + NPY_LOGE2@c@; - if (sy == 0) + if (sy == 0) { ry = -ry; + } return npy_cpack@c@(rx, ry); } /* Avoid spuriously raising inexact for z = 1. */ - if (x == 1 && y == 0) + if (x == 1 && y == 0) { return npy_cpack@c@(0, -y); + } /* All remaining cases are inexact. */ raise_inexact(); - if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) + if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) { return npy_cpack@c@(pio2_hi - (x - pio2_lo), -y); + } _do_hard_work@c@(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x); if (B_is_usable) { - if (sx == 0) + if (sx == 0) { rx = npy_acos@c@(B); - else + } + else { rx = npy_acos@c@(-B); - } else { - if (sx == 0) + } + } + else { + if (sx == 0) { rx = npy_atan2@c@(sqrt_A2mx2, new_x); - else + } + else { rx = npy_atan2@c@(sqrt_A2mx2, -new_x); + } } - if (sy == 0) + if (sy == 0) { ry = -ry; + } return npy_cpack@c@(rx, ry); } #endif #ifndef HAVE_CASIN@C@ -@ctype@ npy_casin@c@(@ctype@ z) +@ctype@ +npy_casin@c@(@ctype@ z) { /* casin(z) = I * conj( casinh(I * conj(z)) ) */ z = npy_casinh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); @@ -1227,7 +1332,8 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, #endif #ifndef HAVE_CATAN@C@ -@ctype@ npy_catan@c@(@ctype@ z) +@ctype@ +npy_catan@c@(@ctype@ z) { /* catan(z) = I * conj( catanh(I * conj(z)) ) */ z = npy_catanh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); @@ -1236,7 +1342,8 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, #endif #ifndef HAVE_CACOSH@C@ -@ctype@ npy_cacosh@c@(@ctype@ z) +@ctype@ +npy_cacosh@c@(@ctype@ z) { /* * cacosh(z) = I*cacos(z) or -I*cacos(z) @@ -1249,15 +1356,18 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, rx = npy_creal@c@(w); ry = npy_cimag@c@(w); /* cacosh(NaN + I*NaN) = NaN + I*NaN */ - if (npy_isnan(rx) && npy_isnan(ry)) + if (npy_isnan(rx) && npy_isnan(ry)) { return npy_cpack@c@(ry, rx); + } /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */ /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */ - if (npy_isnan(rx)) + if (npy_isnan(rx)) { return npy_cpack@c@(npy_fabs@c@(ry), rx); + } /* cacosh(0 + I*NaN) = NaN + I*NaN */ - if (npy_isnan(ry)) + if (npy_isnan(ry)) { return npy_cpack@c@(ry, ry); + } return npy_cpack@c@(npy_fabs@c@(ry), npy_copysign@c@(rx, npy_cimag@c@(z))); } #endif @@ -1271,7 +1381,8 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, * Im(casinh(z)) = sign(x)*atan2(sign(x)*y, fabs(x)) + O(y/z^3) * as z -> infinity, uniformly in y */ -@ctype@ npy_casinh@c@(@ctype@ z) +@ctype@ +npy_casinh@c@(@ctype@ z) { #if @precision@ == 1 /* this is sqrt(6*EPS) */ @@ -1299,14 +1410,17 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, if (npy_isnan(x) || npy_isnan(y)) { /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */ - if (npy_isinf(x)) + if (npy_isinf(x)) { return npy_cpack@c@(x, y + y); + } /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */ - if (npy_isinf(y)) + if (npy_isinf(y)) { return npy_cpack@c@(y, x + x); + } /* casinh(NaN + I*0) = NaN + I*0 */ - if (y == 0) + if (y == 0) { return npy_cpack@c@(x + x, y); + } /* * All other cases involving NaN return NaN + I*NaN. * C99 leaves it optional whether to raise invalid if one of @@ -1329,20 +1443,24 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, } /* Avoid spuriously raising inexact for z = 0. */ - if (x == 0 && y == 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) + if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) { return (z); + } _do_hard_work@c@(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y); - if (B_is_usable) + if (B_is_usable) { ry = npy_asin@c@(B); - else + } + else { ry = npy_atan2@c@(new_y, sqrt_A2my2); + } return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); } #endif @@ -1355,7 +1473,8 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, * Assumes y is non-negative. * Assumes fabs(x) >= DBL_EPSILON. */ -static inline @type@ _sum_squares@c@(@type@ x, @type@ y) +static NPY_INLINE @type@ +_sum_squares@c@(@type@ x, @type@ y) { #if @precision@ == 1 const npy_float SQRT_MIN = 1.0842022e-19f; @@ -1368,8 +1487,9 @@ const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */ const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; #endif /* Avoid underflow when y is small. */ - if (y < SQRT_MIN) + if (y < SQRT_MIN) { return (x * x); + } return (x * x + y * y); } @@ -1386,7 +1506,8 @@ const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; #if @precision@ == 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) +static NPY_INLINE npy_float +_real_part_reciprocalf(npy_float x, npy_float y) { npy_float scale; npy_uint32 hx, hy; @@ -1396,12 +1517,15 @@ static inline npy_float _real_part_reciprocalf(npy_float x, npy_float y) ix = hx & 0x7f800000; GET_FLOAT_WORD(hy, y); iy = hy & 0x7f800000; - if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) + if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) { return (1 / x); - if (iy - ix >= CUTOFF << 23) + } + if (iy - ix >= CUTOFF << 23) { return (x / y / y); - if (ix <= (BIAS + FLT_MAX_EXP / 2 - CUTOFF) << 23) + } + 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; @@ -1414,7 +1538,8 @@ static inline npy_float _real_part_reciprocalf(npy_float x, npy_float y) #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) +static NPY_INLINE npy_double +_real_part_reciprocal(npy_double x, npy_double y) { npy_double scale; npy_uint32 hx, hy; @@ -1428,12 +1553,17 @@ static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) ix = hx & 0x7ff00000; GET_HIGH_WORD(hy, y); iy = hy & 0x7ff00000; - if (ix - iy >= CUTOFF << 20 || npy_isinf(x)) - return (1 / x); /* +-Inf -> +-0 is special */ - if (iy - ix >= CUTOFF << 20) - return (x / y / y); /* should avoid double div, but hard */ - if (ix <= (BIAS + DBL_MAX_EXP / 2 - CUTOFF) << 20) + 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; @@ -1447,7 +1577,8 @@ static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) #ifndef HAVE_LDOUBLE_DOUBLE_DOUBLE_BE #define BIAS (LDBL_MAX_EXP - 1) #define CUTOFF (LDBL_MANT_DIG / 2 + 1) -static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, +static NPY_INLINE npy_longdouble +_real_part_reciprocall(npy_longdouble x, npy_longdouble y) { npy_longdouble scale; @@ -1458,12 +1589,15 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, ix = GET_LDOUBLE_EXP(ux); uy.e = y; iy = GET_LDOUBLE_EXP(uy); - if (ix - iy >= CUTOFF || npy_isinf(x)) + if (ix - iy >= CUTOFF || npy_isinf(x)) { return (1/x); - if (iy - ix >= CUTOFF) + } + if (iy - ix >= CUTOFF) { return (x/y/y); - if (ix <= BIAS + LDBL_MAX_EXP / 2 - CUTOFF) + } + 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; @@ -1474,7 +1608,8 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, #undef BIAS #undef CUTOFF #else -static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, +static NPY_INLINE npy_longdouble +_real_part_reciprocall(npy_longdouble x, npy_longdouble y) { return x/(x*x + y*y); @@ -1482,7 +1617,8 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, #endif #endif -@ctype@ npy_catanh@c@(@ctype@ z) +@ctype@ +npy_catanh@c@(@ctype@ z) { #if @precision@ == 1 /* this is sqrt(3*EPS) */ @@ -1508,21 +1644,25 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, ay = npy_fabs@c@(y); /* This helps handle many cases. */ - if (y == 0 && ax <= 1) + if (y == 0 && ax <= 1) { return npy_cpack@c@(npy_atanh@c@(x), y); + } /* To ensure the same accuracy as atan(), and to filter out z = 0. */ - if (x == 0) + if (x == 0) { return npy_cpack@c@(x, npy_atan@c@(y)); + } if (npy_isnan(x) || npy_isnan(y)) { /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */ - if (npy_isinf(x)) + if (npy_isinf(x)) { return npy_cpack@c@(npy_copysign@c@(0, x), y + y); + } /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */ - if (npy_isinf(y)) + if (npy_isinf(y)) { return npy_cpack@c@(npy_copysign@c@(0, x), npy_copysign@c@(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 @@ -1531,9 +1671,10 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, return npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); } - if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { return npy_cpack@c@(_real_part_reciprocal@c@(x, y), npy_copysign@c@(pio2_hi + pio2_lo, y)); + } if (ax < SQRT_3_EPSILON / 2 && ay < SQRT_3_EPSILON / 2) { /* @@ -1545,17 +1686,22 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, return (z); } - if (ax == 1 && ay < @TEPS@) + if (ax == 1 && ay < @TEPS@) { rx = (NPY_LOGE2@c@ - npy_log@c@(ay)) / 2; - else + } + else { rx = npy_log1p@c@(4 * ax / _sum_squares@c@(ax - 1, ay)) / 4; + } - if (ax == 1) + if (ax == 1) { ry = npy_atan2@c@(2, -ay) / 2; - else if (ay < @TEPS@) + } + else if (ay < @TEPS@) { ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax)) / 2; - else + } + else { ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2; + } return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); } @@ -1578,7 +1724,8 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, * #KIND = CABS,CARG# */ #ifdef HAVE_@KIND@@C@ -@type@ npy_@kind@@c@(@ctype@ z) +@type@ +npy_@kind@@c@(@ctype@ z) { __@ctype@_to_c99_cast z1; z1.npy_z = z; @@ -1588,11 +1735,14 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, /**end repeat1**/ /**begin repeat1 - * #kind = cexp,clog,csqrt,ccos,csin,ctan,ccosh,csinh,ctanh,cacos,casin,catan,cacosh,casinh,catanh# - * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CCOSH,CSINH,CTANH,CACOS,CASIN,CATAN,CACOSH,CASINH,CATANH# + * #kind = cexp,clog,csqrt,ccos,csin,ctan,ccosh,csinh,ctanh, + * cacos,casin,catan,cacosh,casinh,catanh# + * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CCOSH,CSINH,CTANH, + * CACOS,CASIN,CATAN,CACOSH,CASINH,CATANH# */ #ifdef HAVE_@KIND@@C@ -@ctype@ npy_@kind@@c@(@ctype@ z) +@ctype@ +npy_@kind@@c@(@ctype@ z) { __@ctype@_to_c99_cast z1; __@ctype@_to_c99_cast ret; @@ -1608,7 +1758,8 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, * #KIND = CPOW# */ #ifdef HAVE_@KIND@@C@ -@ctype@ npy_@kind@@c@(@ctype@ x, @ctype@ y) +@ctype@ +npy_@kind@@c@(@ctype@ x, @ctype@ y) { __@ctype@_to_c99_cast xcast; __@ctype@_to_c99_cast ycast; diff --git a/numpy/core/src/npymath/npymath_tests.c b/numpy/core/src/npymath/npymath_tests.c index 6c4be49b7102..81be534bb5f1 100644 --- a/numpy/core/src/npymath/npymath_tests.c +++ b/numpy/core/src/npymath/npymath_tests.c @@ -37,7 +37,9 @@ #undef LONGDOUBLE #define TESTFUNC_INT(func, suffix) \ - static PyObject * CONCAT3(_test_, func, suffix)(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) \ + static PyObject * \ + CONCAT3(_test_, func, suffix)(PyObject *NPY_UNUSED(self), \ + PyObject *NPY_UNUSED(args)) \ { \ PyObject *errs; \ errs = CONCAT3(test_, func, suffix)(); \ @@ -61,7 +63,9 @@ TESTFUNC_INT(func, l) #define TESTMETHODDEF_INT(func, suffix) \ - {STRINGIZE(CONCAT3(test_, func, suffix)), CONCAT3(_test_, func, suffix), METH_VARARGS, ""} + {STRINGIZE(CONCAT3(test_, func, suffix)), \ + CONCAT3(_test_, func, suffix), \ + METH_VARARGS, ""} #define TESTMETHODDEF(func) \ TESTMETHODDEF_INT(func, f), \ @@ -132,4 +136,3 @@ initnpymath_tests(void) Py_InitModule("npymath_tests", methods); #endif } -