|
| 1 | +/** |
| 2 | + * This module provides the inner loops for the clip ufunc |
| 3 | + */ |
| 4 | +#define _UMATHMODULE |
| 5 | +#define _MULTIARRAYMODULE |
| 6 | +#define NPY_NO_DEPRECATED_API NPY_API_VERSION |
| 7 | + |
| 8 | +#include "Python.h" |
| 9 | + |
| 10 | +#include "numpy/halffloat.h" |
| 11 | +#include "numpy/npy_math.h" |
| 12 | +#include "numpy/ndarraytypes.h" |
| 13 | +#include "numpy/npy_common.h" |
| 14 | +#include "numpy/utils.h" |
| 15 | +#include "fast_loop_macros.h" |
| 16 | + |
| 17 | +namespace npy { |
| 18 | + |
| 19 | + struct integral_tag {}; |
| 20 | + struct floating_point_tag {}; |
| 21 | + struct complex_tag {}; |
| 22 | + struct date_tag {}; |
| 23 | + |
| 24 | + struct bool_tag : integral_tag { using type = npy_bool; }; |
| 25 | + struct byte_tag : integral_tag {using type = npy_byte; } ; |
| 26 | + struct ubyte_tag : integral_tag {using type = npy_ubyte; } ; |
| 27 | + struct short_tag : integral_tag {using type = npy_short; } ; |
| 28 | + struct ushort_tag : integral_tag {using type = npy_ushort; } ; |
| 29 | + struct int_tag : integral_tag {using type = npy_int; } ; |
| 30 | + struct uint_tag : integral_tag {using type = npy_uint; } ; |
| 31 | + struct long_tag : integral_tag {using type = npy_long ; } ; |
| 32 | + struct ulong_tag : integral_tag {using type = npy_ulong ; } ; |
| 33 | + struct longlong_tag : integral_tag {using type = npy_longlong ; } ; |
| 34 | + struct ulonglong_tag : integral_tag {using type = npy_ulonglong ; } ; |
| 35 | + struct half_tag {using type = npy_half ; } ; |
| 36 | + struct float_tag : floating_point_tag {using type = npy_float ; } ; |
| 37 | + struct double_tag : floating_point_tag {using type = npy_double ; } ; |
| 38 | + struct longdouble_tag : floating_point_tag {using type = npy_longdouble ; } ; |
| 39 | + struct cfloat_tag : complex_tag {using type = npy_cfloat ; } ; |
| 40 | + struct cdouble_tag : complex_tag {using type = npy_cdouble ; } ; |
| 41 | + struct clongdouble_tag : complex_tag {using type = npy_clongdouble ; } ; |
| 42 | + struct datetime_tag : date_tag {using type = npy_datetime ; } ; |
| 43 | + struct timedelta_tag : date_tag {using type = npy_timedelta ; } ; |
| 44 | + |
| 45 | +} |
| 46 | + |
| 47 | +template<class T> |
| 48 | +T _NPY_MIN(T a, T b, npy::integral_tag const &) { return PyArray_MIN(a, b); } |
| 49 | +template<class T> |
| 50 | +T _NPY_MAX(T a, T b, npy::integral_tag const &) { return PyArray_MAX(a, b); } |
| 51 | + |
| 52 | +template<class T> |
| 53 | +T _NPY_MIN(T a, T b, npy::half_tag const &) { return npy_half_isnan(a) || npy_half_le(a, b) ? (a) : (b); } |
| 54 | +template<class T> |
| 55 | +T _NPY_MAX(T a, T b, npy::half_tag const &) { return npy_half_isnan(a) || npy_half_ge(a, b) ? (a) : (b); } |
| 56 | + |
| 57 | +template<class T> |
| 58 | +T _NPY_MIN(T a, T b, npy::floating_point_tag const &) { return npy_isnan(a) ? (a) : PyArray_MIN(a, b); } |
| 59 | +template<class T> |
| 60 | +T _NPY_MAX(T a, T b, npy::floating_point_tag const &) { return npy_isnan(a) ? (a) : PyArray_MAX(a, b); } |
| 61 | + |
| 62 | +template<class T> |
| 63 | +T _NPY_MIN(T a, T b, npy::complex_tag const &) { return npy_isnan((a).real) || npy_isnan((a).imag) || PyArray_CLT(a, b) ? (a) : (b); } |
| 64 | +template<class T> |
| 65 | +T _NPY_MAX(T a, T b, npy::complex_tag const &) { return npy_isnan((a).real)
10000
|| npy_isnan((a).imag) || PyArray_CGT(a, b) ? (a) : (b); } |
| 66 | + |
| 67 | +template<class T> |
| 68 | +T _NPY_MIN(T a, T b, npy::date_tag const &) { |
| 69 | + return (a) == NPY_DATETIME_NAT |
| 70 | + ? (a) |
| 71 | + : (b) == NPY_DATETIME_NAT ? (b) : (a) < (b) ? (a) : (b); |
| 72 | +} |
| 73 | +template<class T> |
| 74 | +T _NPY_MAX(T a, T b, npy::date_tag const &) { |
| 75 | + return (a) == NPY_DATETIME_NAT |
| 76 | + ? (a) |
| 77 | + : (b) == NPY_DATETIME_NAT ? (b) : (a) > (b) ? (a) : (b); |
| 78 | +} |
| 79 | + |
| 80 | +/* generic dispatcher */ |
| 81 | +template<class Tag, class T=typename Tag::type> |
| 82 | +T _NPY_MIN(T const& a, T const& b) { |
| 83 | + return _NPY_MIN(a, b, Tag{}); |
| 84 | +} |
| 85 | +template<class Tag, class T=typename Tag::type> |
| 86 | +T _NPY_MAX(T const& a, T const& b) { |
| 87 | + return _NPY_MAX(a, b, Tag{}); |
| 88 | +} |
| 89 | + |
| 90 | + |
| 91 | +template<class Tag, class T> |
| 92 | +auto _NPY_CLIP(T x, T min, T max) { |
| 93 | + return _NPY_MIN<Tag>(_NPY_MAX<Tag>((x), (min)), (max)); |
| 94 | +} |
| 95 | + |
| 96 | +template<class Tag> |
| 97 | +static void |
| 98 | +_npy_clip(char **args, npy_intp const *dimensions, npy_intp const *steps) |
| 99 | +{ |
| 100 | + using T = typename Tag::type; |
| 101 | + if (steps[1] == 0 && steps[2] == 0) { |
| 102 | + /* min and max are constant throughout the loop, the most common case */ |
| 103 | + /* NOTE: it may be possible to optimize these checks for nan */ |
| 104 | + T min_val = *(T *)args[1]; |
| 105 | + T max_val = *(T *)args[2]; |
| 106 | + |
| 107 | + char *ip1 = args[0], *op1 = args[3]; |
| 108 | + npy_intp is1 = steps[0], os1 = steps[3]; |
| 109 | + npy_intp n = dimensions[0]; |
| 110 | + |
| 111 | + /* contiguous, branch to let the compiler optimize */ |
| 112 | + if (is1 == sizeof(T) && os1 == sizeof(T)) { |
| 113 | + for(npy_intp i = 0; i < n; i++, ip1 += is1, op1 += os1) { |
| 114 | + *(T *)op1 = _NPY_CLIP<Tag>(*(T *)ip1, min_val, max_val); |
| 115 | + } |
| 116 | + } |
| 117 | + else { |
| 118 | + for(npy_intp i = 0; i < n; i++, ip1 += is1, op1 += os1) { |
| 119 | + *(T *)op1 = _NPY_CLIP<Tag>(*(T *)ip1, min_val, max_val); |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + else { |
| 124 | + TERNARY_LOOP { |
| 125 | + *(T *)op1 = _NPY_CLIP<Tag>(*(T *)ip1, *(T *)ip2, *(T *)ip3); |
| 126 | + } |
| 127 | + } |
| 128 | + npy_clear_floatstatus_barrier((char*)dimensions); |
| 129 | +} |
| 130 | + |
| 131 | +extern "C" { |
| 132 | +NPY_NO_EXPORT void BOOL_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::bool_tag>(args, dimensions, steps); } |
| 133 | +NPY_NO_EXPORT void BYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::byte_tag>(args, dimensions, steps); } |
| 134 | +NPY_NO_EXPORT void UBYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::ubyte_tag>(args, dimensions, steps); } |
| 135 | +NPY_NO_EXPORT void SHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::short_tag>(args, dimensions, steps); } |
| 136 | +NPY_NO_EXPORT void USHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::ushort_tag>(args, dimensions, steps); } |
| 137 | +NPY_NO_EXPORT void INT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::int_tag>(args, dimensions, steps); } |
| 138 | +NPY_NO_EXPORT void UINT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::uint_tag>(args, dimensions, steps); } |
| 139 | +NPY_NO_EXPORT void LONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::long_tag>(args, dimensions, steps); } |
| 140 | +NPY_NO_EXPORT void ULONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::ulong_tag>(args, dimensions, steps); } |
| 141 | +NPY_NO_EXPORT void LONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::longlong_tag>(args, dimensions, steps); } |
| 142 | +NPY_NO_EXPORT void ULONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::ulonglong_tag>(args, dimensions, steps); } |
| 143 | +NPY_NO_EXPORT void HALF_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::half_tag>(args, dimensions, steps); } |
| 144 | +NPY_NO_EXPORT void FLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::float_tag>(args, dimensions, steps); } |
| 145 | +NPY_NO_EXPORT void DOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::double_tag>(args, dimensions, steps); } |
| 146 | +NPY_NO_EXPORT void LONGDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::longdouble_tag>(args, dimensions, steps); } |
| 147 | +NPY_NO_EXPORT void CFLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::cfloat_tag>(args, dimensions, steps); } |
| 148 | +NPY_NO_EXPORT void CDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::cdouble_tag>(args, dimensions, steps); } |
| 149 | +NPY_NO_EXPORT void CLONGDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::clongdouble_tag>(args, dimensions, steps); } |
| 150 | +NPY_NO_EXPORT void DATETIME_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::datetime_tag>(args, dimensions, steps); } |
| 151 | +NPY_NO_EXPORT void TIMEDELTA_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { return _npy_clip<npy::timedelta_tag>(args, dimensions, steps); } |
| 152 | +} |
0 commit comments