LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_maths.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 289 305 94.8 %
Date: 2026-07-09 08:35:43 Functions: 27 27 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_maths.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  Mathematic operations on GDALMDArray
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_multidim.h"
      15             : #include "gdal_pam_multidim.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cmath>
      20             : #include <functional>
      21             : #include <limits>
      22             : 
      23             : /************************************************************************/
      24             : /*                    GDALMDArray::HasSameShapeAs()                     */
      25             : /************************************************************************/
      26             : 
      27             : /** Returns true if both arrays have the same shape.
      28             :  *
      29             :  * That is to say the same number of dimensions and the size of corresponding
      30             :  * dimensions is the same.
      31             :  *
      32             :  * @since 3.14
      33             :  */
      34         161 : bool GDALMDArray::HasSameShapeAs(const GDALMDArray &other) const
      35             : {
      36         161 :     bool bRet = (GetDimensionCount() == other.GetDimensionCount());
      37         161 :     if (bRet)
      38             :     {
      39         161 :         const auto &apoThisDims = GetDimensions();
      40         161 :         const auto &apoOtherDims = other.GetDimensions();
      41         376 :         for (size_t i = 0; bRet && i < apoThisDims.size(); ++i)
      42             :         {
      43         215 :             bRet = (apoThisDims[i]->GetSize() == apoOtherDims[i]->GetSize());
      44             :         }
      45             :     }
      46         161 :     return bRet;
      47             : }
      48             : 
      49             : /************************************************************************/
      50             : /*                       GDALMathOperationMDArray                       */
      51             : /************************************************************************/
      52             : 
      53             : //! @cond Doxygen_Suppress
      54             : 
      55             : class GDALMathOperationMDArray final : public GDALPamMDArray
      56             : {
      57             :   public:
      58             :     enum class Operation
      59             :     {
      60             :         OP_ADD,
      61             :         OP_SUBTRACT,
      62             :         OP_MULTIPLY,
      63             :         OP_DIVIDE,
      64             :     };
      65             : 
      66         290 :     static const char *OperationToString(Operation op)
      67             :     {
      68         290 :         switch (op)
      69             :         {
      70          92 :             case Operation::OP_ADD:
      71          92 :                 break;
      72          84 :             case Operation::OP_SUBTRACT:
      73          84 :                 return "-";
      74          65 :             case Operation::OP_MULTIPLY:
      75          65 :                 return "*";
      76          49 :             case Operation::OP_DIVIDE:
      77          49 :                 return "/";
      78             :         }
      79          92 :         return "+";
      80             :     }
      81             : 
      82             :     static std::shared_ptr<GDALMathOperationMDArray>
      83             :     Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
      84             :            const std::shared_ptr<GDALMDArray> &arrayRight, Operation op);
      85             : 
      86             :   protected:
      87         284 :     static std::string GetName(const std::shared_ptr<GDALMDArray> &arrayLeft,
      88             :                                const std::shared_ptr<GDALMDArray> &arrayRight,
      89             :                                Operation op)
      90             :     {
      91         568 :         return std::string(arrayLeft->GetFullName())
      92         284 :             .append(" ")
      93         284 :             .append(OperationToString(op))
      94         284 :             .append(" ")
      95         568 :             .append(arrayRight->GetFullName());
      96             :     }
      97             : 
      98         142 :     GDALMathOperationMDArray(const std::shared_ptr<GDALMDArray> &arrayLeft,
      99             :                              const std::shared_ptr<GDALMDArray> &arrayRight,
     100             :                              Operation op)
     101         284 :         : GDALAbstractMDArray(std::string(),
     102         142 :                               GetName(arrayLeft, arrayRight, op)),
     103         284 :           GDALPamMDArray(std::string(), GetName(arrayLeft, arrayRight, op),
     104             :                          // Arbitrary linking to arrayLeft for PAM purposes
     105         284 :                          GDALPamMultiDim::GetPAM(arrayLeft),
     106             :                          arrayLeft->GetContext()),
     107             :           m_arrayLeft(arrayLeft), m_arrayRight(arrayRight), m_op(op),
     108             :           m_dt(GDALExtendedDataType::Create(GDT_Float64)),
     109             :           m_dfNoDataLeft(
     110         142 :               m_arrayLeft->GetNoDataValueAsDouble(&m_bHasNoDataLeft)),
     111             :           m_dfNoDataRight(
     112         852 :               m_arrayRight->GetNoDataValueAsDouble(&m_bHasNoDataRight))
     113             :     {
     114         142 :         if (m_bHasNoDataLeft || m_bHasNoDataRight)
     115             :         {
     116          50 :             if (m_bHasNoDataLeft && m_bHasNoDataRight)
     117             :             {
     118          37 :                 if (m_dfNoDataLeft == m_dfNoDataRight)
     119          32 :                     m_dfNoData = m_dfNoDataLeft;
     120             :             }
     121          13 :             else if (m_bHasNoDataLeft)
     122           7 :                 m_dfNoData = m_dfNoDataLeft;
     123             :             else
     124           6 :                 m_dfNoData = m_dfNoDataRight;
     125          50 :             m_abyRawNoDataValue.resize(sizeof(double));
     126          50 :             memcpy(m_abyRawNoDataValue.data(), &m_dfNoData, sizeof(m_dfNoData));
     127             :         }
     128             : 
     129         142 :         const auto &leftUnit = m_arrayLeft->GetUnit();
     130         142 :         const auto &rightUnit = m_arrayRight->GetUnit();
     131         142 :         switch (m_op)
     132             :         {
     133          88 :             case Operation::OP_ADD:
     134             :             case Operation::OP_SUBTRACT:
     135             :             {
     136          88 :                 if (leftUnit == rightUnit)
     137          84 :                     m_osUnit = leftUnit;
     138          88 :                 break;
     139             :             }
     140          54 :             case Operation::OP_MULTIPLY:
     141             :             case Operation::OP_DIVIDE:
     142             :             {
     143          54 :                 if (!leftUnit.empty() && !rightUnit.empty())
     144             :                 {
     145           6 :                     m_osUnit = leftUnit;
     146           6 :                     m_osUnit += ' ';
     147           6 :                     m_osUnit += OperationToString(m_op);
     148           6 :                     m_osUnit += ' ';
     149           6 :                     m_osUnit += rightUnit;
     150             :                 }
     151          54 :                 break;
     152             :             }
     153             :         }
     154         142 :     }
     155             : 
     156           1 :     bool IsWritable() const override
     157             :     {
     158           1 :         return false;
     159             :     }
     160             : 
     161         123 :     const std::string &GetFilename() const override
     162             :     {
     163         123 :         const auto &filename1 = m_arrayLeft->GetFilename();
     164         123 :         const auto &filename2 = m_arrayRight->GetFilename();
     165         123 :         if (filename1 == filename2)
     166          82 :             return filename1;
     167          41 :         static std::string emptyString;
     168          41 :         return emptyString;
     169             :     }
     170             : 
     171             :     const std::vector<std::shared_ptr<GDALDimension>> &
     172         862 :     GetDimensions() const override
     173             :     {
     174         862 :         return m_arrayLeft->GetDimensions();
     175             :     }
     176             : 
     177             :     std::vector<std::shared_ptr<GDALMDArray>>
     178          17 :     GetCoordinateVariables() const override
     179             :     {
     180          34 :         auto left = m_arrayLeft->GetCoordinateVariables();
     181          34 :         auto right = m_arrayRight->GetCoordinateVariables();
     182          17 :         if (left == right)
     183          17 :             return left;
     184           0 :         return GDALMDArray::GetCoordinateVariables();
     185             :     }
     186             : 
     187         410 :     const GDALExtendedDataType &GetDataType() const override
     188             :     {
     189         410 :         return m_dt;
     190             :     }
     191             : 
     192          71 :     const void *GetRawNoDataValue() const override
     193             :     {
     194          71 :         return m_abyRawNoDataValue.empty() ? nullptr
     195          71 :                                            : m_abyRawNoDataValue.data();
     196             :     }
     197             : 
     198          40 :     const std::string &GetUnit() const override
     199             :     {
     200          40 :         return m_osUnit;
     201             :     }
     202             : 
     203          20 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     204             :     {
     205          40 :         auto leftSRS = m_arrayLeft->GetSpatialRef();
     206          40 :         auto rightSRS = m_arrayRight->GetSpatialRef();
     207          20 :         if (!leftSRS || !rightSRS || !leftSRS->IsSame(rightSRS.get()))
     208          19 :             return nullptr;
     209           1 :         return leftSRS;
     210             :     }
     211             : 
     212          20 :     double GetOffset(bool *pbHasOffset,
     213             :                      GDALDataType *peStorageType) const override
     214             :     {
     215          20 :         bool bHasLeftOffset = false;
     216          20 :         GDALDataType leftStorageType = GDT_Unknown;
     217             :         const double dfLeftOffset =
     218          20 :             m_arrayLeft->GetOffset(&bHasLeftOffset, &leftStorageType);
     219             : 
     220          20 :         bool bHasRightOffset = false;
     221          20 :         GDALDataType rightStorageType = GDT_Unknown;
     222             :         const double dfRightOffset =
     223          20 :             m_arrayRight->GetOffset(&bHasRightOffset, &rightStorageType);
     224             : 
     225          20 :         bool bHasLeftScale = false;
     226          20 :         const double dfLeftScale = m_arrayLeft->GetScale(&bHasLeftScale);
     227             : 
     228          20 :         bool bHasRightScale = false;
     229          20 :         const double dfRightScale = m_arrayRight->GetScale(&bHasRightScale);
     230             : 
     231          20 :         bool bRet = false;
     232          20 :         double dfRes = 0.0;
     233             : 
     234          20 :         switch (m_op)
     235             :         {
     236           7 :             case Operation::OP_ADD:
     237             :             {
     238          10 :                 bRet = bHasLeftOffset && bHasRightOffset &&
     239           3 :                        (bHasLeftScale == bHasRightScale &&
     240           3 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     241           7 :                 if (bRet)
     242           2 :                     dfRes = dfLeftOffset + dfRightOffset;
     243           7 :                 break;
     244             :             }
     245             : 
     246           7 :             case Operation::OP_SUBTRACT:
     247             :             {
     248          10 :                 bRet = bHasLeftOffset && bHasRightOffset &&
     249           3 :                        (bHasLeftScale == bHasRightScale &&
     250           3 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     251           7 :                 if (bRet)
     252           2 :                     dfRes = dfLeftOffset - dfRightOffset;
     253           7 :                 break;
     254             :             }
     255             : 
     256           6 :             case Operation::OP_MULTIPLY:
     257             :             case Operation::OP_DIVIDE:
     258           6 :                 break;
     259             :         }
     260             : 
     261          20 :         if (pbHasOffset)
     262          20 :             *pbHasOffset = bRet;
     263          20 :         if (bRet && peStorageType)
     264             :         {
     265           0 :             *peStorageType =
     266           0 :                 GDALDataTypeUnion(leftStorageType, rightStorageType);
     267             :         }
     268             : 
     269          20 :         return dfRes;
     270             :     }
     271             : 
     272           8 :     double GetScale(bool *pbHasScale,
     273             :                     GDALDataType *peStorageType) const override
     274             :     {
     275           8 :         bool bHasLeftOffset = false;
     276           8 :         const double dfLeftOffset = m_arrayLeft->GetOffset(&bHasLeftOffset);
     277             : 
     278           8 :         bool bHasRightOffset = false;
     279           8 :         const double dfRightOffset = m_arrayRight->GetOffset(&bHasRightOffset);
     280             : 
     281           8 :         bool bHasLeftScale = false;
     282           8 :         GDALDataType leftStorageType = GDT_Unknown;
     283             :         const double dfLeftScale =
     284           8 :             m_arrayLeft->GetScale(&bHasLeftScale, &leftStorageType);
     285             : 
     286           8 :         bool bHasRightScale = false;
     287           8 :         GDALDataType rightStorageType = GDT_Unknown;
     288             :         const double dfRightScale =
     289           8 :             m_arrayRight->GetScale(&bHasRightScale, &rightStorageType);
     290             : 
     291           8 :         bool bRet = false;
     292           8 :         double dfRes = 1.0;
     293             : 
     294           8 :         const bool bZeroOffset =
     295          16 :             (bHasLeftOffset == bHasRightOffset &&
     296           8 :              (!bHasLeftOffset ||
     297           4 :               (dfLeftOffset == dfRightOffset && dfLeftOffset == 0)));
     298             : 
     299           8 :         switch (m_op)
     300             :         {
     301           4 :             case Operation::OP_ADD:
     302             :             case Operation::OP_SUBTRACT:
     303             :             {
     304           6 :                 bRet = bZeroOffset &&
     305           2 :                        (bHasLeftScale == bHasRightScale &&
     306           2 :                         (!bHasLeftScale || dfLeftScale == dfRightScale));
     307           4 :                 if (bRet)
     308           2 :                     dfRes = dfLeftScale;
     309           4 :                 break;
     310             :             }
     311             : 
     312           2 :             case Operation::OP_MULTIPLY:
     313             :             {
     314           2 :                 bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
     315           2 :                 if (bRet)
     316           1 :                     dfRes = dfLeftScale * dfRightScale;
     317           2 :                 break;
     318             :             }
     319             : 
     320           2 :             case Operation::OP_DIVIDE:
     321             :             {
     322           2 :                 bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
     323           2 :                 if (bRet)
     324           1 :                     dfRes = dfLeftScale / dfRightScale;
     325           2 :                 break;
     326             :             }
     327             :         }
     328             : 
     329           8 :         if (pbHasScale)
     330           8 :             *pbHasScale = bRet;
     331           8 :         if (bRet && peStorageType)
     332             :         {
     333           0 :             *peStorageType =
     334           0 :                 GDALDataTypeUnion(leftStorageType, rightStorageType);
     335             :         }
     336             : 
     337           8 :         return dfRes;
     338             :     }
     339             : 
     340          32 :     std::vector<GUInt64> GetBlockSize() const override
     341             :     {
     342          64 :         const auto leftBlockSize = m_arrayLeft->GetBlockSize();
     343          64 :         const auto rightBlockSize = m_arrayRight->GetBlockSize();
     344          32 :         if (leftBlockSize != rightBlockSize)
     345           2 :             return GDALMDArray::GetBlockSize();
     346          30 :         return leftBlockSize;
     347             :     }
     348             : 
     349             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     350             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     351             :                const GDALExtendedDataType &bufferDataType,
     352             :                void *pDstBuffer) const override;
     353             : 
     354             :   private:
     355             :     const std::shared_ptr<GDALMDArray> m_arrayLeft;
     356             :     const std::shared_ptr<GDALMDArray> m_arrayRight;
     357             :     const Operation m_op;
     358             :     const GDALExtendedDataType m_dt;
     359             :     bool m_bHasNoDataLeft = false;
     360             :     bool m_bHasNoDataRight = false;
     361             :     const double m_dfNoDataLeft;
     362             :     const double m_dfNoDataRight;
     363             :     double m_dfNoData = std::numeric_limits<double>::quiet_NaN();
     364             :     std::vector<GByte> m_abyRawNoDataValue{};
     365             :     std::string m_osUnit{};
     366             :     mutable std::vector<double> m_leftValues{};
     367             :     mutable std::vector<double> m_rightValues{};
     368             : 
     369         896 :     inline bool IsInvalid(double dfLeft) const
     370             :     {
     371        1792 :         return std::isnan(dfLeft) ||
     372        1792 :                (m_bHasNoDataLeft && m_dfNoDataLeft == dfLeft);
     373             :     }
     374             : 
     375         899 :     inline bool IsInvalidTuple(double dfLeft, double dfRight) const
     376             :     {
     377         899 :         return std::isnan(dfLeft) ||
     378        1785 :                (m_bHasNoDataLeft && m_dfNoDataLeft == dfLeft) ||
     379        2684 :                std::isnan(dfRight) ||
     380        1785 :                (m_bHasNoDataRight && m_dfNoDataRight == dfRight);
     381             :     }
     382             : 
     383             :     template <class Op>
     384             :     void PerformOperation(double *leftValues, const double *rightValues,
     385             :                           size_t nElts, bool bIntegerAndAllValid) const;
     386             : };
     387             : 
     388             : /************************************************************************/
     389             : /*                  GDALMathOperationMDArray::Create()                  */
     390             : /************************************************************************/
     391             : 
     392             : /* static */ std::shared_ptr<GDALMathOperationMDArray>
     393         148 : GDALMathOperationMDArray::Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
     394             :                                  const std::shared_ptr<GDALMDArray> &arrayRight,
     395             :                                  Operation op)
     396             : {
     397         148 :     if (!arrayLeft)
     398             :     {
     399           0 :         CPLError(CE_Failure, CPLE_AppDefined, "arrayLeft is null");
     400           0 :         return nullptr;
     401             :     }
     402             : 
     403         148 :     if (!arrayRight)
     404             :     {
     405           0 :         CPLError(CE_Failure, CPLE_AppDefined, "arrayRight is null");
     406           0 :         return nullptr;
     407             :     }
     408             : 
     409         148 :     if (!arrayLeft->HasSameShapeAs(*arrayRight))
     410             :     {
     411           4 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     412           8 :                  ("Arrays " + arrayLeft->GetFullName() + " and " +
     413          12 :                   arrayRight->GetFullName() + " do not have the same shape")
     414             :                      .c_str());
     415           4 :         return nullptr;
     416             :     }
     417             : 
     418         717 :     for (const auto &array : {arrayLeft, arrayRight})
     419             :     {
     420         287 :         if (array->GetDataType().GetClass() != GEDTC_NUMERIC)
     421             :         {
     422           2 :             CPLError(
     423             :                 CE_Failure, CPLE_AppDefined, "%s",
     424           4 :                 ("Array " + array->GetFullName() + " is not numeric").c_str());
     425           2 :             return nullptr;
     426             :         }
     427             :     }
     428             : 
     429             :     auto newAr(std::shared_ptr<GDALMathOperationMDArray>(
     430         284 :         new GDALMathOperationMDArray(arrayLeft, arrayRight, op)));
     431         142 :     newAr->SetSelf(newAr);
     432         142 :     return newAr;
     433             : }
     434             : 
     435             : /************************************************************************/
     436             : /*             GDALMathOperationMDArray::PerformOperation()             */
     437             : /************************************************************************/
     438             : 
     439             : template <class Op>
     440             : #if defined(__GNUC__) && !defined(__clang__)
     441             : __attribute__((optimize("tree-vectorize")))
     442             : #endif
     443         107 : void GDALMathOperationMDArray::PerformOperation(double *leftValues,
     444             :                                                 const double *rightValues,
     445             :                                                 size_t nElts,
     446             :                                                 bool bIntegerAndAllValid) const
     447             : {
     448         107 :     if (bIntegerAndAllValid)
     449             :     {
     450          28 :         if (!rightValues)
     451             :         {
     452          21 :             for (size_t i = 0; i < nElts; ++i)
     453             :             {
     454          18 :                 leftValues[i] = Op()(leftValues[i], leftValues[i]);
     455             :             }
     456             :         }
     457             :         else
     458             :         {
     459        4467 :             for (size_t i = 0; i < nElts; ++i)
     460             :             {
     461        4442 :                 leftValues[i] = Op()(leftValues[i], rightValues[i]);
     462             :             }
     463             :         }
     464             :     }
     465             :     else
     466             :     {
     467          79 :         if (!rightValues)
     468             :         {
     469          46 :             if ((!m_bHasNoDataLeft || std::isnan(m_dfNoDataLeft)) &&
     470          14 :                 std::isnan(m_dfNoData))
     471             :             {
     472         998 :                 for (size_t i = 0; i < nElts; ++i)
     473             :                 {
     474         984 :                     leftValues[i] = Op()(leftValues[i], leftValues[i]);
     475             :                 }
     476             :             }
     477             :             else
     478             :             {
     479         914 :                 for (size_t i = 0; i < nElts; ++i)
     480             :                 {
     481         896 :                     if (IsInvalid(leftValues[i]))
     482           8 :                         leftValues[i] = m_dfNoData;
     483             :                     else
     484         888 :                         leftValues[i] = Op()(leftValues[i], leftValues[i]);
     485             :                 }
     486             :             }
     487             :         }
     488             :         else
     489             :         {
     490          23 :             if ((!m_bHasNoDataLeft || std::isnan(m_dfNoDataLeft)) &&
     491          90 :                 (!m_bHasNoDataRight || std::isnan(m_dfNoDataRight)) &&
     492          20 :                 std::isnan(m_dfNoData))
     493             :             {
     494         364 :                 for (size_t i = 0; i < nElts; ++i)
     495             :                 {
     496         344 :                     leftValues[i] = Op()(leftValues[i], rightValues[i]);
     497             :                 }
     498             :             }
     499             :             else
     500             :             {
     501         926 :                 for (size_t i = 0; i < nElts; ++i)
     502             :                 {
     503         899 :                     if (IsInvalidTuple(leftValues[i], rightValues[i]))
     504          26 :                         leftValues[i] = m_dfNoData;
     505             :                     else
     506         873 :                         leftValues[i] = Op()(leftValues[i], rightValues[i]);
     507             :                 }
     508             :             }
     509             :         }
     510             :     }
     511         107 : }
     512             : 
     513             : /************************************************************************/
     514             : /*                  GDALMathOperationMDArray::IRead()                   */
     515             : /************************************************************************/
     516             : 
     517         110 : bool GDALMathOperationMDArray::IRead(const GUInt64 *arrayStartIdx,
     518             :                                      const size_t *count,
     519             :                                      const GInt64 *arrayStep,
     520             :                                      const GPtrDiff_t *bufferStride,
     521             :                                      const GDALExtendedDataType &bufferDataType,
     522             :                                      void *pDstBuffer) const
     523             : {
     524         110 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
     525             :     {
     526           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     527             :                  "GDALMathOperationMDArray::IRead(): not supported with "
     528             :                  "non-numeric buffer data type");
     529           0 :         return false;
     530             :     }
     531             : 
     532         110 :     const auto &apoDims = GetDimensions();
     533         110 :     const size_t nDims = apoDims.size();
     534             : 
     535             :     const bool bIntegerAndAllValid =
     536         110 :         m_abyRawNoDataValue.empty() &&
     537          65 :         GDALDataTypeIsInteger(
     538         175 :             m_arrayLeft->GetDataType().GetNumericDataType()) &&
     539          31 :         GDALDataTypeIsInteger(m_arrayRight->GetDataType().GetNumericDataType());
     540             : 
     541         127 :     if (bIntegerAndAllValid && m_op == Operation::OP_SUBTRACT &&
     542          17 :         m_arrayLeft == m_arrayRight)
     543             :     {
     544           3 :         CopyContiguousBufferToBuffer(nDims, count, nullptr,
     545           6 :                                      GDALExtendedDataType::Create(GDT_Unknown),
     546             :                                      pDstBuffer, bufferDataType, bufferStride);
     547           3 :         return true;
     548             :     }
     549             : 
     550             :     // Check if the output buffer is in contiguous row major order
     551             :     // If this is true and its type is also the type of the array, we can
     552             :     // directly read the left array into the output buffer and update it in
     553             :     // place.
     554         107 :     bool bRowMajorBufferStride = true;
     555         107 :     GPtrDiff_t nExpectedStrideValue = 1;
     556         268 :     for (size_t i = nDims; i > 0; /* decremented in loop */)
     557             :     {
     558         161 :         --i;
     559         161 :         if (bufferStride[i] != nExpectedStrideValue)
     560             :         {
     561           0 :             bRowMajorBufferStride = false;
     562           0 :             break;
     563             :         }
     564         161 :         nExpectedStrideValue = static_cast<GPtrDiff_t>(nExpectedStrideValue *
     565         161 :                                                        apoDims[i]->GetSize());
     566             :     }
     567             : 
     568         107 :     size_t nElts = 1;
     569         107 :     bool bFullArrayRequested = true;
     570         268 :     for (size_t i = 0; i < nDims; ++i)
     571             :     {
     572         161 :         nElts *= count[i];
     573         161 :         if (bFullArrayRequested)
     574         161 :             bFullArrayRequested = (count[i] == apoDims[i]->GetSize());
     575             :     }
     576             : 
     577             :     const bool bLeftValuesAllocNeeded =
     578         107 :         !bRowMajorBufferStride || bufferDataType != m_dt;
     579             : 
     580             :     // We can skip both I/O and memory allocations if operating on the same
     581             :     // array.
     582         107 :     const bool bRightValuesAllocNeeded = m_arrayLeft != m_arrayRight;
     583             : 
     584             :     try
     585             :     {
     586         107 :         if (bLeftValuesAllocNeeded)
     587             :         {
     588           2 :             if (nElts > m_leftValues.size())
     589           2 :                 m_leftValues.resize(nElts);
     590             :         }
     591         107 :         if (bRightValuesAllocNeeded)
     592             :         {
     593          72 :             if (nElts > m_rightValues.size())
     594          72 :                 m_rightValues.resize(nElts);
     595             :         }
     596             :     }
     597           0 :     catch (const std::exception &)
     598             :     {
     599           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     600             :                  "GDALMathOperationMDArray::IRead(): out of memory");
     601           0 :         return false;
     602             :     }
     603             : 
     604             :     double *leftValues = bLeftValuesAllocNeeded
     605         107 :                              ? m_leftValues.data()
     606         107 :                              : static_cast<double *>(pDstBuffer);
     607             : 
     608         107 :     const bool ret = m_arrayLeft->Read(arrayStartIdx, count, arrayStep, nullptr,
     609         214 :                                        m_dt, leftValues) &&
     610         107 :                      (!bRightValuesAllocNeeded ||
     611         144 :                       m_arrayRight->Read(arrayStartIdx, count, arrayStep,
     612          72 :                                          nullptr, m_dt, m_rightValues.data()));
     613             : 
     614             :     const double *rightValues =
     615         107 :         bRightValuesAllocNeeded ? m_rightValues.data() : nullptr;
     616             : 
     617         107 :     if (ret)
     618             :     {
     619         107 :         switch (m_op)
     620             :         {
     621          19 :             case Operation::OP_ADD:
     622             :             {
     623          19 :                 PerformOperation<std::plus<double>>(leftValues, rightValues,
     624             :                                                     nElts, bIntegerAndAllValid);
     625          19 :                 break;
     626             :             }
     627          44 :             case Operation::OP_SUBTRACT:
     628             :             {
     629          44 :                 PerformOperation<std::minus<double>>(
     630             :                     leftValues, rightValues, nElts, bIntegerAndAllValid);
     631          44 :                 break;
     632             :             }
     633          28 :             case Operation::OP_MULTIPLY:
     634             :             {
     635          28 :                 PerformOperation<std::multiplies<double>>(
     636             :                     leftValues, rightValues, nElts, bIntegerAndAllValid);
     637          28 :                 break;
     638             :             }
     639          16 :             case Operation::OP_DIVIDE:
     640             :             {
     641          16 :                 PerformOperation<std::divides<double>>(
     642             :                     leftValues, rightValues, nElts, bIntegerAndAllValid);
     643          16 :                 break;
     644             :             }
     645             :         }
     646             : 
     647         107 :         if (bLeftValuesAllocNeeded)
     648             :         {
     649           2 :             CopyContiguousBufferToBuffer(nDims, count, leftValues, m_dt,
     650             :                                          pDstBuffer, bufferDataType,
     651             :                                          bufferStride);
     652             :         }
     653             :     }
     654             : 
     655         107 :     if (bFullArrayRequested)
     656             :     {
     657         107 :         m_leftValues.clear();
     658         107 :         m_rightValues.clear();
     659             :     }
     660             : 
     661         107 :     return ret;
     662             : }
     663             : 
     664             : //! @endcond
     665             : 
     666             : /************************************************************************/
     667             : /*                             operator+()                              */
     668             : /************************************************************************/
     669             : 
     670             : /** Add this array with another one of the same shape.
     671             :  *
     672             :  * The resulting array is lazy evaluated.
     673             :  *
     674             :  * The resulting array type is Float64.
     675             :  *
     676             :  * The operation is nodata-aware.
     677             :  *
     678             :  * This is the same as C function GDALMDArrayBinaryOperation().
     679             :  *
     680             :  * @since 3.14
     681             :  * @return a new array, or nullptr in case of error
     682             :  */
     683             : std::shared_ptr<GDALMDArray>
     684          49 : GDALMDArray::operator+(const std::shared_ptr<GDALMDArray> &other) const
     685             : {
     686          98 :     return GDALMathOperationMDArray::Create(
     687         147 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_ADD);
     688             : }
     689             : 
     690             : /************************************************************************/
     691             : /*                             operator-()                              */
     692             : /************************************************************************/
     693             : 
     694             : /** Subtract this array with another one of the same shape.
     695             :  *
     696             :  * The resulting array is lazy evaluated.
     697             :  *
     698             :  * The resulting array type is Float64.
     699             :  *
     700             :  * The operation is nodata-aware.
     701             :  *
     702             :  * This is the same as C function GDALMDArrayBinaryOperation().
     703             :  *
     704             :  * @since 3.14
     705             :  * @return a new array, or nullptr in case of error
     706             :  */
     707             : std::shared_ptr<GDALMDArray>
     708          43 : GDALMDArray::operator-(const std::shared_ptr<GDALMDArray> &other) const
     709             : {
     710          86 :     return GDALMathOperationMDArray::Create(
     711         129 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_SUBTRACT);
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                             operator*()                              */
     716             : /************************************************************************/
     717             : 
     718             : /** Multiply this array with another one of the same shape.
     719             :  *
     720             :  * The resulting array is lazy evaluated.
     721             :  *
     722             :  * The resulting array type is Float64.
     723             :  *
     724             :  * The operation is nodata-aware.
     725             :  *
     726             :  * This is the same as C function GDALMDArrayBinaryOperation().
     727             :  *
     728             :  * @since 3.14
     729             :  * @return a new array, or nullptr in case of error
     730             :  */
     731             : std::shared_ptr<GDALMDArray>
     732          31 : GDALMDArray::operator*(const std::shared_ptr<GDALMDArray> &other) const
     733             : {
     734          62 :     return GDALMathOperationMDArray::Create(
     735          93 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_MULTIPLY);
     736             : }
     737             : 
     738             : /************************************************************************/
     739             : /*                             operator/()                              */
     740             : /************************************************************************/
     741             : 
     742             : /** Divide this array by another one of the same shape.
     743             :  *
     744             :  * The resulting array is lazy evaluated.
     745             :  *
     746             :  * The resulting array type is Float64.
     747             :  *
     748             :  * The operation is nodata-aware.
     749             :  *
     750             :  * This is the same as C function GDALMDArrayBinaryOperation().
     751             :  *
     752             :  * @since 3.14
     753             :  * @return a new array, or nullptr in case of error
     754             :  */
     755             : std::shared_ptr<GDALMDArray>
     756          25 : GDALMDArray::operator/(const std::shared_ptr<GDALMDArray> &other) const
     757             : {
     758          50 :     return GDALMathOperationMDArray::Create(
     759          75 :         GetSelf(), other, GDALMathOperationMDArray::Operation::OP_DIVIDE);
     760             : }

Generated by: LCOV version 1.14