LCOV - code coverage report
Current view: top level - gcore - gdal_minmax_element.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 345 345 100.0 %
Date: 2024-11-21 22:18:42 Functions: 222 229 96.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  GDAL Core
       3             :  * Purpose:  Utility functions to find minimum and maximum values in a buffer
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #ifndef GDAL_MINMAX_ELEMENT_INCLUDED
      13             : #define GDAL_MINMAX_ELEMENT_INCLUDED
      14             : 
      15             : // NOTE: This header requires C++17
      16             : 
      17             : // This file may be vendored by other applications than GDAL
      18             : // WARNING: if modifying this file, please also update the upstream GDAL version
      19             : // at https://github.com/OSGeo/gdal/blob/master/gcore/gdal_minmax_element.hpp
      20             : 
      21             : #include <algorithm>
      22             : #include <cmath>
      23             : #include <cstdint>
      24             : #include <limits>
      25             : #include <type_traits>
      26             : #include <utility>
      27             : 
      28             : #include "gdal.h"
      29             : 
      30             : #ifdef GDAL_COMPILATION
      31             : #define GDAL_MINMAXELT_NS gdal
      32             : #elif !defined(GDAL_MINMAXELT_NS)
      33             : #error "Please define the GDAL_MINMAXELT_NS macro to define the namespace"
      34             : #endif
      35             : 
      36             : #ifdef USE_NEON_OPTIMIZATIONS
      37             : #include "include_sse2neon.h"
      38             : #define GDAL_MINMAX_ELEMENT_USE_SSE2
      39             : #else
      40             : #if defined(__x86_64) || defined(_M_X64)
      41             : #define GDAL_MINMAX_ELEMENT_USE_SSE2
      42             : #endif
      43             : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
      44             : // SSE2 header
      45             : #include <emmintrin.h>
      46             : #ifdef __SSE4_1__
      47             : #include <smmintrin.h>
      48             : #endif
      49             : #endif
      50             : #endif
      51             : 
      52             : #include "gdal_priv_templates.hpp"
      53             : 
      54             : namespace GDAL_MINMAXELT_NS
      55             : {
      56             : namespace detail
      57             : {
      58             : 
      59             : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
      60             : /************************************************************************/
      61             : /*                            compScalar()                              */
      62             : /************************************************************************/
      63             : 
      64      949167 : template <class T, bool IS_MAX> inline static bool compScalar(T x, T y)
      65             : {
      66             :     if constexpr (IS_MAX)
      67        2982 :         return x > y;
      68             :     else
      69      946185 :         return x < y;
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                         extremum_element()                           */
      74             : /************************************************************************/
      75             : 
      76             : template <class T, bool IS_MAX>
      77          16 : size_t extremum_element(const T *v, size_t size, T noDataValue)
      78             : {
      79             :     static_assert(!(std::is_floating_point_v<T>));
      80          16 :     if (size == 0)
      81           2 :         return 0;
      82          14 :     size_t idx_of_extremum = 0;
      83          14 :     T extremum = v[0];
      84          14 :     bool extremum_is_nodata = extremum == noDataValue;
      85          14 :     size_t i = 1;
      86         550 :     for (; i < size; ++i)
      87             :     {
      88        1578 :         if (v[i] != noDataValue &&
      89        1042 :             (compScalar<T, IS_MAX>(v[i], extremum) || extremum_is_nodata))
      90             :         {
      91          12 :             extremum = v[i];
      92          12 :             idx_of_extremum = i;
      93          12 :             extremum_is_nodata = false;
      94             :         }
      95             :     }
      96          14 :     return idx_of_extremum;
      97             : }
      98             : 
      99             : /************************************************************************/
     100             : /*                         extremum_element()                           */
     101             : /************************************************************************/
     102             : 
     103          10 : template <class T, bool IS_MAX> size_t extremum_element(const T *v, size_t size)
     104             : {
     105             :     static_assert(!(std::is_floating_point_v<T>));
     106          10 :     if (size == 0)
     107           2 :         return 0;
     108           8 :     size_t idx_of_extremum = 0;
     109           8 :     T extremum = v[0];
     110           8 :     size_t i = 1;
     111        1048 :     for (; i < size; ++i)
     112             :     {
     113        1040 :         if (compScalar<T, IS_MAX>(v[i], extremum))
     114             :         {
     115          10 :             extremum = v[i];
     116          10 :             idx_of_extremum = i;
     117             :         }
     118             :     }
     119           8 :     return idx_of_extremum;
     120             : }
     121             : 
     122             : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
     123             : 
     124             : /************************************************************************/
     125             : /*                   extremum_element_with_nan()                        */
     126             : /************************************************************************/
     127             : 
     128          77 : static inline int8_t Shift8(uint8_t x)
     129             : {
     130          77 :     return static_cast<int8_t>(x + std::numeric_limits<int8_t>::min());
     131             : }
     132             : 
     133          32 : static inline int16_t Shift16(uint16_t x)
     134             : {
     135          32 :     return static_cast<int16_t>(x + std::numeric_limits<int16_t>::min());
     136             : }
     137             : 
     138             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     139          38 : static inline int32_t Shift32(uint32_t x)
     140             : {
     141          38 :     x += static_cast<uint32_t>(std::numeric_limits<int32_t>::min());
     142             :     int32_t ret;
     143          38 :     memcpy(&ret, &x, sizeof(x));
     144          38 :     return ret;
     145             : }
     146             : 
     147             : // Return a _mm128[i|d] register with all its elements set to x
     148       78601 : template <class T> static inline auto set1(T x)
     149             : {
     150             :     if constexpr (std::is_same_v<T, uint8_t>)
     151         154 :         return _mm_set1_epi8(Shift8(x));
     152             :     else if constexpr (std::is_same_v<T, int8_t>)
     153          34 :         return _mm_set1_epi8(x);
     154             :     else if constexpr (std::is_same_v<T, uint16_t>)
     155          64 :         return _mm_set1_epi16(Shift16(x));
     156             :     else if constexpr (std::is_same_v<T, int16_t>)
     157          70 :         return _mm_set1_epi16(x);
     158             :     else if constexpr (std::is_same_v<T, uint32_t>)
     159          76 :         return _mm_set1_epi32(Shift32(x));
     160             :     else if constexpr (std::is_same_v<T, int32_t>)
     161          40 :         return _mm_set1_epi32(x);
     162             :     else if constexpr (std::is_same_v<T, float>)
     163       39191 :         return _mm_set1_ps(x);
     164             :     else
     165       39171 :         return _mm_set1_pd(x);
     166             : }
     167             : 
     168             : // Return a _mm128[i|d] register with all its elements set to x
     169       39552 : template <class T> static inline auto set1_unshifted(T x)
     170             : {
     171             :     if constexpr (std::is_same_v<T, uint8_t>)
     172             :     {
     173             :         int8_t xSigned;
     174         102 :         memcpy(&xSigned, &x, sizeof(xSigned));
     175         204 :         return _mm_set1_epi8(xSigned);
     176             :     }
     177             :     else if constexpr (std::is_same_v<T, int8_t>)
     178          72 :         return _mm_set1_epi8(x);
     179             :     else if constexpr (std::is_same_v<T, uint16_t>)
     180             :     {
     181             :         int16_t xSigned;
     182          43 :         memcpy(&xSigned, &x, sizeof(xSigned));
     183          86 :         return _mm_set1_epi16(xSigned);
     184             :     }
     185             :     else if constexpr (std::is_same_v<T, int16_t>)
     186          90 :         return _mm_set1_epi16(x);
     187             :     else if constexpr (std::is_same_v<T, uint32_t>)
     188             :     {
     189             :         int32_t xSigned;
     190          46 :         memcpy(&xSigned, &x, sizeof(xSigned));
     191          92 :         return _mm_set1_epi32(xSigned);
     192             :     }
     193             :     else if constexpr (std::is_same_v<T, int32_t>)
     194          47 :         return _mm_set1_epi32(x);
     195             :     else if constexpr (std::is_same_v<T, float>)
     196       19617 :         return _mm_set1_ps(x);
     197             :     else
     198       19616 :         return _mm_set1_pd(x);
     199             : }
     200             : 
     201             : // Load as many values of type T at a _mm128[i|d] register can contain from x
     202    47501384 : template <class T> static inline auto loadv(const T *x)
     203             : {
     204             :     if constexpr (std::is_same_v<T, float>)
     205    10000280 :         return _mm_loadu_ps(x);
     206             :     else if constexpr (std::is_same_v<T, double>)
     207    20000260 :         return _mm_loadu_pd(x);
     208             :     else
     209    17500844 :         return _mm_loadu_si128(reinterpret_cast<const __m128i *>(x));
     210             : }
     211             : 
     212             : // Return a __m128i register with bits set when x[i] < y[i] when !IS_MAX
     213             : // or x[i] > y[i] when IS_MAX
     214             : template <class T, bool IS_MAX, class SSE_T>
     215    47501384 : static inline __m128i comp(SSE_T x, SSE_T y)
     216             : {
     217             :     if constexpr (IS_MAX)
     218             :     {
     219             :         if constexpr (std::is_same_v<T, uint8_t>)
     220         492 :             return _mm_cmpgt_epi8(
     221             :                 _mm_add_epi8(x,
     222         164 :                              _mm_set1_epi8(std::numeric_limits<int8_t>::min())),
     223         164 :                 y);
     224             :         else if constexpr (std::is_same_v<T, int8_t>)
     225          12 :             return _mm_cmpgt_epi8(x, y);
     226             :         else if constexpr (std::is_same_v<T, uint16_t>)
     227          84 :             return _mm_cmpgt_epi16(
     228             :                 _mm_add_epi16(
     229          28 :                     x, _mm_set1_epi16(std::numeric_limits<int16_t>::min())),
     230          28 :                 y);
     231             :         else if constexpr (std::is_same_v<T, int16_t>)
     232          28 :             return _mm_cmpgt_epi16(x, y);
     233             :         else if constexpr (std::is_same_v<T, uint32_t>)
     234         180 :             return _mm_cmpgt_epi32(
     235             :                 _mm_add_epi32(
     236             :                     x, _mm_set1_epi32(std::numeric_limits<int32_t>::min())),
     237          60 :                 y);
     238             :         else if constexpr (std::is_same_v<T, int32_t>)
     239          60 :             return _mm_cmpgt_epi32(x, y);
     240             :         // We could use _mm_cmpgt_pX() if there was no NaN values
     241             :         else if constexpr (std::is_same_v<T, float>)
     242         240 :             return _mm_castps_si128(_mm_cmpnle_ps(x, y));
     243             :         else
     244         136 :             return _mm_castpd_si128(_mm_cmpnle_pd(x, y));
     245             :     }
     246             :     else
     247             :     {
     248             :         if constexpr (std::is_same_v<T, uint8_t>)
     249     3750508 :             return _mm_cmplt_epi8(
     250             :                 _mm_add_epi8(x,
     251     1250166 :                              _mm_set1_epi8(std::numeric_limits<int8_t>::min())),
     252     1250166 :                 y);
     253             :         else if constexpr (std::is_same_v<T, int8_t>)
     254     1250014 :             return _mm_cmplt_epi8(x, y);
     255             :         else if constexpr (std::is_same_v<T, uint16_t>)
     256     7500148 :             return _mm_cmplt_epi16(
     257             :                 _mm_add_epi16(
     258     2500046 :                     x, _mm_set1_epi16(std::numeric_limits<int16_t>::min())),
     259     2500046 :                 y);
     260             :         else if constexpr (std::is_same_v<T, int16_t>)
     261     2500046 :             return _mm_cmplt_epi16(x, y);
     262             :         else if constexpr (std::is_same_v<T, uint32_t>)
     263    15000360 :             return _mm_cmplt_epi32(
     264             :                 _mm_add_epi32(
     265             :                     x, _mm_set1_epi32(std::numeric_limits<int32_t>::min())),
     266     5000110 :                 y);
     267             :         else if constexpr (std::is_same_v<T, int32_t>)
     268     5000110 :             return _mm_cmplt_epi32(x, y);
     269             :         // We could use _mm_cmplt_pX() if there was no NaN values
     270             :         else if constexpr (std::is_same_v<T, float>)
     271    20000360 :             return _mm_castps_si128(_mm_cmpnge_ps(x, y));
     272             :         else
     273    40000384 :             return _mm_castpd_si128(_mm_cmpnge_pd(x, y));
     274             :     }
     275             : }
     276             : 
     277             : template <class T, class SSE_T> static inline SSE_T compeq(SSE_T a, SSE_T b);
     278             : 
     279      625024 : template <> __m128i compeq<uint8_t>(__m128i a, __m128i b)
     280             : {
     281      625024 :     return _mm_cmpeq_epi8(a, b);
     282             : }
     283             : 
     284      625008 : template <> __m128i compeq<int8_t>(__m128i a, __m128i b)
     285             : {
     286      625008 :     return _mm_cmpeq_epi8(a, b);
     287             : }
     288             : 
     289     1250028 : template <> __m128i compeq<uint16_t>(__m128i a, __m128i b)
     290             : {
     291     1250028 :     return _mm_cmpeq_epi16(a, b);
     292             : }
     293             : 
     294     1250028 : template <> __m128i compeq<int16_t>(__m128i a, __m128i b)
     295             : {
     296     1250028 :     return _mm_cmpeq_epi16(a, b);
     297             : }
     298             : 
     299     2500060 : template <> __m128i compeq<uint32_t>(__m128i a, __m128i b)
     300             : {
     301     2500060 :     return _mm_cmpeq_epi32(a, b);
     302             : }
     303             : 
     304     2500060 : template <> __m128i compeq<int32_t>(__m128i a, __m128i b)
     305             : {
     306     2500060 :     return _mm_cmpeq_epi32(a, b);
     307             : }
     308             : 
     309     5000050 : template <> __m128 compeq<float>(__m128 a, __m128 b)
     310             : {
     311     5000050 :     return _mm_cmpeq_ps(a, b);
     312             : }
     313             : 
     314    10000114 : template <> __m128d compeq<double>(__m128d a, __m128d b)
     315             : {
     316    10000114 :     return _mm_cmpeq_pd(a, b);
     317             : }
     318             : 
     319             : template <class T> static inline T blendv(T a, T b, T mask);
     320             : 
     321     8750196 : template <> __m128i blendv(__m128i a, __m128i b, __m128i mask)
     322             : {
     323             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
     324             :     return _mm_blendv_epi8(a, b, mask);
     325             : #else
     326    26250548 :     return _mm_or_si128(_mm_andnot_si128(mask, a), _mm_and_si128(mask, b));
     327             : #endif
     328             : }
     329             : 
     330     5000050 : template <> __m128 blendv(__m128 a, __m128 b, __m128 mask)
     331             : {
     332             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
     333             :     return _mm_blendv_ps(a, b, mask);
     334             : #else
     335    15000180 :     return _mm_or_ps(_mm_andnot_ps(mask, a), _mm_and_ps(mask, b));
     336             : #endif
     337             : }
     338             : 
     339    10000114 : template <> __m128d blendv(__m128d a, __m128d b, __m128d mask)
     340             : {
     341             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
     342             :     return _mm_blendv_pd(a, b, mask);
     343             : #else
     344    30000372 :     return _mm_or_pd(_mm_andnot_pd(mask, a), _mm_and_pd(mask, b));
     345             : #endif
     346             : }
     347             : 
     348             : // Using SSE2
     349             : template <class T, bool IS_MAX, bool HAS_NODATA>
     350         170 : inline size_t extremum_element_with_nan(const T *v, size_t size, T noDataValue)
     351             : {
     352             :     static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
     353             :                   std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
     354             :                   std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
     355             :                   std::is_floating_point_v<T>);
     356         170 :     if (size == 0)
     357          16 :         return 0;
     358         154 :     size_t idx_of_extremum = 0;
     359         154 :     T extremum = v[0];
     360         154 :     [[maybe_unused]] bool extremum_is_invalid = false;
     361             :     if constexpr (std::is_floating_point_v<T>)
     362             :     {
     363          48 :         extremum_is_invalid = std::isnan(extremum);
     364             :     }
     365             :     if constexpr (HAS_NODATA)
     366             :     {
     367          75 :         if (extremum == noDataValue)
     368          19 :             extremum_is_invalid = true;
     369             :     }
     370         154 :     size_t i = 1;
     371             : 
     372         154 :     constexpr size_t VALS_PER_REG = sizeof(set1(extremum)) / sizeof(extremum);
     373         154 :     constexpr int LOOP_UNROLLING = 4;
     374             :     // If changing the value, then we need to adjust the number of sse_valX
     375             :     // loading in the loop.
     376             :     static_assert(LOOP_UNROLLING == 4);
     377         154 :     constexpr size_t VALS_PER_ITER = VALS_PER_REG * LOOP_UNROLLING;
     378             : 
     379     3785759 :     const auto update = [v, noDataValue, &extremum, &idx_of_extremum,
     380             :                          &extremum_is_invalid](size_t idx)
     381             :     {
     382             :         if constexpr (HAS_NODATA)
     383             :         {
     384      472296 :             if (v[idx] == noDataValue)
     385         671 :                 return;
     386      471625 :             if (extremum_is_invalid)
     387             :             {
     388             :                 if constexpr (std::is_floating_point_v<T>)
     389             :                 {
     390          20 :                     if (std::isnan(v[idx]))
     391           8 :                         return;
     392             :                 }
     393          26 :                 extremum = v[idx];
     394          26 :                 idx_of_extremum = idx;
     395          26 :                 extremum_is_invalid = false;
     396          26 :                 return;
     397             :             }
     398             :         }
     399             :         else
     400             :         {
     401      476010 :             CPL_IGNORE_RET_VAL(noDataValue);
     402             :         }
     403      947601 :         if (compScalar<T, IS_MAX>(v[idx], extremum))
     404             :         {
     405         416 :             extremum = v[idx];
     406         416 :             idx_of_extremum = idx;
     407         416 :             extremum_is_invalid = false;
     408             :         }
     409             :         else if constexpr (std::is_floating_point_v<T>)
     410             :         {
     411      940305 :             if (extremum_is_invalid && !std::isnan(v[idx]))
     412             :             {
     413          14 :                 extremum = v[idx];
     414          14 :                 idx_of_extremum = idx;
     415          14 :                 extremum_is_invalid = false;
     416             :             }
     417             :         }
     418             :     };
     419             : 
     420        3101 :     for (; i < VALS_PER_ITER && i < size; ++i)
     421             :     {
     422        2947 :         update(i);
     423             :     }
     424             : 
     425         154 :     [[maybe_unused]] auto sse_neutral = set1_unshifted(static_cast<T>(0));
     426         154 :     [[maybe_unused]] auto sse_nodata = set1_unshifted(noDataValue);
     427             :     if constexpr (HAS_NODATA)
     428             :     {
     429         334 :         for (; i < size && extremum_is_invalid; ++i)
     430             :         {
     431         259 :             update(i);
     432             :         }
     433          75 :         if (!extremum_is_invalid)
     434             :         {
     435         136 :             for (; i < size && (i % VALS_PER_ITER) != 0; ++i)
     436             :             {
     437          62 :                 update(i);
     438             :             }
     439          74 :             sse_neutral = set1_unshifted(extremum);
     440             :         }
     441             :     }
     442             : 
     443         154 :     auto sse_extremum = set1(extremum);
     444             : 
     445         154 :     [[maybe_unused]] size_t hits = 0;
     446         154 :     const auto sse_iter_count = (size / VALS_PER_ITER) * VALS_PER_ITER;
     447    11875496 :     for (; i < sse_iter_count; i += VALS_PER_ITER)
     448             :     {
     449             :         // A bit of loop unrolling to save 3/4 of slow movemask operations.
     450    11875354 :         auto sse_val0 = loadv(v + i + 0 * VALS_PER_REG);
     451    11875354 :         auto sse_val1 = loadv(v + i + 1 * VALS_PER_REG);
     452    11875354 :         auto sse_val2 = loadv(v + i + 2 * VALS_PER_REG);
     453    11875354 :         auto sse_val3 = loadv(v + i + 3 * VALS_PER_REG);
     454             : 
     455             :         if constexpr (HAS_NODATA)
     456             :         {
     457             :             // Replace all components that are at the nodata value by a
     458             :             // neutral value (current minimum)
     459     5937594 :             const auto replaceNoDataByNeutral =
     460    47500720 :                 [sse_neutral, sse_nodata](auto sse_val)
     461             :             {
     462    23750400 :                 const auto eq_nodata = compeq<T>(sse_val, sse_nodata);
     463    23750400 :                 return blendv(sse_val, sse_neutral, eq_nodata);
     464             :             };
     465             : 
     466     5937594 :             sse_val0 = replaceNoDataByNeutral(sse_val0);
     467     5937594 :             sse_val1 = replaceNoDataByNeutral(sse_val1);
     468     5937594 :             sse_val2 = replaceNoDataByNeutral(sse_val2);
     469     5937594 :             sse_val3 = replaceNoDataByNeutral(sse_val3);
     470             :         }
     471             : 
     472    47501408 :         if (_mm_movemask_epi8(_mm_or_si128(
     473             :                 _mm_or_si128(comp<T, IS_MAX>(sse_val0, sse_extremum),
     474             :                              comp<T, IS_MAX>(sse_val1, sse_extremum)),
     475             :                 _mm_or_si128(comp<T, IS_MAX>(sse_val2, sse_extremum),
     476    11875354 :                              comp<T, IS_MAX>(sse_val3, sse_extremum)))) != 0)
     477             :         {
     478             :             if constexpr (!std::is_same_v<T, int8_t> &&
     479             :                           !std::is_same_v<T, uint8_t>)
     480             :             {
     481             :                 // The above tests excluding int8_t/uint8_t is due to the fact
     482             :                 // with those small ranges of values we will quickly converge
     483             :                 // to the minimum, so no need to do the below "smart" test.
     484             : 
     485       78411 :                 if (++hits == size / 16)
     486             :                 {
     487             :                     // If we have an almost sorted array, then using this code path
     488             :                     // will hurt performance. Arbitrary give up if we get here
     489             :                     // more than 1. / 16 of the size of the array.
     490             :                     // fprintf(stderr, "going to non-vector path\n");
     491           4 :                     break;
     492             :                 }
     493             :             }
     494     1022991 :             for (size_t j = 0; j < VALS_PER_ITER; j++)
     495             :             {
     496      944544 :                 update(i + j);
     497             :             }
     498       78447 :             sse_extremum = set1(extremum);
     499             :             if constexpr (HAS_NODATA)
     500             :             {
     501       39170 :                 sse_neutral = set1_unshifted(extremum);
     502             :             }
     503             :         }
     504             :     }
     505         648 :     for (; i < size; ++i)
     506             :     {
     507         494 :         update(i);
     508             :     }
     509         154 :     return idx_of_extremum;
     510             : }
     511             : 
     512             : #else
     513             : 
     514             : /************************************************************************/
     515             : /*                    extremum_element_with_nan()                       */
     516             : /************************************************************************/
     517             : 
     518             : template <class T, bool IS_MAX, bool HAS_NODATA>
     519             : inline size_t extremum_element_with_nan(const T *v, size_t size, T /* nodata */)
     520             : {
     521             :     static_assert(!HAS_NODATA);
     522             :     if (size == 0)
     523             :         return 0;
     524             :     size_t idx_of_extremum = 0;
     525             :     auto extremum = v[0];
     526             :     bool extremum_is_nan = std::isnan(extremum);
     527             :     size_t i = 1;
     528             :     for (; i < size; ++i)
     529             :     {
     530             :         if (compScalar<T, IS_MAX>(v[i], extremum) ||
     531             :             (extremum_is_nan && !std::isnan(v[i])))
     532             :         {
     533             :             extremum = v[i];
     534             :             idx_of_extremum = i;
     535             :             extremum_is_nan = false;
     536             :         }
     537             :     }
     538             :     return idx_of_extremum;
     539             : }
     540             : #endif
     541             : 
     542             : /************************************************************************/
     543             : /*                         extremum_element()                           */
     544             : /************************************************************************/
     545             : 
     546             : #ifdef GDAL_MINMAX_ELEMENT_USE_SSE2
     547             : 
     548             : template <>
     549           4 : size_t extremum_element<uint8_t, true>(const uint8_t *v, size_t size,
     550             :                                        uint8_t noDataValue)
     551             : {
     552           4 :     return extremum_element_with_nan<uint8_t, true, true>(v, size, noDataValue);
     553             : }
     554             : 
     555             : template <>
     556           8 : size_t extremum_element<uint8_t, false>(const uint8_t *v, size_t size,
     557             :                                         uint8_t noDataValue)
     558             : {
     559           8 :     return extremum_element_with_nan<uint8_t, false, true>(v, size,
     560           8 :                                                            noDataValue);
     561             : }
     562             : 
     563             : template <>
     564          15 : size_t extremum_element<uint8_t, true>(const uint8_t *v, size_t size)
     565             : {
     566          15 :     return extremum_element_with_nan<uint8_t, true, false>(v, size, 0);
     567             : }
     568             : 
     569             : template <>
     570          16 : size_t extremum_element<uint8_t, false>(const uint8_t *v, size_t size)
     571             : {
     572          16 :     return extremum_element_with_nan<uint8_t, false, false>(v, size, 0);
     573             : }
     574             : 
     575             : template <>
     576           3 : size_t extremum_element<int8_t, true>(const int8_t *v, size_t size,
     577             :                                       int8_t noDataValue)
     578             : {
     579           3 :     return extremum_element_with_nan<int8_t, true, true>(v, size, noDataValue);
     580             : }
     581             : 
     582             : template <>
     583           6 : size_t extremum_element<int8_t, false>(const int8_t *v, size_t size,
     584             :                                        int8_t noDataValue)
     585             : {
     586           6 :     return extremum_element_with_nan<int8_t, false, true>(v, size, noDataValue);
     587             : }
     588             : 
     589           2 : template <> size_t extremum_element<int8_t, true>(const int8_t *v, size_t size)
     590             : {
     591           2 :     return extremum_element_with_nan<int8_t, true, false>(v, size, 0);
     592             : }
     593             : 
     594           4 : template <> size_t extremum_element<int8_t, false>(const int8_t *v, size_t size)
     595             : {
     596           4 :     return extremum_element_with_nan<int8_t, false, false>(v, size, 0);
     597             : }
     598             : 
     599             : template <>
     600           3 : size_t extremum_element<uint16_t, true>(const uint16_t *v, size_t size,
     601             :                                         uint16_t noDataValue)
     602             : {
     603           3 :     return extremum_element_with_nan<uint16_t, true, true>(v, size,
     604           3 :                                                            noDataValue);
     605             : }
     606             : 
     607             : template <>
     608           6 : size_t extremum_element<uint16_t, false>(const uint16_t *v, size_t size,
     609             :                                          uint16_t noDataValue)
     610             : {
     611           6 :     return extremum_element_with_nan<uint16_t, false, true>(v, size,
     612           6 :                                                             noDataValue);
     613             : }
     614             : 
     615             : template <>
     616           2 : size_t extremum_element<uint16_t, true>(const uint16_t *v, size_t size)
     617             : {
     618           2 :     return extremum_element_with_nan<uint16_t, true, false>(v, size, 0);
     619             : }
     620             : 
     621             : template <>
     622           4 : size_t extremum_element<uint16_t, false>(const uint16_t *v, size_t size)
     623             : {
     624           4 :     return extremum_element_with_nan<uint16_t, false, false>(v, size, 0);
     625             : }
     626             : 
     627             : template <>
     628           3 : size_t extremum_element<int16_t, true>(const int16_t *v, size_t size,
     629             :                                        int16_t noDataValue)
     630             : {
     631           3 :     return extremum_element_with_nan<int16_t, true, true>(v, size, noDataValue);
     632             : }
     633             : 
     634             : template <>
     635           6 : size_t extremum_element<int16_t, false>(const int16_t *v, size_t size,
     636             :                                         int16_t noDataValue)
     637             : {
     638           6 :     return extremum_element_with_nan<int16_t, false, true>(v, size,
     639           6 :                                                            noDataValue);
     640             : }
     641             : 
     642             : template <>
     643           2 : size_t extremum_element<int16_t, true>(const int16_t *v, size_t size)
     644             : {
     645           2 :     return extremum_element_with_nan<int16_t, true, false>(v, size, 0);
     646             : }
     647             : 
     648             : template <>
     649           4 : size_t extremum_element<int16_t, false>(const int16_t *v, size_t size)
     650             : {
     651           4 :     return extremum_element_with_nan<int16_t, false, false>(v, size, 0);
     652             : }
     653             : 
     654             : template <>
     655           3 : size_t extremum_element<uint32_t, true>(const uint32_t *v, size_t size,
     656             :                                         uint32_t noDataValue)
     657             : {
     658           3 :     return extremum_element_with_nan<uint32_t, true, true>(v, size,
     659           3 :                                                            noDataValue);
     660             : }
     661             : 
     662             : template <>
     663           6 : size_t extremum_element<uint32_t, false>(const uint32_t *v, size_t size,
     664             :                                          uint32_t noDataValue)
     665             : {
     666           6 :     return extremum_element_with_nan<uint32_t, false, true>(v, size,
     667           6 :                                                             noDataValue);
     668             : }
     669             : 
     670             : template <>
     671           2 : size_t extremum_element<uint32_t, true>(const uint32_t *v, size_t size)
     672             : {
     673           2 :     return extremum_element_with_nan<uint32_t, true, false>(v, size, 0);
     674             : }
     675             : 
     676             : template <>
     677           4 : size_t extremum_element<uint32_t, false>(const uint32_t *v, size_t size)
     678             : {
     679           4 :     return extremum_element_with_nan<uint32_t, false, false>(v, size, 0);
     680             : }
     681             : 
     682             : template <>
     683           3 : size_t extremum_element<int32_t, true>(const int32_t *v, size_t size,
     684             :                                        int32_t noDataValue)
     685             : {
     686           3 :     return extremum_element_with_nan<int32_t, true, true>(v, size, noDataValue);
     687             : }
     688             : 
     689             : template <>
     690           6 : size_t extremum_element<int32_t, false>(const int32_t *v, size_t size,
     691             :                                         int32_t noDataValue)
     692             : {
     693           6 :     return extremum_element_with_nan<int32_t, false, true>(v, size,
     694           6 :                                                            noDataValue);
     695             : }
     696             : 
     697             : template <>
     698           2 : size_t extremum_element<int32_t, true>(const int32_t *v, size_t size)
     699             : {
     700           2 :     return extremum_element_with_nan<int32_t, true, false>(v, size, 0);
     701             : }
     702             : 
     703             : template <>
     704           4 : size_t extremum_element<int32_t, false>(const int32_t *v, size_t size)
     705             : {
     706           4 :     return extremum_element_with_nan<int32_t, false, false>(v, size, 0);
     707             : }
     708             : 
     709             : template <>
     710           5 : size_t extremum_element<float, true>(const float *v, size_t size,
     711             :                                      float noDataValue)
     712             : {
     713           5 :     return extremum_element_with_nan<float, true, true>(v, size, noDataValue);
     714             : }
     715             : 
     716             : template <>
     717           9 : size_t extremum_element<float, false>(const float *v, size_t size,
     718             :                                       float noDataValue)
     719             : {
     720           9 :     return extremum_element_with_nan<float, false, true>(v, size, noDataValue);
     721             : }
     722             : 
     723             : template <>
     724           4 : size_t extremum_element<double, true>(const double *v, size_t size,
     725             :                                       double noDataValue)
     726             : {
     727           4 :     return extremum_element_with_nan<double, true, true>(v, size, noDataValue);
     728             : }
     729             : 
     730             : template <>
     731           8 : size_t extremum_element<double, false>(const double *v, size_t size,
     732             :                                        double noDataValue)
     733             : {
     734           8 :     return extremum_element_with_nan<double, false, true>(v, size, noDataValue);
     735             : }
     736             : 
     737             : #endif
     738             : 
     739           5 : template <> size_t extremum_element<float, true>(const float *v, size_t size)
     740             : {
     741           5 :     return extremum_element_with_nan<float, true, false>(v, size, 0);
     742             : }
     743             : 
     744           5 : template <> size_t extremum_element<double, true>(const double *v, size_t size)
     745             : {
     746           5 :     return extremum_element_with_nan<double, true, false>(v, size, 0);
     747             : }
     748             : 
     749           8 : template <> size_t extremum_element<float, false>(const float *v, size_t size)
     750             : {
     751           8 :     return extremum_element_with_nan<float, false, false>(v, size, 0);
     752             : }
     753             : 
     754           8 : template <> size_t extremum_element<double, false>(const double *v, size_t size)
     755             : {
     756           8 :     return extremum_element_with_nan<double, false, false>(v, size, 0);
     757             : }
     758             : 
     759             : /************************************************************************/
     760             : /*                       extremum_element_with_nan()                    */
     761             : /************************************************************************/
     762             : 
     763             : template <class T, bool IS_MAX>
     764             : inline size_t extremum_element_with_nan(const T *v, size_t size, T noDataValue)
     765             : {
     766             :     if (std::isnan(noDataValue))
     767             :         return extremum_element_with_nan<T, IS_MAX, false>(v, size, 0);
     768             :     if (size == 0)
     769             :         return 0;
     770             :     size_t idx_of_extremum = 0;
     771             :     auto extremum = v[0];
     772             :     bool extremum_is_nan_or_nodata =
     773             :         std::isnan(extremum) || (extremum == noDataValue);
     774             :     size_t i = 1;
     775             :     for (; i < size; ++i)
     776             :     {
     777             :         if (v[i] != noDataValue &&
     778             :             (compScalar<T, IS_MAX>(v[i], extremum) ||
     779             :              (extremum_is_nan_or_nodata && !std::isnan(v[i]))))
     780             :         {
     781             :             extremum = v[i];
     782             :             idx_of_extremum = i;
     783             :             extremum_is_nan_or_nodata = false;
     784             :         }
     785             :     }
     786             :     return idx_of_extremum;
     787             : }
     788             : 
     789             : /************************************************************************/
     790             : /*                            extremum_element()                        */
     791             : /************************************************************************/
     792             : 
     793             : #if !defined(GDAL_MINMAX_ELEMENT_USE_SSE2)
     794             : 
     795             : template <>
     796             : size_t extremum_element<float, true>(const float *v, size_t size,
     797             :                                      float noDataValue)
     798             : {
     799             :     return extremum_element_with_nan<float, true>(v, size, noDataValue);
     800             : }
     801             : 
     802             : template <>
     803             : size_t extremum_element<float, false>(const float *v, size_t size,
     804             :                                       float noDataValue)
     805             : {
     806             :     return extremum_element_with_nan<float, false>(v, size, noDataValue);
     807             : }
     808             : 
     809             : template <>
     810             : size_t extremum_element<double, true>(const double *v, size_t size,
     811             :                                       double noDataValue)
     812             : {
     813             :     return extremum_element_with_nan<double, true>(v, size, noDataValue);
     814             : }
     815             : 
     816             : template <>
     817             : size_t extremum_element<double, false>(const double *v, size_t size,
     818             :                                        double noDataValue)
     819             : {
     820             :     return extremum_element_with_nan<double, false>(v, size, noDataValue);
     821             : }
     822             : 
     823             : #endif
     824             : 
     825             : template <class T, bool IS_MAX>
     826         196 : inline size_t extremum_element(const T *buffer, size_t size, bool bHasNoData,
     827             :                                T noDataValue)
     828             : {
     829         196 :     if (bHasNoData)
     830          99 :         return extremum_element<T, IS_MAX>(buffer, size, noDataValue);
     831             :     else
     832          97 :         return extremum_element<T, IS_MAX>(buffer, size);
     833             : }
     834             : 
     835             : #else
     836             : 
     837             : template <class T, bool IS_MAX>
     838             : inline size_t extremum_element(const T *buffer, size_t size, bool bHasNoData,
     839             :                                T noDataValue)
     840             : {
     841             :     if (bHasNoData)
     842             :     {
     843             :         if constexpr (std::is_floating_point_v<T>)
     844             :         {
     845             :             if (std::isnan(noDataValue))
     846             :             {
     847             :                 if constexpr (IS_MAX)
     848             :                 {
     849             :                     return std::max_element(buffer, buffer + size,
     850             :                                             [](T a, T b) {
     851             :                                                 return std::isnan(b)   ? false
     852             :                                                        : std::isnan(a) ? true
     853             :                                                                        : a < b;
     854             :                                             }) -
     855             :                            buffer;
     856             :                 }
     857             :                 else
     858             :                 {
     859             :                     return std::min_element(buffer, buffer + size,
     860             :                                             [](T a, T b) {
     861             :                                                 return std::isnan(b)   ? true
     862             :                                                        : std::isnan(a) ? false
     863             :                                                                        : a < b;
     864             :                                             }) -
     865             :                            buffer;
     866             :                 }
     867             :             }
     868             :             else
     869             :             {
     870             :                 if constexpr (IS_MAX)
     871             :                 {
     872             :                     return std::max_element(buffer, buffer + size,
     873             :                                             [noDataValue](T a, T b)
     874             :                                             {
     875             :                                                 return std::isnan(b)   ? false
     876             :                                                        : std::isnan(a) ? true
     877             :                                                        : (b == noDataValue)
     878             :                                                            ? false
     879             :                                                        : (a == noDataValue)
     880             :                                                            ? true
     881             :                                                            : a < b;
     882             :                                             }) -
     883             :                            buffer;
     884             :                 }
     885             :                 else
     886             :                 {
     887             :                     return std::min_element(buffer, buffer + size,
     888             :                                             [noDataValue](T a, T b)
     889             :                                             {
     890             :                                                 return std::isnan(b)   ? true
     891             :                                                        : std::isnan(a) ? false
     892             :                                                        : (b == noDataValue)
     893             :                                                            ? true
     894             :                                                        : (a == noDataValue)
     895             :                                                            ? false
     896             :                                                            : a < b;
     897             :                                             }) -
     898             :                            buffer;
     899             :                 }
     900             :             }
     901             :         }
     902             :         else
     903             :         {
     904             :             if constexpr (IS_MAX)
     905             :             {
     906             :                 return std::max_element(buffer, buffer + size,
     907             :                                         [noDataValue](T a, T b) {
     908             :                                             return (b == noDataValue)   ? false
     909             :                                                    : (a == noDataValue) ? true
     910             :                                                                         : a < b;
     911             :                                         }) -
     912             :                        buffer;
     913             :             }
     914             :             else
     915             :             {
     916             :                 return std::min_element(buffer, buffer + size,
     917             :                                         [noDataValue](T a, T b) {
     918             :                                             return (b == noDataValue)   ? true
     919             :                                                    : (a == noDataValue) ? false
     920             :                                                                         : a < b;
     921             :                                         }) -
     922             :                        buffer;
     923             :             }
     924             :         }
     925             :     }
     926             :     else
     927             :     {
     928             :         if constexpr (std::is_floating_point_v<T>)
     929             :         {
     930             :             if constexpr (IS_MAX)
     931             :             {
     932             :                 return std::max_element(buffer, buffer + size,
     933             :                                         [](T a, T b) {
     934             :                                             return std::isnan(b)   ? false
     935             :                                                    : std::isnan(a) ? true
     936             :                                                                    : a < b;
     937             :                                         }) -
     938             :                        buffer;
     939             :             }
     940             :             else
     941             :             {
     942             :                 return std::min_element(buffer, buffer + size,
     943             :                                         [](T a, T b) {
     944             :                                             return std::isnan(b)   ? true
     945             :                                                    : std::isnan(a) ? false
     946             :                                                                    : a < b;
     947             :                                         }) -
     948             :                        buffer;
     949             :             }
     950             :         }
     951             :         else
     952             :         {
     953             :             if constexpr (IS_MAX)
     954             :             {
     955             :                 return std::max_element(buffer, buffer + size) - buffer;
     956             :             }
     957             :             else
     958             :             {
     959             :                 return std::min_element(buffer, buffer + size) - buffer;
     960             :             }
     961             :         }
     962             :     }
     963             : }
     964             : #endif
     965             : 
     966             : template <bool IS_MAX>
     967         168 : size_t extremum_element(const void *buffer, size_t nElts, GDALDataType eDT,
     968             :                         bool bHasNoData, double dfNoDataValue)
     969             : {
     970         168 :     switch (eDT)
     971             :     {
     972             : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0)
     973          13 :         case GDT_Int8:
     974             :         {
     975             :             using T = int8_t;
     976          13 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
     977          13 :             return extremum_element<T, IS_MAX>(
     978             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
     979          13 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
     980             :         }
     981             : #endif
     982          33 :         case GDT_Byte:
     983             :         {
     984             :             using T = uint8_t;
     985          33 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
     986          33 :             return extremum_element<T, IS_MAX>(
     987             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
     988          33 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
     989             :         }
     990          13 :         case GDT_Int16:
     991             :         {
     992             :             using T = int16_t;
     993          13 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
     994          13 :             return extremum_element<T, IS_MAX>(
     995             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
     996          13 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
     997             :         }
     998          13 :         case GDT_UInt16:
     999             :         {
    1000             :             using T = uint16_t;
    1001          13 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1002          13 :             return extremum_element<T, IS_MAX>(
    1003             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1004          13 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1005             :         }
    1006          13 :         case GDT_Int32:
    1007             :         {
    1008             :             using T = int32_t;
    1009          13 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1010          13 :             return extremum_element<T, IS_MAX>(
    1011             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1012          13 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1013             :         }
    1014          13 :         case GDT_UInt32:
    1015             :         {
    1016             :             using T = uint32_t;
    1017          13 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1018          13 :             return extremum_element<T, IS_MAX>(
    1019             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1020          13 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1021             :         }
    1022             : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0)
    1023          11 :         case GDT_Int64:
    1024             :         {
    1025             :             using T = int64_t;
    1026          11 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1027          11 :             return extremum_element<T, IS_MAX>(
    1028             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1029          11 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1030             :         }
    1031          11 :         case GDT_UInt64:
    1032             :         {
    1033             :             using T = uint64_t;
    1034          11 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1035          11 :             return extremum_element<T, IS_MAX>(
    1036             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1037          11 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1038             :         }
    1039             : #endif
    1040          25 :         case GDT_Float32:
    1041             :         {
    1042             :             using T = float;
    1043          25 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1044          25 :             return extremum_element<T, IS_MAX>(
    1045             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1046          25 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1047             :         }
    1048          21 :         case GDT_Float64:
    1049             :         {
    1050             :             using T = double;
    1051          21 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1052          21 :             return extremum_element<T, IS_MAX>(
    1053             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1054          21 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1055             :         }
    1056           2 :         default:
    1057           2 :             break;
    1058             :     }
    1059           2 :     CPLError(CE_Failure, CPLE_NotSupported,
    1060             :              "%s not supported for this data type.", __FUNCTION__);
    1061           2 :     return 0;
    1062             : }
    1063             : 
    1064             : }  // namespace detail
    1065             : 
    1066             : /************************************************************************/
    1067             : /*                            max_element()                             */
    1068             : /************************************************************************/
    1069             : 
    1070             : /** Return the index of the element where the maximum value is hit.
    1071             :  *
    1072             :  * If it is hit in several locations, it is not specified which one will be
    1073             :  * returned.
    1074             :  *
    1075             :  * @param buffer Vector of nElts elements of type eDT.
    1076             :  * @param nElts Number of elements in buffer.
    1077             :  * @param eDT Data type of the elements of buffer.
    1078             :  * @param bHasNoData Whether dfNoDataValue is valid.
    1079             :  * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
    1080             :  *
    1081             :  * @since GDAL 3.11
    1082             :  */
    1083          59 : inline size_t max_element(const void *buffer, size_t nElts, GDALDataType eDT,
    1084             :                           bool bHasNoData, double dfNoDataValue)
    1085             : {
    1086          59 :     return detail::extremum_element<true>(buffer, nElts, eDT, bHasNoData,
    1087          59 :                                           dfNoDataValue);
    1088             : }
    1089             : 
    1090             : /************************************************************************/
    1091             : /*                            min_element()                             */
    1092             : /************************************************************************/
    1093             : 
    1094             : /** Return the index of the element where the minimum value is hit.
    1095             :  *
    1096             :  * If it is hit in several locations, it is not specified which one will be
    1097             :  * returned.
    1098             :  *
    1099             :  * @param buffer Vector of nElts elements of type eDT.
    1100             :  * @param nElts Number of elements in buffer.
    1101             :  * @param eDT Data type of the elements of buffer.
    1102             :  * @param bHasNoData Whether dfNoDataValue is valid.
    1103             :  * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
    1104             :  *
    1105             :  * @since GDAL 3.11
    1106             :  */
    1107         109 : inline size_t min_element(const void *buffer, size_t nElts, GDALDataType eDT,
    1108             :                           bool bHasNoData, double dfNoDataValue)
    1109             : {
    1110         109 :     return detail::extremum_element<false>(buffer, nElts, eDT, bHasNoData,
    1111         109 :                                            dfNoDataValue);
    1112             : }
    1113             : 
    1114             : namespace detail
    1115             : {
    1116             : 
    1117             : #ifdef NOT_EFFICIENT
    1118             : 
    1119             : /************************************************************************/
    1120             : /*                         minmax_element()                             */
    1121             : /************************************************************************/
    1122             : 
    1123             : template <class T>
    1124             : std::pair<size_t, size_t> minmax_element(const T *v, size_t size, T noDataValue)
    1125             : {
    1126             :     static_assert(!(std::is_floating_point_v<T>));
    1127             :     if (size == 0)
    1128             :         return std::pair(0, 0);
    1129             :     size_t idx_of_min = 0;
    1130             :     size_t idx_of_max = 0;
    1131             :     T vmin = v[0];
    1132             :     T vmax = v[0];
    1133             :     bool extremum_is_nodata = vmin == noDataValue;
    1134             :     size_t i = 1;
    1135             :     for (; i < size; ++i)
    1136             :     {
    1137             :         if (v[i] != noDataValue && (v[i] < vmin || extremum_is_nodata))
    1138             :         {
    1139             :             vmin = v[i];
    1140             :             idx_of_min = i;
    1141             :             extremum_is_nodata = false;
    1142             :         }
    1143             :         if (v[i] != noDataValue && (v[i] > vmax || extremum_is_nodata))
    1144             :         {
    1145             :             vmax = v[i];
    1146             :             idx_of_max = i;
    1147             :             extremum_is_nodata = false;
    1148             :         }
    1149             :     }
    1150             :     return std::pair(idx_of_min, idx_of_max);
    1151             : }
    1152             : 
    1153             : template <class T>
    1154             : std::pair<size_t, size_t> minmax_element(const T *v, size_t size)
    1155             : {
    1156             :     static_assert(!(std::is_floating_point_v<T>));
    1157             :     if (size == 0)
    1158             :         return std::pair(0, 0);
    1159             :     size_t idx_of_min = 0;
    1160             :     size_t idx_of_max = 0;
    1161             :     T vmin = v[0];
    1162             :     T vmax = v[0];
    1163             :     size_t i = 1;
    1164             :     for (; i < size; ++i)
    1165             :     {
    1166             :         if (v[i] < vmin)
    1167             :         {
    1168             :             vmin = v[i];
    1169             :             idx_of_min = i;
    1170             :         }
    1171             :         if (v[i] > vmax)
    1172             :         {
    1173             :             vmax = v[i];
    1174             :             idx_of_max = i;
    1175             :         }
    1176             :     }
    1177             :     return std::pair(idx_of_min, idx_of_max);
    1178             : }
    1179             : 
    1180             : template <class T>
    1181             : inline std::pair<size_t, size_t> minmax_element_with_nan(const T *v,
    1182             :                                                          size_t size)
    1183             : {
    1184             :     if (size == 0)
    1185             :         return std::pair(0, 0);
    1186             :     size_t idx_of_min = 0;
    1187             :     size_t idx_of_max = 0;
    1188             :     T vmin = v[0];
    1189             :     T vmax = v[0];
    1190             :     size_t i = 1;
    1191             :     if (std::isnan(v[0]))
    1192             :     {
    1193             :         for (; i < size; ++i)
    1194             :         {
    1195             :             if (!std::isnan(v[i]))
    1196             :             {
    1197             :                 vmin = v[i];
    1198             :                 idx_of_min = i;
    1199             :                 vmax = v[i];
    1200             :                 idx_of_max = i;
    1201             :                 break;
    1202             :             }
    1203             :         }
    1204             :     }
    1205             :     for (; i < size; ++i)
    1206             :     {
    1207             :         if (v[i] < vmin)
    1208             :         {
    1209             :             vmin = v[i];
    1210             :             idx_of_min = i;
    1211             :         }
    1212             :         if (v[i] > vmax)
    1213             :         {
    1214             :             vmax = v[i];
    1215             :             idx_of_max = i;
    1216             :         }
    1217             :     }
    1218             :     return std::pair(idx_of_min, idx_of_max);
    1219             : }
    1220             : 
    1221             : template <>
    1222             : std::pair<size_t, size_t> minmax_element<float>(const float *v, size_t size)
    1223             : {
    1224             :     return minmax_element_with_nan<float>(v, size);
    1225             : }
    1226             : 
    1227             : template <>
    1228             : std::pair<size_t, size_t> minmax_element<double>(const double *v, size_t size)
    1229             : {
    1230             :     return minmax_element_with_nan<double>(v, size);
    1231             : }
    1232             : 
    1233             : template <class T>
    1234             : inline std::pair<size_t, size_t> minmax_element(const T *buffer, size_t size,
    1235             :                                                 bool bHasNoData, T noDataValue)
    1236             : {
    1237             :     if (bHasNoData)
    1238             :     {
    1239             :         return minmax_element<T>(buffer, size, noDataValue);
    1240             :     }
    1241             :     else
    1242             :     {
    1243             :         return minmax_element<T>(buffer, size);
    1244             :     }
    1245             : }
    1246             : #else
    1247             : 
    1248             : /************************************************************************/
    1249             : /*                         minmax_element()                             */
    1250             : /************************************************************************/
    1251             : 
    1252             : template <class T>
    1253          15 : inline std::pair<size_t, size_t> minmax_element(const T *buffer, size_t size,
    1254             :                                                 bool bHasNoData, T noDataValue)
    1255             : {
    1256             : #ifdef NOT_EFFICIENT
    1257             :     if (bHasNoData)
    1258             :     {
    1259             :         return minmax_element<T>(buffer, size, noDataValue);
    1260             :     }
    1261             :     else
    1262             :     {
    1263             :         return minmax_element<T>(buffer, size);
    1264             :         //auto [imin, imax] = std::minmax_element(buffer, buffer + size);
    1265             :         //return std::pair(imin - buffer, imax - buffer);
    1266             :     }
    1267             : #else
    1268             : 
    1269             : #if !defined(GDAL_MINMAX_ELEMENT_USE_SSE2)
    1270             :     if constexpr (!std::is_floating_point_v<T>)
    1271             :     {
    1272             :         if (!bHasNoData)
    1273             :         {
    1274             :             auto [min_iter, max_iter] =
    1275             :                 std::minmax_element(buffer, buffer + size);
    1276             :             return std::pair(min_iter - buffer, max_iter - buffer);
    1277             :         }
    1278             :     }
    1279             : #endif
    1280             : 
    1281             :     // Using separately min and max is more efficient than computing them
    1282             :     // within the same loop
    1283             :     return std::pair(
    1284          45 :         extremum_element<T, false>(buffer, size, bHasNoData, noDataValue),
    1285          15 :         extremum_element<T, true>(buffer, size, bHasNoData, noDataValue));
    1286             : 
    1287             : #endif
    1288             : }
    1289             : #endif
    1290             : 
    1291             : }  // namespace detail
    1292             : 
    1293             : /************************************************************************/
    1294             : /*                          minmax_element()                            */
    1295             : /************************************************************************/
    1296             : 
    1297             : /** Return the index of the elements where the minimum and maximum values are hit.
    1298             :  *
    1299             :  * If they are hit in several locations, it is not specified which one will be
    1300             :  * returned (contrary to std::minmax_element).
    1301             :  *
    1302             :  * @param buffer Vector of nElts elements of type eDT.
    1303             :  * @param nElts Number of elements in buffer.
    1304             :  * @param eDT Data type of the elements of buffer.
    1305             :  * @param bHasNoData Whether dfNoDataValue is valid.
    1306             :  * @param dfNoDataValue Nodata value, only taken into account if bHasNoData == true
    1307             :  *
    1308             :  * @since GDAL 3.11
    1309             :  */
    1310          16 : inline std::pair<size_t, size_t> minmax_element(const void *buffer,
    1311             :                                                 size_t nElts, GDALDataType eDT,
    1312             :                                                 bool bHasNoData,
    1313             :                                                 double dfNoDataValue)
    1314             : {
    1315          16 :     switch (eDT)
    1316             :     {
    1317             : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 7, 0)
    1318           1 :         case GDT_Int8:
    1319             :         {
    1320             :             using T = int8_t;
    1321           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1322             :             return detail::minmax_element<T>(
    1323             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1324           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1325             :         }
    1326             : #endif
    1327           5 :         case GDT_Byte:
    1328             :         {
    1329             :             using T = uint8_t;
    1330           5 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1331             :             return detail::minmax_element<T>(
    1332             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1333           5 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1334             :         }
    1335           1 :         case GDT_Int16:
    1336             :         {
    1337             :             using T = int16_t;
    1338           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1339             :             return detail::minmax_element<T>(
    1340             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1341           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1342             :         }
    1343           1 :         case GDT_UInt16:
    1344             :         {
    1345             :             using T = uint16_t;
    1346           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1347             :             return detail::minmax_element<T>(
    1348             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1349           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1350             :         }
    1351           1 :         case GDT_Int32:
    1352             :         {
    1353             :             using T = int32_t;
    1354           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1355             :             return detail::minmax_element<T>(
    1356             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1357           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1358             :         }
    1359           1 :         case GDT_UInt32:
    1360             :         {
    1361             :             using T = uint32_t;
    1362           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1363             :             return detail::minmax_element<T>(
    1364             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1365           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1366             :         }
    1367             : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0)
    1368           1 :         case GDT_Int64:
    1369             :         {
    1370             :             using T = int64_t;
    1371           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1372             :             return detail::minmax_element<T>(
    1373             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1374           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1375             :         }
    1376           1 :         case GDT_UInt64:
    1377             :         {
    1378             :             using T = uint64_t;
    1379           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1380             :             return detail::minmax_element<T>(
    1381             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1382           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1383             :         }
    1384             : #endif
    1385           1 :         case GDT_Float32:
    1386             :         {
    1387             :             using T = float;
    1388           1 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1389             :             return detail::minmax_element<T>(
    1390             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1391           1 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1392             :         }
    1393           2 :         case GDT_Float64:
    1394             :         {
    1395             :             using T = double;
    1396           2 :             bHasNoData = bHasNoData && GDALIsValueExactAs<T>(dfNoDataValue);
    1397             :             return detail::minmax_element<T>(
    1398             :                 static_cast<const T *>(buffer), nElts, bHasNoData,
    1399           2 :                 bHasNoData ? static_cast<T>(dfNoDataValue) : 0);
    1400             :         }
    1401           1 :         default:
    1402           1 :             break;
    1403             :     }
    1404           1 :     CPLError(CE_Failure, CPLE_NotSupported,
    1405             :              "%s not supported for this data type.", __FUNCTION__);
    1406           1 :     return std::pair(0, 0);
    1407             : }
    1408             : 
    1409             : }  // namespace GDAL_MINMAXELT_NS
    1410             : 
    1411             : #endif  // GDAL_MINMAX_ELEMENT_INCLUDED

Generated by: LCOV version 1.14