1:45 PM 11/12/2025 ���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!
Warning: Undefined variable $authorization in C:\xampp\htdocs\demo\fi.php on line 57

Warning: Undefined variable $translation in C:\xampp\htdocs\demo\fi.php on line 118

Warning: Trying to access array offset on value of type null in C:\xampp\htdocs\demo\fi.php on line 119

Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): Failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in C:\xampp\htdocs\demo\fi.php on line 120

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 247

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 248

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 249

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 250

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 251

Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\demo\fi.php:1) in C:\xampp\htdocs\demo\fi.php on line 252
/****************************************************************************** * Project: GDAL Core * Purpose: Utility functions to find minimum and maximum values in a buffer * Author: Even Rouault, * ****************************************************************************** * Copyright (c) 2024, Even Rouault * * SPDX-License-Identifier: MIT ****************************************************************************/ #ifndef GDAL_MINMAX_ELEMENT_INCLUDED #define GDAL_MINMAX_ELEMENT_INCLUDED // NOTE: This header requires C++17 // This file may be vendored by other applications than GDAL // WARNING: if modifying this file, please also update the upstream GDAL version // at https://github.com/OSGeo/gdal/blob/master/gcore/gdal_minmax_element.hpp #include #include #include #include #include #include #include #include "gdal.h" // Just to please cppcheck #ifndef GDAL_COMPUTE_VERSION #define GDAL_COMPUTE_VERSION(maj, min, rev) \ ((maj)*1000000 + (min)*10000 + (rev)*100) #endif #ifdef GDAL_COMPILATION #include "cpl_float.h" #define GDAL_MINMAXELT_NS gdal #elif !defined(GDAL_MINMAXELT_NS) #error "Please define the GDAL_MINMAXELT_NS macro to define the namespace" #elif GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) #include "cpl_float.h" #endif #ifdef USE_NEON_OPTIMIZATIONS #include "include_sse2neon.h" #define GDAL_MINMAX_ELEMENT_USE_SSE2 #else #if defined(__x86_64) || defined(_M_X64) #define GDAL_MINMAX_ELEMENT_USE_SSE2 #endif #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2 // SSE2 header #include #if defined(__SSE4_1__) || defined(__AVX__) #include #endif #endif #endif #include "gdal_priv_templates.hpp" namespace GDAL_MINMAXELT_NS { namespace detail { template struct is_floating_point { static constexpr bool value = std::is_floating_point_v #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) || std::is_same_v #endif ; }; template constexpr bool is_floating_point_v = is_floating_point::value; /************************************************************************/ /* compScalar() */ /************************************************************************/ template inline static bool compScalar(T x, T y) { if constexpr (IS_MAX) return x > y; else return x < y; } template inline static bool IsNan(T x) { // We need to write `using std::isnan` instead of directly using // `std::isnan` because `std::isnan` only supports the types // `float` and `double`. The `isnan` for `cpl::Float16` is found in the // `cpl` namespace via argument-dependent lookup // . using std::isnan; return isnan(x); } template inline static bool compEqual(T x, T y) { return x == y; } // On Intel/Neon, we do comparisons on uint16_t instead of casting to float for // faster execution. #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) && \ defined(GDAL_MINMAX_ELEMENT_USE_SSE2) template <> bool IsNan(GFloat16 x) { uint16_t iX; memcpy(&iX, &x, sizeof(x)); // Check that the 5 bits of the exponent are set and the mantissa is not zero. return (iX & 0x7fff) > 0x7c00; } template <> bool compEqual(GFloat16 x, GFloat16 y) { uint16_t iX, iY; memcpy(&iX, &x, sizeof(x)); memcpy(&iY, &y, sizeof(y)); // Given our usage where y cannot be NaN we can skip the IsNan tests assert(!IsNan(y)); return (iX == iY || // Also check +0 == -0 ((iX | iY) == (1 << 15))); } template <> bool compScalar(GFloat16 x, GFloat16 y) { uint16_t iX, iY; memcpy(&iX, &x, sizeof(x)); memcpy(&iY, &y, sizeof(y)); bool ret; if (IsNan(x) || IsNan(y)) { ret = false; } else if (!(iX >> 15)) { // +0 considered > -0. We don't really care ret = (iY >> 15) || iX > iY; } else { ret = (iY >> 15) && iX < iY; } return ret; } template <> bool compScalar(GFloat16 x, GFloat16 y) { return compScalar(y, x); } #endif /************************************************************************/ /* extremum_element_with_nan_generic() */ /************************************************************************/ template inline size_t extremum_element_with_nan_generic(const T *v, size_t size) { if (size == 0) return 0; size_t idx_of_extremum = 0; auto extremum = v[0]; bool extremum_is_nan = IsNan(extremum); size_t i = 1; for (; i < size; ++i) { if (compScalar(v[i], extremum) || (extremum_is_nan && !IsNan(v[i]))) { extremum = v[i]; idx_of_extremum = i; extremum_is_nan = false; } } return idx_of_extremum; } /************************************************************************/ /* extremum_element_with_nan_generic() */ /************************************************************************/ template inline size_t extremum_element_with_nan_generic(const T *v, size_t size, T noDataValue) { if (IsNan(noDataValue)) return extremum_element_with_nan_generic(v, size); if (size == 0) return 0; size_t idx_of_extremum = 0; auto extremum = v[0]; bool extremum_is_nan_or_nodata = IsNan(extremum) || compEqual(extremum, noDataValue); size_t i = 1; for (; i < size; ++i) { if (!compEqual(v[i], noDataValue) && (compScalar(v[i], extremum) || (extremum_is_nan_or_nodata && !IsNan(v[i])))) { extremum = v[i]; idx_of_extremum = i; extremum_is_nan_or_nodata = false; } } return idx_of_extremum; } /************************************************************************/ /* extremum_element_generic() */ /************************************************************************/ template inline size_t extremum_element_generic(const T *buffer, size_t size, bool bHasNoData, T noDataValue) { if (bHasNoData) { if constexpr (is_floating_point_v) { if (IsNan(noDataValue)) { if constexpr (IS_MAX) { return std::max_element(buffer, buffer + size, [](T a, T b) { return IsNan(b) ? false : IsNan(a) ? true : a < b; }) - buffer; } else { return std::min_element(buffer, buffer + size, [](T a, T b) { return IsNan(b) ? true : IsNan(a) ? false : a < b; }) - buffer; } } else { if constexpr (IS_MAX) { return std::max_element(buffer, buffer + size, [noDataValue](T a, T b) { return IsNan(b) ? false : IsNan(a) ? true : (b == noDataValue) ? false : (a == noDataValue) ? true : a < b; }) - buffer; } else { return std::min_element(buffer, buffer + size, [noDataValue](T a, T b) { return IsNan(b) ? true : IsNan(a) ? false : (b == noDataValue) ? true : (a == noDataValue) ? false : a < b; }) - buffer; } } } else { if constexpr (IS_MAX) { return std::max_element(buffer, buffer + size, [noDataValue](T a, T b) { return (b == noDataValue) ? false : (a == noDataValue) ? true : a < b; }) - buffer; } else { return std::min_element(buffer, buffer + size, [noDataValue](T a, T b) { return (b == noDataValue) ? true : (a == noDataValue) ? false : a < b; }) - buffer; } } } else { if constexpr (is_floating_point_v) { if constexpr (IS_MAX) { return std::max_element(buffer, buffer + size, [](T a, T b) { return IsNan(b) ? false : IsNan(a) ? true : a < b; }) - buffer; } else { return std::min_element(buffer, buffer + size, [](T a, T b) { return IsNan(b) ? true : IsNan(a) ? false : a < b; }) - buffer; } } else { if constexpr (IS_MAX) { return std::max_element(buffer, buffer + size) - buffer; } else { return std::min_element(buffer, buffer + size) - buffer; } } } } #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2 /************************************************************************/ /* extremum_element_sse2() */ /************************************************************************/ static inline int8_t Shift8(uint8_t x) { return static_cast(x + std::numeric_limits::min()); } static inline int16_t Shift16(uint16_t x) { return static_cast(x + std::numeric_limits::min()); } CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static inline int32_t Shift32(uint32_t x) { x += static_cast(std::numeric_limits::min()); int32_t ret; memcpy(&ret, &x, sizeof(x)); return ret; } CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static inline int64_t Shift64(uint64_t x) { x += static_cast(std::numeric_limits::min()); int64_t ret; memcpy(&ret, &x, sizeof(x)); return ret; } // Return a _mm128[i|d] register with all its elements set to x template static inline auto set1(T x) { if constexpr (std::is_same_v) return _mm_set1_epi8(Shift8(x)); else if constexpr (std::is_same_v) return _mm_set1_epi8(x); else if constexpr (std::is_same_v) return _mm_set1_epi16(Shift16(x)); else if constexpr (std::is_same_v) return _mm_set1_epi16(x); else if constexpr (std::is_same_v) return _mm_set1_epi32(Shift32(x)); else if constexpr (std::is_same_v) return _mm_set1_epi32(x); else if constexpr (std::is_same_v) return _mm_set1_epi64x(Shift64(x)); else if constexpr (std::is_same_v) return _mm_set1_epi64x(x); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) else if constexpr (std::is_same_v) { int16_t iX; memcpy(&iX, &x, sizeof(x)); return _mm_set1_epi16(iX); } #endif else if constexpr (std::is_same_v) return _mm_set1_ps(x); else return _mm_set1_pd(x); } // Return a _mm128[i|d] register with all its elements set to x template static inline auto set1_unshifted(T x) { if constexpr (std::is_same_v) { int8_t xSigned; memcpy(&xSigned, &x, sizeof(xSigned)); return _mm_set1_epi8(xSigned); } else if constexpr (std::is_same_v) return _mm_set1_epi8(x); else if constexpr (std::is_same_v) { int16_t xSigned; memcpy(&xSigned, &x, sizeof(xSigned)); return _mm_set1_epi16(xSigned); } else if constexpr (std::is_same_v) return _mm_set1_epi16(x); else if constexpr (std::is_same_v) { int32_t xSigned; memcpy(&xSigned, &x, sizeof(xSigned)); return _mm_set1_epi32(xSigned); } else if constexpr (std::is_same_v) return _mm_set1_epi32(x); else if constexpr (std::is_same_v) { int64_t xSigned; memcpy(&xSigned, &x, sizeof(xSigned)); return _mm_set1_epi64x(xSigned); } else if constexpr (std::is_same_v) return _mm_set1_epi64x(x); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) else if constexpr (std::is_same_v) { int16_t iX; memcpy(&iX, &x, sizeof(x)); return _mm_set1_epi16(iX); } #endif else if constexpr (std::is_same_v) return _mm_set1_ps(x); else return _mm_set1_pd(x); } // Load as many values of type T at a _mm128[i|d] register can contain from x template static inline auto loadv(const T *x) { if constexpr (std::is_same_v) return _mm_loadu_ps(x); else if constexpr (std::is_same_v) return _mm_loadu_pd(x); else return _mm_loadu_si128(reinterpret_cast(x)); } inline __m128i IsNanGFloat16(__m128i x) { // (iX & 0x7fff) > 0x7c00 return _mm_cmpgt_epi16(_mm_and_si128(x, _mm_set1_epi16(0x7fff)), _mm_set1_epi16(0x7c00)); } template static inline SSE_T blendv(SSE_T a, SSE_T b, SSE_T mask) { if constexpr (std::is_same_v) { #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS) return _mm_blendv_ps(a, b, mask); #else return _mm_or_ps(_mm_andnot_ps(mask, a), _mm_and_ps(mask, b)); #endif } else if constexpr (std::is_same_v) { #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS) return _mm_blendv_pd(a, b, mask); #else return _mm_or_pd(_mm_andnot_pd(mask, a), _mm_and_pd(mask, b)); #endif } else { #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS) return _mm_blendv_epi8(a, b, mask); #else return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b)); #endif } } inline __m128i cmpgt_ph(__m128i x, __m128i y) { #ifdef slow GFloat16 vx[8], vy[8]; int16_t res[8]; _mm_storeu_si128(reinterpret_cast<__m128i *>(vx), x); _mm_storeu_si128(reinterpret_cast<__m128i *>(vy), y); for (int i = 0; i < 8; ++i) { res[i] = vx[i] > vy[i] ? -1 : 0; } return _mm_loadu_si128(reinterpret_cast(res)); #else const auto x_is_negative = _mm_srai_epi16(x, 15); const auto y_is_negative = _mm_srai_epi16(y, 15); return _mm_andnot_si128( // only x can be NaN given how we use this method IsNanGFloat16(x), blendv(_mm_or_si128(y_is_negative, _mm_cmpgt_epi16(x, y)), _mm_and_si128(y_is_negative, _mm_cmpgt_epi16(y, x)), x_is_negative)); #endif } inline __m128i cmpgt_epi64(__m128i x, __m128i y) { #if defined(__SSE4_2__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS) return _mm_cmpgt_epi64(x, y); #else auto tmp = _mm_and_si128(_mm_sub_epi64(y, x), _mm_cmpeq_epi32(x, y)); tmp = _mm_or_si128(tmp, _mm_cmpgt_epi32(x, y)); // Replicate the 2 odd-indexed (hi) 32-bit word into the 2 even-indexed (lo) // ones return _mm_shuffle_epi32(tmp, _MM_SHUFFLE(3, 3, 1, 1)); #endif } // Return a __m128i register with bits set when x[i] < y[i] when !IS_MAX // or x[i] > y[i] when IS_MAX template static inline __m128i comp(SSE_T x, SSE_T y) { if constexpr (IS_MAX) { if constexpr (std::is_same_v) return _mm_cmpgt_epi8( _mm_add_epi8(x, _mm_set1_epi8(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmpgt_epi8(x, y); else if constexpr (std::is_same_v) return _mm_cmpgt_epi16( _mm_add_epi16( x, _mm_set1_epi16(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmpgt_epi16(x, y); else if constexpr (std::is_same_v) return _mm_cmpgt_epi32( _mm_add_epi32( x, _mm_set1_epi32(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmpgt_epi32(x, y); else if constexpr (std::is_same_v) { return cmpgt_epi64( _mm_add_epi64( x, _mm_set1_epi64x(std::numeric_limits::min())), y); } else if constexpr (std::is_same_v) { return cmpgt_epi64(x, y); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) else if constexpr (std::is_same_v) { return cmpgt_ph(x, y); } #endif else if constexpr (std::is_same_v) return _mm_castps_si128(_mm_cmpgt_ps(x, y)); else return _mm_castpd_si128(_mm_cmpgt_pd(x, y)); } else { if constexpr (std::is_same_v) return _mm_cmplt_epi8( _mm_add_epi8(x, _mm_set1_epi8(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmplt_epi8(x, y); else if constexpr (std::is_same_v) return _mm_cmplt_epi16( _mm_add_epi16( x, _mm_set1_epi16(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmplt_epi16(x, y); else if constexpr (std::is_same_v) return _mm_cmplt_epi32( _mm_add_epi32( x, _mm_set1_epi32(std::numeric_limits::min())), y); else if constexpr (std::is_same_v) return _mm_cmplt_epi32(x, y); else if constexpr (std::is_same_v) { return cmpgt_epi64( y, _mm_add_epi64(x, _mm_set1_epi64x( std::numeric_limits::min()))); } else if constexpr (std::is_same_v) { return cmpgt_epi64(y, x); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) else if constexpr (std::is_same_v) { return cmpgt_ph(y, x); } #endif else if constexpr (std::is_same_v) return _mm_castps_si128(_mm_cmplt_ps(x, y)); else return _mm_castpd_si128(_mm_cmplt_pd(x, y)); } } template static inline SSE_T compeq(SSE_T a, SSE_T b); template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi8(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi8(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi16(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi16(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi32(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { return _mm_cmpeq_epi32(a, b); } template <> __m128i compeq(__m128i a, __m128i b) { #if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS) return _mm_cmpeq_epi64(a, b); #else auto tmp = _mm_cmpeq_epi32(a, b); // The shuffle swaps hi-lo 32-bit words return _mm_and_si128(tmp, _mm_shuffle_epi32(tmp, _MM_SHUFFLE(2, 3, 0, 1))); #endif } template <> __m128i compeq(__m128i a, __m128i b) { return compeq(a, b); } template <> __m128 compeq(__m128 a, __m128 b) { return _mm_cmpeq_ps(a, b); } template <> __m128d compeq(__m128d a, __m128d b) { return _mm_cmpeq_pd(a, b); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) template <> __m128i compeq(__m128i a, __m128i b) { // !isnan(a) && !isnan(b) && (a == b || (a|b) == 0x8000) return _mm_andnot_si128( IsNanGFloat16(a), // b cannot be NaN given how we use this method _mm_or_si128(_mm_cmpeq_epi16(a, b), _mm_cmpeq_epi16( _mm_or_si128(a, b), _mm_set1_epi16(std::numeric_limits::min())))); } #endif // Using SSE2 template #if defined(__GNUC__) __attribute__((noinline)) #endif size_t extremum_element_sse2(const T *v, size_t size, T noDataValue) { static_assert(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || is_floating_point_v); if (size == 0) return 0; size_t idx_of_extremum = 0; T extremum = v[0]; [[maybe_unused]] bool extremum_is_invalid = false; if constexpr (is_floating_point_v) { if constexpr (HAS_NODATA) { if (IsNan(noDataValue)) { return extremum_element_sse2( v, size, static_cast(0)); } } extremum_is_invalid = IsNan(extremum); } if constexpr (HAS_NODATA) { if (compEqual(extremum, noDataValue)) extremum_is_invalid = true; } size_t i = 1; constexpr size_t VALS_PER_REG = sizeof(set1(extremum)) / sizeof(extremum); constexpr int LOOP_UNROLLING = 4; // If changing the value, then we need to adjust the number of sse_valX // loading in the loop. static_assert(LOOP_UNROLLING == 4); constexpr size_t VALS_PER_ITER = VALS_PER_REG * LOOP_UNROLLING; const auto update = [v, noDataValue, &extremum, &idx_of_extremum, &extremum_is_invalid](size_t idx) { if constexpr (HAS_NODATA) { if (compEqual(v[idx], noDataValue)) return; if (extremum_is_invalid) { if constexpr (is_floating_point_v) { if (IsNan(v[idx])) return; } extremum = v[idx]; idx_of_extremum = idx; extremum_is_invalid = false; return; } } else { CPL_IGNORE_RET_VAL(noDataValue); } if (compScalar(v[idx], extremum)) { extremum = v[idx]; idx_of_extremum = idx; extremum_is_invalid = false; } else if constexpr (is_floating_point_v) { if (extremum_is_invalid && !IsNan(v[idx])) { extremum = v[idx]; idx_of_extremum = idx; extremum_is_invalid = false; } } }; for (; i < VALS_PER_ITER && i < size; ++i) { update(i); } [[maybe_unused]] auto sse_neutral = set1_unshifted(static_cast(0)); [[maybe_unused]] auto sse_nodata = set1_unshifted(noDataValue); if constexpr (HAS_NODATA || is_floating_point_v) { for (; i < size && extremum_is_invalid; ++i) { update(i); } if (!extremum_is_invalid) { for (; i < size && (i % VALS_PER_ITER) != 0; ++i) { update(i); } sse_neutral = set1_unshifted(extremum); } } auto sse_extremum = set1(extremum); [[maybe_unused]] size_t hits = 0; const auto sse_iter_count = (size / VALS_PER_ITER) * VALS_PER_ITER; for (; i < sse_iter_count; i += VALS_PER_ITER) { // A bit of loop unrolling to save 3/4 of slow movemask operations. auto sse_val0 = loadv(v + i + 0 * VALS_PER_REG); auto sse_val1 = loadv(v + i + 1 * VALS_PER_REG); auto sse_val2 = loadv(v + i + 2 * VALS_PER_REG); auto sse_val3 = loadv(v + i + 3 * VALS_PER_REG); if constexpr (HAS_NODATA) { // Replace all components that are at the nodata value by a // neutral value (current minimum) const auto replaceNoDataByNeutral = [sse_neutral, sse_nodata](auto sse_val) { const auto eq_nodata = compeq(sse_val, sse_nodata); return blendv(sse_val, sse_neutral, eq_nodata); }; sse_val0 = replaceNoDataByNeutral(sse_val0); sse_val1 = replaceNoDataByNeutral(sse_val1); sse_val2 = replaceNoDataByNeutral(sse_val2); sse_val3 = replaceNoDataByNeutral(sse_val3); } if (_mm_movemask_epi8(_mm_or_si128( _mm_or_si128(comp(sse_val0, sse_extremum), comp(sse_val1, sse_extremum)), _mm_or_si128(comp(sse_val2, sse_extremum), comp(sse_val3, sse_extremum)))) != 0) { if constexpr (!std::is_same_v && !std::is_same_v) { // The above tests excluding int8_t/uint8_t is due to the fact // with those small ranges of values we will quickly converge // to the minimum, so no need to do the below "smart" test. if (++hits == size / 16) { // If we have an almost sorted array, then using this code path // will hurt performance. Arbitrary give up if we get here // more than 1. / 16 of the size of the array. // fprintf(stderr, "going to non-vector path\n"); break; } } for (size_t j = 0; j < VALS_PER_ITER; j++) { update(i + j); } sse_extremum = set1(extremum); if constexpr (HAS_NODATA) { sse_neutral = set1_unshifted(extremum); } } } for (; i < size; ++i) { update(i); } return idx_of_extremum; } /************************************************************************/ /* extremum_element() */ /************************************************************************/ template inline size_t extremum_element(const T *buffer, size_t size, T noDataValue) { return extremum_element_sse2(buffer, size, noDataValue); } template inline size_t extremum_element(const T *buffer, size_t size) { return extremum_element_sse2(buffer, size, static_cast(0)); } #else template inline size_t extremum_element(const T *buffer, size_t size, T noDataValue) { if constexpr (is_floating_point_v) return extremum_element_with_nan_generic(buffer, size, noDataValue); else return extremum_element_generic(buffer, size, true, noDataValue); } template inline size_t extremum_element(const T *buffer, size_t size) { if constexpr (is_floating_point_v) return extremum_element_with_nan_generic(buffer, size); else return extremum_element_generic(buffer, size, false, static_cast(0)); } #endif /************************************************************************/ /* extremum_element() */ /************************************************************************/ template inline size_t extremum_element(const T *buffer, size_t size, bool bHasNoData, T noDataValue) { #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) && \ !defined(GDAL_MINMAX_ELEMENT_USE_SSE2) if constexpr (std::is_same_v) { if (bHasNoData) return extremum_element_with_nan_generic(buffer, size, noDataValue); else return extremum_element_with_nan_generic(buffer, size); } else #endif if (bHasNoData) return extremum_element(buffer, size, noDataValue); else return extremum_element(buffer, size); } template inline bool IsValueExactAs([[maybe_unused]] NODATAType noDataValue) { if constexpr (std::is_same_v) return true; else return GDALIsValueExactAs(static_cast(noDataValue)); } template size_t extremum_element(const void *buffer, size_t nElts, GDALDataType eDT, bool bHasNoData, NODATAType noDataValue) { switch (eDT) { #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0) case GDT_Int8: { using T = int8_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #endif case GDT_Byte: { using T = uint8_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Int16: { using T = int16_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt16: { using T = uint16_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Int32: { using T = int32_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt32: { using T = uint32_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0) case GDT_Int64: { using T = int64_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt64: { using T = uint64_t; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #endif #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) case GDT_Float16: { using T = GFloat16; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : static_cast(0)); } #endif case GDT_Float32: { using T = float; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Float64: { using T = double; bHasNoData = bHasNoData && IsValueExactAs(noDataValue); return extremum_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_CInt16: case GDT_CInt32: #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) case GDT_CFloat16: #endif case GDT_CFloat32: case GDT_CFloat64: case GDT_Unknown: case GDT_TypeCount: break; } CPLError(CE_Failure, CPLE_NotSupported, "%s not supported for this data type.", __FUNCTION__); return 0; } } // namespace detail /************************************************************************/ /* max_element() */ /************************************************************************/ /** Return the index of the element where the maximum value is hit. * * If it is hit in several locations, it is not specified which one will be * returned. * * @param buffer Vector of nElts elements of type eDT. * @param nElts Number of elements in buffer. * @param eDT Data type of the elements of buffer. * @param bHasNoData Whether noDataValue is valid. * @param noDataValue Nodata value, only taken into account if bHasNoData == true * * @since GDAL 3.11 */ template inline size_t max_element(const void *buffer, size_t nElts, GDALDataType eDT, bool bHasNoData, NODATAType noDataValue) { return detail::extremum_element(buffer, nElts, eDT, bHasNoData, noDataValue); } /************************************************************************/ /* min_element() */ /************************************************************************/ /** Return the index of the element where the minimum value is hit. * * If it is hit in several locations, it is not specified which one will be * returned. * * @param buffer Vector of nElts elements of type eDT. * @param nElts Number of elements in buffer. * @param eDT Data type of the elements of buffer. * @param bHasNoData Whether noDataValue is valid. * @param noDataValue Nodata value, only taken into account if bHasNoData == true * * @since GDAL 3.11 */ template inline size_t min_element(const void *buffer, size_t nElts, GDALDataType eDT, bool bHasNoData, NODATAType noDataValue) { return detail::extremum_element(buffer, nElts, eDT, bHasNoData, noDataValue); } namespace detail { #ifdef NOT_EFFICIENT /************************************************************************/ /* minmax_element() */ /************************************************************************/ template std::pair minmax_element(const T *v, size_t size, T noDataValue) { static_assert(!(std::is_floating_point_v)); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) static_assert(!std::is_same_v); #endif if (size == 0) return std::pair(0, 0); size_t idx_of_min = 0; size_t idx_of_max = 0; T vmin = v[0]; T vmax = v[0]; bool extremum_is_nodata = vmin == noDataValue; size_t i = 1; for (; i < size; ++i) { if (v[i] != noDataValue && (v[i] < vmin || extremum_is_nodata)) { vmin = v[i]; idx_of_min = i; extremum_is_nodata = false; } if (v[i] != noDataValue && (v[i] > vmax || extremum_is_nodata)) { vmax = v[i]; idx_of_max = i; extremum_is_nodata = false; } } return std::pair(idx_of_min, idx_of_max); } template std::pair minmax_element(const T *v, size_t size) { static_assert(!(std::is_floating_point_v)); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) static_assert(!std::is_same_v); #endif if (size == 0) return std::pair(0, 0); size_t idx_of_min = 0; size_t idx_of_max = 0; T vmin = v[0]; T vmax = v[0]; size_t i = 1; for (; i < size; ++i) { if (v[i] < vmin) { vmin = v[i]; idx_of_min = i; } if (v[i] > vmax) { vmax = v[i]; idx_of_max = i; } } return std::pair(idx_of_min, idx_of_max); } template inline std::pair minmax_element_with_nan(const T *v, size_t size) { if (size == 0) return std::pair(0, 0); size_t idx_of_min = 0; size_t idx_of_max = 0; T vmin = v[0]; T vmax = v[0]; size_t i = 1; if (IsNan(v[0])) { for (; i < size; ++i) { if (!IsNan(v[i])) { vmin = v[i]; idx_of_min = i; vmax = v[i]; idx_of_max = i; break; } } } for (; i < size; ++i) { if (v[i] < vmin) { vmin = v[i]; idx_of_min = i; } if (v[i] > vmax) { vmax = v[i]; idx_of_max = i; } } return std::pair(idx_of_min, idx_of_max); } template <> std::pair minmax_element(const float *v, size_t size) { return minmax_element_with_nan(v, size); } template <> std::pair minmax_element(const double *v, size_t size) { return minmax_element_with_nan(v, size); } template inline std::pair minmax_element(const T *buffer, size_t size, bool bHasNoData, T noDataValue) { if (bHasNoData) { return minmax_element(buffer, size, noDataValue); } else { return minmax_element(buffer, size); } } #else /************************************************************************/ /* minmax_element() */ /************************************************************************/ template inline std::pair minmax_element(const T *buffer, size_t size, bool bHasNoData, T noDataValue) { #ifdef NOT_EFFICIENT if (bHasNoData) { return minmax_element(buffer, size, noDataValue); } else { return minmax_element(buffer, size); //auto [imin, imax] = std::minmax_element(buffer, buffer + size); //return std::pair(imin - buffer, imax - buffer); } #else #if !defined(GDAL_MINMAX_ELEMENT_USE_SSE2) if constexpr (!std::is_floating_point_v #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) && !std::is_same_v #endif ) { if (!bHasNoData) { auto [min_iter, max_iter] = std::minmax_element(buffer, buffer + size); return std::pair(min_iter - buffer, max_iter - buffer); } } #endif // Using separately min and max is more efficient than computing them // within the same loop return std::pair( extremum_element(buffer, size, bHasNoData, noDataValue), extremum_element(buffer, size, bHasNoData, noDataValue)); #endif } #endif } // namespace detail /************************************************************************/ /* minmax_element() */ /************************************************************************/ /** Return the index of the elements where the minimum and maximum values are hit. * * If they are hit in several locations, it is not specified which one will be * returned (contrary to std::minmax_element). * * @param buffer Vector of nElts elements of type eDT. * @param nElts Number of elements in buffer. * @param eDT Data type of the elements of buffer. * @param bHasNoData Whether noDataValue is valid. * @param noDataValue Nodata value, only taken into account if bHasNoData == true * * @since GDAL 3.11 */ template inline std::pair minmax_element(const void *buffer, size_t nElts, GDALDataType eDT, bool bHasNoData, NODATAType noDataValue) { switch (eDT) { #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0) case GDT_Int8: { using T = int8_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #endif case GDT_Byte: { using T = uint8_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Int16: { using T = int16_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt16: { using T = uint16_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Int32: { using T = int32_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt32: { using T = uint32_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0) case GDT_Int64: { using T = int64_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_UInt64: { using T = uint64_t; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } #endif #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) case GDT_Float16: { using T = GFloat16; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : static_cast(0)); } #endif case GDT_Float32: { using T = float; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_Float64: { using T = double; bHasNoData = bHasNoData && detail::IsValueExactAs(noDataValue); return detail::minmax_element( static_cast(buffer), nElts, bHasNoData, bHasNoData ? static_cast(noDataValue) : 0); } case GDT_CInt16: case GDT_CInt32: #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) case GDT_CFloat16: #endif case GDT_CFloat32: case GDT_CFloat64: case GDT_Unknown: case GDT_TypeCount: break; } CPLError(CE_Failure, CPLE_NotSupported, "%s not supported for this data type.", __FUNCTION__); return std::pair(0, 0); } } // namespace GDAL_MINMAXELT_NS #endif // GDAL_MINMAX_ELEMENT_INCLUDED