This is an automated email from the ASF dual-hosted git repository.

jimjag pushed a commit to branch openformula
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 44bd4476f60fd9f48305bf4517478e4a49b01a83
Author: Jim Jagielski <[email protected]>
AuthorDate: Fri Nov 13 09:40:30 2026 -0500

    sc: replace #N/A element-by-element when IFNA gets an array
    
    The fallback is only evaluated if the array holds a #N/A.
---
 main/sc/source/core/inc/jumpmatrix.hxx |  5 +++
 main/sc/source/core/tool/interpr1.cxx  | 67 +++++++++++++++++++++++++++++++++-
 test/odf-validate/calcval-cases14.tsv  | 11 ++++++
 test/odf-validate/calcval.py           |  3 +-
 4 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/main/sc/source/core/inc/jumpmatrix.hxx 
b/main/sc/source/core/inc/jumpmatrix.hxx
index 9af8cc4676..11a852697c 100644
--- a/main/sc/source/core/inc/jumpmatrix.hxx
+++ b/main/sc/source/core/inc/jumpmatrix.hxx
@@ -69,6 +69,7 @@ class ScJumpMatrix
             SCSIZE              nResMatCols;
             SCSIZE              nResMatRows;
             bool                bStarted;
+            bool                bKeepNoPath;
 
                                 // not implemented, prevent usage
                                 ScJumpMatrix( const ScJumpMatrix& );
@@ -86,6 +87,7 @@ public:
                                         , nResMatCols( nColsP )
                                         , nResMatRows( nRowsP )
                                         , bStarted( false )
+                                        , bKeepNoPath( false )
                                     {
                                         // Initialize result matrix in case of
                                         // a premature end of the interpreter
@@ -154,6 +156,9 @@ public:
                                                     nNext, nStop);
                                         }
                                     }
+            /// Elements without a jump path keep the value already in the 
result matrix.
+            void                SetKeepNoPath() { bKeepNoPath = true; }
+            bool                IsKeepNoPath() const { return bKeepNoPath; }
             void                SetJumpParameters( ScTokenVec* p )
                                     { pParams = p; }
             const ScTokenVec*   GetJumpParameters() const { return pParams; }
diff --git a/main/sc/source/core/tool/interpr1.cxx 
b/main/sc/source/core/tool/interpr1.cxx
index 36ed4d9f1d..78a4f6216a 100644
--- a/main/sc/source/core/tool/interpr1.cxx
+++ b/main/sc/source/core/tool/interpr1.cxx
@@ -349,9 +349,70 @@ void ScInterpreter::ScIfna()
         }
         break;
         case svRefList :
-        case svMatrix :
             // No per-element #N/A substitution, the value passes through.
         break;
+        case svMatrix :
+        {
+            ScMatrixRef pMat = PopMatrix();
+            SCSIZE nCols = 0, nRows = 0;
+            if ( pMat )
+                pMat->GetDimensions( nCols, nRows );
+            if ( !nCols || !nRows )
+            {
+                PushIllegalParameter();
+                aCode.Jump( pJump[ 2 ], pJump[ 2 ] );
+                return;
+            }
+            FormulaTokenRef xNew;
+            ScTokenMatrixMap::const_iterator aMapIter;
+            if ( pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur 
)) != pTokenMatrixMap->end()) )
+                xNew = (*aMapIter).second;
+            else
+            {
+                bool bAnyNA = false;
+                for ( SCSIZE nC = 0; nC < nCols && !bAnyNA; ++nC )
+                    for ( SCSIZE nR = 0; nR < nRows && !bAnyNA; ++nR )
+                        bAnyNA = pMat->IsValue( nC, nR ) && pMat->GetError( 
nC, nR ) == NOTAVAILABLE;
+                if ( !bAnyNA )
+                {
+                    // nothing to substitute, the fallback is not evaluated
+                    PushMatrix( pMat );
+                    aCode.Jump( pJump[ 2 ], pJump[ 2 ] );
+                    return;
+                }
+                // DoubleError handled by JumpMatrix
+                pMat->SetErrorInterpreter( NULL );
+                ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
+                pJumpMat->SetKeepNoPath();
+                ScMatrix* pRes = pJumpMat->GetResultMatrix();
+                for ( SCSIZE nC = 0; nC < nCols; ++nC )
+                {
+                    for ( SCSIZE nR = 0; nR < nRows; ++nR )
+                    {
+                        if ( pMat->IsValue( nC, nR ) && pMat->GetError( nC, nR 
) == NOTAVAILABLE )
+                            pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ 
2 ] );
+                        else
+                        {
+                            ScMatValType nType = 0;
+                            const ScMatrixValue* pMatVal = pMat->Get( nC, nR, 
nType );
+                            if ( ScMatrix::IsValueType( nType ) )
+                                pRes->PutDouble( pMatVal->fVal, nC, nR );
+                            else if ( ScMatrix::IsRealStringType( nType ) )
+                                pRes->PutString( pMat->GetString( nC, nR ), 
nC, nR );
+                            else
+                                pRes->PutEmpty( nC, nR );
+                            pJumpMat->SetJump( nC, nR, 1.0, pJump[ 2 ], pJump[ 
2 ] );
+                        }
+                    }
+                }
+                xNew = new ScJumpMatrixToken( pJumpMat );
+                GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( 
pCur, xNew ) );
+            }
+            PushTempToken( xNew );
+            // set endpoint of path for main code line
+            aCode.Jump( pJump[ 2 ], pJump[ 2 ] );
+            return;
+        }
         default:
             if ( nGlobalError == NOTAVAILABLE || (GetStackType() == svError &&
                         pStack[ sp - 1 ]->GetError() == NOTAVAILABLE) )
@@ -636,7 +697,9 @@ bool ScInterpreter::JumpMatrix( short nStackLevel )
             if ( pResMat )
             {
                 // a sal_False without path results in an empty path value
-                if ( fBool == 0.0 )
+                if ( pJumpMatrix->IsKeepNoPath() )
+                    ;   // result already in place
+                else if ( fBool == 0.0 )
                     pResMat->PutEmptyPath( nC, nR );
                 else
                     pResMat->PutDouble( fBool, nC, nR );
diff --git a/test/odf-validate/calcval-cases14.tsv 
b/test/odf-validate/calcval-cases14.tsv
index 2683300e46..a40f566121 100644
--- a/test/odf-validate/calcval-cases14.tsv
+++ b/test/odf-validate/calcval-cases14.tsv
@@ -142,6 +142,17 @@ IFNA.range =IFNA(D1:D3;99) cell    10      6.x range 
reference, implicit intersection ta
 IFNA.ref.na    =IFNA(AM1;99)   cell    99      6.x cell reference containing 
#N/A (AM1 = NA()), fallback returned
 IFNA.ref.err   =IFNA(AM2;99)   cell    #ERR    6.x cell reference containing 
another error (AM2 = 1/0), error propagated
 IFNA.ref.empty =IFNA(AM4;99)   cell    0       6.x reference to an empty cell 
(AM4 unset) is not #N/A; the empty cell shows as 0
+IFNA.arr.sub   =IFNA(AN1:AN6;99)       array:1x6       5|99|s:x|99|7|#ERR      
6.x array argument: each #N/A element is replaced (AN1:AN6 = 5, #N/A, x, #N/A, 
7, 1/0), other errors stay
+IFNA.arr.fallback      =IFNA(AN1:AN4;D1:D4)    array:1x4       5|20|s:x|40     
6.x array fallback is taken element by element (D1:D4 = 10, 20, 30, 40)
+IFNA.arr.text  =IFNA(AN1:AN4;"n")      array:1x4       5|s:n|s:x|s:n   6.x 
text fallback
+IFNA.arr.none  =IFNA(D1:D3;99) array:1x3       10|20|30        6.x array 
without #N/A is returned unchanged
+IFNA.arr.lazy  =IFNA(D1:D3;1/0)        array:1x3       10|20|30        6.x 
fallback not evaluated when no element is #N/A
+IFNA.arr.fallerr       =IFNA(AN1:AN2;1/0)      array:1x2       5|#ERR  6.x 
fallback evaluated only for the #N/A element
+IFNA.arr.inline        =IFNA({1;2|3;4};9)      array:2x2       1|2|3|4 6.x 
inline array without #N/A
+IFNA.arr.sum   =SUM(IFNA(AN1:AN4;0))   array   5       6.x IFNA inside SUM in 
an array formula; the text element is ignored by SUM
+IFNA.arr.nested        =IFNA(IFNA(AN1:AN4;AN1:AN4);0)  array:1x4       
5|0|s:x|0       6.x nested: the inner fallback is #N/A again and the outer IFNA 
replaces it
+IFNA.arr.inif  =IF(D1:D3>15;IFNA(AN2:AN4;1);0) array:1x3       0|s:x|1 6.x 
IFNA array in an IF path (AN2:AN4 = #N/A, x, #N/A)
+IFNA.arr.sumprod       =SUMPRODUCT(IFNA(AN1:AN2+0;100))        cell    -       
6.x record only: whether the force-array context reaches the operator inside 
IFNA
 SKEWP.two      =SKEWP(1;2)     cell    0       6.x two observations, 
non-constant, zero skewness
 SKEW.text      =SKEW(1;"text";2)       cell    -       6.3.7 a direct Text 
argument converts as in 6.3.5, implementation-defined (0 or an Error); AOO 
returns an Error
 SKEW.err       =SKEW(1/0)      cell    #ERR    6.x regression: error argument 
propagates
diff --git a/test/odf-validate/calcval.py b/test/odf-validate/calcval.py
index 319611a422..4262c9ce8c 100644
--- a/test/odf-validate/calcval.py
+++ b/test/odf-validate/calcval.py
@@ -140,8 +140,9 @@ def setup_data(doc):
     put("AI1", [[v] for v in (1, 3, 5)])
     put("AJ1", [[v] for v in (2, 4, 7, 9)])
     put("AK1", [[v] for v in (2, 4, 7, 9)])
-    for addr, f in (("AM1", "=NA()"), ("AM2", "=1/0"), ("AM3", '=""')):
+    for addr, f in (("AM1", "=NA()"), ("AM2", "=1/0"), ("AM3", '=""'), ("AN1", 
"5"), ("AN2", "=NA()"), ("AN4", "=NA()"), ("AN5", "7"), ("AN6", "=1/0")):
         sh.getCellRangeByName(addr).setFormula(f)
+    sh.getCellRangeByName("AN3").setString("x")
     return sh
 
 

Reply via email to