diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index e3c9cf28bee0..1d2dc270c1dc 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -20,6 +20,12 @@ class FullTypeDescr(object): pass +class FuncNameSuffix(object): + """Stores the suffix to append when generating function names. + """ + def __init__(self, suffix): + self.suffix = suffix + class TypeDescription(object): """Type signature for a ufunc. @@ -27,7 +33,7 @@ class TypeDescription(object): ---------- type : str Character representing the nominal type. - func_data : str or None or FullTypeDescr, optional + func_data : str or None or FullTypeDescr or FuncNameSuffix, optional The string representing the expression to insert into the data array, if any. in_ : str or None, optional @@ -795,6 +801,30 @@ def english_upper(s): None, TD(flts), ), +'ldexp' : + Ufunc(2, 1, None, + docstrings.get('numpy.core.umath.ldexp'), + None, + [TypeDescription('e', None, 'ei', 'e'), + TypeDescription('f', None, 'fi', 'f'), + TypeDescription('e', FuncNameSuffix('long'), 'el', 'e'), + TypeDescription('f', FuncNameSuffix('long'), 'fl', 'f'), + TypeDescription('d', None, 'di', 'd'), + TypeDescription('d', FuncNameSuffix('long'), 'dl', 'd'), + TypeDescription('g', None, 'gi', 'g'), + TypeDescription('g', FuncNameSuffix('long'), 'gl', 'g'), + ], + ), +'frexp' : + Ufunc(1, 2, None, + docstrings.get('numpy.core.umath.frexp'), + None, + [TypeDescription('e', None, 'e', 'ei'), + TypeDescription('f', None, 'f', 'fi'), + TypeDescription('d', None, 'd', 'di'), + TypeDescription('g', None, 'g', 'gi'), + ], + ) } if sys.version_info[0] >= 3: @@ -854,7 +884,7 @@ def make_arrays(funcdict): thedict = chartotype1 # one input and one output for t in uf.type_descriptions: - if t.func_data not in (None, FullTypeDescr): + if t.func_data not in (None, FullTypeDescr) and not isinstance(t.func_data, FuncNameSuffix): funclist.append('NULL') astype = '' if not t.astype is None: @@ -880,6 +910,10 @@ def make_arrays(funcdict): tname = english_upper(chartoname[t.type]) datalist.append('(void *)NULL') funclist.append('%s_%s_%s_%s' % (tname, t.in_, t.out, name)) + elif isinstance(t.func_data, FuncNameSuffix): + datalist.append('(void *)NULL') + tname = english_upper(chartoname[t.type]) + funclist.append('%s_%s_%s' % (tname, name, t.func_data.suffix)) else: datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index 59e8777506f0..28564d7af343 100644 --- a/numpy/core/code_generators/ufunc_docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -3312,3 +3312,65 @@ def add_newdoc(place, name, doc): array([0, 0, 0, 0, 1]) """) + +add_newdoc('numpy.core.umath', 'ldexp', + """ + Scale floating point value by a power of 2, element-wise. + + Equivalent to ``x1 * 2**x2``. + + Parameters + ---------- + x1 : array_like + Input array to be scaled. + x2 : array_like + Input array of exponents. + + Returns + ------- + y: ndarray + The corresponding scaled values. + + See Also + -------- + frexp : Extract significand and exponent from floating point number + + Examples + -------- + >>> np.ldexp(0.5, np.arange(-1,4)) + array([ 0.25, 0.5 , 1. , 2. , 4. ]) + + """) + +add_newdoc('numpy.core.umath', 'frexp', + """ + Extract significand and exponent from a floating point number. + + All floating point numbers are of the form: ``y1 * 2**y2`` where `y1`, + the significand, is a number between 0.5 and 1, or 0 and `y2`, the + exponent, is an integer. + + Parameters + ---------- + x: array_like + Input array. + + Returns + ------- + y1 : ndarray + The normalized significands from `x`. + y2 : ndarray + The exponents from `x`. + + See Also + -------- + ldexp : Scale floating point number by power of 2 + + Examples + -------- + >>> np.frexp([0.25, 0.5, 1, 2, 4]) + (array([ 0.5, 0.5, 0.5, 0.5, 0.5]), + array([-1, 0, 1, 2, 3], dtype=int32)) + + """) + diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index b7920460d88a..9a8c8b407b92 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -118,10 +118,6 @@ double npy_tanh(double x); double npy_asin(double x); double npy_acos(double x); double npy_atan(double x); -double npy_aexp(double x); -double npy_alog(double x); -double npy_asqrt(double x); -double npy_afabs(double x); double npy_log(double x); double npy_log10(double x); @@ -152,6 +148,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 */ @@ -256,8 +255,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); @@ -297,6 +299,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 */ @@ -420,6 +425,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 @@ -435,6 +453,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 @@ -450,6 +482,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 a93311ec5ccd..e05253659be1 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -220,6 +220,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') @@ -690,7 +716,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') @@ -996,6 +1023,16 @@ def get_dotblas_sources(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 2db4b4331ed6..fc4452d419c1 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -143,8 +143,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 6e3426606142..b3157f8e0787 100644 --- a/numpy/core/src/npymath/ieee754.c.src +++ b/numpy/core/src/npymath/ieee754.c.src @@ -556,258 +556,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.c.src b/numpy/core/src/npymath/npy_math.c.src index 61f1d79abe9d..ebc209fc2992 100644 --- a/numpy/core/src/npymath/npy_math.c.src +++ b/numpy/core/src/npymath/npy_math.c.src @@ -305,6 +305,7 @@ double npy_log2(double x) * asinh, acosh, atanh * * hypot, atan2, pow, fmod, modf + * ldexp, frexp * * We assume the above are always available in their double versions. * @@ -367,6 +368,26 @@ double npy_log2(double x) } #endif +#ifdef ldexp@c@ +#undef ldexp@c@ +#endif +#ifndef HAVE_LDEXP@C@ +@type@ npy_ldexp@c@(@type@ x, npy_int exp) +{ + return (@type@) npy_ldexp((double)x, exp); +} +#endif + +#ifdef frexp@c@ +#undef frexp@c@ +#endif +#ifndef HAVE_FREXP@C@ +@type@ npy_frexp@c@(@type@ x, npy_int* exp) +{ + return (@type@) npy_frexp(x, exp); +} +#endif + /**end repeat**/ @@ -413,6 +434,20 @@ double npy_log2(double x) } #endif +#ifdef HAVE_LDEXP@C@ +@type@ npy_ldexp@c@(@type@ x, npy_int exp) +{ + return ldexp@c@(x, exp); +} +#endif + +#ifdef HAVE_FREXP@C@ +@type@ npy_frexp@c@(@type@ x, npy_int* exp) +{ + return frexp@c@(x, exp); +} +#endif + /**end repeat**/ diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 920f107b89b2..8e890f6ce5bf 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**/ @@ -254,8 +1587,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/private/npy_config.h b/numpy/core/src/private/npy_config.h index 453dbd065af9..69786fea4db7 100644 --- a/numpy/core/src/private/npy_config.h +++ b/numpy/core/src/private/npy_config.h @@ -10,16 +10,6 @@ #undef HAVE_HYPOT #endif -/* Safe to use ldexp and frexp for long double for MSVC builds */ -#if (NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE) || defined(_MSC_VER) - #ifdef HAVE_LDEXP - #define HAVE_LDEXPL 1 - #endif - #ifdef HAVE_FREXP - #define HAVE_FREXPL 1 - #endif -#endif - /* Disable broken Sun Workshop Pro math functions */ #ifdef __SUNPRO_C #undef HAVE_ATAN2 diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index 9df39e41f0d0..30059eadc993 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -206,49 +206,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) { @@ -257,26 +216,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) { @@ -336,164 +275,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); - return; -} - - -static void -nc_prodi@c@(@ctype@ *x, @ctype@ *r) -{ - @ftype@ xr = x->real; - r->real = -x->imag; - r->imag = xr; + *r = npy_cpow@c@(*a, *b); 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; } @@ -501,134 +304,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; } @@ -653,63 +357,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/loops.c.src b/numpy/core/src/umath/loops.c.src index 43419780e768..2fcb11d547da 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1717,25 +1717,22 @@ NPY_NO_EXPORT void } } -#ifdef HAVE_FREXP@C@ NPY_NO_EXPORT void @TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = frexp@c@(in1, (int *)op2); + *((@type@ *)op1) = npy_frexp@c@(in1, (int *)op2); } } -#endif -#ifdef HAVE_LDEXP@C@ NPY_NO_EXPORT void @TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const int in2 = *(int *)ip2; - *((@type@ *)op1) = ldexp@c@(in1, in2); + *((@type@ *)op1) = npy_ldexp@c@(in1, in2); } } @@ -1752,7 +1749,7 @@ NPY_NO_EXPORT void const long in2 = *(long *)ip2; if (((int)in2) == in2) { /* Range OK */ - *((@type@ *)op1) = ldexp@c@(in1, ((int)in2)); + *((@type@ *)op1) = npy_ldexp@c@(in1, ((int)in2)); } else { /* @@ -1760,15 +1757,14 @@ NPY_NO_EXPORT void * given that exponent has less bits than npy_int. */ if (in2 > 0) { - *((@type@ *)op1) = ldexp@c@(in1, NPY_MAX_INT); + *((@type@ *)op1) = npy_ldexp@c@(in1, NPY_MAX_INT); } else { - *((@type@ *)op1) = ldexp@c@(in1, NPY_MIN_INT); + *((@type@ *)op1) = npy_ldexp@c@(in1, NPY_MIN_INT); } } } } -#endif #define @TYPE@_true_divide @TYPE@_divide @@ -2033,25 +2029,22 @@ HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f } } -#ifdef HAVE_FREXPF NPY_NO_EXPORT void HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const float in1 = npy_half_to_float(*(npy_half *)ip1); - *((npy_half *)op1) = npy_float_to_half(frexpf(in1, (int *)op2)); + *((npy_half *)op1) = npy_float_to_half(npy_frexpf(in1, (int *)op2)); } } -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const float in1 = npy_half_to_float(*(npy_half *)ip1); const int in2 = *(int *)ip2; - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, in2)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, in2)); } } @@ -2068,7 +2061,7 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN const long in2 = *(long *)ip2; if (((int)in2) == in2) { /* Range OK */ - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, ((int)in2))); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, ((int)in2))); } else { /* @@ -2076,15 +2069,14 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN * given that exponent has less bits than npy_int. */ if (in2 > 0) { - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, NPY_MAX_INT)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, NPY_MAX_INT)); } else { - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, NPY_MIN_INT)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, NPY_MIN_INT)); } } } } -#endif #define HALF_true_divide HALF_divide diff --git a/numpy/core/src/umath/loops.h b/numpy/core/src/umath/loops.h index 62890f370b55..40b8532d09f1 100644 --- a/numpy/core/src/umath/loops.h +++ b/numpy/core/src/umath/loops.h @@ -1689,17 +1689,13 @@ HALF_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f NPY_NO_EXPORT void HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPF NPY_NO_EXPORT void HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define HALF_true_divide HALF_divide @@ -1843,17 +1839,13 @@ FLOAT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED( NPY_NO_EXPORT void FLOAT_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPF NPY_NO_EXPORT void FLOAT_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void FLOAT_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void FLOAT_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define FLOAT_true_divide FLOAT_divide @@ -1997,17 +1989,13 @@ DOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED NPY_NO_EXPORT void DOUBLE_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXP NPY_NO_EXPORT void DOUBLE_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXP NPY_NO_EXPORT void DOUBLE_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void DOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define DOUBLE_true_divide DOUBLE_divide @@ -2151,17 +2139,13 @@ LONGDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN NPY_NO_EXPORT void LONGDOUBLE_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPL NPY_NO_EXPORT void LONGDOUBLE_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPL NPY_NO_EXPORT void LONGDOUBLE_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void LONGDOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define LONGDOUBLE_true_divide LONGDOUBLE_divide @@ -2181,14 +2165,14 @@ LONGDOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void * #define CEQ(xr,xi,yr,yi) (xr == yr && xi == yi); #define CNE(xr,xi,yr,yi) (xr != yr || xi != yi); -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CFLOAT_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CFLOAT_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2203,36 +2187,36 @@ CFLOAT_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS NPY_NO_EXPORT void CFLOAT_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CFLOAT_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CFLOAT_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2242,15 +2226,15 @@ CFLOAT_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void CFLOAT_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2276,20 +2260,20 @@ CFLOAT__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED NPY_NO_EXPORT void CFLOAT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CFLOAT_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CFLOAT_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CFLOAT_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CFLOAT_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2297,14 +2281,14 @@ CFLOAT_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED #define CFLOAT_true_divide CFLOAT_divide -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CDOUBLE_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CDOUBLE_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2319,36 +2303,36 @@ CDOUBLE_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU NPY_NO_EXPORT void CDOUBLE_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CDOUBLE_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CDOUBLE_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2358,15 +2342,15 @@ CDOUBLE_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NP NPY_NO_EXPORT void CDOUBLE_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2392,20 +2376,20 @@ CDOUBLE__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSE NPY_NO_EXPORT void CDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CDOUBLE_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CDOUBLE_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CDOUBLE_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2413,14 +2397,14 @@ CDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSE #define CDOUBLE_true_divide CDOUBLE_divide -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CLONGDOUBLE_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CLONGDOUBLE_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2435,36 +2419,36 @@ CLONGDOUBLE_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void CLONGDOUBLE_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CLONGDOUBLE_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CLONGDOUBLE_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2474,15 +2458,15 @@ CLONGDOUBLE_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void NPY_NO_EXPORT void CLONGDOUBLE_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2508,20 +2492,20 @@ CLONGDOUBLE__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_U NPY_NO_EXPORT void CLONGDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CLONGDOUBLE_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CLONGDOUBLE_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CLONGDOUBLE_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CLONGDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2552,81 +2536,81 @@ TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 433 +#line 429 NPY_NO_EXPORT void DATETIME__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void DATETIME_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void DATETIME_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 433 +#line 429 NPY_NO_EXPORT void TIMEDELTA__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void TIMEDELTA_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void TIMEDELTA_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2691,27 +2675,27 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * ***************************************************************************** */ -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2726,3 +2710,4 @@ OBJECT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED */ #endif + diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index a8a58c5def1e..65d7056e7b8f 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -277,17 +277,13 @@ NPY_NO_EXPORT void NPY_NO_EXPORT void @TYPE@_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXP@C@ NPY_NO_EXPORT void @TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXP@C@ NPY_NO_EXPORT void @TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void @TYPE@_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define @TYPE@_true_divide @TYPE@_divide diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c index f395d509671c..25295a2812b1 100644 --- a/numpy/core/src/umath/umathmodule.c +++ b/numpy/core/src/umath/umathmodule.c @@ -201,99 +201,6 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS ** SETUP UFUNCS ** ***************************************************************************** */ - -/* Less automated additions to the ufuncs */ - -static PyUFuncGenericFunction frexp_functions[] = { -#ifdef HAVE_FREXPF - HALF_frexp, - FLOAT_frexp, -#endif - DOUBLE_frexp -#ifdef HAVE_FREXPL - ,LONGDOUBLE_frexp -#endif -}; - -static void * blank3_data[] = { (void *)NULL, (void *)NULL, (void *)NULL}; -static void * blank6_data[] = { (void *)NULL, (void *)NULL, (void *)NULL, - (void *)NULL, (void *)NULL, (void *)NULL}; -static char frexp_signatures[] = { -#ifdef HAVE_FREXPF - NPY_HALF, NPY_HALF, NPY_INT, - NPY_FLOAT, NPY_FLOAT, NPY_INT, -#endif - NPY_DOUBLE, NPY_DOUBLE, NPY_INT -#ifdef HAVE_FREXPL - ,NPY_LONGDOUBLE, NPY_LONGDOUBLE, NPY_INT -#endif -}; - -#if NPY_SIZEOF_LONG == NPY_SIZEOF_INT -#define LDEXP_LONG(typ) typ##_ldexp -#else -#define LDEXP_LONG(typ) typ##_ldexp_long -#endif - -static PyUFuncGenericFunction ldexp_functions[] = { -#ifdef HAVE_LDEXPF - HALF_ldexp, - FLOAT_ldexp, - LDEXP_LONG(HALF), - LDEXP_LONG(FLOAT), -#endif - DOUBLE_ldexp, - LDEXP_LONG(DOUBLE) -#ifdef HAVE_LDEXPL - , - LONGDOUBLE_ldexp, - LDEXP_LONG(LONGDOUBLE) -#endif -}; - -static char ldexp_signatures[] = { -#ifdef HAVE_LDEXPF - NPY_HALF, NPY_INT, NPY_HALF, - NPY_FLOAT, NPY_INT, NPY_FLOAT, - NPY_HALF, NPY_LONG, NPY_HALF, - NPY_FLOAT, NPY_LONG, NPY_FLOAT, -#endif - NPY_DOUBLE, NPY_INT, NPY_DOUBLE, - NPY_DOUBLE, NPY_LONG, NPY_DOUBLE -#ifdef HAVE_LDEXPL - ,NPY_LONGDOUBLE, NPY_INT, NPY_LONGDOUBLE - ,NPY_LONGDOUBLE, NPY_LONG, NPY_LONGDOUBLE -#endif -}; - -static void -InitOtherOperators(PyObject *dictionary) { - PyObject *f; - int num; - - num = sizeof(frexp_functions) / sizeof(frexp_functions[0]); - f = PyUFunc_FromFuncAndData(frexp_functions, blank3_data, - frexp_signatures, num, - 1, 2, PyUFunc_None, "frexp", - "Split the number, x, into a normalized"\ - " fraction (y1) and exponent (y2)",0); - PyDict_SetItemString(dictionary, "frexp", f); - Py_DECREF(f); - - num = sizeof(ldexp_functions) / sizeof(ldexp_functions[0]); - f = PyUFunc_FromFuncAndData(ldexp_functions, blank6_data, ldexp_signatures, num, - 2, 1, PyUFunc_None, "ldexp", - "Compute y = x1 * 2**x2.",0); - PyDict_SetItemString(dictionary, "ldexp", f); - Py_DECREF(f); - -#if defined(NPY_PY3K) - f = PyDict_GetItemString(dictionary, "true_divide"); - PyDict_SetItemString(dictionary, "divide", f); -#endif - return; -} - 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; @@ -409,8 +316,6 @@ PyMODINIT_FUNC initumath(void) /* Load the ufunc operators into the array module's namespace */ InitOperators(d); - InitOtherOperators(d); - PyDict_SetItemString(d, "pi", s = PyFloat_FromDouble(NPY_PI)); Py_DECREF(s); PyDict_SetItemString(d, "e", s = PyFloat_FromDouble(NPY_E)); @@ -418,6 +323,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 6db95e9a6dff..3f31902fb296 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1114,45 +1114,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 @@ -1316,8 +1324,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)) @@ -1332,16 +1344,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: