[Libreoffice-commits] core.git: sc/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Fri Aug 24 07:27:25 UTC 2018


 sc/source/core/tool/interpr1.cxx |  106 +++++++++++++++++++++++++++++++++------
 1 file changed, 91 insertions(+), 15 deletions(-)

New commits:
commit cbdcf8955b6bd1def3e6e8f728bd93746b8df203
Author:     Eike Rathke <erack at redhat.com>
AuthorDate: Thu Aug 23 23:20:04 2018 +0200
Commit:     Eike Rathke <erack at redhat.com>
CommitDate: Fri Aug 24 09:27:00 2018 +0200

    Resolves: tdf#117016 omit error values from an interim array in LOOKUP()
    
    for all these LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A) and variations
    that obtain the last non-empty cell in a range.
    
    Change-Id: Icfb8477f26fd2fb8cbeeb2fd362f0592811b5019
    Reviewed-on: https://gerrit.libreoffice.org/59529
    Reviewed-by: Eike Rathke <erack at redhat.com>
    Tested-by: Jenkins

diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 75a754180dcd..bfc26230ec35 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -6746,27 +6746,100 @@ void ScInterpreter::ScLookup()
         // Do not propagate errors from matrix while copying to vector.
         pDataMat->SetErrorInterpreter( nullptr);
 
+        // Excel has an undocumented behaviour in that it seems to internally
+        // sort an interim array (i.e. error values specifically #DIV/0! are
+        // sorted to the end) or ignore error values that makes these "get last
+        // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
+        // see tdf#117016
+        // Instead of sorting a million entries of which mostly only a bunch of
+        // rows are filled and moving error values to the end which most are
+        // already anyway, assume the matrix to be sorted except error values
+        // and omit the coded DoubleError values.
+        // Do this only for a numeric matrix (that includes errors coded as
+        // doubles), which covers the case in question.
+        /* TODO: it's unclear whether this really matches Excel behaviour in
+         * all constellations or if there are cases that include unsorted error
+         * values and thus yield arbitrary binary search results or something
+         * different or whether there are cases where error values are also
+         * omitted from mixed numeric/string arrays or if it's not an interim
+         * matrix but a cell range reference instead. */
+        const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
+
         // In case of non-vector matrix, only search the first row or column.
         ScMatrixRef pDataMat2;
-        if (bVertical)
+        std::vector<long> vIndex;
+        if (bOmitErrorValues)
         {
-            ScMatrixRef pTempMat = GetNewMat(1, nR);
-            for (SCSIZE i = 0; i < nR; ++i)
-                if (pDataMat->IsValue(0, i))
-                    pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i);
-                else
-                    pTempMat->PutString(pDataMat->GetString(0, i), 0, i);
-            pDataMat2 = pTempMat;
+            std::vector<double> vArray;
+            if (bVertical)
+            {
+                for (SCSIZE i = 0; i < nR; ++i)
+                {
+                    const double fVal = pDataMat->GetDouble(0, i);
+                    if (rtl::math::isFinite(fVal))
+                    {
+                        vArray.push_back(fVal);
+                        vIndex.push_back(i);
+                    }
+                }
+                if (vArray.empty())
+                {
+                    PushNA();
+                    return;
+                }
+                ScMatrixRef pTempMat = GetNewMat( 1, vArray.size());
+                pTempMat->PutDoubleVector( vArray, 0, 0);
+                pDataMat2 = pTempMat;
+            }
+            else
+            {
+                for (SCSIZE i = 0; i < nC; ++i)
+                {
+                    const double fVal = pDataMat->GetDouble(i, 0);
+                    if (rtl::math::isFinite(fVal))
+                    {
+                        vArray.push_back(fVal);
+                        vIndex.push_back(i);
+                    }
+                }
+                if (vArray.empty())
+                {
+                    PushNA();
+                    return;
+                }
+                ScMatrixRef pTempMat = GetNewMat( vArray.size(), 1);
+                const size_t n = vArray.size();
+                for (size_t i=0; i < n; ++i)
+                    pTempMat->PutDouble( vArray[i], i, 0);
+                pDataMat2 = pTempMat;
+            }
+            if (vArray.size() == nLenMajor)
+                std::vector<long>().swap( vIndex);  // no error value omitted
+            else
+                nLenMajor = vArray.size();
         }
         else
         {
-            ScMatrixRef pTempMat = GetNewMat(nC, 1);
-            for (SCSIZE i = 0; i < nC; ++i)
-                if (pDataMat->IsValue(i, 0))
-                    pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0);
-                else
-                    pTempMat->PutString(pDataMat->GetString(i, 0), i, 0);
-            pDataMat2 = pTempMat;
+            if (bVertical)
+            {
+                ScMatrixRef pTempMat = GetNewMat(1, nR);
+                for (SCSIZE i = 0; i < nR; ++i)
+                    if (pDataMat->IsValue(0, i))
+                        pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i);
+                    else
+                        pTempMat->PutString(pDataMat->GetString(0, i), 0, i);
+                pDataMat2 = pTempMat;
+            }
+            else
+            {
+                ScMatrixRef pTempMat = GetNewMat(nC, 1);
+                for (SCSIZE i = 0; i < nC; ++i)
+                    if (pDataMat->IsValue(i, 0))
+                        pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0);
+                    else
+                        pTempMat->PutString(pDataMat->GetString(i, 0), i, 0);
+                pDataMat2 = pTempMat;
+            }
         }
 
         // Do not propagate errors from matrix while searching.
@@ -6830,6 +6903,9 @@ void ScInterpreter::ScLookup()
 
         if (bFound)
         {
+            if (!vIndex.empty())
+                nDelta = vIndex[nDelta];
+
             VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
             SCCOLROW i = nDelta;
             SCSIZE n = aMatAcc.GetElementCount();


More information about the Libreoffice-commits mailing list